GNU Linux-libre 4.14.290-gnu1
[releases.git] / drivers / gpu / drm / nouveau / nvkm / subdev / iccsense / base.c
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, int index,
316                    struct nvkm_iccsense *iccsense)
317 {
318         nvkm_subdev_ctor(&iccsense_func, device, index, &iccsense->subdev);
319 }
320
321 int
322 nvkm_iccsense_new_(struct nvkm_device *device, int index,
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, index, *iccsense);
330         return 0;
331 }