xref: /netbsd-src/sys/dev/ic/apple_smc.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: apple_smc.c,v 1.7 2021/04/24 23:36:55 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.7 2021/04/24 23:36:55 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
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
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 	config_search(smc->smc_dev, smc,
112 	    CFARG_SEARCH, apple_smc_search,
113 	    CFARG_EOL);
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 	    CFARG_LOCATORS, locators,
155 	    CFARG_EOL);
156 	return 0;
157 }
158 
159 void
160 apple_smc_child_detached(struct apple_smc_tag *smc __unused,
161     device_t child __unused)
162 {
163 	/* We keep no books about our children.  */
164 }
165 
166 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
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 {
340 	char ask_name[4 + 1];
341 	struct apple_smc_desc ask_desc;
342 #ifdef DIAGNOSTIC
343 	struct apple_smc_tag *ask_smc;
344 #endif
345 };
346 
347 const char *
348 apple_smc_key_name(const struct apple_smc_key *key)
349 {
350 
351 	return key->ask_name;
352 }
353 
354 const struct apple_smc_desc *
355 apple_smc_key_desc(const struct apple_smc_key *key)
356 {
357 
358 	return &key->ask_desc;
359 }
360 
361 uint32_t
362 apple_smc_nkeys(struct apple_smc_tag *smc)
363 {
364 
365 	return smc->smc_nkeys;
366 }
367 
368 int
369 apple_smc_nth_key(struct apple_smc_tag *smc, uint32_t index,
370     const char type[4 + 1], struct apple_smc_key **keyp)
371 {
372 	union { uint32_t u32; char name[4]; } index_be;
373 	struct apple_smc_key *key;
374 	int error;
375 
376 	/* Paranoia: type must be NULL or 4 non-null characters long.  */
377 	if ((type != NULL) && (strlen(type) != 4))
378 		return EINVAL;
379 
380 	/* Create a new key.  XXX Consider caching these.  */
381 	key = kmem_alloc(sizeof(*key), KM_SLEEP);
382 #ifdef DIAGNOSTIC
383 	key->ask_smc = smc;
384 #endif
385 
386 	/* Ask the SMC what the name of the key by this number is.  */
387 	index_be.u32 = htobe32(index);
388 	error = apple_smc_input(smc, APPLE_SMC_CMD_NTH_KEY, index_be.name,
389 	    key->ask_name, 4);
390 	if (error)
391 		goto fail;
392 
393 	/* Null-terminate the name. */
394 	key->ask_name[4] = '\0';
395 
396 	/* Ask the SMC for a description of this key by name.  */
397 	CTASSERT(sizeof(key->ask_desc) == 6);
398 	error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name,
399 	    &key->ask_desc, 6);
400 	if (error)
401 		goto fail;
402 
403 	/* Fail with EINVAL if the types don't match.  */
404 	if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) {
405 		error = EINVAL;
406 		goto fail;
407 	}
408 
409 	/* Success!  */
410 	*keyp = key;
411 	return 0;
412 
413 fail:	kmem_free(key, sizeof(*key));
414 	return error;
415 }
416 
417 int
418 apple_smc_named_key(struct apple_smc_tag *smc, const char name[4 + 1],
419     const char type[4 + 1], struct apple_smc_key **keyp)
420 {
421 	struct apple_smc_key *key;
422 	int error;
423 
424 	/* Paranoia: name must be 4 non-null characters long.  */
425 	KASSERT(name != NULL);
426 	if (strlen(name) != 4)
427 		return EINVAL;
428 
429 	/* Paranoia: type must be NULL or 4 non-null characters long.  */
430 	if ((type != NULL) && (strlen(type) != 4))
431 		return EINVAL;
432 
433 	/* Create a new key.  XXX Consider caching these.  */
434 	key = kmem_alloc(sizeof(*key), KM_SLEEP);
435 #ifdef DIAGNOSTIC
436 	key->ask_smc = smc;
437 #endif
438 
439 	/* Use the specified name, and make sure it's null-terminated.  */
440 	(void)memcpy(key->ask_name, name, 4);
441 	key->ask_name[4] = '\0';
442 
443 	/* Ask the SMC for a description of this key by name.  */
444 	CTASSERT(sizeof(key->ask_desc) == 6);
445 	error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name,
446 	    &key->ask_desc, 6);
447 	if (error)
448 		goto fail;
449 
450 	/* Fail with EINVAL if the types don't match.  */
451 	if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) {
452 		error = EINVAL;
453 		goto fail;
454 	}
455 
456 	/* Success!  */
457 	*keyp = key;
458 	return 0;
459 
460 fail:	kmem_free(key, sizeof(*key));
461 	return error;
462 }
463 
464 void
465 apple_smc_release_key(struct apple_smc_tag *smc, struct apple_smc_key *key)
466 {
467 
468 #ifdef DIAGNOSTIC
469 	/* Make sure the caller didn't mix up SMC tags.  */
470 	if (key->ask_smc != smc)
471 		aprint_error_dev(smc->smc_dev,
472 		    "releasing key with wrong tag: %p != %p",
473 		    smc, key->ask_smc);
474 #endif
475 
476 	/* Nothing to do but free the key's memory.  */
477 	kmem_free(key, sizeof(*key));
478 }
479 
480 int
481 apple_smc_key_search(struct apple_smc_tag *smc, const char *name,
482     uint32_t *result)
483 {
484 	struct apple_smc_key *key;
485 	uint32_t start = 0, end = apple_smc_nkeys(smc), median;
486 	int cmp;
487 	int error;
488 
489 	/* Do a binary search on the SMC's key space.  */
490 	while (start < end) {
491 		median = (start + ((end - start) / 2));
492 		error = apple_smc_nth_key(smc, median, NULL, &key);
493 		if (error)
494 			return error;
495 
496 		cmp = memcmp(name, apple_smc_key_name(key), 4);
497 		if (cmp < 0)
498 			end = median;
499 		else if (cmp > 0)
500 			start = (median + 1);
501 		else
502 			start = end = median; /* stop here */
503 
504 		apple_smc_release_key(smc, key);
505 	}
506 
507 	/* Success!  */
508 	*result = start;
509 	return 0;
510 }
511 
512 int
513 apple_smc_read_key(struct apple_smc_tag *smc, const struct apple_smc_key *key,
514     void *buffer, uint8_t size)
515 {
516 
517 	/* Refuse if software and hardware disagree on the key's size.  */
518 	if (key->ask_desc.asd_size != size)
519 		return EINVAL;
520 
521 	/* Refuse if the hardware doesn't want us to read it.  */
522 	if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_READ))
523 		return EACCES;
524 
525 	/* Looks good.  Try reading it from the hardware.  */
526 	return apple_smc_input(smc, APPLE_SMC_CMD_READ_KEY, key->ask_name,
527 	    buffer, size);
528 }
529 
530 int
531 apple_smc_read_key_1(struct apple_smc_tag *smc,
532     const struct apple_smc_key *key, uint8_t *p)
533 {
534 
535 	return apple_smc_read_key(smc, key, p, 1);
536 }
537 
538 int
539 apple_smc_read_key_2(struct apple_smc_tag *smc,
540     const struct apple_smc_key *key, uint16_t *p)
541 {
542 	uint16_t be;
543 	int error;
544 
545 	/* Read a big-endian quantity from the hardware.  */
546 	error = apple_smc_read_key(smc, key, &be, 2);
547 	if (error)
548 		return error;
549 
550 	/* Convert it to host order.  */
551 	*p = be16toh(be);
552 
553 	/* Success!  */
554 	return 0;
555 }
556 
557 int
558 apple_smc_read_key_4(struct apple_smc_tag *smc,
559     const struct apple_smc_key *key, uint32_t *p)
560 {
561 	uint32_t be;
562 	int error;
563 
564 	/* Read a big-endian quantity from the hardware.  */
565 	error = apple_smc_read_key(smc, key, &be, 4);
566 	if (error)
567 		return error;
568 
569 	/* Convert it to host order.  */
570 	*p = be32toh(be);
571 
572 	/* Success!  */
573 	return 0;
574 }
575 
576 int
577 apple_smc_write_key(struct apple_smc_tag *smc, const struct apple_smc_key *key,
578     const void *buffer, uint8_t size)
579 {
580 
581 	/* Refuse if software and hardware disagree on the key's size.  */
582 	if (key->ask_desc.asd_size != size)
583 		return EINVAL;
584 
585 	/* Refuse if the hardware doesn't want us to write it.  */
586 	if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_WRITE))
587 		return EACCES;
588 
589 	/* Looks good.  Try writing it to the hardware.  */
590 	return apple_smc_output(smc, APPLE_SMC_CMD_WRITE_KEY, key->ask_name,
591 	    buffer, size);
592 }
593 
594 int
595 apple_smc_write_key_1(struct apple_smc_tag *smc,
596     const struct apple_smc_key *key, uint8_t v)
597 {
598 
599 	return apple_smc_write_key(smc, key, &v, 1);
600 }
601 
602 int
603 apple_smc_write_key_2(struct apple_smc_tag *smc,
604     const struct apple_smc_key *key, uint16_t v)
605 {
606 	/* Convert the quantity from host to big-endian byte order.  */
607 	const uint16_t v_be = htobe16(v);
608 
609 	/* Write the big-endian quantity to the hardware.  */
610 	return apple_smc_write_key(smc, key, &v_be, 2);
611 }
612 
613 int
614 apple_smc_write_key_4(struct apple_smc_tag *smc,
615     const struct apple_smc_key *key, uint32_t v)
616 {
617 	/* Convert the quantity from host to big-endian byte order.  */
618 	const uint32_t v_be = htobe32(v);
619 
620 	/* Write the big-endian quantity to the hardware.  */
621 	return apple_smc_write_key(smc, key, &v_be, 4);
622 }
623 
624 MODULE(MODULE_CLASS_MISC, apple_smc, NULL)
625 
626 static int
627 apple_smc_modcmd(modcmd_t cmd, void *data __unused)
628 {
629 
630 	/* Nothing to do for now to set up or tear down the module.  */
631 	switch (cmd) {
632 	case MODULE_CMD_INIT:
633 		return 0;
634 
635 	case MODULE_CMD_FINI:
636 		return 0;
637 
638 	default:
639 		return ENOTTY;
640 	}
641 }
642