xref: /netbsd-src/sys/dev/ic/apple_smc_temp.c (revision df4d49549c23968fb3f4045c8429ffd8f5b9965b)
1 /*	$NetBSD: apple_smc_temp.c,v 1.7 2023/08/08 05:20:14 mrg 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.7 2023/08/08 05:20:14 mrg 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
apple_smc_temp_match(device_t parent,cfdata_t match,void * aux)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
apple_smc_temp_attach(device_t parent,device_t self,void * aux)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
apple_smc_temp_detach(device_t self,int flags)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 
157 		KASSERT(sc->sc_sensors != NULL);
158 		KASSERT(sc->sc_nsensors > 0);
159 
160 		/* Release the keys and free the memory for sensor records.  */
161 		apple_smc_temp_release_keys(sc);
162 		kmem_free(sc->sc_sensors,
163 		    (sizeof(sc->sc_sensors[0]) * sc->sc_nsensors));
164 		sc->sc_sensors = NULL;
165 		sc->sc_nsensors = 0;
166 	}
167 
168 	/* Success!  */
169 	return 0;
170 }
171 
172 static void
apple_smc_temp_refresh(struct sysmon_envsys * sme,struct envsys_data * edata)173 apple_smc_temp_refresh(struct sysmon_envsys *sme, struct envsys_data *edata)
174 {
175 	struct apple_smc_temp_softc *const sc = sme->sme_cookie;
176 	const struct apple_smc_key *key;
177 	uint16_t utemp16;
178 	int32_t temp;
179 	int error;
180 
181 	/* Sanity-check the sensor number out of paranoia.  */
182 	if (edata->sensor >= sc->sc_nsensors) {
183 		aprint_error_dev(sc->sc_dev, "unknown sensor %"PRIu32"\n",
184 		    edata->sensor);
185 		return;
186 	}
187 
188 	/* Read the raw temperature sensor value.  */
189 	key = sc->sc_sensors[edata->sensor].sensor_key;
190 	KASSERT(key != NULL);
191 	error = apple_smc_read_key_2(sc->sc_smc, key, &utemp16);
192 	if (error) {
193 		aprint_error_dev(sc->sc_dev,
194 		    "failed to read temperature sensor %"PRIu32" (%s): %d\n",
195 		    edata->sensor, apple_smc_key_name(key), error);
196 		edata->state = ENVSYS_SINVALID;
197 		return;
198 	}
199 
200 	/* Sign-extend, in case we ever get below freezing...  */
201 	temp = (int16_t)utemp16;
202 
203 	/* Convert to `millicentigrade'.  */
204 	temp *= 250;
205 	temp >>= 6;
206 
207 	/* Convert to millikelvins.  */
208 	temp += 273150;
209 
210 	/* Finally, convert to microkelvins as sysmon_envsys wants.  */
211 	temp *= 1000;
212 
213 	/* Success!  */
214 	edata->value_cur = temp;
215 	edata->state = ENVSYS_SVALID;
216 }
217 
218 static int
apple_smc_temp_count_sensors(struct apple_smc_tag * smc,uint32_t * nsensors)219 apple_smc_temp_count_sensors(struct apple_smc_tag *smc, uint32_t *nsensors)
220 {
221 
222 	/* Start with zero sensors.  */
223 	*nsensors = 0;
224 
225 	/* Count 'em.  */
226 	return apple_smc_scan_temp_sensors(smc, nsensors,
227 	    NULL,
228 	    &apple_smc_temp_count_sensors_scanner);
229 }
230 
231 static void
apple_smc_temp_count_sensors_scanner(struct apple_smc_tag * smc,void * arg,struct apple_smc_key * key)232 apple_smc_temp_count_sensors_scanner(struct apple_smc_tag *smc, void *arg,
233     struct apple_smc_key *key)
234 {
235 	uint32_t *const nsensors = arg;
236 
237 	(*nsensors)++;
238 	apple_smc_release_key(smc, key);
239 }
240 
241 struct fss {			/* Find Sensors State */
242 	struct apple_smc_temp_softc	*fss_sc;
243 	unsigned int			fss_sensor;
244 };
245 
246 static int
apple_smc_temp_find_sensors(struct apple_smc_temp_softc * sc)247 apple_smc_temp_find_sensors(struct apple_smc_temp_softc *sc)
248 {
249 	struct fss fss;
250 	int error;
251 
252 	/* Start with zero sensors.  */
253 	fss.fss_sc = sc;
254 	fss.fss_sensor = 0;
255 
256 	/* Find 'em.  */
257 	error = apple_smc_scan_temp_sensors(sc->sc_smc, &fss,
258 	    &apple_smc_temp_find_sensors_init,
259 	    &apple_smc_temp_find_sensors_scanner);
260 	if (error)
261 		return error;
262 
263 	/*
264 	 * Success guarantees that sc->sc_nsensors will be nonzero and
265 	 * sc->sc_sensors will be allocated.
266 	 */
267 	KASSERT(sc->sc_sensors != NULL);
268 	KASSERT(sc->sc_nsensors > 0);
269 
270 	/* If we didn't find any sensors, bail.  */
271 	if (fss.fss_sensor == 0) {
272 		kmem_free(sc->sc_sensors, sc->sc_nsensors);
273 		sc->sc_sensors = NULL;
274 		sc->sc_nsensors = 0;
275 		return EIO;
276 	}
277 
278 	/* Shrink the array if we overshot.  */
279 	if (fss.fss_sensor < sc->sc_nsensors) {
280 		void *const sensors = kmem_alloc((fss.fss_sensor *
281 			sizeof(sc->sc_sensors[0])), KM_SLEEP);
282 
283 		(void)memcpy(sensors, sc->sc_sensors,
284 		    (fss.fss_sensor * sizeof(sc->sc_sensors[0])));
285 		kmem_free(sc->sc_sensors, sc->sc_nsensors);
286 		sc->sc_sensors = sensors;
287 		sc->sc_nsensors = fss.fss_sensor;
288 	}
289 
290 	/* Success!  */
291 	return 0;
292 }
293 
294 static int
apple_smc_temp_find_sensors_init(struct apple_smc_tag * smc,void * arg,uint32_t nsensors)295 apple_smc_temp_find_sensors_init(struct apple_smc_tag *smc, void *arg,
296     uint32_t nsensors)
297 {
298 	struct fss *const fss = arg;
299 
300 	/* Record the maximum number of sensors we may have.  */
301 	fss->fss_sc->sc_nsensors = nsensors;
302 
303 	/* If we found a maximum of zero sensors, bail.  */
304 	if (nsensors == 0) {
305 		fss->fss_sc->sc_sensors = NULL;
306 		return EIO;
307 	}
308 
309 	/*
310 	 * If there may be any sensors, optimistically allocate as many
311 	 * records for them as we may possibly need.
312 	 */
313 	fss->fss_sc->sc_sensors = kmem_alloc((nsensors *
314 		sizeof(fss->fss_sc->sc_sensors[0])), KM_SLEEP);
315 
316 	/* Success!  */
317 	return 0;
318 }
319 
320 static void
apple_smc_temp_find_sensors_scanner(struct apple_smc_tag * smc,void * arg,struct apple_smc_key * key)321 apple_smc_temp_find_sensors_scanner(struct apple_smc_tag *smc, void *arg,
322     struct apple_smc_key *key)
323 {
324 	struct fss *const fss = arg;
325 	const uint32_t sensor = fss->fss_sensor;
326 	struct envsys_data *const edata =
327 	    &fss->fss_sc->sc_sensors[sensor].sensor_data;
328 	int error;
329 
330 	/* Initialize the envsys_data record for this temperature sensor.  */
331 	edata->units = ENVSYS_STEMP;
332 	edata->state = ENVSYS_SINVALID;
333 	edata->flags = ENVSYS_FHAS_ENTROPY;
334 
335 	/*
336 	 * Use the SMC key name as the temperature sensor's name.
337 	 *
338 	 * XXX We ought to use a more meaningful name based on a table
339 	 * of known temperature sensors.
340 	 */
341 	CTASSERT(sizeof(edata->desc) >= 4);
342 	(void)strlcpy(edata->desc, apple_smc_key_name(key), 4);
343 
344 	/* Attach this temperature sensor to sysmon_envsys.  */
345 	error = sysmon_envsys_sensor_attach(fss->fss_sc->sc_sme, edata);
346 	if (error) {
347 		aprint_error_dev(fss->fss_sc->sc_dev,
348 		    "failed to attach temperature sensor %s: %d\n",
349 		    apple_smc_key_name(key), error);
350 		return;
351 	}
352 
353 	/* Success!  */
354 	fss->fss_sc->sc_sensors[sensor].sensor_key = key;
355 	fss->fss_sensor++;
356 }
357 
358 static void
apple_smc_temp_release_keys(struct apple_smc_temp_softc * sc)359 apple_smc_temp_release_keys(struct apple_smc_temp_softc *sc)
360 {
361 	uint32_t sensor;
362 
363 	for (sensor = 0; sensor < sc->sc_nsensors; sensor++) {
364 		KASSERT(sc->sc_sensors[sensor].sensor_key != NULL);
365 		apple_smc_release_key(sc->sc_smc,
366 		    sc->sc_sensors[sensor].sensor_key);
367 	}
368 }
369 
370 static int
apple_smc_scan_temp_sensors(struct apple_smc_tag * smc,void * arg,int (* init)(struct apple_smc_tag *,void *,uint32_t),void (* scanner)(struct apple_smc_tag *,void *,struct apple_smc_key *))371 apple_smc_scan_temp_sensors(struct apple_smc_tag *smc, void *arg,
372     int (*init)(struct apple_smc_tag *, void *, uint32_t),
373     void (*scanner)(struct apple_smc_tag *, void *, struct apple_smc_key *))
374 {
375 	uint32_t tstart, ustart, i;
376 	struct apple_smc_key *key;
377 	int error;
378 
379 	/* Find [start, end) bounds on the temperature sensor key indices.  */
380 	error = apple_smc_bound_temp_sensors(smc, &tstart, &ustart);
381 	if (error)
382 		return error;
383 	KASSERT(tstart <= ustart);
384 
385 	/* Inform the caller of the number of candidates.  */
386 	if (init != NULL) {
387 		error = (*init)(smc, arg, (ustart - tstart));
388 		if (error)
389 			return error;
390 	}
391 
392 	/* Take a closer look at all the candidates.  */
393 	for (i = tstart; i < ustart; i++) {
394 		error = apple_smc_nth_key(smc, i, NULL, &key);
395 		if (error)
396 			continue;
397 
398 		/* Skip it if it's not a temperature sensor.  */
399 		if (!apple_smc_temp_sensor_p(key)) {
400 			apple_smc_release_key(smc, key);
401 			continue;
402 		}
403 
404 		/* Scan it if it is one.  */
405 		(*scanner)(smc, arg, key);
406 	}
407 
408 	/* Success!  */
409 	return 0;
410 }
411 
412 static bool
apple_smc_temp_sensor_p(const struct apple_smc_key * key)413 apple_smc_temp_sensor_p(const struct apple_smc_key *key)
414 {
415 
416 	/* It's a temperature sensor iff its type is sp78.  */
417 	return (0 == memcmp(apple_smc_key_desc(key)->asd_type,
418 		APPLE_SMC_TYPE_SP78, 4));
419 }
420 
421 static int
apple_smc_bound_temp_sensors(struct apple_smc_tag * smc,uint32_t * tstart,uint32_t * ustart)422 apple_smc_bound_temp_sensors(struct apple_smc_tag *smc, uint32_t *tstart,
423     uint32_t *ustart)
424 {
425 	int error;
426 
427 	/* Find the first `T...' key.  */
428 	error = apple_smc_key_search(smc, "T\0\0\0", tstart);
429 	if (error)
430 		return error;
431 
432 	/* Find the first `U...' key.  */
433 	error = apple_smc_key_search(smc, "U\0\0\0", ustart);
434 	if (error)
435 		return error;
436 
437 	/* Sanity check: `T...' keys had better precede `U...' keys.  */
438 	if (!(*tstart <= *ustart))
439 		return EIO;
440 
441 	/* Success!  */
442 	return 0;
443 }
444 
445 MODULE(MODULE_CLASS_DRIVER, apple_smc_temp, "apple_smc,sysmon_envsys");
446 
447 #ifdef _MODULE
448 #include "ioconf.c"
449 #endif
450 
451 static int
apple_smc_temp_modcmd(modcmd_t cmd,void * arg __unused)452 apple_smc_temp_modcmd(modcmd_t cmd, void *arg __unused)
453 {
454 #ifdef _MODULE
455 	int error;
456 #endif
457 
458 	switch (cmd) {
459 	case MODULE_CMD_INIT:
460 #ifdef _MODULE
461 		error = config_init_component(cfdriver_ioconf_apple_smc_temp,
462 		    cfattach_ioconf_apple_smc_temp,
463 		    cfdata_ioconf_apple_smc_temp);
464 		if (error)
465 			return error;
466 #endif
467 		return 0;
468 
469 	case MODULE_CMD_FINI:
470 #ifdef _MODULE
471 		error = config_fini_component(cfdriver_ioconf_apple_smc_temp,
472 		    cfattach_ioconf_apple_smc_temp,
473 		    cfdata_ioconf_apple_smc_temp);
474 		if (error)
475 			return error;
476 #endif
477 		return 0;
478 
479 	default:
480 		return ENOTTY;
481 	}
482 }
483