xref: /netbsd-src/sys/dev/ic/apple_smc_temp.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: apple_smc_temp.c,v 1.5 2015/04/23 23:23:00 pgoyette Exp $	*/
2 
3 /*
4  * Apple System Management Controller: Temperature Sensors
5  */
6 
7 /*-
8  * Copyright (c) 2013 The NetBSD Foundation, Inc.
9  * All rights reserved.
10  *
11  * This code is derived from software contributed to The NetBSD Foundation
12  * by Taylor R. Campbell.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: apple_smc_temp.c,v 1.5 2015/04/23 23:23:00 pgoyette Exp $");
38 
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/device.h>
42 #include <sys/kmem.h>
43 #include <sys/module.h>
44 #include <sys/systm.h>
45 
46 #include <dev/ic/apple_smc.h>
47 
48 #include <dev/sysmon/sysmonvar.h>
49 
50 struct apple_smc_temp_softc {
51 	device_t		sc_dev;
52 	struct apple_smc_tag	*sc_smc;
53 	struct sysmon_envsys	*sc_sme;
54 	struct {
55 		struct apple_smc_key	*sensor_key;
56 		struct envsys_data	sensor_data;
57 	}			*sc_sensors;
58 	size_t			sc_nsensors;
59 };
60 
61 static int	apple_smc_temp_match(device_t, cfdata_t, void *);
62 static void	apple_smc_temp_attach(device_t, device_t, void *);
63 static int	apple_smc_temp_detach(device_t, int);
64 static void	apple_smc_temp_refresh(struct sysmon_envsys *,
65 		    struct envsys_data *);
66 static int	apple_smc_temp_count_sensors(struct apple_smc_tag *,
67 		    uint32_t *);
68 static void	apple_smc_temp_count_sensors_scanner(struct apple_smc_tag *,
69 		    void *, struct apple_smc_key *);
70 static int	apple_smc_temp_find_sensors(struct apple_smc_temp_softc *);
71 static int	apple_smc_temp_find_sensors_init(struct apple_smc_tag *,
72 		    void *, uint32_t);
73 static void	apple_smc_temp_find_sensors_scanner(struct apple_smc_tag *,
74 		    void *, struct apple_smc_key *);
75 static void	apple_smc_temp_release_keys(struct apple_smc_temp_softc *);
76 static int	apple_smc_scan_temp_sensors(struct apple_smc_tag *, void *,
77 		    int (*)(struct apple_smc_tag *, void *, uint32_t),
78 		    void (*)(struct apple_smc_tag *, void *,
79 			struct apple_smc_key *));
80 static int	apple_smc_bound_temp_sensors(struct apple_smc_tag *,
81 		    uint32_t *, uint32_t *);
82 static bool	apple_smc_temp_sensor_p(const struct apple_smc_key *);
83 
84 CFATTACH_DECL_NEW(apple_smc_temp, sizeof(struct apple_smc_temp_softc),
85     apple_smc_temp_match, apple_smc_temp_attach, apple_smc_temp_detach, NULL);
86 
87 static int
88 apple_smc_temp_match(device_t parent, cfdata_t match, void *aux)
89 {
90 	const struct apple_smc_attach_args *const asa = aux;
91 	uint32_t nsensors;
92 	int error;
93 
94 	/* Find how many temperature sensors we have. */
95 	error = apple_smc_temp_count_sensors(asa->asa_smc, &nsensors);
96 	if (error)
97 		return 0;
98 
99 	/* If there aren't any, don't bother attaching.  */
100 	if (nsensors == 0)
101 		return 0;
102 
103 	return 1;
104 }
105 
106 static void
107 apple_smc_temp_attach(device_t parent, device_t self, void *aux)
108 {
109 	struct apple_smc_temp_softc *const sc = device_private(self);
110 	const struct apple_smc_attach_args *const asa = aux;
111 	int error;
112 
113 	/* Identify ourselves.  */
114 	aprint_normal(": Apple SMC temperature sensors\n");
115 
116 	/* Initialize the softc. */
117 	sc->sc_dev = self;
118 	sc->sc_smc = asa->asa_smc;
119 
120 	/* Create a sysmon_envsys record, but don't register it yet.  */
121 	sc->sc_sme = sysmon_envsys_create();
122 	sc->sc_sme->sme_name = device_xname(self);
123 	sc->sc_sme->sme_cookie = sc;
124 	sc->sc_sme->sme_refresh = apple_smc_temp_refresh;
125 
126 	/* Find and attach all the sensors.  */
127 	error = apple_smc_temp_find_sensors(sc);
128 	if (error) {
129 		aprint_error_dev(self, "failed to find sensors: %d\n", error);
130 		goto fail;
131 	}
132 
133 	/* Sensors are all attached.  Register with sysmon_envsys now.  */
134 	error = sysmon_envsys_register(sc->sc_sme);
135 	if (error) {
136 		aprint_error_dev(self, "failed to register with sysmon_envsys:"
137 		    " %d\n", error);
138 		goto fail;
139 	}
140 
141 	/* Success!  */
142 	return;
143 
144 fail:	sysmon_envsys_destroy(sc->sc_sme);
145 	sc->sc_sme = NULL;
146 }
147 
148 static int
149 apple_smc_temp_detach(device_t self, int flags)
150 {
151 	struct apple_smc_temp_softc *const sc = device_private(self);
152 
153 	/* If we registered with sysmon_envsys, unregister.  */
154 	if (sc->sc_sme != NULL) {
155 		sysmon_envsys_unregister(sc->sc_sme);
156 		sc->sc_sme = NULL;
157 
158 		KASSERT(sc->sc_sensors != NULL);
159 		KASSERT(sc->sc_nsensors > 0);
160 
161 		/* Release the keys and free the memory for sensor records.  */
162 		apple_smc_temp_release_keys(sc);
163 		kmem_free(sc->sc_sensors,
164 		    (sizeof(sc->sc_sensors[0]) * sc->sc_nsensors));
165 		sc->sc_sensors = NULL;
166 		sc->sc_nsensors = 0;
167 	}
168 
169 	/* Success!  */
170 	return 0;
171 }
172 
173 static void
174 apple_smc_temp_refresh(struct sysmon_envsys *sme, struct envsys_data *edata)
175 {
176 	struct apple_smc_temp_softc *const sc = sme->sme_cookie;
177 	const struct apple_smc_key *key;
178 	uint16_t utemp16;
179 	int32_t temp;
180 	int error;
181 
182 	/* Sanity-check the sensor number out of paranoia.  */
183 	if (edata->sensor >= sc->sc_nsensors) {
184 		aprint_error_dev(sc->sc_dev, "unknown sensor %"PRIu32"\n",
185 		    edata->sensor);
186 		return;
187 	}
188 
189 	/* Read the raw temperature sensor value.  */
190 	key = sc->sc_sensors[edata->sensor].sensor_key;
191 	KASSERT(key != NULL);
192 	error = apple_smc_read_key_2(sc->sc_smc, key, &utemp16);
193 	if (error) {
194 		aprint_error_dev(sc->sc_dev,
195 		    "failed to read temperature sensor %"PRIu32" (%s): %d\n",
196 		    edata->sensor, apple_smc_key_name(key), error);
197 		edata->state = ENVSYS_SINVALID;
198 		return;
199 	}
200 
201 	/* Sign-extend, in case we ever get below freezing...  */
202 	temp = (int16_t)utemp16;
203 
204 	/* Convert to `millicentigrade'.  */
205 	temp *= 250;
206 	temp >>= 6;
207 
208 	/* Convert to millikelvins.  */
209 	temp += 273150;
210 
211 	/* Finally, convert to microkelvins as sysmon_envsys wants.  */
212 	temp *= 1000;
213 
214 	/* Success!  */
215 	edata->value_cur = temp;
216 	edata->state = ENVSYS_SVALID;
217 }
218 
219 static int
220 apple_smc_temp_count_sensors(struct apple_smc_tag *smc, uint32_t *nsensors)
221 {
222 
223 	/* Start with zero sensors.  */
224 	*nsensors = 0;
225 
226 	/* Count 'em.  */
227 	return apple_smc_scan_temp_sensors(smc, nsensors,
228 	    NULL,
229 	    &apple_smc_temp_count_sensors_scanner);
230 }
231 
232 static void
233 apple_smc_temp_count_sensors_scanner(struct apple_smc_tag *smc, void *arg,
234     struct apple_smc_key *key)
235 {
236 	uint32_t *const nsensors = arg;
237 
238 	(*nsensors)++;
239 	apple_smc_release_key(smc, key);
240 }
241 
242 struct fss {			/* Find Sensors State */
243 	struct apple_smc_temp_softc	*fss_sc;
244 	unsigned int			fss_sensor;
245 };
246 
247 static int
248 apple_smc_temp_find_sensors(struct apple_smc_temp_softc *sc)
249 {
250 	struct fss fss;
251 	int error;
252 
253 	/* Start with zero sensors.  */
254 	fss.fss_sc = sc;
255 	fss.fss_sensor = 0;
256 
257 	/* Find 'em.  */
258 	error = apple_smc_scan_temp_sensors(sc->sc_smc, &fss,
259 	    &apple_smc_temp_find_sensors_init,
260 	    &apple_smc_temp_find_sensors_scanner);
261 	if (error)
262 		return error;
263 
264 	/*
265 	 * Success guarantees that sc->sc_nsensors will be nonzero and
266 	 * sc->sc_sensors will be allocated.
267 	 */
268 	KASSERT(sc->sc_sensors != NULL);
269 	KASSERT(sc->sc_nsensors > 0);
270 
271 	/* If we didn't find any sensors, bail.  */
272 	if (fss.fss_sensor == 0) {
273 		kmem_free(sc->sc_sensors, sc->sc_nsensors);
274 		sc->sc_sensors = NULL;
275 		sc->sc_nsensors = 0;
276 		return EIO;
277 	}
278 
279 	/* Shrink the array if we overshot.  */
280 	if (fss.fss_sensor < sc->sc_nsensors) {
281 		void *const sensors = kmem_alloc((fss.fss_sensor *
282 			sizeof(sc->sc_sensors[0])), KM_SLEEP);
283 
284 		(void)memcpy(sensors, sc->sc_sensors,
285 		    (fss.fss_sensor * sizeof(sc->sc_sensors[0])));
286 		kmem_free(sc->sc_sensors, sc->sc_nsensors);
287 		sc->sc_sensors = sensors;
288 		sc->sc_nsensors = fss.fss_sensor;
289 	}
290 
291 	/* Success!  */
292 	return 0;
293 }
294 
295 static int
296 apple_smc_temp_find_sensors_init(struct apple_smc_tag *smc, void *arg,
297     uint32_t nsensors)
298 {
299 	struct fss *const fss = arg;
300 
301 	/* Record the maximum number of sensors we may have.  */
302 	fss->fss_sc->sc_nsensors = nsensors;
303 
304 	/* If we found a maximum of zero sensors, bail.  */
305 	if (nsensors == 0) {
306 		fss->fss_sc->sc_sensors = NULL;
307 		return EIO;
308 	}
309 
310 	/*
311 	 * If there may be any sensors, optimistically allocate as many
312 	 * records for them as we may possibly need.
313 	 */
314 	fss->fss_sc->sc_sensors = kmem_alloc((nsensors *
315 		sizeof(fss->fss_sc->sc_sensors[0])), KM_SLEEP);
316 
317 	/* Success!  */
318 	return 0;
319 }
320 
321 static void
322 apple_smc_temp_find_sensors_scanner(struct apple_smc_tag *smc, void *arg,
323     struct apple_smc_key *key)
324 {
325 	struct fss *const fss = arg;
326 	const uint32_t sensor = fss->fss_sensor;
327 	struct envsys_data *const edata =
328 	    &fss->fss_sc->sc_sensors[sensor].sensor_data;
329 	int error;
330 
331 	/* Initialize the envsys_data record for this temperature sensor.  */
332 	edata->units = ENVSYS_STEMP;
333 	edata->state = ENVSYS_SINVALID;
334 	edata->flags = ENVSYS_FHAS_ENTROPY;
335 
336 	/*
337 	 * Use the SMC key name as the temperature sensor's name.
338 	 *
339 	 * XXX We ought to use a more meaningful name based on a table
340 	 * of known temperature sensors.
341 	 */
342 	CTASSERT(sizeof(edata->desc) >= 4);
343 	(void)strlcpy(edata->desc, apple_smc_key_name(key), 4);
344 
345 	/* Attach this temperature sensor to sysmon_envsys.  */
346 	error = sysmon_envsys_sensor_attach(fss->fss_sc->sc_sme, edata);
347 	if (error) {
348 		aprint_error_dev(fss->fss_sc->sc_dev,
349 		    "failed to attach temperature sensor %s: %d\n",
350 		    apple_smc_key_name(key), error);
351 		return;
352 	}
353 
354 	/* Success!  */
355 	fss->fss_sc->sc_sensors[sensor].sensor_key = key;
356 	fss->fss_sensor++;
357 }
358 
359 static void
360 apple_smc_temp_release_keys(struct apple_smc_temp_softc *sc)
361 {
362 	uint32_t sensor;
363 
364 	for (sensor = 0; sensor < sc->sc_nsensors; sensor++) {
365 		KASSERT(sc->sc_sensors[sensor].sensor_key != NULL);
366 		apple_smc_release_key(sc->sc_smc,
367 		    sc->sc_sensors[sensor].sensor_key);
368 	}
369 }
370 
371 static int
372 apple_smc_scan_temp_sensors(struct apple_smc_tag *smc, void *arg,
373     int (*init)(struct apple_smc_tag *, void *, uint32_t),
374     void (*scanner)(struct apple_smc_tag *, void *, struct apple_smc_key *))
375 {
376 	uint32_t tstart, ustart, i;
377 	struct apple_smc_key *key;
378 	int error;
379 
380 	/* Find [start, end) bounds on the temperature sensor key indices.  */
381 	error = apple_smc_bound_temp_sensors(smc, &tstart, &ustart);
382 	if (error)
383 		return error;
384 	KASSERT(tstart <= ustart);
385 
386 	/* Inform the caller of the number of candidates.  */
387 	if (init != NULL) {
388 		error = (*init)(smc, arg, (ustart - tstart));
389 		if (error)
390 			return error;
391 	}
392 
393 	/* Take a closer look at all the candidates.  */
394 	for (i = tstart; i < ustart; i++) {
395 		error = apple_smc_nth_key(smc, i, NULL, &key);
396 		if (error)
397 			continue;
398 
399 		/* Skip it if it's not a temperature sensor.  */
400 		if (!apple_smc_temp_sensor_p(key)) {
401 			apple_smc_release_key(smc, key);
402 			continue;
403 		}
404 
405 		/* Scan it if it is one.  */
406 		(*scanner)(smc, arg, key);
407 	}
408 
409 	/* Success!  */
410 	return 0;
411 }
412 
413 static bool
414 apple_smc_temp_sensor_p(const struct apple_smc_key *key)
415 {
416 
417 	/* It's a temperature sensor iff its type is sp78.  */
418 	return (0 == memcmp(apple_smc_key_desc(key)->asd_type,
419 		APPLE_SMC_TYPE_SP78, 4));
420 }
421 
422 static int
423 apple_smc_bound_temp_sensors(struct apple_smc_tag *smc, uint32_t *tstart,
424     uint32_t *ustart)
425 {
426 	int error;
427 
428 	/* Find the first `T...' key.  */
429 	error = apple_smc_key_search(smc, "T", tstart);
430 	if (error)
431 		return error;
432 
433 	/* Find the first `U...' key.  */
434 	error = apple_smc_key_search(smc, "U", ustart);
435 	if (error)
436 		return error;
437 
438 	/* Sanity check: `T...' keys had better precede `U...' keys.  */
439 	if (!(*tstart <= *ustart))
440 		return EIO;
441 
442 	/* Success!  */
443 	return 0;
444 }
445 
446 MODULE(MODULE_CLASS_DRIVER, apple_smc_temp, "apple_smc,sysmon_envsys");
447 
448 #ifdef _MODULE
449 #include "ioconf.c"
450 #endif
451 
452 static int
453 apple_smc_temp_modcmd(modcmd_t cmd, void *arg __unused)
454 {
455 #ifdef _MODULE
456 	int error;
457 #endif
458 
459 	switch (cmd) {
460 	case MODULE_CMD_INIT:
461 #ifdef _MODULE
462 		error = config_init_component(cfdriver_ioconf_apple_smc_temp,
463 		    cfattach_ioconf_apple_smc_temp,
464 		    cfdata_ioconf_apple_smc_temp);
465 		if (error)
466 			return error;
467 #endif
468 		return 0;
469 
470 	case MODULE_CMD_FINI:
471 #ifdef _MODULE
472 		error = config_fini_component(cfdriver_ioconf_apple_smc_temp,
473 		    cfattach_ioconf_apple_smc_temp,
474 		    cfdata_ioconf_apple_smc_temp);
475 		if (error)
476 			return error;
477 #endif
478 		return 0;
479 
480 	default:
481 		return ENOTTY;
482 	}
483 }
484