static u16 nvbios_dpout_entry(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len); if (data && idx < *cnt) { u16 outp = nvbios_rd16(bios, data + *hdr + idx * *len); switch (*ver * !!outp) { case 0x20: case 0x21: case 0x30: *hdr = nvbios_rd08(bios, data + 0x04); *len = nvbios_rd08(bios, data + 0x05); *cnt = nvbios_rd08(bios, outp + 0x04); break; case 0x40: case 0x41: case 0x42: *hdr = nvbios_rd08(bios, data + 0x04); *cnt = 0; *len = 0; break; default: break; } return outp; } *ver = 0x00; return 0x0000; }
u16 nvbios_dp_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { struct bit_entry d; if (!bit_entry(bios, 'd', &d)) { if (d.version == 1 && d.length >= 2) { u16 data = nvbios_rd16(bios, d.offset); if (data) { *ver = nvbios_rd08(bios, data + 0x00); switch (*ver) { case 0x20: case 0x21: case 0x30: case 0x40: case 0x41: case 0x42: *hdr = nvbios_rd08(bios, data + 0x01); *len = nvbios_rd08(bios, data + 0x02); *cnt = nvbios_rd08(bios, data + 0x03); return data; default: break; } } } } return 0x0000; }
u16 nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_volt *info) { u16 volt = nvbios_volt_table(bios, ver, hdr, cnt, len); memset(info, 0x00, sizeof(*info)); switch (!!volt * *ver) { case 0x12: info->type = NVBIOS_VOLT_GPIO; info->vidmask = nvbios_rd08(bios, volt + 0x04); break; case 0x20: info->type = NVBIOS_VOLT_GPIO; info->vidmask = nvbios_rd08(bios, volt + 0x05); break; case 0x30: info->type = NVBIOS_VOLT_GPIO; info->vidmask = nvbios_rd08(bios, volt + 0x04); break; case 0x40: info->type = NVBIOS_VOLT_GPIO; info->base = nvbios_rd32(bios, volt + 0x04); info->step = nvbios_rd16(bios, volt + 0x08); info->vidmask = nvbios_rd08(bios, volt + 0x0b); /*XXX*/ info->min = 0; info->max = info->base; break; case 0x50: info->min = nvbios_rd32(bios, volt + 0x0a); info->max = nvbios_rd32(bios, volt + 0x0e); info->base = nvbios_rd32(bios, volt + 0x12) & 0x00ffffff; /* offset 4 seems to be a flag byte */ if (nvbios_rd32(bios, volt + 0x4) & 1) { info->type = NVBIOS_VOLT_PWM; info->pwm_freq = nvbios_rd32(bios, volt + 0x5) / 1000; info->pwm_range = nvbios_rd32(bios, volt + 0x16); } else { info->type = NVBIOS_VOLT_GPIO; info->vidmask = nvbios_rd08(bios, volt + 0x06); info->step = nvbios_rd16(bios, volt + 0x16); } break; } return volt; }
u32 nvbios_rammapEp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p) { u32 data = nvbios_rammapEe(bios, idx, ver, hdr, cnt, len), temp; memset(p, 0x00, sizeof(*p)); p->rammap_ver = *ver; p->rammap_hdr = *hdr; switch (!!data * *ver) { case 0x10: p->rammap_min = nvbios_rd16(bios, data + 0x00); p->rammap_max = nvbios_rd16(bios, data + 0x02); p->rammap_10_04_02 = (nvbios_rd08(bios, data + 0x04) & 0x02) >> 1; p->rammap_10_04_08 = (nvbios_rd08(bios, data + 0x04) & 0x08) >> 3; break; case 0x11: p->rammap_min = nvbios_rd16(bios, data + 0x00); p->rammap_max = nvbios_rd16(bios, data + 0x02); p->rammap_11_08_01 = (nvbios_rd08(bios, data + 0x08) & 0x01) >> 0; p->rammap_11_08_0c = (nvbios_rd08(bios, data + 0x08) & 0x0c) >> 2; p->rammap_11_08_10 = (nvbios_rd08(bios, data + 0x08) & 0x10) >> 4; temp = nvbios_rd32(bios, data + 0x09); p->rammap_11_09_01ff = (temp & 0x000001ff) >> 0; p->rammap_11_0a_03fe = (temp & 0x0003fe00) >> 9; p->rammap_11_0a_0400 = (temp & 0x00040000) >> 18; p->rammap_11_0a_0800 = (temp & 0x00080000) >> 19; p->rammap_11_0b_01f0 = (temp & 0x01f00000) >> 20; p->rammap_11_0b_0200 = (temp & 0x02000000) >> 25; p->rammap_11_0b_0400 = (temp & 0x04000000) >> 26; p->rammap_11_0b_0800 = (temp & 0x08000000) >> 27; p->rammap_11_0d = nvbios_rd08(bios, data + 0x0d); p->rammap_11_0e = nvbios_rd08(bios, data + 0x0e); p->rammap_11_0f = nvbios_rd08(bios, data + 0x0f); p->rammap_11_11_0c = (nvbios_rd08(bios, data + 0x11) & 0x0c) >> 2; break; default: data = 0; break; } return data; }
u16 nvbios_volt_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { struct bit_entry bit_P; u16 volt = 0x0000; if (!bit_entry(bios, 'P', &bit_P)) { if (bit_P.version == 2) volt = nvbios_rd16(bios, bit_P.offset + 0x0c); else if (bit_P.version == 1) volt = nvbios_rd16(bios, bit_P.offset + 0x10); if (volt) { *ver = nvbios_rd08(bios, volt + 0); switch (*ver) { case 0x12: *hdr = 5; *cnt = nvbios_rd08(bios, volt + 2); *len = nvbios_rd08(bios, volt + 1); return volt; case 0x20: *hdr = nvbios_rd08(bios, volt + 1); *cnt = nvbios_rd08(bios, volt + 2); *len = nvbios_rd08(bios, volt + 3); return volt; case 0x30: case 0x40: case 0x50: *hdr = nvbios_rd08(bios, volt + 1); *cnt = nvbios_rd08(bios, volt + 3); *len = nvbios_rd08(bios, volt + 2); return volt; } } } return 0x0000; }
u32 nvbios_connTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { u32 dcb = dcb_table(bios, ver, hdr, cnt, len); if (dcb && *ver >= 0x30 && *hdr >= 0x16) { u32 data = nvbios_rd16(bios, dcb + 0x14); if (data) { *ver = nvbios_rd08(bios, data + 0); *hdr = nvbios_rd08(bios, data + 1); *cnt = nvbios_rd08(bios, data + 2); *len = nvbios_rd08(bios, data + 3); return data; } } return 0x00000000; }
int nvbios_perf_fan_parse(struct nvkm_bios *bios, struct nvbios_perf_fan *fan) { u8 ver, hdr, cnt, len, snr, ssz; u16 perf = nvbios_perf_table(bios, &ver, &hdr, &cnt, &len, &snr, &ssz); if (!perf) return -ENODEV; if (ver >= 0x20 && ver < 0x40 && hdr > 6) fan->pwm_divisor = nvbios_rd16(bios, perf + 6); else fan->pwm_divisor = 0; return 0; }
u32 nvbios_perfSp(struct nvkm_bios *bios, u32 perfE, int idx, u8 *ver, u8 *hdr, u8 cnt, u8 len, struct nvbios_perfS *info) { u32 data = nvbios_perfSe(bios, perfE, idx, ver, hdr, cnt, len); memset(info, 0x00, sizeof(*info)); switch (!!data * *ver) { case 0x40: info->v40.freq = (nvbios_rd16(bios, data + 0x00) & 0x3fff) * 1000; break; default: break; } return data; }
static bool nvbios_imagen(struct nvkm_bios *bios, struct nvbios_image *image) { struct nvkm_subdev *subdev = &bios->subdev; struct nvbios_pcirT pcir; struct nvbios_npdeT npde; u8 ver; u16 hdr; u32 data; switch ((data = nvbios_rd16(bios, image->base + 0x00))) { case 0xaa55: case 0xbb77: case 0x4e56: /* NV */ break; default: nvkm_debug(subdev, "%08x: ROM signature (%04x) unknown\n", image->base, data); return false; } if (!(data = nvbios_pcirTp(bios, image->base, &ver, &hdr, &pcir))) return false; image->size = pcir.image_size; image->type = pcir.image_type; image->last = pcir.last; if (image->type != 0x70) { if (!(data = nvbios_npdeTp(bios, image->base, &npde))) return true; image->size = npde.image_size; image->last = npde.last; } else { image->last = true; } return true; }
u8 mxm_sor_map(struct nvkm_bios *bios, u8 conn) { struct nvkm_subdev *subdev = &bios->subdev; u8 ver, hdr; u16 mxm = mxm_table(bios, &ver, &hdr); if (mxm && hdr >= 6) { u16 map = nvbios_rd16(bios, mxm + 4); if (map) { ver = nvbios_rd08(bios, map); if (ver == 0x10) { if (conn < nvbios_rd08(bios, map + 3)) { map += nvbios_rd08(bios, map + 1); map += conn; return nvbios_rd08(bios, map); } return 0x00; } nvkm_warn(subdev, "unknown sor map v%02x\n", ver); } } if (bios->version.chip == 0x84 || bios->version.chip == 0x86) return g84_sor_map[conn]; if (bios->version.chip == 0x92) return g92_sor_map[conn]; if (bios->version.chip == 0x94 || bios->version.chip == 0x96) return g94_sor_map[conn]; if (bios->version.chip == 0x98) return g98_sor_map[conn]; nvkm_warn(subdev, "missing sor map\n"); return 0x00; }
int nvbios_pll_parse(struct nvkm_bios *bios, u32 type, struct nvbios_pll *info) { struct nvkm_subdev *subdev = &bios->subdev; struct nvkm_device *device = subdev->device; u8 ver, len; u32 reg = type; u32 data; if (type > PLL_MAX) { reg = type; data = pll_map_reg(bios, reg, &type, &ver, &len); } else { data = pll_map_type(bios, type, ®, &ver, &len); } if (ver && !data) return -ENOENT; memset(info, 0, sizeof(*info)); info->type = type; info->reg = reg; switch (ver) { case 0x00: break; case 0x10: case 0x11: info->vco1.min_freq = nvbios_rd32(bios, data + 0); info->vco1.max_freq = nvbios_rd32(bios, data + 4); info->vco2.min_freq = nvbios_rd32(bios, data + 8); info->vco2.max_freq = nvbios_rd32(bios, data + 12); info->vco1.min_inputfreq = nvbios_rd32(bios, data + 16); info->vco2.min_inputfreq = nvbios_rd32(bios, data + 20); info->vco1.max_inputfreq = INT_MAX; info->vco2.max_inputfreq = INT_MAX; info->max_p = 0x7; info->max_p_usable = 0x6; /* these values taken from nv30/31/36 */ switch (bios->version.chip) { case 0x36: info->vco1.min_n = 0x5; break; default: info->vco1.min_n = 0x1; break; } info->vco1.max_n = 0xff; info->vco1.min_m = 0x1; info->vco1.max_m = 0xd; /* * On nv30, 31, 36 (i.e. all cards with two stage PLLs with this * table version (apart from nv35)), N2 is compared to * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and * save a comparison */ info->vco2.min_n = 0x4; switch (bios->version.chip) { case 0x30: case 0x35: info->vco2.max_n = 0x1f; break; default: info->vco2.max_n = 0x28; break; } info->vco2.min_m = 0x1; info->vco2.max_m = 0x4; break; case 0x20: case 0x21: info->vco1.min_freq = nvbios_rd16(bios, data + 4) * 1000; info->vco1.max_freq = nvbios_rd16(bios, data + 6) * 1000; info->vco2.min_freq = nvbios_rd16(bios, data + 8) * 1000; info->vco2.max_freq = nvbios_rd16(bios, data + 10) * 1000; info->vco1.min_inputfreq = nvbios_rd16(bios, data + 12) * 1000; info->vco2.min_inputfreq = nvbios_rd16(bios, data + 14) * 1000; info->vco1.max_inputfreq = nvbios_rd16(bios, data + 16) * 1000; info->vco2.max_inputfreq = nvbios_rd16(bios, data + 18) * 1000; info->vco1.min_n = nvbios_rd08(bios, data + 20); info->vco1.max_n = nvbios_rd08(bios, data + 21); info->vco1.min_m = nvbios_rd08(bios, data + 22); info->vco1.max_m = nvbios_rd08(bios, data + 23); info->vco2.min_n = nvbios_rd08(bios, data + 24); info->vco2.max_n = nvbios_rd08(bios, data + 25); info->vco2.min_m = nvbios_rd08(bios, data + 26); info->vco2.max_m = nvbios_rd08(bios, data + 27); info->max_p = nvbios_rd08(bios, data + 29); info->max_p_usable = info->max_p; if (bios->version.chip < 0x60) info->max_p_usable = 0x6; info->bias_p = nvbios_rd08(bios, data + 30); if (len > 0x22) info->refclk = nvbios_rd32(bios, data + 31); break; case 0x30: data = nvbios_rd16(bios, data + 1); info->vco1.min_freq = nvbios_rd16(bios, data + 0) * 1000; info->vco1.max_freq = nvbios_rd16(bios, data + 2) * 1000; info->vco2.min_freq = nvbios_rd16(bios, data + 4) * 1000; info->vco2.max_freq = nvbios_rd16(bios, data + 6) * 1000; info->vco1.min_inputfreq = nvbios_rd16(bios, data + 8) * 1000; info->vco2.min_inputfreq = nvbios_rd16(bios, data + 10) * 1000; info->vco1.max_inputfreq = nvbios_rd16(bios, data + 12) * 1000; info->vco2.max_inputfreq = nvbios_rd16(bios, data + 14) * 1000; info->vco1.min_n = nvbios_rd08(bios, data + 16); info->vco1.max_n = nvbios_rd08(bios, data + 17); info->vco1.min_m = nvbios_rd08(bios, data + 18); info->vco1.max_m = nvbios_rd08(bios, data + 19); info->vco2.min_n = nvbios_rd08(bios, data + 20); info->vco2.max_n = nvbios_rd08(bios, data + 21); info->vco2.min_m = nvbios_rd08(bios, data + 22); info->vco2.max_m = nvbios_rd08(bios, data + 23); info->max_p_usable = info->max_p = nvbios_rd08(bios, data + 25); info->bias_p = nvbios_rd08(bios, data + 27); info->refclk = nvbios_rd32(bios, data + 28); break; case 0x40: info->refclk = nvbios_rd16(bios, data + 9) * 1000; data = nvbios_rd16(bios, data + 1); info->vco1.min_freq = nvbios_rd16(bios, data + 0) * 1000; info->vco1.max_freq = nvbios_rd16(bios, data + 2) * 1000; info->vco1.min_inputfreq = nvbios_rd16(bios, data + 4) * 1000; info->vco1.max_inputfreq = nvbios_rd16(bios, data + 6) * 1000; info->vco1.min_m = nvbios_rd08(bios, data + 8); info->vco1.max_m = nvbios_rd08(bios, data + 9); info->vco1.min_n = nvbios_rd08(bios, data + 10); info->vco1.max_n = nvbios_rd08(bios, data + 11); info->min_p = nvbios_rd08(bios, data + 12); info->max_p = nvbios_rd08(bios, data + 13); break; default: nvkm_error(subdev, "unknown pll limits version 0x%02x\n", ver); return -EINVAL; } if (!info->refclk) { info->refclk = device->crystal; if (bios->version.chip == 0x51) { u32 sel_clk = nvkm_rd32(device, 0x680524); if ((info->reg == 0x680508 && sel_clk & 0x20) || (info->reg == 0x680520 && sel_clk & 0x80)) { if (nvkm_rdvgac(device, 0, 0x27) < 0xa3) info->refclk = 200000; else info->refclk = 25000; } } } /* * By now any valid limit table ought to have set a max frequency for * vco1, so if it's zero it's either a pre limit table bios, or one * with an empty limit table (seen on nv18) */ if (!info->vco1.max_freq) { info->vco1.max_freq = nvbios_rd32(bios, bios->bmp_offset + 67); info->vco1.min_freq = nvbios_rd32(bios, bios->bmp_offset + 71); if (bmp_version(bios) < 0x0506) { info->vco1.max_freq = 256000; info->vco1.min_freq = 128000; } info->vco1.min_inputfreq = 0; info->vco1.max_inputfreq = INT_MAX; info->vco1.min_n = 0x1; info->vco1.max_n = 0xff; info->vco1.min_m = 0x1; if (device->crystal == 13500) { /* nv05 does this, nv11 doesn't, nv10 unknown */ if (bios->version.chip < 0x11) info->vco1.min_m = 0x7; info->vco1.max_m = 0xd; } else { if (bios->version.chip < 0x11) info->vco1.min_m = 0x8; info->vco1.max_m = 0xe; } if (bios->version.chip < 0x17 || bios->version.chip == 0x1a || bios->version.chip == 0x20) info->max_p = 4; else info->max_p = 5; info->max_p_usable = info->max_p; } return 0; }
int nvbios_iccsense_parse(struct nvkm_bios *bios, struct nvbios_iccsense *iccsense) { struct nvkm_subdev *subdev = &bios->subdev; u8 ver, hdr, cnt, len, i; u32 table, entry; table = nvbios_iccsense_table(bios, &ver, &hdr, &cnt, &len); if (!table || !cnt) return -EINVAL; if (ver != 0x10 && ver != 0x20) { nvkm_error(subdev, "ICCSENSE version 0x%02x unknown\n", ver); return -EINVAL; } iccsense->nr_entry = cnt; iccsense->rail = kmalloc(sizeof(struct pwr_rail_t) * cnt, GFP_KERNEL); if (!iccsense->rail) return -ENOMEM; for (i = 0; i < cnt; ++i) { struct nvbios_extdev_func extdev; struct pwr_rail_t *rail = &iccsense->rail[i]; u8 res_start = 0; int r; entry = table + hdr + i * len; switch(ver) { case 0x10: rail->mode = nvbios_rd08(bios, entry + 0x1); rail->extdev_id = nvbios_rd08(bios, entry + 0x2); res_start = 0x3; break; case 0x20: rail->mode = nvbios_rd08(bios, entry); rail->extdev_id = nvbios_rd08(bios, entry + 0x1); res_start = 0x5; break; }; if (nvbios_extdev_parse(bios, rail->extdev_id, &extdev)) continue; switch (extdev.type) { case NVBIOS_EXTDEV_INA209: case NVBIOS_EXTDEV_INA219: rail->resistor_count = 1; break; case NVBIOS_EXTDEV_INA3221: rail->resistor_count = 3; break; default: rail->resistor_count = 0; break; }; for (r = 0; r < rail->resistor_count; ++r) { rail->resistors[r].mohm = nvbios_rd08(bios, entry + res_start + r * 2); rail->resistors[r].enabled = !(nvbios_rd08(bios, entry + res_start + r * 2 + 1) & 0x40); } rail->config = nvbios_rd16(bios, entry + res_start + rail->resistor_count * 2); } return 0; }
u16 nvbios_dpout_parse(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_dpout *info) { u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len); memset(info, 0x00, sizeof(*info)); if (data && *ver) { info->type = nvbios_rd16(bios, data + 0x00); info->mask = nvbios_rd16(bios, data + 0x02); switch (*ver) { case 0x20: info->mask |= 0x00c0; /* match any link */ /* fall-through */ case 0x21: case 0x30: info->flags = nvbios_rd08(bios, data + 0x05); info->script[0] = nvbios_rd16(bios, data + 0x06); info->script[1] = nvbios_rd16(bios, data + 0x08); if (*len >= 0x0c) info->lnkcmp = nvbios_rd16(bios, data + 0x0a); if (*len >= 0x0f) { info->script[2] = nvbios_rd16(bios, data + 0x0c); info->script[3] = nvbios_rd16(bios, data + 0x0e); } if (*len >= 0x11) info->script[4] = nvbios_rd16(bios, data + 0x10); break; case 0x40: case 0x41: case 0x42: info->flags = nvbios_rd08(bios, data + 0x04); info->script[0] = nvbios_rd16(bios, data + 0x05); info->script[1] = nvbios_rd16(bios, data + 0x07); info->lnkcmp = nvbios_rd16(bios, data + 0x09); info->script[2] = nvbios_rd16(bios, data + 0x0b); info->script[3] = nvbios_rd16(bios, data + 0x0d); info->script[4] = nvbios_rd16(bios, data + 0x0f); break; default: data = 0x0000; break; } } return data; }
u16 nvbios_perfEp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_perfE *info) { u16 perf = nvbios_perf_entry(bios, idx, ver, hdr, cnt, len); memset(info, 0x00, sizeof(*info)); info->pstate = nvbios_rd08(bios, perf + 0x00); switch (!!perf * *ver) { case 0x12: case 0x13: case 0x14: info->core = nvbios_rd32(bios, perf + 0x01) * 10; info->memory = nvbios_rd32(bios, perf + 0x05) * 20; info->fanspeed = nvbios_rd08(bios, perf + 0x37); if (*hdr > 0x38) info->voltage = nvbios_rd08(bios, perf + 0x38); break; case 0x21: case 0x23: case 0x24: info->fanspeed = nvbios_rd08(bios, perf + 0x04); info->voltage = nvbios_rd08(bios, perf + 0x05); info->shader = nvbios_rd16(bios, perf + 0x06) * 1000; info->core = info->shader + (signed char) nvbios_rd08(bios, perf + 0x08) * 1000; switch (bios->subdev.device->chipset) { case 0x49: case 0x4b: info->memory = nvbios_rd16(bios, perf + 0x0b) * 1000; break; default: info->memory = nvbios_rd16(bios, perf + 0x0b) * 2000; break; } break; case 0x25: info->fanspeed = nvbios_rd08(bios, perf + 0x04); info->voltage = nvbios_rd08(bios, perf + 0x05); info->core = nvbios_rd16(bios, perf + 0x06) * 1000; info->shader = nvbios_rd16(bios, perf + 0x0a) * 1000; info->memory = nvbios_rd16(bios, perf + 0x0c) * 1000; break; case 0x30: info->script = nvbios_rd16(bios, perf + 0x02); case 0x35: info->fanspeed = nvbios_rd08(bios, perf + 0x06); info->voltage = nvbios_rd08(bios, perf + 0x07); info->core = nvbios_rd16(bios, perf + 0x08) * 1000; info->shader = nvbios_rd16(bios, perf + 0x0a) * 1000; info->memory = nvbios_rd16(bios, perf + 0x0c) * 1000; info->vdec = nvbios_rd16(bios, perf + 0x10) * 1000; info->disp = nvbios_rd16(bios, perf + 0x14) * 1000; break; case 0x40: info->voltage = nvbios_rd08(bios, perf + 0x02); break; default: return 0x0000; } return perf; }