[v4,3/6] iccsense: implement for ina209, ina219 and ina3221

Submitted by Karol Herbst on Feb. 20, 2016, 5:11 p.m.

Details

Message ID 1455988299-2300-4-git-send-email-nouveau@karolherbst.de
State New
Headers show
Series "Suppor for various power sensors on GF100+" ( rev: 3 ) in Nouveau

Not browsing as part of any series.

Commit Message

Karol Herbst Feb. 20, 2016, 5:11 p.m.
based on Martins initial work

v3: fix ina2x9 calculations
v4: don't kmalloc(0), fix the lsb/pga stuff

Signed-off-by: Karol Herbst <nouveau@karolherbst.de>
---
 drm/nouveau/include/nvkm/subdev/bios/extdev.h |   3 +
 drm/nouveau/include/nvkm/subdev/i2c.h         |  31 ++++++
 drm/nouveau/include/nvkm/subdev/iccsense.h    |   5 +
 drm/nouveau/nvkm/engine/device/base.c         |  20 ++++
 drm/nouveau/nvkm/subdev/iccsense/Kbuild       |   1 +
 drm/nouveau/nvkm/subdev/iccsense/base.c       | 150 +++++++++++++++++++++++++-
 drm/nouveau/nvkm/subdev/iccsense/gf100.c      |  31 ++++++
 drm/nouveau/nvkm/subdev/iccsense/priv.h       |   8 ++
 8 files changed, 248 insertions(+), 1 deletion(-)
 create mode 100644 drm/nouveau/nvkm/subdev/iccsense/gf100.c

Patch hide | download patch | download mbox

diff --git a/drm/nouveau/include/nvkm/subdev/bios/extdev.h b/drm/nouveau/include/nvkm/subdev/bios/extdev.h
index 6d3bedc..bb49bd5 100644
--- a/drm/nouveau/include/nvkm/subdev/bios/extdev.h
+++ b/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/drm/nouveau/include/nvkm/subdev/i2c.h b/drm/nouveau/include/nvkm/subdev/i2c.h
index 864d1ab..a63c5ac 100644
--- a/drm/nouveau/include/nvkm/subdev/i2c.h
+++ b/drm/nouveau/include/nvkm/subdev/i2c.h
@@ -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)
 {
diff --git a/drm/nouveau/include/nvkm/subdev/iccsense.h b/drm/nouveau/include/nvkm/subdev/iccsense.h
index 7fee41d..16b77a6 100644
--- a/drm/nouveau/include/nvkm/subdev/iccsense.h
+++ b/drm/nouveau/include/nvkm/subdev/iccsense.h
@@ -3,8 +3,13 @@ 
 
 #include <core/subdev.h>
 
+struct nkvm_iccsense_rail;
 struct nvkm_iccsense {
 	struct nvkm_subdev subdev;
+	u8 rail_count;
+	struct nvkm_iccsense_rail *rails;
 };
 
+int gf100_iccsense_new(struct nvkm_device *, int index, struct nvkm_iccsense **);
+int nkvm_iccsense_read(struct nvkm_iccsense *iccsense, u8 idx);
 #endif
diff --git a/drm/nouveau/nvkm/engine/device/base.c b/drm/nouveau/nvkm/engine/device/base.c
index a239e49..2536890 100644
--- a/drm/nouveau/nvkm/engine/device/base.c
+++ b/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,
@@ -2004,6 +2022,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,
@@ -2035,6 +2054,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/drm/nouveau/nvkm/subdev/iccsense/Kbuild b/drm/nouveau/nvkm/subdev/iccsense/Kbuild
index b6863da..98a4bd3 100644
--- a/drm/nouveau/nvkm/subdev/iccsense/Kbuild
+++ b/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/drm/nouveau/nvkm/subdev/iccsense/base.c b/drm/nouveau/nvkm/subdev/iccsense/base.c
index 5dfa2fd..29c6641 100644
--- a/drm/nouveau/nvkm/subdev/iccsense/base.c
+++ b/drm/nouveau/nvkm/subdev/iccsense/base.c
@@ -23,13 +23,161 @@ 
  */
 #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 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 vbus, vshunt;
+
+	if (shunt == 0)
+		return 0;
+
+	vshunt = nv_rd16i2cr(i2c, addr, shunt_reg);
+	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)
+{
+	if (rail->rail >= 3)
+		return -EINVAL;
+
+	return nvkm_iccsense_poll_lane(rail->i2c, rail->addr,
+				       1 + (rail->rail * 2), 3,
+				       2 + (rail->rail * 2), 3, rail->mohm,
+				       40 * 8);
+}
+
+int
+nkvm_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);
+}
+
+static void *
+nvkm_iccsense_dtor(struct nvkm_subdev *subdev)
+{
+	struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev);
+
+	if (iccsense->rails)
+		kfree(iccsense->rails);
+
+	return iccsense;
+}
+
+struct nvkm_subdev_func iccsense_func = {
+	.dtor = nvkm_iccsense_dtor,
+};
 
 int
 nvkm_iccsense_ctor(struct nvkm_device *device, int index,
 		   struct nvkm_iccsense *iccsense)
 {
+	struct nvkm_bios *bios;
+	struct nvkm_i2c *i2c;
+	struct nvbios_iccsense stbl;
+	int i;
+
 	nvkm_subdev_ctor(&iccsense_func, device, index, 0, &iccsense->subdev);
+	bios = device->bios;
+	i2c = device->i2c;
+
+	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;
+
+	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;
+
+		if (!r->mode)
+			continue;
+
+		if (nvbios_extdev_parse(bios, r->extdev_id, &extdev))
+			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;
+
+		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;
+		default:
+			continue;
+		}
+		rail->addr = extdev.addr >> 1;
+		rail->rail = r->rail;
+		rail->mohm = r->resistor_mohm;
+		rail->i2c = &i2c_bus->i2c;
+		++iccsense->rail_count;
+	}
+
 	return 0;
 }
 
diff --git a/drm/nouveau/nvkm/subdev/iccsense/gf100.c b/drm/nouveau/nvkm/subdev/iccsense/gf100.c
new file mode 100644
index 0000000..cccff1c
--- /dev/null
+++ b/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/drm/nouveau/nvkm/subdev/iccsense/priv.h b/drm/nouveau/nvkm/subdev/iccsense/priv.h
index f0aa17a..db33d81 100644
--- a/drm/nouveau/nvkm/subdev/iccsense/priv.h
+++ b/drm/nouveau/nvkm/subdev/iccsense/priv.h
@@ -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;
+	u16 addr;
+	u8  rail;
+	u8  mohm;
+};
+
 int nvkm_iccsense_ctor(struct nvkm_device *, int, struct nvkm_iccsense *);
 int nvkm_iccsense_new_(struct nvkm_device *, int, struct nvkm_iccsense **);
 #endif

Comments

On 20/02/16 19:11, Karol Herbst wrote:
> based on Martins initial work
>
> v3: fix ina2x9 calculations
> v4: don't kmalloc(0), fix the lsb/pga stuff
>
> Signed-off-by: Karol Herbst <nouveau@karolherbst.de>
> ---
>   drm/nouveau/include/nvkm/subdev/bios/extdev.h |   3 +
>   drm/nouveau/include/nvkm/subdev/i2c.h         |  31 ++++++
>   drm/nouveau/include/nvkm/subdev/iccsense.h    |   5 +
>   drm/nouveau/nvkm/engine/device/base.c         |  20 ++++
>   drm/nouveau/nvkm/subdev/iccsense/Kbuild       |   1 +
>   drm/nouveau/nvkm/subdev/iccsense/base.c       | 150 +++++++++++++++++++++++++-
>   drm/nouveau/nvkm/subdev/iccsense/gf100.c      |  31 ++++++
>   drm/nouveau/nvkm/subdev/iccsense/priv.h       |   8 ++
>   8 files changed, 248 insertions(+), 1 deletion(-)
>   create mode 100644 drm/nouveau/nvkm/subdev/iccsense/gf100.c
>
> diff --git a/drm/nouveau/nvkm/subdev/iccsense/base.c b/drm/nouveau/nvkm/subdev/iccsense/base.c
> index 5dfa2fd..29c6641 100644
> --- a/drm/nouveau/nvkm/subdev/iccsense/base.c
> +++ b/drm/nouveau/nvkm/subdev/iccsense/base.c
> @@ -23,13 +23,161 @@
>    */
>   #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 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 vbus, vshunt;
> +
> +	if (shunt == 0)
> +		return 0;
> +
> +	vshunt = nv_rd16i2cr(i2c, addr, shunt_reg);
> +	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)
> +{
> +	if (rail->rail >= 3)
> +		return -EINVAL;
> +
> +	return nvkm_iccsense_poll_lane(rail->i2c, rail->addr,
> +				       1 + (rail->rail * 2), 3,
> +				       2 + (rail->rail * 2), 3, rail->mohm,
> +				       40 * 8);
> +}
> +
> +int
> +nkvm_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);
> +}
> +
> +static void *
> +nvkm_iccsense_dtor(struct nvkm_subdev *subdev)
> +{
> +	struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev);
> +
> +	if (iccsense->rails)
> +		kfree(iccsense->rails);
> +
> +	return iccsense;
> +}
> +
> +struct nvkm_subdev_func iccsense_func = {
> +	.dtor = nvkm_iccsense_dtor,
> +};
>   
>   int
>   nvkm_iccsense_ctor(struct nvkm_device *device, int index,
>   		   struct nvkm_iccsense *iccsense)
>   {
> +	struct nvkm_bios *bios;
> +	struct nvkm_i2c *i2c;
> +	struct nvbios_iccsense stbl;
> +	int i;
> +
>   	nvkm_subdev_ctor(&iccsense_func, device, index, 0, &iccsense->subdev);
> +	bios = device->bios;
> +	i2c = device->i2c;
> +
> +	if (!i2c || !bios || nvbios_iccsense_parse(bios, &stbl)
> +	    || !stbl.nr_entry)
I must say that this line is a bit ugly ... but meh!
> +		return 0;
> +
> +	iccsense->rails = kmalloc(sizeof(*iccsense->rails) * stbl.nr_entry,
> +	                          GFP_KERNEL);
> +	if (!iccsense->rails)
> +		return -ENOMEM;
> +
> +	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;
> +
> +		if (!r->mode)
> +			continue;
> +
> +		if (nvbios_extdev_parse(bios, r->extdev_id, &extdev))
> +			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;

Wow, this is great! Thanks, I always wondered how I would get the bus 
working
and it seems like you found a nice way.

> +
> +		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;
> +		default:

It would be nice to add a warning here that there is a new sensor type 
we do not know about yet. This is
especially since some rails may be covered by other devices which would 
be supported and an invalid power
reading would be present.

I would say that more than just displaying a warning, we should also 
change a boolean which would say how
trustful the reading is. This would be good when enabling or disabling 
the usage of boost clocks. Don't you think?

> +			continue;
> +		}
> +		rail->addr = extdev.addr >> 1;
> +		rail->rail = r->rail;
> +		rail->mohm = r->resistor_mohm;
> +		rail->i2c = &i2c_bus->i2c;
> +		++iccsense->rail_count;

There is no verification that the device is actually present. The extdev 
table is untrustful and I really wouldn't mind
more validation here :)

Other than that, nice work! I really like the design :)