smusat.c revision 1.2.4.2 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 <macppc/dev/smuiicvar.h>
45
46 #include "opt_smusat.h"
47
48 extern int smu_get_datablock(int, uint8_t *, size_t);
49
50 enum {
51 SMUSAT_SENSOR_TEMP,
52 SMUSAT_SENSOR_CURRENT,
53 SMUSAT_SENSOR_VOLTAGE,
54 SMUSAT_SENSOR_POWER,
55 };
56
57 struct smusat_softc;
58
59 struct smusat_sensor {
60 struct smusat_softc *sc;
61
62 char location[32];
63 int type;
64 int reg;
65 int zone;
66 int shift;
67 int offset;
68 int scale;
69 int current_value;
70 };
71
72 #define SMUSAT_MAX_SENSORS 16
73 #define SMUSAT_MAX_SME_SENSORS SMUSAT_MAX_SENSORS
74
75 struct smusat_softc {
76 device_t sc_dev;
77 int sc_node;
78 i2c_addr_t sc_addr;
79 uint8_t sc_cache[16];
80 time_t sc_last_update;
81 struct i2c_controller *sc_i2c;
82 struct sysctlnode *sc_sysctl_me;
83
84 int sc_num_sensors;
85 struct smusat_sensor sc_sensors[SMUSAT_MAX_SENSORS];
86
87 struct sysmon_envsys *sc_sme;
88 envsys_data_t sc_sme_sensors[SMUSAT_MAX_SME_SENSORS];
89 };
90
91 #ifdef SMUSAT_DEBUG
92 #define DPRINTF printf
93 #else
94 #define DPRINTF while (0) printf
95 #endif
96
97 static int smusat_match(device_t, struct cfdata *, void *);
98 static void smusat_attach(device_t, device_t, void *);
99 static void smusat_setup_sme(struct smusat_softc *);
100 static void smusat_sme_refresh(struct sysmon_envsys *, envsys_data_t *);
101 static int smusat_sensors_update(struct smusat_softc *);
102 static int smusat_sensor_read(struct smusat_sensor *, int *);
103 static int smusat_sysctl_sensor_value(SYSCTLFN_ARGS);
104
105 CFATTACH_DECL_NEW(smusat, sizeof(struct smusat_softc),
106 smusat_match, smusat_attach, NULL, NULL);
107
108 static const char * smusat_compats[] = {
109 "sat",
110 "smu-sat",
111 NULL
112 };
113
114 static const struct device_compatible_entry smusat_compat_data[] = {
115 DEVICE_COMPAT_ENTRY(smusat_compats),
116 DEVICE_COMPAT_TERMINATOR
117 };
118
119 static int
120 smusat_match(device_t parent, struct cfdata *cf, void *aux)
121 {
122 struct i2c_attach_args *ia = aux;
123 int match_result;
124
125 if (iic_use_direct_match(ia, cf, smusat_compat_data, &match_result))
126 return match_result;
127
128 if (ia->ia_addr == 0x58)
129 return I2C_MATCH_ADDRESS_ONLY;
130
131 return 0;
132 }
133
134 static void
135 smusat_attach(device_t parent, device_t self, void *aux)
136 {
137 struct i2c_attach_args *ia = aux;
138 struct smusat_softc *sc = device_private(self);
139 struct smusat_sensor *sensor;
140 struct sysctlnode *sysctl_sensors, *sysctl_sensor, *sysctl_node;
141 char type[32], sysctl_sensor_name[32];
142 int node, i, j;
143
144 sc->sc_dev = self;
145 sc->sc_node = ia->ia_cookie;
146 sc->sc_addr = ia->ia_addr;
147 sc->sc_i2c = ia->ia_tag;
148
149 sysctl_createv(NULL, 0, NULL, (void *) &sc->sc_sysctl_me,
150 CTLFLAG_READWRITE,
151 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
152 NULL, 0, NULL, 0,
153 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
154
155 for (node = OF_child(sc->sc_node);
156 (node != 0) && (sc->sc_num_sensors < SMUSAT_MAX_SENSORS);
157 node = OF_peer(node)) {
158 sensor = &sc->sc_sensors[sc->sc_num_sensors];
159 sensor->sc = sc;
160
161 memset(sensor->location, 0, sizeof(sensor->location));
162 OF_getprop(node, "location", sensor->location,
163 sizeof(sensor->location));
164
165 if (OF_getprop(node, "reg", &sensor->reg,
166 sizeof(sensor->reg)) <= 0)
167 continue;
168
169 if ((sensor->reg < 0x30) || (sensor->reg > 0x37))
170 continue;
171 sensor->reg -= 0x30;
172
173 if (OF_getprop(node, "zone", &sensor->zone,
174 sizeof(sensor->zone)) <= 0)
175 continue;
176
177 memset(type, 0, sizeof(type));
178 OF_getprop(node, "device_type", type, sizeof(type));
179
180 if (strcmp(type, "temp-sensor") == 0) {
181 sensor->type = SMUSAT_SENSOR_TEMP;
182 sensor->shift = 10;
183 } else if (strcmp(type, "current-sensor") == 0) {
184 sensor->type = SMUSAT_SENSOR_CURRENT;
185 sensor->shift = 8;
186 } else if (strcmp(type, "voltage-sensor") == 0) {
187 sensor->type = SMUSAT_SENSOR_VOLTAGE;
188 sensor->shift = 4;
189 } else if (strcmp(type, "power-sensor") == 0) {
190 sensor->type = SMUSAT_SENSOR_POWER;
191 sensor->shift = 0;
192 }
193
194 DPRINTF("sensor: location %s reg %x zone %d type %s\n",
195 sensor->location, sensor->reg, sensor->zone, type);
196
197 sc->sc_num_sensors++;
198 }
199
200 /* Create sysctl nodes for each sensor */
201
202 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_sensors,
203 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
204 CTLTYPE_NODE, "sensors", NULL,
205 NULL, 0, NULL, 0,
206 CTL_MACHDEP,
207 sc->sc_sysctl_me->sysctl_num,
208 CTL_CREATE, CTL_EOL);
209
210 for (i = 0; i < sc->sc_num_sensors; i++) {
211 sensor = &sc->sc_sensors[i];
212
213 for (j = 0; j < strlen(sensor->location); j++) {
214 sysctl_sensor_name[j] = tolower(sensor->location[j]);
215 if (sysctl_sensor_name[j] == ' ')
216 sysctl_sensor_name[j] = '_';
217 }
218 sysctl_sensor_name[j] = '\0';
219
220 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_sensor,
221 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
222 CTLTYPE_NODE, sysctl_sensor_name, "sensor information",
223 NULL, 0, NULL, 0,
224 CTL_MACHDEP,
225 sc->sc_sysctl_me->sysctl_num,
226 sysctl_sensors->sysctl_num,
227 CTL_CREATE, CTL_EOL);
228
229 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
230 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
231 CTLTYPE_INT, "zone", "sensor zone",
232 NULL, 0, &sensor->zone, 0,
233 CTL_MACHDEP,
234 sc->sc_sysctl_me->sysctl_num,
235 sysctl_sensors->sysctl_num,
236 sysctl_sensor->sysctl_num,
237 CTL_CREATE, CTL_EOL);
238
239 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
240 CTLFLAG_READONLY | CTLFLAG_OWNDESC,
241 CTLTYPE_INT, "value", "sensor current value",
242 smusat_sysctl_sensor_value, 0, (void *) sensor, 0,
243 CTL_MACHDEP,
244 sc->sc_sysctl_me->sysctl_num,
245 sysctl_sensors->sysctl_num,
246 sysctl_sensor->sysctl_num,
247 CTL_CREATE, CTL_EOL);
248 }
249
250 smusat_setup_sme(sc);
251
252 printf("\n");
253 }
254
255 static void
256 smusat_setup_sme(struct smusat_softc *sc)
257 {
258 struct smusat_sensor *sensor;
259 envsys_data_t *sme_sensor;
260 int i;
261
262 sc->sc_sme = sysmon_envsys_create();
263
264 for (i = 0; i < sc->sc_num_sensors; i++) {
265 sme_sensor = &sc->sc_sme_sensors[i];
266 sensor = &sc->sc_sensors[i];
267
268 switch (sensor->type) {
269 case SMUSAT_SENSOR_TEMP:
270 sme_sensor->units = ENVSYS_STEMP;
271 break;
272 case SMUSAT_SENSOR_CURRENT:
273 sme_sensor->units = ENVSYS_SAMPS;
274 break;
275 case SMUSAT_SENSOR_VOLTAGE:
276 sme_sensor->units = ENVSYS_SVOLTS_DC;
277 break;
278 case SMUSAT_SENSOR_POWER:
279 sme_sensor->units = ENVSYS_SWATTS;
280 break;
281 default:
282 sme_sensor->units = ENVSYS_INTEGER;
283 }
284
285 sme_sensor->state = ENVSYS_SINVALID;
286 snprintf(sme_sensor->desc, sizeof(sme_sensor->desc),
287 "%s", sensor->location);
288
289 if (sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor)) {
290 sysmon_envsys_destroy(sc->sc_sme);
291 return;
292 }
293 }
294
295 sc->sc_sme->sme_name = device_xname(sc->sc_dev);
296 sc->sc_sme->sme_cookie = sc;
297 sc->sc_sme->sme_refresh = smusat_sme_refresh;
298
299 if (sysmon_envsys_register(sc->sc_sme)) {
300 aprint_error_dev(sc->sc_dev,
301 "unable to register with sysmon\n");
302 sysmon_envsys_destroy(sc->sc_sme);
303 }
304 }
305
306 static void
307 smusat_sme_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
308 {
309 struct smusat_softc *sc = sme->sme_cookie;
310 struct smusat_sensor *sensor;
311 int which = edata->sensor;
312 int ret;
313
314 edata->state = ENVSYS_SINVALID;
315
316 if (which < sc->sc_num_sensors) {
317 sensor = &sc->sc_sensors[which];
318
319 ret = smusat_sensor_read(sensor, NULL);
320 if (ret == 0) {
321 switch (sensor->type) {
322 case SMUSAT_SENSOR_TEMP:
323 edata->value_cur = sensor->current_value *
324 1000000 + 273150000;
325 break;
326 case SMUSAT_SENSOR_CURRENT:
327 edata->value_cur = sensor->current_value * 1000000;
328 break;
329 case SMUSAT_SENSOR_VOLTAGE:
330 edata->value_cur = sensor->current_value * 1000000;
331 break;
332 case SMUSAT_SENSOR_POWER:
333 edata->value_cur = sensor->current_value * 1000000;
334 break;
335 default:
336 edata->value_cur = sensor->current_value;
337 }
338
339 edata->state = ENVSYS_SVALID;
340 }
341 }
342 }
343
344 static int
345 smusat_sensors_update(struct smusat_softc *sc)
346 {
347 u_char reg = 0x3f;
348 int ret;
349
350 iic_acquire_bus(sc->sc_i2c, 0);
351 ret = iic_exec(sc->sc_i2c, I2C_OP_READ, sc->sc_addr, ®, 1, sc->sc_cache, 16, 0);
352 iic_release_bus(sc->sc_i2c, 0);
353
354 if (ret != 0)
355 return (ret);
356
357 sc->sc_last_update = time_uptime;
358
359 return 0;
360 }
361
362 static int
363 smusat_sensor_read(struct smusat_sensor *sensor, int *value)
364 {
365 struct smusat_softc *sc = sensor->sc;
366 int ret, reg;
367
368 if (time_uptime - sc->sc_last_update > 1) {
369 ret = smusat_sensors_update(sc);
370 if (ret != 0)
371 return ret;
372 }
373
374 reg = sensor->reg << 1;
375 sensor->current_value = (sc->sc_cache[reg] << 8) + sc->sc_cache[reg + 1];
376 sensor->current_value <<= sensor->shift;
377 /* Discard the .16 */
378 sensor->current_value >>= 16;
379
380 if (value != NULL)
381 *value = sensor->current_value;
382
383 return 0;
384 }
385
386 static int
387 smusat_sysctl_sensor_value(SYSCTLFN_ARGS)
388 {
389 struct sysctlnode node = *rnode;
390 struct smusat_sensor *sensor = node.sysctl_data;
391 int value = 0;
392 int ret;
393
394 node.sysctl_data = &value;
395
396 ret = smusat_sensor_read(sensor, &value);
397 if (ret != 0)
398 return (ret);
399
400 return sysctl_lookup(SYSCTLFN_CALL(&node));
401 }
402
403 SYSCTL_SETUP(smusat_sysctl_setup, "SMU-SAT sysctl subtree setup")
404 {
405 sysctl_createv(NULL, 0, NULL, NULL,
406 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
407 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
408 }
409