xref: /netbsd-src/sys/dev/ic/apple_smc.c (revision df4d49549c23968fb3f4045c8429ffd8f5b9965b)
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