smusat.c revision 1.9.14.1 1 /*-
2 * Copyright (c) 2013 Phileas Fogg
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/kernel.h>
30 #include <sys/malloc.h>
31 #include <sys/device.h>
32 #include <sys/proc.h>
33 #include <sys/mutex.h>
34 #include <sys/time.h>
35 #include <sys/sysctl.h>
36
37 #include <machine/autoconf.h>
38
39 #include <dev/ofw/openfirm.h>
40 #include <dev/i2c/i2cvar.h>
41 #include <dev/sysmon/sysmonvar.h>
42 #include <dev/sysmon/sysmon_taskq.h>
43
44 #include "opt_smusat.h"
45
46 extern int smu_get_datablock(int, uint8_t *, size_t);
47
48 enum {
49 SMUSAT_SENSOR_TEMP,
50 SMUSAT_SENSOR_CURRENT,
51 SMUSAT_SENSOR_VOLTAGE,
52 SMUSAT_SENSOR_POWER,
53 };
54
55 struct smusat_softc;
56
57 struct smusat_sensor {
58 struct smusat_softc *sc;
59
60 char location[32];
61 int type;
62 int reg;
63 int zone;
64 int shift;
65 int offset;
66 int scale;
67 int current_value;
68 };
69
70 #define SMUSAT_MAX_SENSORS 16
71 #define SMUSAT_MAX_SME_SENSORS SMUSAT_MAX_SENSORS
72
73 struct smusat_softc {
74 device_t sc_dev;
75 int sc_node;
76 i2c_addr_t sc_addr;
77 uint8_t sc_cache[16];
78 time_t sc_last_update;
79 struct i2c_controller *sc_i2c;
80 struct sysctlnode *sc_sysctl_me;
81
82 int sc_num_sensors;
83 struct smusat_sensor sc_sensors[SMUSAT_MAX_SENSORS];
84
85 struct sysmon_envsys *sc_sme;
86 envsys_data_t sc_sme_sensors[SMUSAT_MAX_SME_SENSORS];
87 };
88
89 #ifdef SMUSAT_DEBUG
90 #define DPRINTF printf
91 #else
92 #define DPRINTF while (0) printf
93 #endif
94
95 static int smusat_match(device_t, struct cfdata *, void *);
96 static void smusat_attach(device_t, device_t, void *);
97 static void smusat_setup_sme(struct smusat_softc *);
98 static void smusat_sme_refresh(struct sysmon_envsys *, envsys_data_t *);
99 static int smusat_sensors_update(struct smusat_softc *);
100 static int smusat_sensor_read(struct smusat_sensor *, int *);
101 static int smusat_sysctl_sensor_value(SYSCTLFN_ARGS);
102
103 CFATTACH_DECL_NEW(smusat, sizeof(struct smusat_softc),
104 smusat_match, smusat_attach, NULL, NULL);
105
106 static const struct device_compatible_entry compat_data[] = {
107 { .compat = "sat" },
108 { .compat = "smu-sat" },
109 DEVICE_COMPAT_EOL
110 };
111
112 static int
113 smusat_match(device_t parent, struct cfdata *cf, void *aux)
114 {
115 struct i2c_attach_args *ia = aux;
116 int match_result;
117
118 if (iic_use_direct_match(ia, cf, compat_data, &match_result))
119 return match_result;
120
121 if (ia->ia_addr == 0x58)
122 return I2C_MATCH_ADDRESS_ONLY;
123
124 return 0;
125 }
126
127 static void
128 smusat_attach(device_t parent, device_t self, void *aux)
129 {
130 struct i2c_attach_args *ia = aux;
131 struct smusat_softc *sc = device_private(self);
132 struct smusat_sensor *sensor;
133 struct sysctlnode *sysctl_sensors, *sysctl_sensor, *sysctl_node;
134 char type[32], sysctl_sensor_name[32];
135 int node, i, j;
136
137 sc->sc_dev = self;
138 sc->sc_node = devhandle_to_of(device_handle(self));
139 sc->sc_addr = ia->ia_addr;
140 sc->sc_i2c = ia->ia_tag;
141
142 sysctl_createv(NULL, 0, NULL, (void *) &sc->sc_sysctl_me,
143 CTLFLAG_READWRITE,
144 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
145 NULL, 0, NULL, 0,
146 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
147
148 for (node = OF_child(sc->sc_node);
149 (node != 0) && (sc->sc_num_sensors < SMUSAT_MAX_SENSORS);
150 node = OF_peer(node)) {
151 sensor = &sc->sc_sensors[sc->sc_num_sensors];
152 sensor->sc = sc;
153
154 memset(sensor->location, 0, sizeof(sensor->location));
155 OF_getprop(node, "location", sensor->location,
156 sizeof(sensor->location));
157
158 if (OF_getprop(node, "reg", &sensor->reg,
159 sizeof(sensor->reg)) <= 0)
160 continue;
161
162 if ((sensor->reg < 0x30) || (sensor->reg > 0x37))
163 continue;
164 sensor->reg -= 0x30;
165
166 if (OF_getprop(node, "zone", &sensor->zone,
167 sizeof(sensor->zone)) <= 0)
168 continue;
169
170 memset(type, 0, sizeof(type));
171 OF_getprop(node, "device_type", type, sizeof(type));
172
173 if (strcmp(type, "temp-sensor") == 0) {
174 sensor->type = SMUSAT_SENSOR_TEMP;
175 sensor->shift = 10;
176 } else if (strcmp(type, "current-sensor") == 0) {
177 sensor->type = SMUSAT_SENSOR_CURRENT;
178 sensor->shift = 8;
179 } else if (strcmp(type, "voltage-sensor") == 0) {
180 sensor->type = SMUSAT_SENSOR_VOLTAGE;
181 sensor->shift = 4;
182 } else if (strcmp(type, "power-sensor") == 0) {
183 sensor->type = SMUSAT_SENSOR_POWER;
184 sensor->shift = 0;
185 }
186
187 DPRINTF("sensor: location %s reg %x zone %d type %s\n",
188 sensor->location, sensor->reg, sensor->zone, type);
189
190 sc->sc_num_sensors++;
191 }
192
193 /* Create sysctl nodes for each sensor */
194
195 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_sensors,
196 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
197 CTLTYPE_NODE, "sensors", NULL,
198 NULL, 0, NULL, 0,
199 CTL_MACHDEP,
200 sc->sc_sysctl_me->sysctl_num,
201 CTL_CREATE, CTL_EOL);
202
203 for (i = 0; i < sc->sc_num_sensors; i++) {
204 sensor = &sc->sc_sensors[i];
205
206 for (j = 0; j < strlen(sensor->location); j++) {
207 sysctl_sensor_name[j] = tolower(sensor->location[j]);
208 if (sysctl_sensor_name[j] == ' ')
209 sysctl_sensor_name[j] = '_';
210 }
211 sysctl_sensor_name[j] = '\0';
212
213 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_sensor,
214 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
215 CTLTYPE_NODE, sysctl_sensor_name, "sensor information",
216 NULL, 0, NULL, 0,
217 CTL_MACHDEP,
218 sc->sc_sysctl_me->sysctl_num,
219 sysctl_sensors->sysctl_num,
220 CTL_CREATE, CTL_EOL);
221
222 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
223 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
224 CTLTYPE_INT, "zone", "sensor zone",
225 NULL, 0, &sensor->zone, 0,
226 CTL_MACHDEP,
227 sc->sc_sysctl_me->sysctl_num,
228 sysctl_sensors->sysctl_num,
229 sysctl_sensor->sysctl_num,
230 CTL_CREATE, CTL_EOL);
231
232 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
233 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
234 CTLTYPE_INT, "value", "sensor current value",
235 smusat_sysctl_sensor_value, 0, (void *) sensor, 0,
236 CTL_MACHDEP,
237 sc->sc_sysctl_me->sysctl_num,
238 sysctl_sensors->sysctl_num,
239 sysctl_sensor->sysctl_num,
240 CTL_CREATE, CTL_EOL);
241 }
242
243 smusat_setup_sme(sc);
244
245 printf("\n");
246 }
247
248 static void
249 smusat_setup_sme(struct smusat_softc *sc)
250 {
251 struct smusat_sensor *sensor;
252 envsys_data_t *sme_sensor;
253 int i;
254
255 sc->sc_sme = sysmon_envsys_create();
256
257 for (i = 0; i < sc->sc_num_sensors; i++) {
258 sme_sensor = &sc->sc_sme_sensors[i];
259 sensor = &sc->sc_sensors[i];
260
261 switch (sensor->type) {
262 case SMUSAT_SENSOR_TEMP:
263 sme_sensor->units = ENVSYS_STEMP;
264 break;
265 case SMUSAT_SENSOR_CURRENT:
266 sme_sensor->units = ENVSYS_SAMPS;
267 break;
268 case SMUSAT_SENSOR_VOLTAGE:
269 sme_sensor->units = ENVSYS_SVOLTS_DC;
270 break;
271 case SMUSAT_SENSOR_POWER:
272 sme_sensor->units = ENVSYS_SWATTS;
273 break;
274 default:
275 sme_sensor->units = ENVSYS_INTEGER;
276 }
277
278 sme_sensor->state = ENVSYS_SINVALID;
279 snprintf(sme_sensor->desc, sizeof(sme_sensor->desc),
280 "%s", sensor->location);
281
282 if (sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor)) {
283 sysmon_envsys_destroy(sc->sc_sme);
284 return;
285 }
286 }
287
288 sc->sc_sme->sme_name = device_xname(sc->sc_dev);
289 sc->sc_sme->sme_cookie = sc;
290 sc->sc_sme->sme_refresh = smusat_sme_refresh;
291
292 if (sysmon_envsys_register(sc->sc_sme)) {
293 aprint_error_dev(sc->sc_dev,
294 "unable to register with sysmon\n");
295 sysmon_envsys_destroy(sc->sc_sme);
296 }
297 }
298
299 static void
300 smusat_sme_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
301 {
302 struct smusat_softc *sc = sme->sme_cookie;
303 struct smusat_sensor *sensor;
304 int which = edata->sensor;
305 int ret;
306
307 edata->state = ENVSYS_SINVALID;
308
309 if (which < sc->sc_num_sensors) {
310 sensor = &sc->sc_sensors[which];
311
312 ret = smusat_sensor_read(sensor, NULL);
313 if (ret == 0) {
314 switch (sensor->type) {
315 case SMUSAT_SENSOR_TEMP:
316 edata->value_cur = sensor->current_value *
317 1000000 + 273150000;
318 break;
319 case SMUSAT_SENSOR_CURRENT:
320 edata->value_cur = sensor->current_value * 1000000;
321 break;
322 case SMUSAT_SENSOR_VOLTAGE:
323 edata->value_cur = sensor->current_value * 1000000;
324 break;
325 case SMUSAT_SENSOR_POWER:
326 edata->value_cur = sensor->current_value * 1000000;
327 break;
328 default:
329 edata->value_cur = sensor->current_value;
330 }
331
332 edata->state = ENVSYS_SVALID;
333 }
334 }
335 }
336
337 static int
338 smusat_sensors_update(struct smusat_softc *sc)
339 {
340 u_char reg = 0x3f;
341 int ret;
342
343 iic_acquire_bus(sc->sc_i2c, 0);
344 ret = iic_exec(sc->sc_i2c, I2C_OP_READ, sc->sc_addr, ®, 1, sc->sc_cache, 16, 0);
345 iic_release_bus(sc->sc_i2c, 0);
346
347 if (ret != 0)
348 return (ret);
349
350 sc->sc_last_update = time_uptime;
351
352 return 0;
353 }
354
355 static int
356 smusat_sensor_read(struct smusat_sensor *sensor, int *value)
357 {
358 struct smusat_softc *sc = sensor->sc;
359 int ret, reg;
360
361 if (time_uptime - sc->sc_last_update > 1) {
362 ret = smusat_sensors_update(sc);
363 if (ret != 0)
364 return ret;
365 }
366
367 reg = sensor->reg << 1;
368 sensor->current_value = (sc->sc_cache[reg] << 8) + sc->sc_cache[reg + 1];
369 sensor->current_value <<= sensor->shift;
370 /* Discard the .16 */
371 sensor->current_value >>= 16;
372
373 if (value != NULL)
374 *value = sensor->current_value;
375
376 return 0;
377 }
378
379 static int
380 smusat_sysctl_sensor_value(SYSCTLFN_ARGS)
381 {
382 struct sysctlnode node = *rnode;
383 struct smusat_sensor *sensor = node.sysctl_data;
384 int value = 0;
385 int ret;
386
387 node.sysctl_data = &value;
388
389 ret = smusat_sensor_read(sensor, &value);
390 if (ret != 0)
391 return (ret);
392
393 return sysctl_lookup(SYSCTLFN_CALL(&node));
394 }
395
396 SYSCTL_SETUP(smusat_sysctl_setup, "SMU-SAT sysctl subtree setup")
397 {
398 sysctl_createv(NULL, 0, NULL, NULL,
399 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
400 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
401 }
402