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