Home | History | Annotate | Line # | Download | only in ic
      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
    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 
    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
    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
    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
    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
    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
    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