Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2015 Martin Peres
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  *
0022  * Authors: Martin Peres
0023  */
0024 #include "priv.h"
0025 
0026 #include <subdev/bios.h>
0027 #include <subdev/bios/extdev.h>
0028 #include <subdev/bios/iccsense.h>
0029 #include <subdev/bios/power_budget.h>
0030 #include <subdev/i2c.h>
0031 
0032 static bool
0033 nvkm_iccsense_validate_device(struct i2c_adapter *i2c, u8 addr,
0034                   enum nvbios_extdev_type type)
0035 {
0036     switch (type) {
0037     case NVBIOS_EXTDEV_INA209:
0038     case NVBIOS_EXTDEV_INA219:
0039         return nv_rd16i2cr(i2c, addr, 0x0) >= 0;
0040     case NVBIOS_EXTDEV_INA3221:
0041         return nv_rd16i2cr(i2c, addr, 0xff) == 0x3220 &&
0042                nv_rd16i2cr(i2c, addr, 0xfe) == 0x5449;
0043     default:
0044         return false;
0045     }
0046 }
0047 
0048 static int
0049 nvkm_iccsense_poll_lane(struct i2c_adapter *i2c, u8 addr, u8 shunt_reg,
0050             u8 shunt_shift, u8 bus_reg, u8 bus_shift, u8 shunt,
0051             u16 lsb)
0052 {
0053     int vshunt = nv_rd16i2cr(i2c, addr, shunt_reg);
0054     int vbus = nv_rd16i2cr(i2c, addr, bus_reg);
0055 
0056     if (vshunt < 0 || vbus < 0)
0057         return -EINVAL;
0058 
0059     vshunt >>= shunt_shift;
0060     vbus >>= bus_shift;
0061 
0062     return vbus * vshunt * lsb / shunt;
0063 }
0064 
0065 static int
0066 nvkm_iccsense_ina2x9_read(struct nvkm_iccsense *iccsense,
0067                           struct nvkm_iccsense_rail *rail,
0068               u8 shunt_reg, u8 bus_reg)
0069 {
0070     return nvkm_iccsense_poll_lane(rail->sensor->i2c, rail->sensor->addr,
0071                        shunt_reg, 0, bus_reg, 3, rail->mohm,
0072                        10 * 4);
0073 }
0074 
0075 static int
0076 nvkm_iccsense_ina209_read(struct nvkm_iccsense *iccsense,
0077               struct nvkm_iccsense_rail *rail)
0078 {
0079     return nvkm_iccsense_ina2x9_read(iccsense, rail, 3, 4);
0080 }
0081 
0082 static int
0083 nvkm_iccsense_ina219_read(struct nvkm_iccsense *iccsense,
0084               struct nvkm_iccsense_rail *rail)
0085 {
0086     return nvkm_iccsense_ina2x9_read(iccsense, rail, 1, 2);
0087 }
0088 
0089 static int
0090 nvkm_iccsense_ina3221_read(struct nvkm_iccsense *iccsense,
0091                struct nvkm_iccsense_rail *rail)
0092 {
0093     return nvkm_iccsense_poll_lane(rail->sensor->i2c, rail->sensor->addr,
0094                        1 + (rail->idx * 2), 3,
0095                        2 + (rail->idx * 2), 3, rail->mohm,
0096                        40 * 8);
0097 }
0098 
0099 static void
0100 nvkm_iccsense_sensor_config(struct nvkm_iccsense *iccsense,
0101                     struct nvkm_iccsense_sensor *sensor)
0102 {
0103     struct nvkm_subdev *subdev = &iccsense->subdev;
0104     nvkm_trace(subdev, "write config of extdev %i: 0x%04x\n", sensor->id, sensor->config);
0105     nv_wr16i2cr(sensor->i2c, sensor->addr, 0x00, sensor->config);
0106 }
0107 
0108 int
0109 nvkm_iccsense_read_all(struct nvkm_iccsense *iccsense)
0110 {
0111     int result = 0;
0112     struct nvkm_iccsense_rail *rail;
0113 
0114     if (!iccsense)
0115         return -EINVAL;
0116 
0117     list_for_each_entry(rail, &iccsense->rails, head) {
0118         int res;
0119         if (!rail->read)
0120             return -ENODEV;
0121 
0122         res = rail->read(iccsense, rail);
0123         if (res < 0)
0124             return res;
0125         result += res;
0126     }
0127     return result;
0128 }
0129 
0130 static void *
0131 nvkm_iccsense_dtor(struct nvkm_subdev *subdev)
0132 {
0133     struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev);
0134     struct nvkm_iccsense_sensor *sensor, *tmps;
0135     struct nvkm_iccsense_rail *rail, *tmpr;
0136 
0137     list_for_each_entry_safe(sensor, tmps, &iccsense->sensors, head) {
0138         list_del(&sensor->head);
0139         kfree(sensor);
0140     }
0141     list_for_each_entry_safe(rail, tmpr, &iccsense->rails, head) {
0142         list_del(&rail->head);
0143         kfree(rail);
0144     }
0145 
0146     return iccsense;
0147 }
0148 
0149 static struct nvkm_iccsense_sensor*
0150 nvkm_iccsense_create_sensor(struct nvkm_iccsense *iccsense, u8 id)
0151 {
0152     struct nvkm_subdev *subdev = &iccsense->subdev;
0153     struct nvkm_bios *bios = subdev->device->bios;
0154     struct nvkm_i2c *i2c = subdev->device->i2c;
0155     struct nvbios_extdev_func extdev;
0156     struct nvkm_i2c_bus *i2c_bus;
0157     struct nvkm_iccsense_sensor *sensor;
0158     u8 addr;
0159 
0160     if (!i2c || !bios || nvbios_extdev_parse(bios, id, &extdev))
0161         return NULL;
0162 
0163     if (extdev.type == 0xff)
0164         return NULL;
0165 
0166     if (extdev.type != NVBIOS_EXTDEV_INA209 &&
0167         extdev.type != NVBIOS_EXTDEV_INA219 &&
0168         extdev.type != NVBIOS_EXTDEV_INA3221) {
0169         iccsense->data_valid = false;
0170         nvkm_error(subdev, "Unknown sensor type %x, power reading "
0171                "disabled\n", extdev.type);
0172         return NULL;
0173     }
0174 
0175     if (extdev.bus)
0176         i2c_bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_SEC);
0177     else
0178         i2c_bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_PRI);
0179     if (!i2c_bus)
0180         return NULL;
0181 
0182     addr = extdev.addr >> 1;
0183     if (!nvkm_iccsense_validate_device(&i2c_bus->i2c, addr,
0184                        extdev.type)) {
0185         iccsense->data_valid = false;
0186         nvkm_warn(subdev, "found invalid sensor id: %i, power reading"
0187               "might be invalid\n", id);
0188         return NULL;
0189     }
0190 
0191     sensor = kmalloc(sizeof(*sensor), GFP_KERNEL);
0192     if (!sensor)
0193         return NULL;
0194 
0195     list_add_tail(&sensor->head, &iccsense->sensors);
0196     sensor->id = id;
0197     sensor->type = extdev.type;
0198     sensor->i2c = &i2c_bus->i2c;
0199     sensor->addr = addr;
0200     sensor->config = 0x0;
0201     return sensor;
0202 }
0203 
0204 static struct nvkm_iccsense_sensor*
0205 nvkm_iccsense_get_sensor(struct nvkm_iccsense *iccsense, u8 id)
0206 {
0207     struct nvkm_iccsense_sensor *sensor;
0208     list_for_each_entry(sensor, &iccsense->sensors, head) {
0209         if (sensor->id == id)
0210             return sensor;
0211     }
0212     return nvkm_iccsense_create_sensor(iccsense, id);
0213 }
0214 
0215 static int
0216 nvkm_iccsense_oneinit(struct nvkm_subdev *subdev)
0217 {
0218     struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev);
0219     struct nvkm_bios *bios = subdev->device->bios;
0220     struct nvbios_power_budget budget;
0221     struct nvbios_iccsense stbl;
0222     int i, ret;
0223 
0224     if (!bios)
0225         return 0;
0226 
0227     ret = nvbios_power_budget_header(bios, &budget);
0228     if (!ret && budget.cap_entry != 0xff) {
0229         struct nvbios_power_budget_entry entry;
0230         ret = nvbios_power_budget_entry(bios, &budget,
0231                                         budget.cap_entry, &entry);
0232         if (!ret) {
0233             iccsense->power_w_max  = entry.avg_w;
0234             iccsense->power_w_crit = entry.max_w;
0235         }
0236     }
0237 
0238     if (nvbios_iccsense_parse(bios, &stbl) || !stbl.nr_entry)
0239         return 0;
0240 
0241     iccsense->data_valid = true;
0242     for (i = 0; i < stbl.nr_entry; ++i) {
0243         struct pwr_rail_t *pwr_rail = &stbl.rail[i];
0244         struct nvkm_iccsense_sensor *sensor;
0245         int r;
0246 
0247         if (pwr_rail->mode != 1 || !pwr_rail->resistor_count)
0248             continue;
0249 
0250         sensor = nvkm_iccsense_get_sensor(iccsense, pwr_rail->extdev_id);
0251         if (!sensor)
0252             continue;
0253 
0254         if (!sensor->config)
0255             sensor->config = pwr_rail->config;
0256         else if (sensor->config != pwr_rail->config)
0257             nvkm_error(subdev, "config mismatch found for extdev %i\n", pwr_rail->extdev_id);
0258 
0259         for (r = 0; r < pwr_rail->resistor_count; ++r) {
0260             struct nvkm_iccsense_rail *rail;
0261             struct pwr_rail_resistor_t *res = &pwr_rail->resistors[r];
0262             int (*read)(struct nvkm_iccsense *,
0263                     struct nvkm_iccsense_rail *);
0264 
0265             if (!res->mohm || !res->enabled)
0266                 continue;
0267 
0268             switch (sensor->type) {
0269             case NVBIOS_EXTDEV_INA209:
0270                 read = nvkm_iccsense_ina209_read;
0271                 break;
0272             case NVBIOS_EXTDEV_INA219:
0273                 read = nvkm_iccsense_ina219_read;
0274                 break;
0275             case NVBIOS_EXTDEV_INA3221:
0276                 read = nvkm_iccsense_ina3221_read;
0277                 break;
0278             default:
0279                 continue;
0280             }
0281 
0282             rail = kmalloc(sizeof(*rail), GFP_KERNEL);
0283             if (!rail)
0284                 return -ENOMEM;
0285 
0286             rail->read = read;
0287             rail->sensor = sensor;
0288             rail->idx = r;
0289             rail->mohm = res->mohm;
0290             nvkm_debug(subdev, "create rail for extdev %i: { idx: %i, mohm: %i }\n", pwr_rail->extdev_id, r, rail->mohm);
0291             list_add_tail(&rail->head, &iccsense->rails);
0292         }
0293     }
0294     return 0;
0295 }
0296 
0297 static int
0298 nvkm_iccsense_init(struct nvkm_subdev *subdev)
0299 {
0300     struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev);
0301     struct nvkm_iccsense_sensor *sensor;
0302     list_for_each_entry(sensor, &iccsense->sensors, head)
0303         nvkm_iccsense_sensor_config(iccsense, sensor);
0304     return 0;
0305 }
0306 
0307 static const struct nvkm_subdev_func
0308 iccsense_func = {
0309     .oneinit = nvkm_iccsense_oneinit,
0310     .init = nvkm_iccsense_init,
0311     .dtor = nvkm_iccsense_dtor,
0312 };
0313 
0314 void
0315 nvkm_iccsense_ctor(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
0316            struct nvkm_iccsense *iccsense)
0317 {
0318     nvkm_subdev_ctor(&iccsense_func, device, type, inst, &iccsense->subdev);
0319 }
0320 
0321 int
0322 nvkm_iccsense_new_(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
0323            struct nvkm_iccsense **iccsense)
0324 {
0325     if (!(*iccsense = kzalloc(sizeof(**iccsense), GFP_KERNEL)))
0326         return -ENOMEM;
0327     INIT_LIST_HEAD(&(*iccsense)->sensors);
0328     INIT_LIST_HEAD(&(*iccsense)->rails);
0329     nvkm_iccsense_ctor(device, type, inst, *iccsense);
0330     return 0;
0331 }