xref: /netbsd-src/sys/dev/ic/apple_smc_fan.c (revision 369b9e751d858fc515b11242165c67b72e9629f4)
1 /*	$NetBSD: apple_smc_fan.c,v 1.6 2022/06/29 15:58:12 mlelstv Exp $	*/
2 
3 /*
4  * Apple System Management Controller: Fans
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_fan.c,v 1.6 2022/06/29 15:58:12 mlelstv 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 #if 0                           /* XXX sysctl */
45 #include <sys/sysctl.h>
46 #endif
47 #include <sys/systm.h>
48 
49 #include <dev/ic/apple_smc.h>
50 
51 #include <dev/sysmon/sysmonvar.h>
52 
53 #define	APPLE_SMC_NFANS_KEY		"FNum"
54 
55 static const struct fan_sensor {
56 	const char *fs_name;
57 	const char *fs_key_suffix;
58 } fan_sensors[] = {
59 	{ "actual",	"Ac" },
60 	{ "minimum",	"Mn" },
61 	{ "maximum",	"Mx" },
62 	{ "safe",	"Sf" },
63 	{ "target",	"Tg" },
64 };
65 
66 struct apple_smc_fan_softc {
67 	device_t		sc_dev;
68 	struct apple_smc_tag	*sc_smc;
69 	struct sysmon_envsys	*sc_sme;
70 	uint8_t			sc_nfans;
71 	struct {
72 		struct {
73 			struct apple_smc_key	*sensor_key;
74 			struct envsys_data	sensor_data;
75 		} sensors[__arraycount(fan_sensors)];
76 	}			*sc_fans;
77 
78 #if 0				/* XXX sysctl */
79 	struct sysctllog	*sc_sysctl_log;
80 	const struct sysctlnode	*sc_sysctl_node;
81 #endif
82 };
83 
84 struct fan_desc {
85 	uint8_t fd_type;
86 	uint8_t fd_zone;
87 	uint8_t fd_location;
88 	uint8_t fd_reserved0;
89 	char fd_name[12];
90 } __packed;
91 
92 static int	apple_smc_fan_match(device_t, cfdata_t, void *);
93 static void	apple_smc_fan_attach(device_t, device_t, void *);
94 static int	apple_smc_fan_detach(device_t, int);
95 static int	apple_smc_fan_attach_sensors(struct apple_smc_fan_softc *);
96 static void	apple_smc_fan_attach_sensor(struct apple_smc_fan_softc *,
97 		    uint8_t, const char *, uint8_t);
98 static void	apple_smc_fan_refresh(struct sysmon_envsys *,
99 		    struct envsys_data *);
100 static void	apple_smc_fan_release_keys(struct apple_smc_fan_softc *);
101 #if 0				/* XXX sysctl */
102 static int	apple_smc_fan_sysctl_setup(struct apple_smc_fan_softc *);
103 static void	apple_smc_fan_sysctl_setup_1(struct apple_smc_tag *,
104 		    uint8_t);
105 #endif
106 
107 CFATTACH_DECL_NEW(apple_smc_fan, sizeof(struct apple_smc_fan_softc),
108     apple_smc_fan_match, apple_smc_fan_attach, apple_smc_fan_detach, NULL);
109 
110 static int
apple_smc_fan_match(device_t parent,cfdata_t match,void * aux)111 apple_smc_fan_match(device_t parent, cfdata_t match, void *aux)
112 {
113 	const struct apple_smc_attach_args *asa = aux;
114 	struct apple_smc_key *nfans_key;
115 	uint8_t nfans;
116 	int rv = 0;
117 	int error;
118 
119 	/* Find how to find how many fans there are.  */
120 	error = apple_smc_named_key(asa->asa_smc, APPLE_SMC_NFANS_KEY,
121 	    APPLE_SMC_TYPE_UINT8, &nfans_key);
122 	if (error)
123 		goto out0;
124 
125 	/* Find how many fans there are.  */
126 	error = apple_smc_read_key_1(asa->asa_smc, nfans_key, &nfans);
127 	if (error)
128 		goto out1;
129 
130 	/* Attach only if there's at least one fan.  */
131 	if (nfans > 0)
132 		rv = 1;
133 
134 out1:	apple_smc_release_key(asa->asa_smc, nfans_key);
135 out0:	return rv;
136 }
137 
138 static void
apple_smc_fan_attach(device_t parent,device_t self,void * aux)139 apple_smc_fan_attach(device_t parent, device_t self, void *aux)
140 {
141 	struct apple_smc_fan_softc *sc = device_private(self);
142 	const struct apple_smc_attach_args *asa = aux;
143 	struct apple_smc_key *nfans_key;
144 	int error;
145 
146 	/* Identify ourselves.  */
147 	aprint_normal(": Apple SMC fan sensors\n");
148 
149 	/* Initialize the softc.  */
150 	sc->sc_dev = self;
151 	sc->sc_smc = asa->asa_smc;
152 
153 	/* Find how to find how many fans there are.  */
154 	error = apple_smc_named_key(sc->sc_smc, APPLE_SMC_NFANS_KEY,
155 	    APPLE_SMC_TYPE_UINT8, &nfans_key);
156 	if (error)
157 		goto out0;
158 
159 	/* Find how many fans there are.  */
160 	error = apple_smc_read_key_1(sc->sc_smc, nfans_key, &sc->sc_nfans);
161 	if (error)
162 		goto out1;
163 
164 	/*
165 	 * There should be at least one, but just in case the hardware
166 	 * changed its mind in the interim...
167 	 */
168 	if (sc->sc_nfans == 0) {
169 		aprint_error_dev(self, "no fans\n");
170 		goto out1;
171 	}
172 
173 	/*
174 	 * The number of fans must fit in a single decimal digit for
175 	 * the names of the fan keys; see the fan_sensor table above.
176 	 */
177 	if (sc->sc_nfans >= 10) {
178 		aprint_error_dev(self, "too many fans: %"PRIu8"\n",
179 		    sc->sc_nfans);
180 		sc->sc_nfans = 9;
181 	}
182 
183 #if 0				/* XXX sysctl */
184 	/* Set up the sysctl tree for controlling the fans.  */
185 	error = apple_smc_fan_sysctl_setup(sc);
186 	if (error)
187 		goto fail0;
188 #endif
189 
190 	/* Attach the sensors to sysmon_envsys.  */
191 	error = apple_smc_fan_attach_sensors(sc);
192 	if (error)
193 		goto fail1;
194 
195 	/* Success!  */
196 	goto out1;
197 
198 #if 0
199 fail2:
200 	apple_smc_fan_detach_sensors(sc);
201 #endif
202 
203 fail1:
204 #if 0				/* XXX sysctl */
205 	sysctl_teardown(&sc->sc_sysctl_log);
206 fail0:
207 #endif
208 
209 out1:	apple_smc_release_key(sc->sc_smc, nfans_key);
210 out0:	return;
211 }
212 
213 static int
apple_smc_fan_detach(device_t self,int flags)214 apple_smc_fan_detach(device_t self, int flags)
215 {
216 	struct apple_smc_fan_softc *sc = device_private(self);
217 
218 	/* If we registered with sysmon_envsys, unregister.  */
219 	if (sc->sc_sme != NULL) {
220 		sysmon_envsys_unregister(sc->sc_sme);
221 
222 		KASSERT(sc->sc_fans != NULL);
223 		KASSERT(sc->sc_nfans > 0);
224 		KASSERT(sc->sc_nfans < 10);
225 
226 		/* Release the keys and free the memory for fan records. */
227 		apple_smc_fan_release_keys(sc);
228 		kmem_free(sc->sc_fans,
229 		    (sizeof(sc->sc_fans[0]) * sc->sc_nfans));
230 		sc->sc_fans = NULL;
231 		sc->sc_nfans = 0;
232 	}
233 
234 #if 0				/* XXX sysctl */
235 	/* Tear down all the sysctl knobs we set up.  */
236 	sysctl_teardown(&sc->sc_sysctl_log);
237 #endif
238 
239 	return 0;
240 }
241 
242 static int
apple_smc_fan_attach_sensors(struct apple_smc_fan_softc * sc)243 apple_smc_fan_attach_sensors(struct apple_smc_fan_softc *sc)
244 {
245 	uint8_t fan, sensor;
246 	char fan_desc_key_name[4 + 1];
247 	struct apple_smc_key *fan_desc_key;
248 	struct fan_desc fan_desc;
249 	char name[sizeof(fan_desc.fd_name) + 1];
250 	int error;
251 
252 	/* Create a sysmon_envsys record, but don't register it yet.  */
253 	sc->sc_sme = sysmon_envsys_create();
254 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
255 	sc->sc_sme->sme_cookie = sc;
256 	sc->sc_sme->sme_refresh = apple_smc_fan_refresh;
257 
258 	/* Create an array of fan sensor records.  */
259 	CTASSERT(10 <= (SIZE_MAX / sizeof(sc->sc_fans[0])));
260 	sc->sc_fans = kmem_zalloc((sizeof(sc->sc_fans[0]) * sc->sc_nfans),
261 	    KM_SLEEP);
262 
263 	/* Find all the fans.  */
264 	for (fan = 0; fan < sc->sc_nfans; fan++) {
265 
266 		/* Format the name of the key for the fan's description.  */
267 		(void)snprintf(fan_desc_key_name, sizeof(fan_desc_key_name),
268 		    "F%"PRIu8"ID", fan);
269 		KASSERT(4 == strlen(fan_desc_key_name));
270 
271 		/* Look up the key for this fan's description.  */
272 		error = apple_smc_named_key(sc->sc_smc, fan_desc_key_name,
273 		    APPLE_SMC_TYPE_FANDESC, &fan_desc_key);
274 		if (error) {
275 			aprint_error_dev(sc->sc_dev,
276 			    "error identifying fan %"PRIu8": %d\n",
277 			    fan, error);
278 			continue;
279 		}
280 
281 		/* Read the description of this fan.  */
282 		error = apple_smc_read_key(sc->sc_smc, fan_desc_key, &fan_desc,
283 		    sizeof(fan_desc));
284 		if (error) {
285 			aprint_error_dev(sc->sc_dev,
286 			    "error identifying fan %"PRIu8": %d\n",
287 			    fan, error);
288 			continue;
289 		}
290 
291 		/*
292 		 * XXX Do more with the fan description...
293 		 */
294 
295 		/* Make a null-terminated copy of this fan's description.  */
296 		(void)memcpy(name, fan_desc.fd_name, sizeof(fan_desc.fd_name));
297 		name[sizeof(fan_desc.fd_name)] = '\0';
298 
299 		/* Attach all the sensors for this fan.  */
300 		for (sensor = 0; sensor < __arraycount(fan_sensors); sensor++)
301 			apple_smc_fan_attach_sensor(sc, fan, name, sensor);
302 
303 #if 0				/* XXX sysctl */
304 		/* Attach sysctl knobs to control this fan.  */
305 		apple_smc_fan_sysctl_setup_1(sc, fan);
306 #endif
307 	}
308 
309 	/* Fan sensors are all attached.  Register with sysmon_envsys now.  */
310 	error = sysmon_envsys_register(sc->sc_sme);
311 	if (error)
312 		goto fail;
313 
314 	/* Success!  */
315 	error = 0;
316 	goto out;
317 
318 fail:	sysmon_envsys_destroy(sc->sc_sme);
319 	sc->sc_sme = NULL;
320 out:	return error;
321 }
322 
323 static void
apple_smc_fan_attach_sensor(struct apple_smc_fan_softc * sc,uint8_t fan,const char * name,uint8_t sensor)324 apple_smc_fan_attach_sensor(struct apple_smc_fan_softc *sc, uint8_t fan,
325     const char *name, uint8_t sensor)
326 {
327 	char key_name[4 + 1];
328 	struct apple_smc_key **keyp;
329 	struct envsys_data *edata;
330 	int error;
331 
332 	KASSERT(fan < sc->sc_nfans);
333 	KASSERT(sensor < __arraycount(fan_sensors));
334 
335 	/* Format the name of the key for this fan sensor.   */
336 	(void)snprintf(key_name, sizeof(key_name), "F%d%s",
337 	    (int)sensor, fan_sensors[sensor].fs_key_suffix);
338 	KASSERT(strlen(key_name) == 4);
339 
340 	/* Look up the key for this fan sensor. */
341 	keyp = &sc->sc_fans[fan].sensors[sensor].sensor_key;
342 	error = apple_smc_named_key(sc->sc_smc, key_name, APPLE_SMC_TYPE_FPE2,
343 	    keyp);
344 	if (error)
345 		goto fail0;
346 
347 	/* Initialize the envsys_data record for this fan sensor.  */
348 	edata = &sc->sc_fans[fan].sensors[sensor].sensor_data;
349 	edata->units = ENVSYS_SFANRPM;
350 	edata->state = ENVSYS_SINVALID;
351 	edata->flags = ENVSYS_FHAS_ENTROPY;
352 	(void)snprintf(edata->desc, sizeof(edata->desc), "fan %s %s speed",
353 	    name, fan_sensors[sensor].fs_name);
354 
355 	/* Attach this fan sensor to sysmon_envsys.  */
356 	error = sysmon_envsys_sensor_attach(sc->sc_sme, edata);
357 	if (error)
358 		goto fail1;
359 
360 	/* Success!  */
361 	return;
362 
363 fail1:	apple_smc_release_key(sc->sc_smc, *keyp);
364 fail0:	*keyp = NULL;
365 	aprint_error_dev(sc->sc_dev,
366 	    "failed to attach fan %s %s speed sensor: %d\n",
367 	    name, fan_sensors[sensor].fs_name, error);
368 }
369 
370 static void
apple_smc_fan_refresh(struct sysmon_envsys * sme,struct envsys_data * edata)371 apple_smc_fan_refresh(struct sysmon_envsys *sme, struct envsys_data *edata)
372 {
373 	struct apple_smc_fan_softc *sc = sme->sme_cookie;
374 	uint8_t fan, sensor;
375 	struct apple_smc_key *key;
376 	uint16_t rpm;
377 	int error;
378 
379 	/* Sanity-check the sensor number out of paranoia.  */
380 	CTASSERT(10 <= (SIZE_MAX / __arraycount(fan_sensors)));
381 	KASSERT(sc->sc_nfans < 10);
382 	if (edata->sensor >= (sc->sc_nfans * __arraycount(fan_sensors))) {
383 		aprint_error_dev(sc->sc_dev, "unknown sensor %"PRIu32"\n",
384 		    edata->sensor);
385 		return;
386 	}
387 
388 	/* Pick apart the fan number and its sensor number.  */
389 	fan = (edata->sensor / __arraycount(fan_sensors));
390 	sensor = (edata->sensor % __arraycount(fan_sensors));
391 
392 	KASSERT(fan < sc->sc_nfans);
393 	KASSERT(sensor < __arraycount(fan_sensors));
394 	KASSERT(edata == &sc->sc_fans[fan].sensors[sensor].sensor_data);
395 
396 	/*
397 	 * If we're refreshing, this sensor got attached, so we ought
398 	 * to have a sensor key.  Grab it.
399 	 */
400 	key = sc->sc_fans[fan].sensors[sensor].sensor_key;
401 	KASSERT(key != NULL);
402 
403 	/* Read the fan sensor value, in rpm.  */
404 	error = apple_smc_read_key_2(sc->sc_smc, key, &rpm);
405 	if (error) {
406 		aprint_error_dev(sc->sc_dev,
407 		    "failed to read fan %d %s speed: %d\n",
408 		    fan, fan_sensors[sensor].fs_name, error);
409 		edata->state = ENVSYS_SINVALID;
410 		return;
411 	}
412 
413 	/* Success!  */
414 	edata->value_cur = rpm;
415 	edata->state = ENVSYS_SVALID;
416 }
417 
418 static void
apple_smc_fan_release_keys(struct apple_smc_fan_softc * sc)419 apple_smc_fan_release_keys(struct apple_smc_fan_softc *sc)
420 {
421 	uint8_t fan, sensor;
422 
423 	for (fan = 0; fan < sc->sc_nfans; fan++) {
424 		for (sensor = 0;
425 		     sensor < __arraycount(fan_sensors);
426 		     sensor++) {
427 			struct apple_smc_key **const keyp =
428 			    &sc->sc_fans[fan].sensors[sensor].sensor_key;
429 			if (*keyp != NULL) {
430 				apple_smc_release_key(sc->sc_smc, *keyp);
431 				*keyp = NULL;
432 			}
433 		}
434 	}
435 }
436 
437 #if 0				/* XXX sysctl */
438 static int
439 apple_smc_fan_sysctl_setup(struct apple_smc_fan_softc *sc)
440 {
441 	...
442 }
443 
444 static void
445 apple_smc_fan_sysctl_setup_1(struct apple_smc_fan_softc *sc, uint8_t fan)
446 {
447 }
448 #endif
449 
450 MODULE(MODULE_CLASS_DRIVER, apple_smc_fan, "apple_smc,sysmon_envsys");
451 
452 #ifdef _MODULE
453 #include "ioconf.c"
454 #endif
455 
456 static int
apple_smc_fan_modcmd(modcmd_t cmd,void * arg __unused)457 apple_smc_fan_modcmd(modcmd_t cmd, void *arg __unused)
458 {
459 #ifdef _MODULE
460 	int error;
461 #endif
462 
463 	switch (cmd) {
464 	case MODULE_CMD_INIT:
465 #ifdef _MODULE
466 		error = config_init_component(cfdriver_ioconf_apple_smc_fan,
467 		    cfattach_ioconf_apple_smc_fan,
468 		    cfdata_ioconf_apple_smc_fan);
469 		if (error)
470 			return error;
471 #endif
472 		return 0;
473 
474 	case MODULE_CMD_FINI:
475 #ifdef _MODULE
476 		error = config_fini_component(cfdriver_ioconf_apple_smc_fan,
477 		    cfattach_ioconf_apple_smc_fan,
478 		    cfdata_ioconf_apple_smc_fan);
479 		if (error)
480 			return error;
481 #endif
482 		return 0;
483 
484 	default:
485 		return ENOTTY;
486 	}
487 }
488