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 59static int apple_smc_search(device_t, cfdata_t, const int *, void *); 60static uint8_t apple_smc_bus_read_1(struct apple_smc_tag *, bus_size_t); 61static void apple_smc_bus_write_1(struct apple_smc_tag *, bus_size_t, 62 uint8_t); 63static int apple_smc_read_data(struct apple_smc_tag *, uint8_t *); 64static int apple_smc_write(struct apple_smc_tag *, bus_size_t, uint8_t); 65static int apple_smc_write_cmd(struct apple_smc_tag *, uint8_t); 66static int apple_smc_write_data(struct apple_smc_tag *, uint8_t); 67static int apple_smc_begin(struct apple_smc_tag *, uint8_t, 68 const char *, uint8_t); 69static int apple_smc_input(struct apple_smc_tag *, uint8_t, 70 const char *, void *, uint8_t); 71static int apple_smc_output(struct apple_smc_tag *, uint8_t, 72 const char *, const void *, uint8_t); 73 74void 75apple_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 87int 88apple_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 105int 106apple_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 116static int 117apple_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 157void 158apple_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 164static uint8_t 165apple_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 171static void 172apple_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 183static int 184apple_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 207static int 208apple_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 234static int 235apple_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 241static int 242apple_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 248static int 249apple_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 277static int 278apple_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 303out: mutex_exit(&smc->smc_io_lock); 304 return error; 305} 306 307static int 308apple_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 333out: mutex_exit(&smc->smc_io_lock); 334 return error; 335} 336 337struct 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 345const char * 346apple_smc_key_name(const struct apple_smc_key *key) 347{ 348 349 return key->ask_name; 350} 351 352const struct apple_smc_desc * 353apple_smc_key_desc(const struct apple_smc_key *key) 354{ 355 356 return &key->ask_desc; 357} 358 359uint32_t 360apple_smc_nkeys(struct apple_smc_tag *smc) 361{ 362 363 return smc->smc_nkeys; 364} 365 366int 367apple_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 411fail: kmem_free(key, sizeof(*key)); 412 return error; 413} 414 415int 416apple_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 458fail: kmem_free(key, sizeof(*key)); 459 return error; 460} 461 462void 463apple_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 478int 479apple_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 510int 511apple_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 528int 529apple_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 536int 537apple_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 555int 556apple_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 574int 575apple_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 592int 593apple_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 600int 601apple_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 611int 612apple_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 622MODULE(MODULE_CLASS_MISC, apple_smc, NULL) 623 624static int 625apple_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