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
apple_smc_attach(struct apple_smc_tag * smc)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, NULL, NULL);
85 }
86 
87 int
apple_smc_detach(struct apple_smc_tag * smc,int flags)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
apple_smc_rescan(struct apple_smc_tag * smc,const char * ifattr,const int * locators)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           config_search(smc->smc_dev, smc,
112               CFARGS(.search = apple_smc_search));
113           return 0;
114 }
115 
116 static int
apple_smc_search(device_t parent,cfdata_t cf,const int * locators,void * aux)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_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 
157 void
apple_smc_child_detached(struct apple_smc_tag * smc __unused,device_t child __unused)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
apple_smc_bus_read_1(struct apple_smc_tag * smc,bus_size_t reg)165 apple_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 
171 static void
apple_smc_bus_write_1(struct apple_smc_tag * smc,bus_size_t reg,uint8_t v)172 apple_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 
183 static int
apple_smc_read_data(struct apple_smc_tag * smc,uint8_t * byte)184 apple_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 
207 static int
apple_smc_write(struct apple_smc_tag * smc,bus_size_t reg,uint8_t byte)208 apple_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 
234 static int
apple_smc_write_cmd(struct apple_smc_tag * smc,uint8_t cmd)235 apple_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 
241 static int
apple_smc_write_data(struct apple_smc_tag * smc,uint8_t data)242 apple_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 
248 static int
apple_smc_begin(struct apple_smc_tag * smc,uint8_t cmd,const char * key,uint8_t size)249 apple_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 
277 static int
apple_smc_input(struct apple_smc_tag * smc,uint8_t cmd,const char * key,void * buffer,uint8_t size)278 apple_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 
303 out:      mutex_exit(&smc->smc_io_lock);
304           return error;
305 }
306 
307 static int
apple_smc_output(struct apple_smc_tag * smc,uint8_t cmd,const char * key,const void * buffer,uint8_t size)308 apple_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 
333 out:      mutex_exit(&smc->smc_io_lock);
334           return error;
335 }
336 
337 struct 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 
345 const char *
apple_smc_key_name(const struct apple_smc_key * key)346 apple_smc_key_name(const struct apple_smc_key *key)
347 {
348 
349           return key->ask_name;
350 }
351 
352 const struct apple_smc_desc *
apple_smc_key_desc(const struct apple_smc_key * key)353 apple_smc_key_desc(const struct apple_smc_key *key)
354 {
355 
356           return &key->ask_desc;
357 }
358 
359 uint32_t
apple_smc_nkeys(struct apple_smc_tag * smc)360 apple_smc_nkeys(struct apple_smc_tag *smc)
361 {
362 
363           return smc->smc_nkeys;
364 }
365 
366 int
apple_smc_nth_key(struct apple_smc_tag * smc,uint32_t index,const char type[4+1],struct apple_smc_key ** keyp)367 apple_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 
411 fail:     kmem_free(key, sizeof(*key));
412           return error;
413 }
414 
415 int
apple_smc_named_key(struct apple_smc_tag * smc,const char name[4+1],const char type[4+1],struct apple_smc_key ** keyp)416 apple_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 
458 fail:     kmem_free(key, sizeof(*key));
459           return error;
460 }
461 
462 void
apple_smc_release_key(struct apple_smc_tag * smc,struct apple_smc_key * key)463 apple_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 
478 int
apple_smc_key_search(struct apple_smc_tag * smc,const char name[4+1],uint32_t * result)479 apple_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 
510 int
apple_smc_read_key(struct apple_smc_tag * smc,const struct apple_smc_key * key,void * buffer,uint8_t size)511 apple_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 
528 int
apple_smc_read_key_1(struct apple_smc_tag * smc,const struct apple_smc_key * key,uint8_t * p)529 apple_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 
536 int
apple_smc_read_key_2(struct apple_smc_tag * smc,const struct apple_smc_key * key,uint16_t * p)537 apple_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 
555 int
apple_smc_read_key_4(struct apple_smc_tag * smc,const struct apple_smc_key * key,uint32_t * p)556 apple_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 
574 int
apple_smc_write_key(struct apple_smc_tag * smc,const struct apple_smc_key * key,const void * buffer,uint8_t size)575 apple_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 
592 int
apple_smc_write_key_1(struct apple_smc_tag * smc,const struct apple_smc_key * key,uint8_t v)593 apple_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 
600 int
apple_smc_write_key_2(struct apple_smc_tag * smc,const struct apple_smc_key * key,uint16_t v)601 apple_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 
611 int
apple_smc_write_key_4(struct apple_smc_tag * smc,const struct apple_smc_key * key,uint32_t v)612 apple_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 
MODULE(MODULE_CLASS_MISC,apple_smc,NULL)622 MODULE(MODULE_CLASS_MISC, apple_smc, NULL)
623 
624 static int
625 apple_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