root/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nvkm_iccsense_validate_device
  2. nvkm_iccsense_poll_lane
  3. nvkm_iccsense_ina2x9_read
  4. nvkm_iccsense_ina209_read
  5. nvkm_iccsense_ina219_read
  6. nvkm_iccsense_ina3221_read
  7. nvkm_iccsense_sensor_config
  8. nvkm_iccsense_read_all
  9. nvkm_iccsense_dtor
  10. nvkm_iccsense_create_sensor
  11. nvkm_iccsense_get_sensor
  12. nvkm_iccsense_oneinit
  13. nvkm_iccsense_init
  14. nvkm_iccsense_ctor
  15. nvkm_iccsense_new_

   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 
  32 static bool
  33 nvkm_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 
  48 static int
  49 nvkm_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 
  65 static int
  66 nvkm_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 
  75 static int
  76 nvkm_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 
  82 static int
  83 nvkm_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 
  89 static int
  90 nvkm_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 
  99 static void
 100 nvkm_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 
 108 int
 109 nvkm_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 
 130 static void *
 131 nvkm_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 
 149 static struct nvkm_iccsense_sensor*
 150 nvkm_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 
 204 static struct nvkm_iccsense_sensor*
 205 nvkm_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 
 215 static int
 216 nvkm_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 
 297 static int
 298 nvkm_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 
 307 static const struct nvkm_subdev_func
 308 iccsense_func = {
 309         .oneinit = nvkm_iccsense_oneinit,
 310         .init = nvkm_iccsense_init,
 311         .dtor = nvkm_iccsense_dtor,
 312 };
 313 
 314 void
 315 nvkm_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 
 321 int
 322 nvkm_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 }

/* [<][>][^][v][top][bottom][index][help] */