linux/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c
<<
>>
Prefs
   1/*
   2 * Copyright 2015 Martin Peres
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: Martin Peres
  23 */
  24#include "priv.h"
  25
  26#include <subdev/bios.h>
  27#include <subdev/bios/extdev.h>
  28#include <subdev/bios/iccsense.h>
  29#include <subdev/bios/power_budget.h>
  30#include <subdev/i2c.h>
  31
  32static bool
  33nvkm_iccsense_validate_device(struct i2c_adapter *i2c, u8 addr,
  34                              enum nvbios_extdev_type type)
  35{
  36        switch (type) {
  37        case NVBIOS_EXTDEV_INA209:
  38        case NVBIOS_EXTDEV_INA219:
  39                return nv_rd16i2cr(i2c, addr, 0x0) >= 0;
  40        case NVBIOS_EXTDEV_INA3221:
  41                return nv_rd16i2cr(i2c, addr, 0xff) == 0x3220 &&
  42                       nv_rd16i2cr(i2c, addr, 0xfe) == 0x5449;
  43        default:
  44                return false;
  45        }
  46}
  47
  48static int
  49nvkm_iccsense_poll_lane(struct i2c_adapter *i2c, u8 addr, u8 shunt_reg,
  50                        u8 shunt_shift, u8 bus_reg, u8 bus_shift, u8 shunt,
  51                        u16 lsb)
  52{
  53        int vshunt = nv_rd16i2cr(i2c, addr, shunt_reg);
  54        int vbus = nv_rd16i2cr(i2c, addr, bus_reg);
  55
  56        if (vshunt < 0 || vbus < 0)
  57                return -EINVAL;
  58
  59        vshunt >>= shunt_shift;
  60        vbus >>= bus_shift;
  61
  62        return vbus * vshunt * lsb / shunt;
  63}
  64
  65static int
  66nvkm_iccsense_ina2x9_read(struct nvkm_iccsense *iccsense,
  67                          struct nvkm_iccsense_rail *rail,
  68                          u8 shunt_reg, u8 bus_reg)
  69{
  70        return nvkm_iccsense_poll_lane(rail->sensor->i2c, rail->sensor->addr,
  71                                       shunt_reg, 0, bus_reg, 3, rail->mohm,
  72                                       10 * 4);
  73}
  74
  75static int
  76nvkm_iccsense_ina209_read(struct nvkm_iccsense *iccsense,
  77                          struct nvkm_iccsense_rail *rail)
  78{
  79        return nvkm_iccsense_ina2x9_read(iccsense, rail, 3, 4);
  80}
  81
  82static int
  83nvkm_iccsense_ina219_read(struct nvkm_iccsense *iccsense,
  84                          struct nvkm_iccsense_rail *rail)
  85{
  86        return nvkm_iccsense_ina2x9_read(iccsense, rail, 1, 2);
  87}
  88
  89static int
  90nvkm_iccsense_ina3221_read(struct nvkm_iccsense *iccsense,
  91                           struct nvkm_iccsense_rail *rail)
  92{
  93        return nvkm_iccsense_poll_lane(rail->sensor->i2c, rail->sensor->addr,
  94                                       1 + (rail->idx * 2), 3,
  95                                       2 + (rail->idx * 2), 3, rail->mohm,
  96                                       40 * 8);
  97}
  98
  99static void
 100nvkm_iccsense_sensor_config(struct nvkm_iccsense *iccsense,
 101                            struct nvkm_iccsense_sensor *sensor)
 102{
 103        struct nvkm_subdev *subdev = &iccsense->subdev;
 104        nvkm_trace(subdev, "write config of extdev %i: 0x%04x\n", sensor->id, sensor->config);
 105        nv_wr16i2cr(sensor->i2c, sensor->addr, 0x00, sensor->config);
 106}
 107
 108int
 109nvkm_iccsense_read_all(struct nvkm_iccsense *iccsense)
 110{
 111        int result = 0;
 112        struct nvkm_iccsense_rail *rail;
 113
 114        if (!iccsense)
 115                return -EINVAL;
 116
 117        list_for_each_entry(rail, &iccsense->rails, head) {
 118                int res;
 119                if (!rail->read)
 120                        return -ENODEV;
 121
 122                res = rail->read(iccsense, rail);
 123                if (res < 0)
 124                        return res;
 125                result += res;
 126        }
 127        return result;
 128}
 129
 130static void *
 131nvkm_iccsense_dtor(struct nvkm_subdev *subdev)
 132{
 133        struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev);
 134        struct nvkm_iccsense_sensor *sensor, *tmps;
 135        struct nvkm_iccsense_rail *rail, *tmpr;
 136
 137        list_for_each_entry_safe(sensor, tmps, &iccsense->sensors, head) {
 138                list_del(&sensor->head);
 139                kfree(sensor);
 140        }
 141        list_for_each_entry_safe(rail, tmpr, &iccsense->rails, head) {
 142                list_del(&rail->head);
 143                kfree(rail);
 144        }
 145
 146        return iccsense;
 147}
 148
 149static struct nvkm_iccsense_sensor*
 150nvkm_iccsense_create_sensor(struct nvkm_iccsense *iccsense, u8 id)
 151{
 152        struct nvkm_subdev *subdev = &iccsense->subdev;
 153        struct nvkm_bios *bios = subdev->device->bios;
 154        struct nvkm_i2c *i2c = subdev->device->i2c;
 155        struct nvbios_extdev_func extdev;
 156        struct nvkm_i2c_bus *i2c_bus;
 157        struct nvkm_iccsense_sensor *sensor;
 158        u8 addr;
 159
 160        if (!i2c || !bios || nvbios_extdev_parse(bios, id, &extdev))
 161                return NULL;
 162
 163        if (extdev.type == 0xff)
 164                return NULL;
 165
 166        if (extdev.type != NVBIOS_EXTDEV_INA209 &&
 167            extdev.type != NVBIOS_EXTDEV_INA219 &&
 168            extdev.type != NVBIOS_EXTDEV_INA3221) {
 169                iccsense->data_valid = false;
 170                nvkm_error(subdev, "Unknown sensor type %x, power reading "
 171                           "disabled\n", extdev.type);
 172                return NULL;
 173        }
 174
 175        if (extdev.bus)
 176                i2c_bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_SEC);
 177        else
 178                i2c_bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_PRI);
 179        if (!i2c_bus)
 180                return NULL;
 181
 182        addr = extdev.addr >> 1;
 183        if (!nvkm_iccsense_validate_device(&i2c_bus->i2c, addr,
 184                                           extdev.type)) {
 185                iccsense->data_valid = false;
 186                nvkm_warn(subdev, "found invalid sensor id: %i, power reading"
 187                          "might be invalid\n", id);
 188                return NULL;
 189        }
 190
 191        sensor = kmalloc(sizeof(*sensor), GFP_KERNEL);
 192        if (!sensor)
 193                return NULL;
 194
 195        list_add_tail(&sensor->head, &iccsense->sensors);
 196        sensor->id = id;
 197        sensor->type = extdev.type;
 198        sensor->i2c = &i2c_bus->i2c;
 199        sensor->addr = addr;
 200        sensor->config = 0x0;
 201        return sensor;
 202}
 203
 204static struct nvkm_iccsense_sensor*
 205nvkm_iccsense_get_sensor(struct nvkm_iccsense *iccsense, u8 id)
 206{
 207        struct nvkm_iccsense_sensor *sensor;
 208        list_for_each_entry(sensor, &iccsense->sensors, head) {
 209                if (sensor->id == id)
 210                        return sensor;
 211        }
 212        return nvkm_iccsense_create_sensor(iccsense, id);
 213}
 214
 215static int
 216nvkm_iccsense_oneinit(struct nvkm_subdev *subdev)
 217{
 218        struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev);
 219        struct nvkm_bios *bios = subdev->device->bios;
 220        struct nvbios_power_budget budget;
 221        struct nvbios_iccsense stbl;
 222        int i, ret;
 223
 224        if (!bios)
 225                return 0;
 226
 227        ret = nvbios_power_budget_header(bios, &budget);
 228        if (!ret && budget.cap_entry != 0xff) {
 229                struct nvbios_power_budget_entry entry;
 230                ret = nvbios_power_budget_entry(bios, &budget,
 231                                                budget.cap_entry, &entry);
 232                if (!ret) {
 233                        iccsense->power_w_max  = entry.avg_w;
 234                        iccsense->power_w_crit = entry.max_w;
 235                }
 236        }
 237
 238        if (nvbios_iccsense_parse(bios, &stbl) || !stbl.nr_entry)
 239                return 0;
 240
 241        iccsense->data_valid = true;
 242        for (i = 0; i < stbl.nr_entry; ++i) {
 243                struct pwr_rail_t *pwr_rail = &stbl.rail[i];
 244                struct nvkm_iccsense_sensor *sensor;
 245                int r;
 246
 247                if (pwr_rail->mode != 1 || !pwr_rail->resistor_count)
 248                        continue;
 249
 250                sensor = nvkm_iccsense_get_sensor(iccsense, pwr_rail->extdev_id);
 251                if (!sensor)
 252                        continue;
 253
 254                if (!sensor->config)
 255                        sensor->config = pwr_rail->config;
 256                else if (sensor->config != pwr_rail->config)
 257                        nvkm_error(subdev, "config mismatch found for extdev %i\n", pwr_rail->extdev_id);
 258
 259                for (r = 0; r < pwr_rail->resistor_count; ++r) {
 260                        struct nvkm_iccsense_rail *rail;
 261                        struct pwr_rail_resistor_t *res = &pwr_rail->resistors[r];
 262                        int (*read)(struct nvkm_iccsense *,
 263                                    struct nvkm_iccsense_rail *);
 264
 265                        if (!res->mohm || !res->enabled)
 266                                continue;
 267
 268                        switch (sensor->type) {
 269                        case NVBIOS_EXTDEV_INA209:
 270                                read = nvkm_iccsense_ina209_read;
 271                                break;
 272                        case NVBIOS_EXTDEV_INA219:
 273                                read = nvkm_iccsense_ina219_read;
 274                                break;
 275                        case NVBIOS_EXTDEV_INA3221:
 276                                read = nvkm_iccsense_ina3221_read;
 277                                break;
 278                        default:
 279                                continue;
 280                        }
 281
 282                        rail = kmalloc(sizeof(*rail), GFP_KERNEL);
 283                        if (!rail)
 284                                return -ENOMEM;
 285
 286                        rail->read = read;
 287                        rail->sensor = sensor;
 288                        rail->idx = r;
 289                        rail->mohm = res->mohm;
 290                        nvkm_debug(subdev, "create rail for extdev %i: { idx: %i, mohm: %i }\n", pwr_rail->extdev_id, r, rail->mohm);
 291                        list_add_tail(&rail->head, &iccsense->rails);
 292                }
 293        }
 294        return 0;
 295}
 296
 297static int
 298nvkm_iccsense_init(struct nvkm_subdev *subdev)
 299{
 300        struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev);
 301        struct nvkm_iccsense_sensor *sensor;
 302        list_for_each_entry(sensor, &iccsense->sensors, head)
 303                nvkm_iccsense_sensor_config(iccsense, sensor);
 304        return 0;
 305}
 306
 307static const struct nvkm_subdev_func
 308iccsense_func = {
 309        .oneinit = nvkm_iccsense_oneinit,
 310        .init = nvkm_iccsense_init,
 311        .dtor = nvkm_iccsense_dtor,
 312};
 313
 314void
 315nvkm_iccsense_ctor(struct nvkm_device *device, int index,
 316                   struct nvkm_iccsense *iccsense)
 317{
 318        nvkm_subdev_ctor(&iccsense_func, device, index, &iccsense->subdev);
 319}
 320
 321int
 322nvkm_iccsense_new_(struct nvkm_device *device, int index,
 323                   struct nvkm_iccsense **iccsense)
 324{
 325        if (!(*iccsense = kzalloc(sizeof(**iccsense), GFP_KERNEL)))
 326                return -ENOMEM;
 327        INIT_LIST_HEAD(&(*iccsense)->sensors);
 328        INIT_LIST_HEAD(&(*iccsense)->rails);
 329        nvkm_iccsense_ctor(device, index, *iccsense);
 330        return 0;
 331}
 332