xref: /netbsd-src/sys/dev/ic/apple_smc_fan.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: apple_smc_fan.c,v 1.5 2015/04/23 23:23:00 pgoyette 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.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 #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
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
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
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 		sc->sc_sme = NULL;
222 
223 		KASSERT(sc->sc_fans != NULL);
224 		KASSERT(sc->sc_nfans > 0);
225 		KASSERT(sc->sc_nfans < 10);
226 
227 		/* Release the keys and free the memory for fan records. */
228 		apple_smc_fan_release_keys(sc);
229 		kmem_free(sc->sc_fans,
230 		    (sizeof(sc->sc_fans[0]) * sc->sc_nfans));
231 		sc->sc_fans = NULL;
232 		sc->sc_nfans = 0;
233 	}
234 
235 #if 0				/* XXX sysctl */
236 	/* Tear down all the sysctl knobs we set up.  */
237 	sysctl_teardown(&sc->sc_sysctl_log);
238 #endif
239 
240 	return 0;
241 }
242 
243 static int
244 apple_smc_fan_attach_sensors(struct apple_smc_fan_softc *sc)
245 {
246 	uint8_t fan, sensor;
247 	char fan_desc_key_name[4 + 1];
248 	struct apple_smc_key *fan_desc_key;
249 	struct fan_desc fan_desc;
250 	char name[sizeof(fan_desc.fd_name) + 1];
251 	int error;
252 
253 	/* Create a sysmon_envsys record, but don't register it yet.  */
254 	sc->sc_sme = sysmon_envsys_create();
255 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
256 	sc->sc_sme->sme_cookie = sc;
257 	sc->sc_sme->sme_refresh = apple_smc_fan_refresh;
258 
259 	/* Create an array of fan sensor records.  */
260 	CTASSERT(10 <= (SIZE_MAX / sizeof(sc->sc_fans[0])));
261 	sc->sc_fans = kmem_zalloc((sizeof(sc->sc_fans[0]) * sc->sc_nfans),
262 	    KM_SLEEP);
263 
264 	/* Find all the fans.  */
265 	for (fan = 0; fan < sc->sc_nfans; fan++) {
266 
267 		/* Format the name of the key for the fan's description.  */
268 		(void)snprintf(fan_desc_key_name, sizeof(fan_desc_key_name),
269 		    "F%"PRIu8"ID", fan);
270 		KASSERT(4 == strlen(fan_desc_key_name));
271 
272 		/* Look up the key for this fan's description.  */
273 		error = apple_smc_named_key(sc->sc_smc, fan_desc_key_name,
274 		    APPLE_SMC_TYPE_FANDESC, &fan_desc_key);
275 		if (error) {
276 			aprint_error_dev(sc->sc_dev,
277 			    "error identifying fan %"PRIu8": %d\n",
278 			    fan, error);
279 			continue;
280 		}
281 
282 		/* Read the description of this fan.  */
283 		error = apple_smc_read_key(sc->sc_smc, fan_desc_key, &fan_desc,
284 		    sizeof(fan_desc));
285 		if (error) {
286 			aprint_error_dev(sc->sc_dev,
287 			    "error identifying fan %"PRIu8": %d\n",
288 			    fan, error);
289 			continue;
290 		}
291 
292 		/*
293 		 * XXX Do more with the fan description...
294 		 */
295 
296 		/* Make a null-terminated copy of this fan's description.  */
297 		(void)memcpy(name, fan_desc.fd_name, sizeof(fan_desc.fd_name));
298 		name[sizeof(fan_desc.fd_name)] = '\0';
299 
300 		/* Attach all the sensors for this fan.  */
301 		for (sensor = 0; sensor < __arraycount(fan_sensors); sensor++)
302 			apple_smc_fan_attach_sensor(sc, fan, name, sensor);
303 
304 #if 0				/* XXX sysctl */
305 		/* Attach sysctl knobs to control this fan.  */
306 		apple_smc_fan_sysctl_setup_1(sc, fan);
307 #endif
308 	}
309 
310 	/* Fan sensors are all attached.  Register with sysmon_envsys now.  */
311 	error = sysmon_envsys_register(sc->sc_sme);
312 	if (error)
313 		goto fail;
314 
315 	/* Success!  */
316 	error = 0;
317 	goto out;
318 
319 fail:	sysmon_envsys_destroy(sc->sc_sme);
320 	sc->sc_sme = NULL;
321 out:	return error;
322 }
323 
324 static void
325 apple_smc_fan_attach_sensor(struct apple_smc_fan_softc *sc, uint8_t fan,
326     const char *name, uint8_t sensor)
327 {
328 	char key_name[4 + 1];
329 	struct apple_smc_key **keyp;
330 	struct envsys_data *edata;
331 	int error;
332 
333 	KASSERT(fan < sc->sc_nfans);
334 	KASSERT(sensor < __arraycount(fan_sensors));
335 
336 	/* Format the name of the key for this fan sensor.   */
337 	(void)snprintf(key_name, sizeof(key_name), "F%d%s",
338 	    (int)sensor, fan_sensors[sensor].fs_key_suffix);
339 	KASSERT(strlen(key_name) == 4);
340 
341 	/* Look up the key for this fan sensor. */
342 	keyp = &sc->sc_fans[fan].sensors[sensor].sensor_key;
343 	error = apple_smc_named_key(sc->sc_smc, key_name, APPLE_SMC_TYPE_FPE2,
344 	    keyp);
345 	if (error)
346 		goto fail0;
347 
348 	/* Initialize the envsys_data record for this fan sensor.  */
349 	edata = &sc->sc_fans[fan].sensors[sensor].sensor_data;
350 	edata->units = ENVSYS_SFANRPM;
351 	edata->state = ENVSYS_SINVALID;
352 	edata->flags = ENVSYS_FHAS_ENTROPY;
353 	(void)snprintf(edata->desc, sizeof(edata->desc), "fan %s %s speed",
354 	    name, fan_sensors[sensor].fs_name);
355 
356 	/* Attach this fan sensor to sysmon_envsys.  */
357 	error = sysmon_envsys_sensor_attach(sc->sc_sme, edata);
358 	if (error)
359 		goto fail1;
360 
361 	/* Success!  */
362 	return;
363 
364 fail1:	apple_smc_release_key(sc->sc_smc, *keyp);
365 fail0:	*keyp = NULL;
366 	aprint_error_dev(sc->sc_dev,
367 	    "failed to attach fan %s %s speed sensor: %d\n",
368 	    name, fan_sensors[sensor].fs_name, error);
369 }
370 
371 static void
372 apple_smc_fan_refresh(struct sysmon_envsys *sme, struct envsys_data *edata)
373 {
374 	struct apple_smc_fan_softc *sc = sme->sme_cookie;
375 	uint8_t fan, sensor;
376 	struct apple_smc_key *key;
377 	uint16_t rpm;
378 	int error;
379 
380 	/* Sanity-check the sensor number out of paranoia.  */
381 	CTASSERT(10 <= (SIZE_MAX / __arraycount(fan_sensors)));
382 	KASSERT(sc->sc_nfans < 10);
383 	if (edata->sensor >= (sc->sc_nfans * __arraycount(fan_sensors))) {
384 		aprint_error_dev(sc->sc_dev, "unknown sensor %"PRIu32"\n",
385 		    edata->sensor);
386 		return;
387 	}
388 
389 	/* Pick apart the fan number and its sensor number.  */
390 	fan = (edata->sensor / __arraycount(fan_sensors));
391 	sensor = (edata->sensor % __arraycount(fan_sensors));
392 
393 	KASSERT(fan < sc->sc_nfans);
394 	KASSERT(sensor < __arraycount(fan_sensors));
395 	KASSERT(edata == &sc->sc_fans[fan].sensors[sensor].sensor_data);
396 
397 	/*
398 	 * If we're refreshing, this sensor got attached, so we ought
399 	 * to have a sensor key.  Grab it.
400 	 */
401 	key = sc->sc_fans[fan].sensors[sensor].sensor_key;
402 	KASSERT(key != NULL);
403 
404 	/* Read the fan sensor value, in rpm.  */
405 	error = apple_smc_read_key_2(sc->sc_smc, key, &rpm);
406 	if (error) {
407 		aprint_error_dev(sc->sc_dev,
408 		    "failed to read fan %d %s speed: %d\n",
409 		    fan, fan_sensors[sensor].fs_name, error);
410 		edata->state = ENVSYS_SINVALID;
411 		return;
412 	}
413 
414 	/* Success!  */
415 	edata->value_cur = rpm;
416 	edata->state = ENVSYS_SVALID;
417 }
418 
419 static void
420 apple_smc_fan_release_keys(struct apple_smc_fan_softc *sc)
421 {
422 	uint8_t fan, sensor;
423 
424 	for (fan = 0; fan < sc->sc_nfans; fan++) {
425 		for (sensor = 0;
426 		     sensor < __arraycount(fan_sensors);
427 		     sensor++) {
428 			struct apple_smc_key **const keyp =
429 			    &sc->sc_fans[fan].sensors[sensor].sensor_key;
430 			if (*keyp != NULL) {
431 				apple_smc_release_key(sc->sc_smc, *keyp);
432 				*keyp = NULL;
433 			}
434 		}
435 	}
436 }
437 
438 #if 0				/* XXX sysctl */
439 static int
440 apple_smc_fan_sysctl_setup(struct apple_smc_fan_softc *sc)
441 {
442 	...
443 }
444 
445 static void
446 apple_smc_fan_sysctl_setup_1(struct apple_smc_fan_softc *sc, uint8_t fan)
447 {
448 }
449 #endif
450 
451 MODULE(MODULE_CLASS_DRIVER, apple_smc_fan, "apple_smc,sysmon_envsys");
452 
453 #ifdef _MODULE
454 #include "ioconf.c"
455 #endif
456 
457 static int
458 apple_smc_fan_modcmd(modcmd_t cmd, void *arg __unused)
459 {
460 #ifdef _MODULE
461 	int error;
462 #endif
463 
464 	switch (cmd) {
465 	case MODULE_CMD_INIT:
466 #ifdef _MODULE
467 		error = config_init_component(cfdriver_ioconf_apple_smc_fan,
468 		    cfattach_ioconf_apple_smc_fan,
469 		    cfdata_ioconf_apple_smc_fan);
470 		if (error)
471 			return error;
472 #endif
473 		return 0;
474 
475 	case MODULE_CMD_FINI:
476 #ifdef _MODULE
477 		error = config_fini_component(cfdriver_ioconf_apple_smc_fan,
478 		    cfattach_ioconf_apple_smc_fan,
479 		    cfdata_ioconf_apple_smc_fan);
480 		if (error)
481 			return error;
482 #endif
483 		return 0;
484 
485 	default:
486 		return ENOTTY;
487 	}
488 }
489