1 /* $NetBSD: apple_smc.c,v 1.9 2023/08/08 05:20:14 mrg Exp $ */ 2 3 /* 4 * Apple System Management Controller 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.c,v 1.9 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/errno.h> 43 #include <sys/kmem.h> 44 #include <sys/module.h> 45 #include <sys/mutex.h> 46 #include <sys/rwlock.h> 47 #if 0 /* XXX sysctl */ 48 #include <sys/sysctl.h> 49 #endif 50 #include <sys/systm.h> 51 52 #include <dev/ic/apple_smc.h> 53 #include <dev/ic/apple_smcreg.h> 54 #include <dev/ic/apple_smcvar.h> 55 56 /* Must match the config(5) name. */ 57 #define APPLE_SMC_BUS "applesmcbus" 58 59 static int apple_smc_search(device_t, cfdata_t, const int *, void *); 60 static uint8_t apple_smc_bus_read_1(struct apple_smc_tag *, bus_size_t); 61 static void apple_smc_bus_write_1(struct apple_smc_tag *, bus_size_t, 62 uint8_t); 63 static int apple_smc_read_data(struct apple_smc_tag *, uint8_t *); 64 static int apple_smc_write(struct apple_smc_tag *, bus_size_t, uint8_t); 65 static int apple_smc_write_cmd(struct apple_smc_tag *, uint8_t); 66 static int apple_smc_write_data(struct apple_smc_tag *, uint8_t); 67 static int apple_smc_begin(struct apple_smc_tag *, uint8_t, 68 const char *, uint8_t); 69 static int apple_smc_input(struct apple_smc_tag *, uint8_t, 70 const char *, void *, uint8_t); 71 static int apple_smc_output(struct apple_smc_tag *, uint8_t, 72 const char *, const void *, uint8_t); 73 74 void 76 apple_smc_attach(struct apple_smc_tag *smc) 77 { 78 79 mutex_init(&smc->smc_io_lock, MUTEX_DEFAULT, IPL_NONE); 80 #if 0 /* XXX sysctl */ 81 apple_smc_sysctl_setup(smc); 82 #endif 83 84 /* Attach any children. */ 85 (void)apple_smc_rescan(smc, NULL, NULL); 86 } 87 88 int 89 apple_smc_detach(struct apple_smc_tag *smc, int flags) 90 { 91 int error; 92 93 /* Fail if we can't detach all our children. */ 94 error = config_detach_children(smc->smc_dev, flags); 95 if (error) 96 return error; 97 98 #if 0 /* XXX sysctl */ 99 sysctl_teardown(&smc->smc_log); 100 #endif 101 mutex_destroy(&smc->smc_io_lock); 102 103 return 0; 104 } 105 106 int 107 apple_smc_rescan(struct apple_smc_tag *smc, const char *ifattr, 108 const int *locators) 109 { 110 111 /* Let autoconf(9) do the work of finding new children. */ 112 config_search(smc->smc_dev, smc, 113 CFARGS(.search = apple_smc_search)); 114 return 0; 115 } 116 117 static int 118 apple_smc_search(device_t parent, cfdata_t cf, const int *locators, void *aux) 119 { 120 struct apple_smc_tag *const smc = aux; 121 static const struct apple_smc_attach_args zero_asa; 122 struct apple_smc_attach_args asa = zero_asa; 123 device_t dev; 124 deviter_t di; 125 bool attached = false; 126 127 /* 128 * If this device has already attached, don't attach it again. 129 * 130 * XXX This is a pretty silly way to query the children, but 131 * struct device doesn't seem to list its children. 132 */ 133 for (dev = deviter_first(&di, DEVITER_F_LEAVES_FIRST); 134 dev != NULL; 135 dev = deviter_next(&di)) { 136 if (device_parent(dev) != parent) 137 continue; 138 if (!device_is_a(dev, cf->cf_name)) 139 continue; 140 attached = true; 141 break; 142 } 143 deviter_release(&di); 144 if (attached) 145 return 0; 146 147 /* If this device doesn't match, don't attach it. */ 148 if (!config_probe(parent, cf, aux)) 149 return 0; 150 151 /* Looks hunky-dory. Attach. */ 152 asa.asa_smc = smc; 153 config_attach(parent, cf, &asa, NULL, 154 CFARGS(.locators = locators)); 155 return 0; 156 } 157 158 void 159 apple_smc_child_detached(struct apple_smc_tag *smc __unused, 160 device_t child __unused) 161 { 162 /* We keep no books about our children. */ 163 } 164 165 static uint8_t 167 apple_smc_bus_read_1(struct apple_smc_tag *smc, bus_size_t reg) 168 { 169 170 return bus_space_read_1(smc->smc_bst, smc->smc_bsh, reg); 171 } 172 173 static void 174 apple_smc_bus_write_1(struct apple_smc_tag *smc, bus_size_t reg, uint8_t v) 175 { 176 177 bus_space_write_1(smc->smc_bst, smc->smc_bsh, reg, v); 178 } 179 180 /* 181 * XXX These delays are pretty randomly chosen. Wait in 100 us 182 * increments, up to a total of 1 ms. 183 */ 184 185 static int 186 apple_smc_read_data(struct apple_smc_tag *smc, uint8_t *byte) 187 { 188 uint8_t status; 189 unsigned int i; 190 191 KASSERT(mutex_owned(&smc->smc_io_lock)); 192 193 /* 194 * Wait until the status register says there's data to read and 195 * read it. 196 */ 197 for (i = 0; i < 100; i++) { 198 status = apple_smc_bus_read_1(smc, APPLE_SMC_CSR); 199 if (status & APPLE_SMC_STATUS_READ_READY) { 200 *byte = apple_smc_bus_read_1(smc, APPLE_SMC_DATA); 201 return 0; 202 } 203 DELAY(100); 204 } 205 206 return ETIMEDOUT; 207 } 208 209 static int 210 apple_smc_write(struct apple_smc_tag *smc, bus_size_t reg, uint8_t byte) 211 { 212 uint8_t status; 213 unsigned int i; 214 215 KASSERT(mutex_owned(&smc->smc_io_lock)); 216 217 /* 218 * Write the byte and then wait until the status register says 219 * it has been accepted. 220 */ 221 apple_smc_bus_write_1(smc, reg, byte); 222 for (i = 0; i < 100; i++) { 223 status = apple_smc_bus_read_1(smc, APPLE_SMC_CSR); 224 if (status & APPLE_SMC_STATUS_WRITE_ACCEPTED) 225 return 0; 226 DELAY(100); 227 228 /* Write again if it hasn't been acknowledged at all. */ 229 if (!(status & APPLE_SMC_STATUS_WRITE_PENDING)) 230 apple_smc_bus_write_1(smc, reg, byte); 231 } 232 233 return ETIMEDOUT; 234 } 235 236 static int 237 apple_smc_write_cmd(struct apple_smc_tag *smc, uint8_t cmd) 238 { 239 240 return apple_smc_write(smc, APPLE_SMC_CSR, cmd); 241 } 242 243 static int 244 apple_smc_write_data(struct apple_smc_tag *smc, uint8_t data) 245 { 246 247 return apple_smc_write(smc, APPLE_SMC_DATA, data); 248 } 249 250 static int 252 apple_smc_begin(struct apple_smc_tag *smc, uint8_t cmd, const char *key, 253 uint8_t size) 254 { 255 unsigned int i; 256 int error; 257 258 KASSERT(mutex_owned(&smc->smc_io_lock)); 259 260 /* Write the command first. */ 261 error = apple_smc_write_cmd(smc, cmd); 262 if (error) 263 return error; 264 265 /* Write the key next. */ 266 for (i = 0; i < 4; i++) { 267 error = apple_smc_write_data(smc, key[i]); 268 if (error) 269 return error; 270 } 271 272 /* Finally, report how many bytes of data we want to send/receive. */ 273 error = apple_smc_write_data(smc, size); 274 if (error) 275 return error; 276 277 return 0; 278 } 279 280 static int 281 apple_smc_input(struct apple_smc_tag *smc, uint8_t cmd, const char *key, 282 void *buffer, uint8_t size) 283 { 284 uint8_t *bytes = buffer; 285 uint8_t i; 286 int error; 287 288 /* Grab the SMC I/O lock. */ 289 mutex_enter(&smc->smc_io_lock); 290 291 /* Initiate the command with this key. */ 292 error = apple_smc_begin(smc, cmd, key, size); 293 if (error) 294 goto out; 295 296 /* Read each byte of data in sequence. */ 297 for (i = 0; i < size; i++) { 298 error = apple_smc_read_data(smc, &bytes[i]); 299 if (error) 300 goto out; 301 } 302 303 /* Success! */ 304 error = 0; 305 306 out: mutex_exit(&smc->smc_io_lock); 307 return error; 308 } 309 310 static int 311 apple_smc_output(struct apple_smc_tag *smc, uint8_t cmd, const char *key, 312 const void *buffer, uint8_t size) 313 { 314 const uint8_t *bytes = buffer; 315 uint8_t i; 316 int error; 317 318 /* Grab the SMC I/O lock. */ 319 mutex_enter(&smc->smc_io_lock); 320 321 /* Initiate the command with this key. */ 322 error = apple_smc_begin(smc, cmd, key, size); 323 if (error) 324 goto out; 325 326 /* Write each byte of data in sequence. */ 327 for (i = 0; i < size; i++) { 328 error = apple_smc_write_data(smc, bytes[i]); 329 if (error) 330 goto out; 331 } 332 333 /* Success! */ 334 error = 0; 335 336 out: mutex_exit(&smc->smc_io_lock); 337 return error; 338 } 339 340 struct apple_smc_key { 342 char ask_name[4 + 1]; 343 struct apple_smc_desc ask_desc; 344 #ifdef DIAGNOSTIC 345 struct apple_smc_tag *ask_smc; 346 #endif 347 }; 348 349 const char * 350 apple_smc_key_name(const struct apple_smc_key *key) 351 { 352 353 return key->ask_name; 354 } 355 356 const struct apple_smc_desc * 357 apple_smc_key_desc(const struct apple_smc_key *key) 358 { 359 360 return &key->ask_desc; 361 } 362 363 uint32_t 364 apple_smc_nkeys(struct apple_smc_tag *smc) 365 { 366 367 return smc->smc_nkeys; 368 } 369 370 int 371 apple_smc_nth_key(struct apple_smc_tag *smc, uint32_t index, 372 const char type[4 + 1], struct apple_smc_key **keyp) 373 { 374 union { uint32_t u32; char name[4]; } index_be; 375 struct apple_smc_key *key; 376 int error; 377 378 /* Paranoia: type must be NULL or 4 non-null characters long. */ 379 if ((type != NULL) && (strlen(type) != 4)) 380 return EINVAL; 381 382 /* Create a new key. XXX Consider caching these. */ 383 key = kmem_alloc(sizeof(*key), KM_SLEEP); 384 #ifdef DIAGNOSTIC 385 key->ask_smc = smc; 386 #endif 387 388 /* Ask the SMC what the name of the key by this number is. */ 389 index_be.u32 = htobe32(index); 390 error = apple_smc_input(smc, APPLE_SMC_CMD_NTH_KEY, index_be.name, 391 key->ask_name, 4); 392 if (error) 393 goto fail; 394 395 /* Null-terminate the name. */ 396 key->ask_name[4] = '\0'; 397 398 /* Ask the SMC for a description of this key by name. */ 399 CTASSERT(sizeof(key->ask_desc) == 6); 400 error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name, 401 &key->ask_desc, 6); 402 if (error) 403 goto fail; 404 405 /* Fail with EINVAL if the types don't match. */ 406 if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) { 407 error = EINVAL; 408 goto fail; 409 } 410 411 /* Success! */ 412 *keyp = key; 413 return 0; 414 415 fail: kmem_free(key, sizeof(*key)); 416 return error; 417 } 418 419 int 420 apple_smc_named_key(struct apple_smc_tag *smc, const char name[4 + 1], 421 const char type[4 + 1], struct apple_smc_key **keyp) 422 { 423 struct apple_smc_key *key; 424 int error; 425 426 /* Paranoia: name must be 4 non-null characters long. */ 427 KASSERT(name != NULL); 428 if (strlen(name) != 4) 429 return EINVAL; 430 431 /* Paranoia: type must be NULL or 4 non-null characters long. */ 432 if ((type != NULL) && (strlen(type) != 4)) 433 return EINVAL; 434 435 /* Create a new key. XXX Consider caching these. */ 436 key = kmem_alloc(sizeof(*key), KM_SLEEP); 437 #ifdef DIAGNOSTIC 438 key->ask_smc = smc; 439 #endif 440 441 /* Use the specified name, and make sure it's null-terminated. */ 442 (void)memcpy(key->ask_name, name, 4); 443 key->ask_name[4] = '\0'; 444 445 /* Ask the SMC for a description of this key by name. */ 446 CTASSERT(sizeof(key->ask_desc) == 6); 447 error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name, 448 &key->ask_desc, 6); 449 if (error) 450 goto fail; 451 452 /* Fail with EINVAL if the types don't match. */ 453 if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) { 454 error = EINVAL; 455 goto fail; 456 } 457 458 /* Success! */ 459 *keyp = key; 460 return 0; 461 462 fail: kmem_free(key, sizeof(*key)); 463 return error; 464 } 465 466 void 467 apple_smc_release_key(struct apple_smc_tag *smc, struct apple_smc_key *key) 468 { 469 470 #ifdef DIAGNOSTIC 471 /* Make sure the caller didn't mix up SMC tags. */ 472 if (key->ask_smc != smc) 473 aprint_error_dev(smc->smc_dev, 474 "releasing key with wrong tag: %p != %p", 475 smc, key->ask_smc); 476 #endif 477 478 /* Nothing to do but free the key's memory. */ 479 kmem_free(key, sizeof(*key)); 480 } 481 482 int 483 apple_smc_key_search(struct apple_smc_tag *smc, const char name[4 + 1], 484 uint32_t *result) 485 { 486 struct apple_smc_key *key; 487 uint32_t start = 0, end = apple_smc_nkeys(smc), median; 488 int cmp; 489 int error; 490 491 /* Do a binary search on the SMC's key space. */ 492 while (start < end) { 493 median = (start + ((end - start) / 2)); 494 error = apple_smc_nth_key(smc, median, NULL, &key); 495 if (error) 496 return error; 497 498 cmp = memcmp(name, apple_smc_key_name(key), 4); 499 if (cmp < 0) 500 end = median; 501 else if (cmp > 0) 502 start = (median + 1); 503 else 504 start = end = median; /* stop here */ 505 506 apple_smc_release_key(smc, key); 507 } 508 509 /* Success! */ 510 *result = start; 511 return 0; 512 } 513 514 int 516 apple_smc_read_key(struct apple_smc_tag *smc, const struct apple_smc_key *key, 517 void *buffer, uint8_t size) 518 { 519 520 /* Refuse if software and hardware disagree on the key's size. */ 521 if (key->ask_desc.asd_size != size) 522 return EINVAL; 523 524 /* Refuse if the hardware doesn't want us to read it. */ 525 if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_READ)) 526 return EACCES; 527 528 /* Looks good. Try reading it from the hardware. */ 529 return apple_smc_input(smc, APPLE_SMC_CMD_READ_KEY, key->ask_name, 530 buffer, size); 531 } 532 533 int 534 apple_smc_read_key_1(struct apple_smc_tag *smc, 535 const struct apple_smc_key *key, uint8_t *p) 536 { 537 538 return apple_smc_read_key(smc, key, p, 1); 539 } 540 541 int 542 apple_smc_read_key_2(struct apple_smc_tag *smc, 543 const struct apple_smc_key *key, uint16_t *p) 544 { 545 uint16_t be; 546 int error; 547 548 /* Read a big-endian quantity from the hardware. */ 549 error = apple_smc_read_key(smc, key, &be, 2); 550 if (error) 551 return error; 552 553 /* Convert it to host order. */ 554 *p = be16toh(be); 555 556 /* Success! */ 557 return 0; 558 } 559 560 int 561 apple_smc_read_key_4(struct apple_smc_tag *smc, 562 const struct apple_smc_key *key, uint32_t *p) 563 { 564 uint32_t be; 565 int error; 566 567 /* Read a big-endian quantity from the hardware. */ 568 error = apple_smc_read_key(smc, key, &be, 4); 569 if (error) 570 return error; 571 572 /* Convert it to host order. */ 573 *p = be32toh(be); 574 575 /* Success! */ 576 return 0; 577 } 578 579 int 580 apple_smc_write_key(struct apple_smc_tag *smc, const struct apple_smc_key *key, 581 const void *buffer, uint8_t size) 582 { 583 584 /* Refuse if software and hardware disagree on the key's size. */ 585 if (key->ask_desc.asd_size != size) 586 return EINVAL; 587 588 /* Refuse if the hardware doesn't want us to write it. */ 589 if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_WRITE)) 590 return EACCES; 591 592 /* Looks good. Try writing it to the hardware. */ 593 return apple_smc_output(smc, APPLE_SMC_CMD_WRITE_KEY, key->ask_name, 594 buffer, size); 595 } 596 597 int 598 apple_smc_write_key_1(struct apple_smc_tag *smc, 599 const struct apple_smc_key *key, uint8_t v) 600 { 601 602 return apple_smc_write_key(smc, key, &v, 1); 603 } 604 605 int 606 apple_smc_write_key_2(struct apple_smc_tag *smc, 607 const struct apple_smc_key *key, uint16_t v) 608 { 609 /* Convert the quantity from host to big-endian byte order. */ 610 const uint16_t v_be = htobe16(v); 611 612 /* Write the big-endian quantity to the hardware. */ 613 return apple_smc_write_key(smc, key, &v_be, 2); 614 } 615 616 int 617 apple_smc_write_key_4(struct apple_smc_tag *smc, 618 const struct apple_smc_key *key, uint32_t v) 619 { 620 /* Convert the quantity from host to big-endian byte order. */ 621 const uint32_t v_be = htobe32(v); 622 623 /* Write the big-endian quantity to the hardware. */ 624 return apple_smc_write_key(smc, key, &v_be, 4); 625 } 626 627 MODULE(MODULE_CLASS_MISC, apple_smc, NULL) 629 630 static int 631 apple_smc_modcmd(modcmd_t cmd, void *data __unused) 632 { 633 634 /* Nothing to do for now to set up or tear down the module. */ 635 switch (cmd) { 636 case MODULE_CMD_INIT: 637 return 0; 638 639 case MODULE_CMD_FINI: 640 return 0; 641 642 default: 643 return ENOTTY; 644 } 645 } 646