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