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, enum nvkm_subdev_type type, int inst, 316 struct nvkm_iccsense *iccsense) 317 { 318 nvkm_subdev_ctor(&iccsense_func, device, type, inst, &iccsense->subdev); 319 } 320 321 int 322 nvkm_iccsense_new_(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, 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, type, inst, *iccsense); 330 return 0; 331 } 332