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