1 /* $NetBSD: apple_smc.c,v 1.7 2021/04/24 23:36:55 thorpej 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.7 2021/04/24 23:36:55 thorpej 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 75 apple_smc_attach(struct apple_smc_tag *smc) 76 { 77 78 mutex_init(&smc->smc_io_lock, MUTEX_DEFAULT, IPL_NONE); 79 #if 0 /* XXX sysctl */ 80 apple_smc_sysctl_setup(smc); 81 #endif 82 83 /* Attach any children. */ 84 (void)apple_smc_rescan(smc, NULL, NULL); 85 } 86 87 int 88 apple_smc_detach(struct apple_smc_tag *smc, int flags) 89 { 90 int error; 91 92 /* Fail if we can't detach all our children. */ 93 error = config_detach_children(smc->smc_dev, flags); 94 if (error) 95 return error; 96 97 #if 0 /* XXX sysctl */ 98 sysctl_teardown(&smc->smc_log); 99 #endif 100 mutex_destroy(&smc->smc_io_lock); 101 102 return 0; 103 } 104 105 int 106 apple_smc_rescan(struct apple_smc_tag *smc, const char *ifattr, 107 const int *locators) 108 { 109 110 /* Let autoconf(9) do the work of finding new children. */ 111 config_search(smc->smc_dev, smc, 112 CFARG_SEARCH, apple_smc_search, 113 CFARG_EOL); 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 CFARG_LOCATORS, locators, 155 CFARG_EOL); 156 return 0; 157 } 158 159 void 160 apple_smc_child_detached(struct apple_smc_tag *smc __unused, 161 device_t child __unused) 162 { 163 /* We keep no books about our children. */ 164 } 165 166 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 251 apple_smc_begin(struct apple_smc_tag *smc, uint8_t cmd, const char *key, 252 uint8_t size) 253 { 254 unsigned int i; 255 int error; 256 257 KASSERT(mutex_owned(&smc->smc_io_lock)); 258 259 /* Write the command first. */ 260 error = apple_smc_write_cmd(smc, cmd); 261 if (error) 262 return error; 263 264 /* Write the key next. */ 265 for (i = 0; i < 4; i++) { 266 error = apple_smc_write_data(smc, key[i]); 267 if (error) 268 return error; 269 } 270 271 /* Finally, report how many bytes of data we want to send/receive. */ 272 error = apple_smc_write_data(smc, size); 273 if (error) 274 return error; 275 276 return 0; 277 } 278 279 static int 280 apple_smc_input(struct apple_smc_tag *smc, uint8_t cmd, const char *key, 281 void *buffer, uint8_t size) 282 { 283 uint8_t *bytes = buffer; 284 uint8_t i; 285 int error; 286 287 /* Grab the SMC I/O lock. */ 288 mutex_enter(&smc->smc_io_lock); 289 290 /* Initiate the command with this key. */ 291 error = apple_smc_begin(smc, cmd, key, size); 292 if (error) 293 goto out; 294 295 /* Read each byte of data in sequence. */ 296 for (i = 0; i < size; i++) { 297 error = apple_smc_read_data(smc, &bytes[i]); 298 if (error) 299 goto out; 300 } 301 302 /* Success! */ 303 error = 0; 304 305 out: mutex_exit(&smc->smc_io_lock); 306 return error; 307 } 308 309 static int 310 apple_smc_output(struct apple_smc_tag *smc, uint8_t cmd, const char *key, 311 const void *buffer, uint8_t size) 312 { 313 const uint8_t *bytes = buffer; 314 uint8_t i; 315 int error; 316 317 /* Grab the SMC I/O lock. */ 318 mutex_enter(&smc->smc_io_lock); 319 320 /* Initiate the command with this key. */ 321 error = apple_smc_begin(smc, cmd, key, size); 322 if (error) 323 goto out; 324 325 /* Write each byte of data in sequence. */ 326 for (i = 0; i < size; i++) { 327 error = apple_smc_write_data(smc, bytes[i]); 328 if (error) 329 goto out; 330 } 331 332 /* Success! */ 333 error = 0; 334 335 out: mutex_exit(&smc->smc_io_lock); 336 return error; 337 } 338 339 struct apple_smc_key { 340 char ask_name[4 + 1]; 341 struct apple_smc_desc ask_desc; 342 #ifdef DIAGNOSTIC 343 struct apple_smc_tag *ask_smc; 344 #endif 345 }; 346 347 const char * 348 apple_smc_key_name(const struct apple_smc_key *key) 349 { 350 351 return key->ask_name; 352 } 353 354 const struct apple_smc_desc * 355 apple_smc_key_desc(const struct apple_smc_key *key) 356 { 357 358 return &key->ask_desc; 359 } 360 361 uint32_t 362 apple_smc_nkeys(struct apple_smc_tag *smc) 363 { 364 365 return smc->smc_nkeys; 366 } 367 368 int 369 apple_smc_nth_key(struct apple_smc_tag *smc, uint32_t index, 370 const char type[4 + 1], struct apple_smc_key **keyp) 371 { 372 union { uint32_t u32; char name[4]; } index_be; 373 struct apple_smc_key *key; 374 int error; 375 376 /* Paranoia: type must be NULL or 4 non-null characters long. */ 377 if ((type != NULL) && (strlen(type) != 4)) 378 return EINVAL; 379 380 /* Create a new key. XXX Consider caching these. */ 381 key = kmem_alloc(sizeof(*key), KM_SLEEP); 382 #ifdef DIAGNOSTIC 383 key->ask_smc = smc; 384 #endif 385 386 /* Ask the SMC what the name of the key by this number is. */ 387 index_be.u32 = htobe32(index); 388 error = apple_smc_input(smc, APPLE_SMC_CMD_NTH_KEY, index_be.name, 389 key->ask_name, 4); 390 if (error) 391 goto fail; 392 393 /* Null-terminate the name. */ 394 key->ask_name[4] = '\0'; 395 396 /* Ask the SMC for a description of this key by name. */ 397 CTASSERT(sizeof(key->ask_desc) == 6); 398 error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name, 399 &key->ask_desc, 6); 400 if (error) 401 goto fail; 402 403 /* Fail with EINVAL if the types don't match. */ 404 if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) { 405 error = EINVAL; 406 goto fail; 407 } 408 409 /* Success! */ 410 *keyp = key; 411 return 0; 412 413 fail: kmem_free(key, sizeof(*key)); 414 return error; 415 } 416 417 int 418 apple_smc_named_key(struct apple_smc_tag *smc, const char name[4 + 1], 419 const char type[4 + 1], struct apple_smc_key **keyp) 420 { 421 struct apple_smc_key *key; 422 int error; 423 424 /* Paranoia: name must be 4 non-null characters long. */ 425 KASSERT(name != NULL); 426 if (strlen(name) != 4) 427 return EINVAL; 428 429 /* Paranoia: type must be NULL or 4 non-null characters long. */ 430 if ((type != NULL) && (strlen(type) != 4)) 431 return EINVAL; 432 433 /* Create a new key. XXX Consider caching these. */ 434 key = kmem_alloc(sizeof(*key), KM_SLEEP); 435 #ifdef DIAGNOSTIC 436 key->ask_smc = smc; 437 #endif 438 439 /* Use the specified name, and make sure it's null-terminated. */ 440 (void)memcpy(key->ask_name, name, 4); 441 key->ask_name[4] = '\0'; 442 443 /* Ask the SMC for a description of this key by name. */ 444 CTASSERT(sizeof(key->ask_desc) == 6); 445 error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name, 446 &key->ask_desc, 6); 447 if (error) 448 goto fail; 449 450 /* Fail with EINVAL if the types don't match. */ 451 if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) { 452 error = EINVAL; 453 goto fail; 454 } 455 456 /* Success! */ 457 *keyp = key; 458 return 0; 459 460 fail: kmem_free(key, sizeof(*key)); 461 return error; 462 } 463 464 void 465 apple_smc_release_key(struct apple_smc_tag *smc, struct apple_smc_key *key) 466 { 467 468 #ifdef DIAGNOSTIC 469 /* Make sure the caller didn't mix up SMC tags. */ 470 if (key->ask_smc != smc) 471 aprint_error_dev(smc->smc_dev, 472 "releasing key with wrong tag: %p != %p", 473 smc, key->ask_smc); 474 #endif 475 476 /* Nothing to do but free the key's memory. */ 477 kmem_free(key, sizeof(*key)); 478 } 479 480 int 481 apple_smc_key_search(struct apple_smc_tag *smc, const char *name, 482 uint32_t *result) 483 { 484 struct apple_smc_key *key; 485 uint32_t start = 0, end = apple_smc_nkeys(smc), median; 486 int cmp; 487 int error; 488 489 /* Do a binary search on the SMC's key space. */ 490 while (start < end) { 491 median = (start + ((end - start) / 2)); 492 error = apple_smc_nth_key(smc, median, NULL, &key); 493 if (error) 494 return error; 495 496 cmp = memcmp(name, apple_smc_key_name(key), 4); 497 if (cmp < 0) 498 end = median; 499 else if (cmp > 0) 500 start = (median + 1); 501 else 502 start = end = median; /* stop here */ 503 504 apple_smc_release_key(smc, key); 505 } 506 507 /* Success! */ 508 *result = start; 509 return 0; 510 } 511 512 int 513 apple_smc_read_key(struct apple_smc_tag *smc, const struct apple_smc_key *key, 514 void *buffer, uint8_t size) 515 { 516 517 /* Refuse if software and hardware disagree on the key's size. */ 518 if (key->ask_desc.asd_size != size) 519 return EINVAL; 520 521 /* Refuse if the hardware doesn't want us to read it. */ 522 if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_READ)) 523 return EACCES; 524 525 /* Looks good. Try reading it from the hardware. */ 526 return apple_smc_input(smc, APPLE_SMC_CMD_READ_KEY, key->ask_name, 527 buffer, size); 528 } 529 530 int 531 apple_smc_read_key_1(struct apple_smc_tag *smc, 532 const struct apple_smc_key *key, uint8_t *p) 533 { 534 535 return apple_smc_read_key(smc, key, p, 1); 536 } 537 538 int 539 apple_smc_read_key_2(struct apple_smc_tag *smc, 540 const struct apple_smc_key *key, uint16_t *p) 541 { 542 uint16_t be; 543 int error; 544 545 /* Read a big-endian quantity from the hardware. */ 546 error = apple_smc_read_key(smc, key, &be, 2); 547 if (error) 548 return error; 549 550 /* Convert it to host order. */ 551 *p = be16toh(be); 552 553 /* Success! */ 554 return 0; 555 } 556 557 int 558 apple_smc_read_key_4(struct apple_smc_tag *smc, 559 const struct apple_smc_key *key, uint32_t *p) 560 { 561 uint32_t be; 562 int error; 563 564 /* Read a big-endian quantity from the hardware. */ 565 error = apple_smc_read_key(smc, key, &be, 4); 566 if (error) 567 return error; 568 569 /* Convert it to host order. */ 570 *p = be32toh(be); 571 572 /* Success! */ 573 return 0; 574 } 575 576 int 577 apple_smc_write_key(struct apple_smc_tag *smc, const struct apple_smc_key *key, 578 const void *buffer, uint8_t size) 579 { 580 581 /* Refuse if software and hardware disagree on the key's size. */ 582 if (key->ask_desc.asd_size != size) 583 return EINVAL; 584 585 /* Refuse if the hardware doesn't want us to write it. */ 586 if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_WRITE)) 587 return EACCES; 588 589 /* Looks good. Try writing it to the hardware. */ 590 return apple_smc_output(smc, APPLE_SMC_CMD_WRITE_KEY, key->ask_name, 591 buffer, size); 592 } 593 594 int 595 apple_smc_write_key_1(struct apple_smc_tag *smc, 596 const struct apple_smc_key *key, uint8_t v) 597 { 598 599 return apple_smc_write_key(smc, key, &v, 1); 600 } 601 602 int 603 apple_smc_write_key_2(struct apple_smc_tag *smc, 604 const struct apple_smc_key *key, uint16_t v) 605 { 606 /* Convert the quantity from host to big-endian byte order. */ 607 const uint16_t v_be = htobe16(v); 608 609 /* Write the big-endian quantity to the hardware. */ 610 return apple_smc_write_key(smc, key, &v_be, 2); 611 } 612 613 int 614 apple_smc_write_key_4(struct apple_smc_tag *smc, 615 const struct apple_smc_key *key, uint32_t v) 616 { 617 /* Convert the quantity from host to big-endian byte order. */ 618 const uint32_t v_be = htobe32(v); 619 620 /* Write the big-endian quantity to the hardware. */ 621 return apple_smc_write_key(smc, key, &v_be, 4); 622 } 623 624 MODULE(MODULE_CLASS_MISC, apple_smc, NULL) 625 626 static int 627 apple_smc_modcmd(modcmd_t cmd, void *data __unused) 628 { 629 630 /* Nothing to do for now to set up or tear down the module. */ 631 switch (cmd) { 632 case MODULE_CMD_INIT: 633 return 0; 634 635 case MODULE_CMD_FINI: 636 return 0; 637 638 default: 639 return ENOTTY; 640 } 641 } 642