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; }
static int nvkm_iccsense_oneinit(struct nvkm_subdev *subdev) { struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev); struct nvkm_bios *bios = subdev->device->bios; struct nvkm_i2c *i2c = subdev->device->i2c; struct nvbios_iccsense stbl; int i; if (!i2c || !bios || nvbios_iccsense_parse(bios, &stbl) || !stbl.nr_entry) return 0; iccsense->rails = kmalloc(sizeof(*iccsense->rails) * stbl.nr_entry, GFP_KERNEL); if (!iccsense->rails) return -ENOMEM; iccsense->data_valid = true; for (i = 0; i < stbl.nr_entry; ++i) { struct pwr_rail_t *r = &stbl.rail[i]; struct nvbios_extdev_func extdev; struct nvkm_iccsense_rail *rail; struct nvkm_i2c_bus *i2c_bus; u8 addr; if (!r->mode || r->resistor_mohm == 0) continue; if (nvbios_extdev_parse(bios, r->extdev_id, &extdev)) continue; if (extdev.type == 0xff) continue; if (extdev.bus) i2c_bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_SEC); else i2c_bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_PRI); if (!i2c_bus) continue; addr = extdev.addr >> 1; if (!nvkm_iccsense_validate_device(&i2c_bus->i2c, addr, extdev.type, r->rail)) { iccsense->data_valid = false; nvkm_warn(subdev, "found unknown or invalid rail entry" " type 0x%x rail %i, power reading might be" " invalid\n", extdev.type, r->rail); continue; } rail = &iccsense->rails[iccsense->rail_count]; switch (extdev.type) { case NVBIOS_EXTDEV_INA209: rail->read = nvkm_iccsense_ina209_read; break; case NVBIOS_EXTDEV_INA219: rail->read = nvkm_iccsense_ina219_read; break; case NVBIOS_EXTDEV_INA3221: rail->read = nvkm_iccsense_ina3221_read; break; } rail->addr = addr; rail->rail = r->rail; rail->mohm = r->resistor_mohm; rail->i2c = &i2c_bus->i2c; ++iccsense->rail_count; } return 0; }