Home | History | Annotate | Line # | Download | only in iccsense
      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