From b71c0892631af3dd2aea708529d282a65c683be5 Mon Sep 17 00:00:00 2001 From: Karol Herbst Date: Thu, 18 Feb 2016 16:53:44 +0100 Subject: [PATCH] drm/nouveau/iccsense: implement for ina209, ina219 and ina3221 based on Martins initial work v3: fix ina2x9 calculations v4: don't kmalloc(0), fix the lsb/pga stuff v5: add a field to tell if the power reading may be invalid add nkvm_iccsense_read_all function check for the device on the i2c bus Signed-off-by: Karol Herbst Reviewed-by: Martin Peres --- .../nouveau/include/nvkm/subdev/bios/extdev.h | 3 + .../gpu/drm/nouveau/include/nvkm/subdev/i2c.h | 31 +++ .../nouveau/include/nvkm/subdev/iccsense.h | 7 + .../gpu/drm/nouveau/nvkm/engine/device/base.c | 20 ++ .../drm/nouveau/nvkm/subdev/iccsense/Kbuild | 1 + .../drm/nouveau/nvkm/subdev/iccsense/base.c | 191 +++++++++++++++++- .../drm/nouveau/nvkm/subdev/iccsense/gf100.c | 31 +++ .../drm/nouveau/nvkm/subdev/iccsense/priv.h | 8 + 8 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/gf100.c diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h index 6d3bedc633b3..bb49bd5f879e 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h @@ -5,6 +5,9 @@ enum nvbios_extdev_type { NVBIOS_EXTDEV_VT1103M = 0x40, NVBIOS_EXTDEV_PX3540 = 0x41, NVBIOS_EXTDEV_VT1105M = 0x42, /* or close enough... */ + NVBIOS_EXTDEV_INA219 = 0x4c, + NVBIOS_EXTDEV_INA209 = 0x4d, + NVBIOS_EXTDEV_INA3221 = 0x4e, NVBIOS_EXTDEV_ADT7473 = 0x70, /* can also be a LM64 */ NVBIOS_EXTDEV_HDCP_EEPROM = 0x90, NVBIOS_EXTDEV_NONE = 0xff, diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h index 864d1aba7b68..a63c5ac69f66 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h @@ -107,6 +107,22 @@ nvkm_rdi2cr(struct i2c_adapter *adap, u8 addr, u8 reg) return val; } +static inline int +nv_rd16i2cr(struct i2c_adapter *adap, u8 addr, u8 reg) +{ + u8 val[2]; + struct i2c_msg msgs[] = { + { .addr = addr, .flags = 0, .len = 1, .buf = ® }, + { .addr = addr, .flags = I2C_M_RD, .len = 2, .buf = val }, + }; + + int ret = i2c_transfer(adap, msgs, ARRAY_SIZE(msgs)); + if (ret != 2) + return -EIO; + + return val[0] << 8 | val[1]; +} + static inline int nvkm_wri2cr(struct i2c_adapter *adap, u8 addr, u8 reg, u8 val) { @@ -122,6 +138,21 @@ nvkm_wri2cr(struct i2c_adapter *adap, u8 addr, u8 reg, u8 val) return 0; } +static inline int +nv_wr16i2cr(struct i2c_adapter *adap, u8 addr, u8 reg, u16 val) +{ + u8 buf[3] = { reg, val >> 8, val & 0xff}; + struct i2c_msg msgs[] = { + { .addr = addr, .flags = 0, .len = 3, .buf = buf }, + }; + + int ret = i2c_transfer(adap, msgs, ARRAY_SIZE(msgs)); + if (ret != 1) + return -EIO; + + return 0; +} + static inline bool nvkm_probe_i2c(struct i2c_adapter *adap, u8 addr) { diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/iccsense.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/iccsense.h index 7fee41dfae7f..530c6215fe4f 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/iccsense.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/iccsense.h @@ -3,8 +3,15 @@ #include +struct nkvm_iccsense_rail; struct nvkm_iccsense { struct nvkm_subdev subdev; + u8 rail_count; + bool data_valid; + struct nvkm_iccsense_rail *rails; }; +int gf100_iccsense_new(struct nvkm_device *, int index, struct nvkm_iccsense **); +int nvkm_iccsense_read(struct nvkm_iccsense *iccsense, u8 idx); +int nvkm_iccsense_read_all(struct nvkm_iccsense *iccsense); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index 5afa1072ee40..86b8a242e621 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -1347,6 +1347,7 @@ nvc0_chipset = { .gpio = g94_gpio_new, .i2c = g94_i2c_new, .ibus = gf100_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gf100_ltc_new, .mc = gf100_mc_new, @@ -1383,6 +1384,7 @@ nvc1_chipset = { .gpio = g94_gpio_new, .i2c = g94_i2c_new, .ibus = gf100_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gf100_ltc_new, .mc = gf100_mc_new, @@ -1418,6 +1420,7 @@ nvc3_chipset = { .gpio = g94_gpio_new, .i2c = g94_i2c_new, .ibus = gf100_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gf100_ltc_new, .mc = gf100_mc_new, @@ -1453,6 +1456,7 @@ nvc4_chipset = { .gpio = g94_gpio_new, .i2c = g94_i2c_new, .ibus = gf100_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gf100_ltc_new, .mc = gf100_mc_new, @@ -1489,6 +1493,7 @@ nvc8_chipset = { .gpio = g94_gpio_new, .i2c = g94_i2c_new, .ibus = gf100_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gf100_ltc_new, .mc = gf100_mc_new, @@ -1525,6 +1530,7 @@ nvce_chipset = { .gpio = g94_gpio_new, .i2c = g94_i2c_new, .ibus = gf100_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gf100_ltc_new, .mc = gf100_mc_new, @@ -1561,6 +1567,7 @@ nvcf_chipset = { .gpio = g94_gpio_new, .i2c = g94_i2c_new, .ibus = gf100_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gf100_ltc_new, .mc = gf100_mc_new, @@ -1596,6 +1603,7 @@ nvd7_chipset = { .gpio = gf119_gpio_new, .i2c = gf117_i2c_new, .ibus = gf117_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gf100_ltc_new, .mc = gf100_mc_new, @@ -1629,6 +1637,7 @@ nvd9_chipset = { .gpio = gf119_gpio_new, .i2c = gf119_i2c_new, .ibus = gf117_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gf100_ltc_new, .mc = gf100_mc_new, @@ -1664,6 +1673,7 @@ nve4_chipset = { .gpio = gk104_gpio_new, .i2c = gk104_i2c_new, .ibus = gk104_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gk104_ltc_new, .mc = gf100_mc_new, @@ -1701,6 +1711,7 @@ nve6_chipset = { .gpio = gk104_gpio_new, .i2c = gk104_i2c_new, .ibus = gk104_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gk104_ltc_new, .mc = gf100_mc_new, @@ -1738,6 +1749,7 @@ nve7_chipset = { .gpio = gk104_gpio_new, .i2c = gk104_i2c_new, .ibus = gk104_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gk104_ltc_new, .mc = gf100_mc_new, @@ -1799,6 +1811,7 @@ nvf0_chipset = { .gpio = gk104_gpio_new, .i2c = gk104_i2c_new, .ibus = gk104_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gk104_ltc_new, .mc = gf100_mc_new, @@ -1835,6 +1848,7 @@ nvf1_chipset = { .gpio = gk104_gpio_new, .i2c = gf119_i2c_new, .ibus = gk104_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gk104_ltc_new, .mc = gf100_mc_new, @@ -1871,6 +1885,7 @@ nv106_chipset = { .gpio = gk104_gpio_new, .i2c = gk104_i2c_new, .ibus = gk104_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gk104_ltc_new, .mc = gk20a_mc_new, @@ -1907,6 +1922,7 @@ nv108_chipset = { .gpio = gk104_gpio_new, .i2c = gk104_i2c_new, .ibus = gk104_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gk104_ltc_new, .mc = gk20a_mc_new, @@ -1943,6 +1959,7 @@ nv117_chipset = { .gpio = gk104_gpio_new, .i2c = gf119_i2c_new, .ibus = gk104_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gm107_ltc_new, .mc = gk20a_mc_new, @@ -1974,6 +1991,7 @@ nv120_chipset = { .gpio = gk104_gpio_new, .i2c = gm200_i2c_new, .ibus = gm200_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gm200_ltc_new, .mc = gk20a_mc_new, @@ -2006,6 +2024,7 @@ nv124_chipset = { .gpio = gk104_gpio_new, .i2c = gm200_i2c_new, .ibus = gm200_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gm200_ltc_new, .mc = gk20a_mc_new, @@ -2038,6 +2057,7 @@ nv126_chipset = { .gpio = gk104_gpio_new, .i2c = gm200_i2c_new, .ibus = gm200_ibus_new, + .iccsense = gf100_iccsense_new, .imem = nv50_instmem_new, .ltc = gm200_ltc_new, .mc = gk20a_mc_new, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/Kbuild index b6863da2e97a..98a4bd3e98ed 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/Kbuild @@ -1 +1,2 @@ nvkm-y += nvkm/subdev/iccsense/base.o +nvkm-y += nvkm/subdev/iccsense/gf100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c index a75d87e5b515..c44a85228074 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c @@ -23,7 +23,196 @@ */ #include "priv.h" -struct nvkm_subdev_func iccsense_func = { 0 }; +#include +#include +#include +#include + +static bool +nvkm_iccsense_validate_device(struct i2c_adapter *i2c, u8 addr, + enum nvbios_extdev_type type, u8 rail) +{ + switch (type) { + case NVBIOS_EXTDEV_INA209: + case NVBIOS_EXTDEV_INA219: + return rail == 0 && nv_rd16i2cr(i2c, addr, 0x0) >= 0; + case NVBIOS_EXTDEV_INA3221: + return rail <= 3 && + nv_rd16i2cr(i2c, addr, 0xff) == 0x3220 && + nv_rd16i2cr(i2c, addr, 0xfe) == 0x5449; + default: + return false; + } +} + +static int +nvkm_iccsense_poll_lane(struct i2c_adapter *i2c, u8 addr, u8 shunt_reg, + u8 shunt_shift, u8 bus_reg, u8 bus_shift, u8 shunt, + u16 lsb) +{ + int vshunt = nv_rd16i2cr(i2c, addr, shunt_reg); + int vbus = nv_rd16i2cr(i2c, addr, bus_reg); + + if (vshunt < 0 || vbus < 0) + return -EINVAL; + + vshunt >>= shunt_shift; + vbus >>= bus_shift; + + return vbus * vshunt * lsb / shunt; +} + +static int +nvkm_iccsense_ina2x9_read(struct nvkm_iccsense *iccsense, + struct nvkm_iccsense_rail *rail, + u8 shunt_reg, u8 bus_reg) +{ + return nvkm_iccsense_poll_lane(rail->i2c, rail->addr, shunt_reg, 0, + bus_reg, 3, rail->mohm, 10 * 4); +} + +static int +nvkm_iccsense_ina209_read(struct nvkm_iccsense *iccsense, + struct nvkm_iccsense_rail *rail) +{ + return nvkm_iccsense_ina2x9_read(iccsense, rail, 3, 4); +} + +static int +nvkm_iccsense_ina219_read(struct nvkm_iccsense *iccsense, + struct nvkm_iccsense_rail *rail) +{ + return nvkm_iccsense_ina2x9_read(iccsense, rail, 1, 2); +} + +static int +nvkm_iccsense_ina3221_read(struct nvkm_iccsense *iccsense, + struct nvkm_iccsense_rail *rail) +{ + return nvkm_iccsense_poll_lane(rail->i2c, rail->addr, + 1 + (rail->rail * 2), 3, + 2 + (rail->rail * 2), 3, rail->mohm, + 40 * 8); +} + +int +nvkm_iccsense_read(struct nvkm_iccsense *iccsense, u8 idx) +{ + struct nvkm_iccsense_rail *rail; + + if (!iccsense || idx >= iccsense->rail_count) + return -EINVAL; + + rail = &iccsense->rails[idx]; + if (!rail->read) + return -ENODEV; + + return rail->read(iccsense, rail); +} + +int +nvkm_iccsense_read_all(struct nvkm_iccsense *iccsense) +{ + int result = 0, i; + for (i = 0; i < iccsense->rail_count; ++i) { + int res = nvkm_iccsense_read(iccsense, i); + if (res >= 0) + result += res; + else + return res; + } + return result; +} + +static void * +nvkm_iccsense_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev); + + if (iccsense->rails) + kfree(iccsense->rails); + + return iccsense; +} + +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; +} + +struct nvkm_subdev_func iccsense_func = { + .oneinit = nvkm_iccsense_oneinit, + .dtor = nvkm_iccsense_dtor, +}; void nvkm_iccsense_ctor(struct nvkm_device *device, int index, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/gf100.c new file mode 100644 index 000000000000..cccff1c8a409 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/gf100.c @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Karol Herbst + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Karol Herbst + */ +#include "priv.h" + +int +gf100_iccsense_new(struct nvkm_device *device, int index, + struct nvkm_iccsense **piccsense) +{ + return nvkm_iccsense_new_(device, index, piccsense); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h index f62b242c56f5..ed398b81e86e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h @@ -3,6 +3,14 @@ #define nvkm_iccsense(p) container_of((p), struct nvkm_iccsense, subdev) #include +struct nvkm_iccsense_rail { + int (*read)(struct nvkm_iccsense *, struct nvkm_iccsense_rail *); + struct i2c_adapter *i2c; + u8 addr; + u8 rail; + u8 mohm; +}; + void nvkm_iccsense_ctor(struct nvkm_device *, int, struct nvkm_iccsense *); int nvkm_iccsense_new_(struct nvkm_device *, int, struct nvkm_iccsense **); #endif