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