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