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