diff options
author | Karol Herbst <nouveau@karolherbst.de> | 2016-02-18 16:53:44 +0100 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2016-03-14 10:13:25 +1000 |
commit | b71c0892631af3dd2aea708529d282a65c683be5 (patch) | |
tree | 8b316ef575fbef8efc237933e93cfee3480ac891 /drivers/gpu/drm/nouveau/nvkm/subdev/iccsense | |
parent | 39b7e6e547ffca0b42a29df5a213f5bf3a19af0b (diff) |
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 <nouveau@karolherbst.de>
Reviewed-by: Martin Peres <martin.peres@free.fr>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/iccsense')
4 files changed, 230 insertions, 1 deletions
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 <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 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 <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 |