static int threshold_check_state(struct hwtest_ctx *ctx, const struct therm_threshold *thrs) { int i, dir, temp, exp_state; threshold_reset(ctx); nva_mask(ctx->cnum, 0x20010, 0x80000000, 0x80000000); i = 1; dir = 1; while (i > 0) { /* set the threshold */ threshold_set(ctx, thrs, i); /* calculate the expected state */ do { temp = nva_rd32(ctx->cnum, 0x20014) & 0x3fff; if (thrs->dir_inverted) exp_state = (temp < i); else exp_state = (temp > i); } while (temp != (nva_rd32(ctx->cnum, 0x20014) & 0x3fff)); /* check the state */ TEST_READ_MASK(thrs->dir_addr, exp_state << thrs->dir_status_bit, 1 << thrs->dir_status_bit, "invalid state at temperature %i: dir (%i), threshold (%i)", temp, dir, i); if (i == 0x3fff) dir = -1; i += dir; } return HWTEST_RES_PASS; }
static int test_threshold_crit_intr_both_disabled(struct hwtest_ctx *ctx) { nva_mask(ctx->cnum, 0x20010, 0x80000000, 0x00000000); if (threshold_check_intr_both(ctx, &nv50_therm_thresholds[therm_threshold_crit]) == HWTEST_RES_FAIL) return HWTEST_RES_PASS; else return HWTEST_RES_FAIL; }
void nv40_stop_monitoring(int cnum, int set, uint8_t *signals, uint32_t *real_signals, uint32_t *cycles) { uint32_t cycles_cnt, s[4]; uint8_t w_s[4]; int i; /* readout */ nva_mask(cnum, 0x400084, 0x0, 0x20); w_s[0] = nva_rd32(cnum, 0xa400 + set * 4); w_s[1] = nva_rd32(cnum, 0xa440 + set * 4); w_s[2] = nva_rd32(cnum, 0xa480 + set * 4); w_s[3] = nva_rd32(cnum, 0xa4c0 + set * 4); cycles_cnt = nva_rd32(cnum, 0xa600 + set * 4); s[0] = nva_rd32(cnum, 0xa700 + set * 4); s[1] = nva_rd32(cnum, 0xa6c0 + set * 4); s[2] = nva_rd32(cnum, 0xa680 + set * 4); s[3] = nva_rd32(cnum, 0xa740 + set * 4); for (i = 0; i < 4; i++) { if (signals) SIGNAL_VALUE(signals, set, w_s[i]) = s[i] * 100 / cycles_cnt; if (real_signals) SIGNAL_VALUE(real_signals, set, w_s[i]) = s[i]; if (cycles) SIGNAL_VALUE(cycles, set, w_s[i]) = cycles_cnt; } }
void find_ctxCtlFlags(int cnum) { uint8_t signals_ref_ctx[0x100 * 8]; uint8_t signals_tmp[0x100 * 8]; struct signals_comparaison diffs; int bit, i; uint32_t r_400824 = nva_rd32(cnum, 0x400824); printf("<CTXCTL FLAGS>\n"); nva_mask(cnum, 0x400824, 0xf0000000, 0); poll_signals(cnum, signals_ref_ctx); for (bit = 28; bit < 32; bit++) { nva_mask(cnum, 0x400824, 0xf0000000, 1 << bit); poll_signals(cnum, signals_tmp); diffs = signals_compare(signals_ref_ctx, signals_tmp); if (diffs.diff_count >= 1) { for (i = 0; i < diffs.diff_count; i++) { uint8_t set, signal; set = diffs.differences[i].set; signal = diffs.differences[i].signal; if (diffs.differences[i].set == 1) { if (diffs.differences[i].change == ZERO_TO_ONE) printf("CTXCTL flag 0x%.2x: Set %u, signal 0x%.2x\n", bit, set, signal); } else { printf("Unexpected difference: "); print_difference(diffs.differences[i]); } } } else printf("Not found. Please re-run when the GPU is idle.\n"); signals_comparaison_free(diffs); } nva_wr32(cnum, 0x400824, r_400824); printf("</CTXCTL FLAGS>\n\n"); }
static int threshold_gen_intr_dir(struct hwtest_ctx *ctx, struct therm_threshold *thrs, int dir) { int temp, lower_thrs, higher_thrs, tmp; temp = nva_rd32(ctx->cnum, 0x20014) & 0xffff; lower_thrs = temp - 0x100; higher_thrs = temp + 0x100; if (lower_thrs < 0) lower_thrs = 0; if (dir > 0) tmp = lower_thrs; else tmp = higher_thrs; nva_mask(ctx->cnum, thrs->thrs_addr, thrs->thrs_mask, (tmp << thrs->thrs_lshift) & thrs->thrs_mask); /* enable the IRQs */ nva_wr32(ctx->cnum, 0x1140, 0x70000); /* ACK any IRQ */ nva_wr32(ctx->cnum, 0x1100, 0xffffffff); /* ack pbus' IRQs */ /* Generate an IRQ */ if (dir > 0) tmp = higher_thrs; else tmp = lower_thrs; nva_mask(ctx->cnum, thrs->thrs_addr, thrs->thrs_mask, (tmp << thrs->thrs_lshift) & thrs->thrs_mask); /* necessary */ usleep(1); return HWTEST_RES_PASS; }
static int test_temperature_enable_state(struct hwtest_ctx *ctx) { uint32_t r010 = nva_mask(ctx->cnum, 0x20010, 0x40000000, 0x40000000); uint32_t temp_disabled, temp_enabled; temp_disabled = nva_rd32(ctx->cnum, 0x20014) & 0x3fff; nva_wr32(ctx->cnum, 0x20010, 0); usleep(20000); temp_enabled = nva_rd32(ctx->cnum, 0x20014) & 0x3fff; nva_wr32(ctx->cnum, 0x20010, r010); if (temp_disabled == 0 && temp_enabled) return HWTEST_RES_PASS; else return HWTEST_RES_FAIL; }
static int nv50_threshold_filter(struct hwtest_ctx *ctx, const struct therm_threshold *thrs) { uint8_t div, cycles; uint64_t before; uint64_t after; /* get the data */ for (div = 0; div < 4; div++) { for (cycles = 1; cycles < 3; cycles++) { threshold_reset(ctx); nva_mask(ctx->cnum, 0x20010, 0x80000000, 0x80000000); threshold_filter_set(ctx, thrs, 0, 0); threshold_set(ctx, thrs, 0x3fff); nva_wr32(ctx->cnum, 0x1100, 0x70000); threshold_set_intr_dir(ctx, thrs, 3); threshold_filter_set(ctx, thrs, div, (cycles * 0x7f)); before = get_time(ctx->cnum); threshold_set(ctx, thrs, 10); while ((nva_rd32(ctx->cnum, 0x1100) & 0x70000) == 0); after = get_time(ctx->cnum); uint64_t time = after - before; double clock_hz = (1000000.0 / (16.0 * pow(16, div))); double expected_time = (1.0 / clock_hz) * cycles * 0x7f * 1e9; double prediction_error = abs(time - expected_time) * 100.0 / expected_time; if (prediction_error > 5.0) { printf("div %x => %f, cycles 0x%x: delay %"PRIu64"; expected delay %.0f (prediction_error = %f%%), clock_hz = %f\n", div, 32 * pow(16, div), cycles * 0x7f, time, expected_time, prediction_error, clock_hz); return HWTEST_RES_FAIL; } } } return HWTEST_RES_PASS; }
void nv40_start_monitoring(int cnum, int set, int s1, int s2, int s3, int s4) { nva_wr32(cnum, 0xa7c0 + set * 4, 0x1); nva_wr32(cnum, 0xa500 + set * 4, 0); nva_wr32(cnum, 0xa520 + set * 4, 0); nva_wr32(cnum, 0xa400 + set * 4, s1); nva_wr32(cnum, 0xa440 + set * 4, s2); nva_wr32(cnum, 0xa480 + set * 4, s3); nva_wr32(cnum, 0xa4c0 + set * 4, s4); nva_wr32(cnum, 0xa420 + set * 4, 0xaaaa); nva_wr32(cnum, 0xa460 + set * 4, 0xaaaa); nva_wr32(cnum, 0xa4a0 + set * 4, 0xaaaa); nva_wr32(cnum, 0xa4e0 + set * 4, 0xaaaa); /* reset the counters */ nva_mask(cnum, 0x400084, 0x20, 0x20); }
static int test_clock_gating_thermal_protect_crit(struct hwtest_ctx *ctx) { int temp, lower_thrs; uint8_t rnd_div = 1 + (rand() % 6); uint8_t rnd_pwm = 1 + (rand() % 0xfe); clock_gating_reset(ctx); temp = nva_rd32(ctx->cnum, 0x20014) & 0x3fff; lower_thrs = temp - 0x200; /* just to be sure the threshold is under the current temp */ if (lower_thrs < 0) lower_thrs = 0; nva_mask(ctx->cnum, 0x20010, 0x80003fff, 0x80000000 | lower_thrs); nva_wr32(ctx->cnum, 0x20060, (rnd_pwm << 8) | rnd_div); /* div = rnd_div */ TEST_READ_MASK(0x20048, 0x800, 0x800, "THERMAL_PROTECT_DIV_ACTIVE is not active!%s", ""); TEST_READ_MASK(0x20048, rnd_pwm, 0xff, "PWM isn't %i", rnd_pwm); TEST_READ_MASK(0x20048, rnd_div << 12, 0x7000, "divisor isn't %i", rnd_div); return HWTEST_RES_PASS; }
static ptime_t pms_launch(int cnum, struct pms_ucode* pms, ptime_t *wall_time) { u32 pbus1098; u32 pms_data, pms_kick; ptime_t ptimer_start, ptimer_end; struct timeval wall_start, wall_end; int i; if (nva_cards[cnum].chipset < 0x90) { pms_data = 0x001400; pms_kick = 0x00000003; } else { pms_data = 0x080000; pms_kick = 0x00000001; } /* upload ucode */ pbus1098 = nva_mask(cnum, 0x001098, 0x00000008, 0x00000000); nva_wr32(cnum, 0x001304, 0x00000000); for (i = 0; i < pms->len / 4; i++) nva_wr32(cnum, pms_data + (i * 4), pms->ptr.u32[i]); nva_wr32(cnum, 0x001098, pbus1098 | 0x18); /* and run it! */ gettimeofday(&wall_start, NULL); ptimer_start = get_time(cnum); nva_wr32(cnum, 0x00130c, pms_kick); /* Wait for completion */ while (nva_rd32(cnum, 0x001308) & 0x100); ptimer_end = get_time(cnum); gettimeofday(&wall_end, NULL); if (wall_time) *wall_time = time_diff_us(wall_start, wall_end); return ptimer_end - ptimer_start - (get_time(cnum) - get_time(cnum)); }
static int test_threshold_crit_intr_both(struct hwtest_ctx *ctx) { threshold_reset(ctx); nva_mask(ctx->cnum, 0x20010, 0x80000000, 0x80000000); return threshold_check_intr_both(ctx, &nv50_therm_thresholds[therm_threshold_crit]); }
/* calibration */ static void start_sensor(struct hwtest_ctx *ctx) { nva_mask(ctx->cnum, 0x20010, 0x40000000, 0x0); usleep(20000); }
static void threshold_filter_set(struct hwtest_ctx *ctx, const struct therm_threshold *thrs, uint8_t div, uint8_t cycles) { uint32_t filter = (div << 8) | cycles; nva_mask(ctx->cnum, thrs->thrs_filter_addr, thrs->thrs_filter_mask, (filter << thrs->thrs_filter_lshift) & thrs->thrs_filter_mask); }
static void threshold_set(struct hwtest_ctx *ctx, const struct therm_threshold *thrs, uint16_t temp) { /* set the irq threshold */ nva_mask(ctx->cnum, thrs->thrs_addr, thrs->thrs_mask, (temp << thrs->thrs_lshift) & thrs->thrs_mask); }
void find_pgraphIdle_and_interrupt(int cnum) { unsigned char signals_idle[0x100 * 8]; struct signals_comparaison diffs; int i; uint32_t r_400500, r_400808, r_40013c; printf("<PGRAPH_IDLE/INTERRUPT> /!\\ no drivers should be loaded!\n"); /* safety check: */ if (nva_rd32(cnum, 0x140) == 1) { printf("You shouldn't run this tool while a driver is running\n"); goto error; } /* reboot PGRAPH */ nva_mask(cnum, 0x200, 0x00201000, 0x00000000); nva_mask(cnum, 0x200, 0x00201000, 0x00201000); r_400500 = nva_rd32(cnum, 0x400500); r_400808 = nva_rd32(cnum, 0x400808); r_40013c = nva_rd32(cnum, 0x40013c); /* generate an illegal method IRQ * that will pull the PGRAP_IDLE signal down and the PGRAPH_INTERRUPT up * * neither nouveau or nvidia should be loaded as they would ack the interrupt * straight away. */ nva_wr32(cnum, 0x400500, 0x10001); nva_wr32(cnum, 0x400808, 0xa00000fc); nva_wr32(cnum, 0x40013c, 0xffffffff); poll_signals(cnum, signals_idle); diffs = signals_compare(signals_ref, signals_idle); if (diffs.diff_count >= 1) { for (i = 0; i < diffs.diff_count; i++) { uint8_t set, signal; set = diffs.differences[i].set; signal = diffs.differences[i].signal; if (diffs.differences[i].set == 1) { if (diffs.differences[i].change == ONE_TO_ZERO) printf("PGRAPH_IDLE: Set %u, signal 0x%.2x\n", set, signal); else if (diffs.differences[i].change == ZERO_TO_ONE) printf("PGRAPH_INTERRUPT: Set %u, signal 0x%.2x\n", set, signal); } else { printf("Unexpected difference: "); print_difference(diffs.differences[i]); } } signals_comparaison_free(diffs); } else printf("Not found. Please re-run when the GPU is idle.\n"); /* restore our mess */ nva_wr32(cnum, 0x400500, r_400500); nva_wr32(cnum, 0x400808, r_400808); nva_wr32(cnum, 0x40013c, r_40013c); /* ACK all PGRAPH's interrupts */ nva_wr32(cnum, 0x400100, 0xffffffff); error: printf("</PGRAPH_IDLE/INTERRUPT>\n\n"); }