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