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