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