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
59static int	apple_smc_search(device_t, cfdata_t, const int *, void *);
60static uint8_t	apple_smc_bus_read_1(struct apple_smc_tag *, bus_size_t);
61static void	apple_smc_bus_write_1(struct apple_smc_tag *, bus_size_t,
62		    uint8_t);
63static int	apple_smc_read_data(struct apple_smc_tag *, uint8_t *);
64static int	apple_smc_write(struct apple_smc_tag *, bus_size_t, uint8_t);
65static int	apple_smc_write_cmd(struct apple_smc_tag *, uint8_t);
66static int	apple_smc_write_data(struct apple_smc_tag *, uint8_t);
67static int	apple_smc_begin(struct apple_smc_tag *, uint8_t,
68		    const char *, uint8_t);
69static int	apple_smc_input(struct apple_smc_tag *, uint8_t,
70		    const char *, void *, uint8_t);
71static int	apple_smc_output(struct apple_smc_tag *, uint8_t,
72		    const char *, const void *, uint8_t);
73
74void
75apple_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
87int
88apple_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
105int
106apple_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
116static int
117apple_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
157void
158apple_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
164static uint8_t
165apple_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
171static void
172apple_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
183static int
184apple_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
207static int
208apple_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
234static int
235apple_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
241static int
242apple_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
248static int
249apple_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
277static int
278apple_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
303out:	mutex_exit(&smc->smc_io_lock);
304	return error;
305}
306
307static int
308apple_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
333out:	mutex_exit(&smc->smc_io_lock);
334	return error;
335}
336
337struct 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
345const char *
346apple_smc_key_name(const struct apple_smc_key *key)
347{
348
349	return key->ask_name;
350}
351
352const struct apple_smc_desc *
353apple_smc_key_desc(const struct apple_smc_key *key)
354{
355
356	return &key->ask_desc;
357}
358
359uint32_t
360apple_smc_nkeys(struct apple_smc_tag *smc)
361{
362
363	return smc->smc_nkeys;
364}
365
366int
367apple_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
411fail:	kmem_free(key, sizeof(*key));
412	return error;
413}
414
415int
416apple_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
458fail:	kmem_free(key, sizeof(*key));
459	return error;
460}
461
462void
463apple_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
478int
479apple_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
510int
511apple_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
528int
529apple_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
536int
537apple_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
555int
556apple_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
574int
575apple_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
592int
593apple_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
600int
601apple_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
611int
612apple_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
622MODULE(MODULE_CLASS_MISC, apple_smc, NULL)
623
624static int
625apple_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