static u16 therm_table(struct nouveau_device *device, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) { struct bit_entry bit_P; u16 therm = 0; if (!nouveau_bit_entry(device, 'P', &bit_P)) { if (bit_P.version == 1) therm = nv_ro16(device, bit_P.offset + 12); else if (bit_P.version == 2) therm = nv_ro16(device, bit_P.offset + 16); else nv_error(device, "unknown offset for thermal in BIT P %d\n", bit_P.version); } /* exit now if we haven't found the thermal table */ if (!therm) return 0x0000; *ver = nv_ro08(device, therm + 0); *hdr = nv_ro08(device, therm + 1); *len = nv_ro08(device, therm + 2); *cnt = nv_ro08(device, therm + 3); return therm + nv_ro08(device, therm + 1); }
u8 nvbios_ramcfg_index(struct nouveau_subdev *subdev) { struct nouveau_bios *bios = nouveau_bios(subdev); u8 strap = nvbios_ramcfg_strap(subdev); u32 xlat = 0x00000000; struct bit_entry bit_M; struct nvbios_M0203E M0203E; u8 ver, hdr; if (!bit_entry(bios, 'M', &bit_M)) { if (bit_M.version == 1 && bit_M.length >= 5) xlat = nv_ro16(bios, bit_M.offset + 3); if (bit_M.version == 2 && bit_M.length >= 3) { /*XXX: is M ever shorter than this? * if not - what is xlat used for now? * also - sigh.. */ if (bit_M.length >= 7 && nvbios_M0203Em(bios, strap, &ver, &hdr, &M0203E)) return M0203E.group; xlat = nv_ro16(bios, bit_M.offset + 1); } } if (xlat) strap = nv_ro08(bios, xlat + strap); return strap; }
u16 nvbios_volt_parse(struct nouveau_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->vidmask = nv_ro08(bios, volt + 0x04); break; case 0x20: info->vidmask = nv_ro08(bios, volt + 0x05); break; case 0x30: info->vidmask = nv_ro08(bios, volt + 0x04); break; case 0x40: info->base = nv_ro32(bios, volt + 0x04); info->step = nv_ro16(bios, volt + 0x08); info->vidmask = nv_ro08(bios, volt + 0x0b); /*XXX*/ info->min = 0; info->max = info->base; break; case 0x50: info->vidmask = nv_ro08(bios, volt + 0x06); info->min = nv_ro32(bios, volt + 0x0a); info->max = nv_ro32(bios, volt + 0x0e); info->base = nv_ro32(bios, volt + 0x12) & 0x00ffffff; info->step = nv_ro16(bios, volt + 0x16); break; } return volt; }
u16 dcb_i2c_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { u16 i2c = 0x0000; u16 dcb = dcb_table(bios, ver, hdr, cnt, len); if (dcb) { if (*ver >= 0x15) i2c = nv_ro16(bios, dcb + 2); if (*ver >= 0x30) i2c = nv_ro16(bios, dcb + 4); } if (i2c && *ver >= 0x42) { nv_warn(bios, "ccb %02x not supported\n", *ver); return 0x0000; } if (i2c && *ver >= 0x30) { *ver = nv_ro08(bios, i2c + 0); *hdr = nv_ro08(bios, i2c + 1); *cnt = nv_ro08(bios, i2c + 2); *len = nv_ro08(bios, i2c + 3); } else { *ver = *ver; /* use DCB version */ *hdr = 0; *cnt = 16; *len = 4; } return i2c; }
u16 nvbios_oclk_match(struct nouveau_bios *bios, u16 cmp, u32 khz) { while (cmp) { if (khz / 10 >= nv_ro16(bios, cmp + 0x00)) return nv_ro16(bios, cmp + 0x02); cmp += 0x04; } return 0x0000; }
u16 nvbios_ocfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *info) { u16 data = nvbios_ocfg_entry(bios, outp, idx, ver, hdr, cnt, len); if (data) { info->match = nv_ro16(bios, data + 0x00); info->clkcmp[0] = nv_ro16(bios, data + 0x02); info->clkcmp[1] = nv_ro16(bios, data + 0x04); } return data; }
u16 nvbios_timingEp(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p) { u16 data = nvbios_timingEe(bios, idx, ver, hdr, cnt, len), temp; p->timing_ver = *ver; p->timing_hdr = *hdr; switch (!!data * *ver) { case 0x10: p->timing_10_WR = nv_ro08(bios, data + 0x00); p->timing_10_CL = nv_ro08(bios, data + 0x02); p->timing_10_ODT = nv_ro08(bios, data + 0x0e) & 0x07; p->timing_10_CWL = nv_ro08(bios, data + 0x13); break; case 0x20: p->timing[0] = nv_ro32(bios, data + 0x00); p->timing[1] = nv_ro32(bios, data + 0x04); p->timing[2] = nv_ro32(bios, data + 0x08); p->timing[3] = nv_ro32(bios, data + 0x0c); p->timing[4] = nv_ro32(bios, data + 0x10); p->timing[5] = nv_ro32(bios, data + 0x14); p->timing[6] = nv_ro32(bios, data + 0x18); p->timing[7] = nv_ro32(bios, data + 0x1c); p->timing[8] = nv_ro32(bios, data + 0x20); p->timing[9] = nv_ro32(bios, data + 0x24); p->timing[10] = nv_ro32(bios, data + 0x28); p->timing_20_2e_03 = (nv_ro08(bios, data + 0x2e) & 0x03) >> 0; p->timing_20_2e_30 = (nv_ro08(bios, data + 0x2e) & 0x30) >> 4; p->timing_20_2e_c0 = (nv_ro08(bios, data + 0x2e) & 0xc0) >> 6; p->timing_20_2f_03 = (nv_ro08(bios, data + 0x2f) & 0x03) >> 0; temp = nv_ro16(bios, data + 0x2c); p->timing_20_2c_003f = (temp & 0x003f) >> 0; p->timing_20_2c_1fc0 = (temp & 0x1fc0) >> 6; p->timing_20_30_07 = (nv_ro08(bios, data + 0x30) & 0x07) >> 0; p->timing_20_30_f8 = (nv_ro08(bios, data + 0x30) & 0xf8) >> 3; temp = nv_ro16(bios, data + 0x31); p->timing_20_31_0007 = (temp & 0x0007) >> 0; p->timing_20_31_0078 = (temp & 0x0078) >> 3; p->timing_20_31_0780 = (temp & 0x0780) >> 7; p->timing_20_31_0800 = (temp & 0x0800) >> 11; p->timing_20_31_7000 = (temp & 0x7000) >> 12; p->timing_20_31_8000 = (temp & 0x8000) >> 15; break; default: data = 0; break; } return data; }
/** * INIT_IO_RESTRICT_PROG - opcode 0x32 * */ static void init_io_restrict_prog(struct nvbios_init *init) { struct nouveau_bios *bios = init->bios; u16 port = nv_ro16(bios, init->offset + 1); u8 index = nv_ro08(bios, init->offset + 3); u8 mask = nv_ro08(bios, init->offset + 4); u8 shift = nv_ro08(bios, init->offset + 5); u8 count = nv_ro08(bios, init->offset + 6); u32 reg = nv_ro32(bios, init->offset + 7); u8 conf, i; trace("IO_RESTRICT_PROG\tR[0x%06x] = " "((0x%04x[0x%02x] & 0x%02x) >> %d) [{\n", reg, port, index, mask, shift); init->offset += 11; conf = (init_rdvgai(init, port, index) & mask) >> shift; for (i = 0; i < count; i++) { u32 data = nv_ro32(bios, init->offset); if (i == conf) { trace("\t0x%08x *\n", data); init_wr32(init, reg, data); } else { trace("\t0x%08x\n", data); } init->offset += 4; } trace("}]\n"); }
u32 nvbios_M0203Te(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { struct bit_entry bit_M; u32 data = 0x00000000; if (!bit_entry(bios, 'M', &bit_M)) { if (bit_M.version == 2 && bit_M.length > 0x04) data = nv_ro16(bios, bit_M.offset + 0x03); if (data) { *ver = nv_ro08(bios, data + 0x00); switch (*ver) { case 0x10: *hdr = nv_ro08(bios, data + 0x01); *len = nv_ro08(bios, data + 0x02); *cnt = nv_ro08(bios, data + 0x03); return data; default: break; } } } return 0x00000000; }
static u16 nvbios_dpout_entry(struct nouveau_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 = nv_ro16(bios, data + *hdr + idx * *len); switch (*ver * !!outp) { case 0x21: case 0x30: *hdr = nv_ro08(bios, data + 0x04); *len = nv_ro08(bios, data + 0x05); *cnt = nv_ro08(bios, outp + 0x04); break; case 0x40: *hdr = nv_ro08(bios, data + 0x04); *cnt = 0; *len = 0; break; default: break; } return outp; } *ver = 0x00; return 0x0000; }
static u16 nvbios_dp_table(struct nouveau_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 = nv_ro16(bios, d.offset); if (data) { *ver = nv_ro08(bios, data + 0x00); switch (*ver) { case 0x21: case 0x30: case 0x40: *hdr = nv_ro08(bios, data + 0x01); *len = nv_ro08(bios, data + 0x02); *cnt = nv_ro08(bios, data + 0x03); return data; default: break; } } } } return 0x0000; }
u16 nvbios_disp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *sub) { struct bit_entry U; if (!bit_entry(bios, 'U', &U)) { if (U.version == 1) { u16 data = nv_ro16(bios, U.offset); if (data) { *ver = nv_ro08(bios, data + 0x00); switch (*ver) { case 0x20: case 0x21: *hdr = nv_ro08(bios, data + 0x01); *len = nv_ro08(bios, data + 0x02); *cnt = nv_ro08(bios, data + 0x03); *sub = nv_ro08(bios, data + 0x04); return data; default: break; } } } } return 0x0000; }
u32 nvbios_rammapTe(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz) { struct bit_entry bit_P; u16 rammap = 0x0000; if (!bit_entry(bios, 'P', &bit_P)) { if (bit_P.version == 2) rammap = nv_ro16(bios, bit_P.offset + 4); if (rammap) { *ver = nv_ro08(bios, rammap + 0); switch (*ver) { case 0x10: case 0x11: *hdr = nv_ro08(bios, rammap + 1); *cnt = nv_ro08(bios, rammap + 5); *len = nv_ro08(bios, rammap + 2); *snr = nv_ro08(bios, rammap + 4); *ssz = nv_ro08(bios, rammap + 3); return rammap; default: break; } } } return 0x0000; }
int nouveau_fb_bios_memtype(struct nouveau_bios *bios) { struct bit_entry M; u8 ramcfg; ramcfg = (nv_rd32(bios, 0x101000) & 0x0000003c) >> 2; if (!bit_entry(bios, 'M', &M) && M.version == 2 && M.length >= 5) { u16 table = nv_ro16(bios, M.offset + 3); u8 version = nv_ro08(bios, table + 0); u8 header = nv_ro08(bios, table + 1); u8 record = nv_ro08(bios, table + 2); u8 entries = nv_ro08(bios, table + 3); if (table && version == 0x10 && ramcfg < entries) { u16 entry = table + header + (ramcfg * record); switch (nv_ro08(bios, entry) & 0x0f) { case 0: return NV_MEM_TYPE_DDR2; case 1: return NV_MEM_TYPE_DDR3; case 2: return NV_MEM_TYPE_GDDR3; case 3: return NV_MEM_TYPE_GDDR5; default: break; } } } return NV_MEM_TYPE_UNKNOWN; }
static u16 init_unknown_script(struct nouveau_bios *bios) { u16 len, data = init_table(bios, &len); if (data && len >= 16) return nv_ro16(bios, data + 14); return 0x0000; }
static u16 init_ram_restrict_table(struct nvbios_init *init) { struct nouveau_bios *bios = init->bios; struct bit_entry bit_M; u16 data = 0x0000; if (!bit_entry(bios, 'M', &bit_M)) { if (bit_M.version == 1 && bit_M.length >= 5) data = nv_ro16(bios, bit_M.offset + 3); if (bit_M.version == 2 && bit_M.length >= 3) data = nv_ro16(bios, bit_M.offset + 1); } if (data == 0x0000) warn("ram restrict table not found\n"); return data; }
static bool init_io_flag_condition_met(struct nvbios_init *init, u8 cond) { struct nouveau_bios *bios = init->bios; u16 table = init_io_flag_condition_table(init); if (table) { u16 port = nv_ro16(bios, table + (cond * 9) + 0); u8 index = nv_ro08(bios, table + (cond * 9) + 2); u8 mask = nv_ro08(bios, table + (cond * 9) + 3); u8 shift = nv_ro08(bios, table + (cond * 9) + 4); u16 data = nv_ro16(bios, table + (cond * 9) + 5); u8 dmask = nv_ro08(bios, table + (cond * 9) + 7); u8 value = nv_ro08(bios, table + (cond * 9) + 8); u8 ioval = (init_rdvgai(init, port, index) & mask) >> shift; return (nv_ro08(bios, data + ioval) & dmask) == value; } return false; }
u32 nvbios_pcirTp(struct nvkm_bios *bios, u32 base, u8 *ver, u16 *hdr, struct nvbios_pcirT *info) { u32 data = nvbios_pcirTe(bios, base, ver, hdr); memset(info, 0x00, sizeof(*info)); if (data) { info->vendor_id = nv_ro16(bios, data + 0x04); info->device_id = nv_ro16(bios, data + 0x06); info->class_code[0] = nv_ro08(bios, data + 0x0d); info->class_code[1] = nv_ro08(bios, data + 0x0e); info->class_code[2] = nv_ro08(bios, data + 0x0f); info->image_size = nv_ro16(bios, data + 0x10) * 512; info->image_rev = nv_ro16(bios, data + 0x12); info->image_type = nv_ro08(bios, data + 0x14); info->last = nv_ro08(bios, data + 0x15) & 0x80; } return data; }
u32 nvbios_rammapEp(struct nouveau_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 = nv_ro16(bios, data + 0x00); p->rammap_max = nv_ro16(bios, data + 0x02); p->rammap_10_04_02 = (nv_ro08(bios, data + 0x04) & 0x02) >> 1; p->rammap_10_04_08 = (nv_ro08(bios, data + 0x04) & 0x08) >> 3; break; case 0x11: p->rammap_min = nv_ro16(bios, data + 0x00); p->rammap_max = nv_ro16(bios, data + 0x02); p->rammap_11_08_01 = (nv_ro08(bios, data + 0x08) & 0x01) >> 0; p->rammap_11_08_0c = (nv_ro08(bios, data + 0x08) & 0x0c) >> 2; p->rammap_11_08_10 = (nv_ro08(bios, data + 0x08) & 0x10) >> 4; temp = nv_ro32(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 = nv_ro08(bios, data + 0x0d); p->rammap_11_0e = nv_ro08(bios, data + 0x0e); p->rammap_11_0f = nv_ro08(bios, data + 0x0f); p->rammap_11_11_0c = (nv_ro08(bios, data + 0x11) & 0x0c) >> 2; break; default: data = 0; break; } return data; }
u16 nvbios_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info) { u16 data = nvbios_outp_entry(bios, idx, ver, hdr, cnt, len); if (data && *hdr >= 0x0a) { info->type = nv_ro16(bios, data + 0x00); info->mask = nv_ro32(bios, data + 0x02); if (*ver <= 0x20) /* match any link */ info->mask |= 0x00c0; info->script[0] = nv_ro16(bios, data + 0x06); info->script[1] = nv_ro16(bios, data + 0x08); info->script[2] = 0x0000; if (*hdr >= 0x0c) info->script[2] = nv_ro16(bios, data + 0x0a); return data; } return 0x0000; }
static u16 init_script(struct nouveau_bios *bios, int index) { struct nvbios_init init = { .bios = bios }; u16 data; if (bmp_version(bios) && bmp_version(bios) < 0x0510) { if (index > 1) return 0x0000; data = bios->bmp_offset + (bios->version.major < 2 ? 14 : 18); return nv_ro16(bios, data + (index * 2)); } data = init_script_table(&init); if (data) return nv_ro16(bios, data + (index * 2)); return 0x0000; }
u16 nvbios_disp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len, u8 *sub, struct nvbios_disp *info) { u16 data = nvbios_disp_entry(bios, idx, ver, len, sub); if (data && *len >= 2) { info->data = nv_ro16(bios, data + 0); return data; } return 0x0000; }
u32 nvbios_pcirTe(struct nvkm_bios *bios, u32 base, u8 *ver, u16 *hdr) { u32 data = nv_ro16(bios, base + 0x18); if (data) { data += base; switch (nv_ro32(bios, data + 0x00)) { case 0x52494350: /* PCIR */ case 0x53494752: /* RGIS */ case 0x5344504e: /* NPDS */ *hdr = nv_ro16(bios, data + 0x0a); *ver = nv_ro08(bios, data + 0x0c); break; default: nv_debug(bios, "%08x: PCIR signature (%08x) unknown\n", data, nv_ro32(bios, data + 0x00)); data = 0; break; } } return data; }
u16 nvbios_volt_table(struct nouveau_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 = nv_ro16(bios, bit_P.offset + 0x0c); else if (bit_P.version == 1) volt = nv_ro16(bios, bit_P.offset + 0x10); if (volt) { *ver = nv_ro08(bios, volt + 0); switch (*ver) { case 0x12: *hdr = 5; *cnt = nv_ro08(bios, volt + 2); *len = nv_ro08(bios, volt + 1); return volt; case 0x20: *hdr = nv_ro08(bios, volt + 1); *cnt = nv_ro08(bios, volt + 2); *len = nv_ro08(bios, volt + 3); return volt; case 0x30: case 0x40: case 0x50: *hdr = nv_ro08(bios, volt + 1); *cnt = nv_ro08(bios, volt + 3); *len = nv_ro08(bios, volt + 2); return volt; } } } return 0x0000; }
u16 nvbios_timingTe(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz) { struct bit_entry bit_P; u16 timing = 0x0000; if (!bit_entry(bios, 'P', &bit_P)) { if (bit_P.version == 1) timing = nv_ro16(bios, bit_P.offset + 4); else if (bit_P.version == 2) timing = nv_ro16(bios, bit_P.offset + 8); if (timing) { *ver = nv_ro08(bios, timing + 0); switch (*ver) { case 0x10: *hdr = nv_ro08(bios, timing + 1); *cnt = nv_ro08(bios, timing + 2); *len = nv_ro08(bios, timing + 3); *snr = 0; *ssz = 0; return timing; case 0x20: *hdr = nv_ro08(bios, timing + 1); *cnt = nv_ro08(bios, timing + 5); *len = nv_ro08(bios, timing + 2); *snr = nv_ro08(bios, timing + 4); *ssz = nv_ro08(bios, timing + 3); return timing; default: break; } } } return 0x0000; }
static u8 init_xlat_(struct nvbios_init *init, u8 index, u8 offset) { struct nouveau_bios *bios = init->bios; u16 table = init_xlat_table(init); if (table) { u16 data = nv_ro16(bios, table + (index * 2)); if (data) return nv_ro08(bios, data + offset); warn("xlat table pointer %d invalid\n", index); } return 0x00; }
static u16 dcb_xpiod_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { u16 data = dcb_gpio_table(bios, ver, hdr, cnt, len); if (data && *ver >= 0x40 && *hdr >= 0x06) { u16 xpio = nv_ro16(bios, data + 0x04); if (xpio) { *ver = nv_ro08(bios, data + 0x00); *hdr = nv_ro08(bios, data + 0x01); *cnt = nv_ro08(bios, data + 0x02); *len = nv_ro08(bios, data + 0x03); return xpio; } } return 0x0000; }
u16 dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { u16 dcb = dcb_table(bios, ver, hdr, cnt, len); if (dcb && *ver >= 0x30 && *hdr >= 0x16) { u16 data = nv_ro16(bios, dcb + 0x14); if (data) { *ver = nv_ro08(bios, data + 0); *hdr = nv_ro08(bios, data + 1); *cnt = nv_ro08(bios, data + 2); *len = nv_ro08(bios, data + 3); return data; } } return 0x0000; }
u32 nvbios_M0203Tp(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_M0203T *info) { u32 data = nvbios_M0203Te(bios, ver, hdr, cnt, len); memset(info, 0x00, sizeof(*info)); switch (!!data * *ver) { case 0x10: info->type = nv_ro08(bios, data + 0x04); info->pointer = nv_ro16(bios, data + 0x05); break; default: break; } return data; }
static bool init_io_condition_met(struct nvbios_init *init, u8 cond) { struct nouveau_bios *bios = init->bios; u16 table = init_io_condition_table(init); if (table) { u16 port = nv_ro16(bios, table + (cond * 5) + 0); u8 index = nv_ro08(bios, table + (cond * 5) + 2); u8 mask = nv_ro08(bios, table + (cond * 5) + 3); u8 value = nv_ro08(bios, table + (cond * 5) + 4); trace("\t[0x%02x] (0x%04x[0x%02x] & 0x%02x) == 0x%02x\n", cond, port, index, mask, value); return (init_rdvgai(init, port, index) & mask) == value; } return false; }