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