xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c (revision c16352b5b35d8f619028ebf855ce42c1f99649e6)
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