drm/nouveau/iccsense: implement for ina209, ina219 and ina3221
authorKarol Herbst <nouveau@karolherbst.de>
Thu, 18 Feb 2016 15:53:44 +0000 (16:53 +0100)
committerBen Skeggs <bskeggs@redhat.com>
Mon, 14 Mar 2016 00:13:25 +0000 (10:13 +1000)
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 <nouveau@karolherbst.de>
Reviewed-by: Martin Peres <martin.peres@free.fr>
drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/iccsense.h
drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/Kbuild
drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c
drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/gf100.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/priv.h

index 6d3bedc..bb49bd5 100644 (file)
@@ -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,
index 864d1ab..a63c5ac 100644 (file)
@@ -108,6 +108,22 @@ nvkm_rdi2cr(struct i2c_adapter *adap, u8 addr, u8 reg)
 }
 
 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 = &reg },
+               { .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)
 {
        u8 buf[2] = { reg, 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)
 {
index 7fee41d..530c621 100644 (file)
@@ -3,8 +3,15 @@
 
 #include <core/subdev.h>
 
+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
index 5afa107..86b8a24 100644 (file)
@@ -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,
index a75d87e..c44a852 100644 (file)
  */
 #include "priv.h"
 
-struct nvkm_subdev_func iccsense_func = { 0 };
+#include <subdev/bios.h>
+#include <subdev/bios/extdev.h>
+#include <subdev/bios/iccsense.h>
+#include <subdev/i2c.h>
+
+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 (file)
index 0000000..cccff1c
--- /dev/null
@@ -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);
+}
index f62b242..ed398b8 100644 (file)
@@ -3,6 +3,14 @@
 #define nvkm_iccsense(p) container_of((p), struct nvkm_iccsense, subdev)
 #include <subdev/iccsense.h>
 
+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