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