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/i2c.h> 30 31 static bool 32 nvkm_iccsense_validate_device(struct i2c_adapter *i2c, u8 addr, 33 enum nvbios_extdev_type type, u8 rail) 34 { 35 switch (type) { 36 case NVBIOS_EXTDEV_INA209: 37 case NVBIOS_EXTDEV_INA219: 38 return rail == 0 && nv_rd16i2cr(i2c, addr, 0x0) >= 0; 39 case NVBIOS_EXTDEV_INA3221: 40 return rail <= 3 && 41 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->i2c, rail->addr, shunt_reg, 0, 71 bus_reg, 3, rail->mohm, 10 * 4); 72 } 73 74 static int 75 nvkm_iccsense_ina209_read(struct nvkm_iccsense *iccsense, 76 struct nvkm_iccsense_rail *rail) 77 { 78 return nvkm_iccsense_ina2x9_read(iccsense, rail, 3, 4); 79 } 80 81 static int 82 nvkm_iccsense_ina219_read(struct nvkm_iccsense *iccsense, 83 struct nvkm_iccsense_rail *rail) 84 { 85 return nvkm_iccsense_ina2x9_read(iccsense, rail, 1, 2); 86 } 87 88 static int 89 nvkm_iccsense_ina3221_read(struct nvkm_iccsense *iccsense, 90 struct nvkm_iccsense_rail *rail) 91 { 92 return nvkm_iccsense_poll_lane(rail->i2c, rail->addr, 93 1 + (rail->rail * 2), 3, 94 2 + (rail->rail * 2), 3, rail->mohm, 95 40 * 8); 96 } 97 98 int 99 nvkm_iccsense_read(struct nvkm_iccsense *iccsense, u8 idx) 100 { 101 struct nvkm_iccsense_rail *rail; 102 103 if (!iccsense || idx >= iccsense->rail_count) 104 return -EINVAL; 105 106 rail = &iccsense->rails[idx]; 107 if (!rail->read) 108 return -ENODEV; 109 110 return rail->read(iccsense, rail); 111 } 112 113 int 114 nvkm_iccsense_read_all(struct nvkm_iccsense *iccsense) 115 { 116 int result = 0, i; 117 for (i = 0; i < iccsense->rail_count; ++i) { 118 int res = nvkm_iccsense_read(iccsense, i); 119 if (res >= 0) 120 result += res; 121 else 122 return res; 123 } 124 return result; 125 } 126 127 static void * 128 nvkm_iccsense_dtor(struct nvkm_subdev *subdev) 129 { 130 struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev); 131 132 if (iccsense->rails) 133 kfree(iccsense->rails); 134 135 return iccsense; 136 } 137 138 static int 139 nvkm_iccsense_oneinit(struct nvkm_subdev *subdev) 140 { 141 struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev); 142 struct nvkm_bios *bios = subdev->device->bios; 143 struct nvkm_i2c *i2c = subdev->device->i2c; 144 struct nvbios_iccsense stbl; 145 int i; 146 147 if (!i2c || !bios || nvbios_iccsense_parse(bios, &stbl) 148 || !stbl.nr_entry) 149 return 0; 150 151 iccsense->rails = kmalloc(sizeof(*iccsense->rails) * stbl.nr_entry, 152 GFP_KERNEL); 153 if (!iccsense->rails) 154 return -ENOMEM; 155 156 iccsense->data_valid = true; 157 for (i = 0; i < stbl.nr_entry; ++i) { 158 struct pwr_rail_t *r = &stbl.rail[i]; 159 struct nvbios_extdev_func extdev; 160 struct nvkm_iccsense_rail *rail; 161 struct nvkm_i2c_bus *i2c_bus; 162 u8 addr; 163 164 if (!r->mode || r->resistor_mohm == 0) 165 continue; 166 167 if (nvbios_extdev_parse(bios, r->extdev_id, &extdev)) 168 continue; 169 170 if (extdev.type == 0xff) 171 continue; 172 173 if (extdev.bus) 174 i2c_bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_SEC); 175 else 176 i2c_bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_PRI); 177 if (!i2c_bus) 178 continue; 179 180 addr = extdev.addr >> 1; 181 if (!nvkm_iccsense_validate_device(&i2c_bus->i2c, addr, 182 extdev.type, r->rail)) { 183 iccsense->data_valid = false; 184 nvkm_warn(subdev, "found unknown or invalid rail entry" 185 " type 0x%x rail %i, power reading might be" 186 " invalid\n", extdev.type, r->rail); 187 continue; 188 } 189 190 rail = &iccsense->rails[iccsense->rail_count]; 191 switch (extdev.type) { 192 case NVBIOS_EXTDEV_INA209: 193 rail->read = nvkm_iccsense_ina209_read; 194 break; 195 case NVBIOS_EXTDEV_INA219: 196 rail->read = nvkm_iccsense_ina219_read; 197 break; 198 case NVBIOS_EXTDEV_INA3221: 199 rail->read = nvkm_iccsense_ina3221_read; 200 break; 201 } 202 203 rail->addr = addr; 204 rail->rail = r->rail; 205 rail->mohm = r->resistor_mohm; 206 rail->i2c = &i2c_bus->i2c; 207 ++iccsense->rail_count; 208 } 209 return 0; 210 } 211 212 struct nvkm_subdev_func iccsense_func = { 213 .oneinit = nvkm_iccsense_oneinit, 214 .dtor = nvkm_iccsense_dtor, 215 }; 216 217 void 218 nvkm_iccsense_ctor(struct nvkm_device *device, int index, 219 struct nvkm_iccsense *iccsense) 220 { 221 nvkm_subdev_ctor(&iccsense_func, device, index, 0, &iccsense->subdev); 222 } 223 224 int 225 nvkm_iccsense_new_(struct nvkm_device *device, int index, 226 struct nvkm_iccsense **iccsense) 227 { 228 if (!(*iccsense = kzalloc(sizeof(**iccsense), GFP_KERNEL))) 229 return -ENOMEM; 230 nvkm_iccsense_ctor(device, index, *iccsense); 231 return 0; 232 } 233