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