apple_smc.c revision 1.6.50.1 1 1.6.50.1 thorpej /* $NetBSD: apple_smc.c,v 1.6.50.1 2021/03/20 19:33:40 thorpej Exp $ */
2 1.1 riastrad
3 1.1 riastrad /*
4 1.1 riastrad * Apple System Management Controller
5 1.1 riastrad */
6 1.1 riastrad
7 1.1 riastrad /*-
8 1.1 riastrad * Copyright (c) 2013 The NetBSD Foundation, Inc.
9 1.1 riastrad * All rights reserved.
10 1.1 riastrad *
11 1.1 riastrad * This code is derived from software contributed to The NetBSD Foundation
12 1.1 riastrad * by Taylor R. Campbell.
13 1.1 riastrad *
14 1.1 riastrad * Redistribution and use in source and binary forms, with or without
15 1.1 riastrad * modification, are permitted provided that the following conditions
16 1.1 riastrad * are met:
17 1.1 riastrad * 1. Redistributions of source code must retain the above copyright
18 1.1 riastrad * notice, this list of conditions and the following disclaimer.
19 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright
20 1.1 riastrad * notice, this list of conditions and the following disclaimer in the
21 1.1 riastrad * documentation and/or other materials provided with the distribution.
22 1.1 riastrad *
23 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE.
34 1.1 riastrad */
35 1.1 riastrad
36 1.1 riastrad #include <sys/cdefs.h>
37 1.6.50.1 thorpej __KERNEL_RCSID(0, "$NetBSD: apple_smc.c,v 1.6.50.1 2021/03/20 19:33:40 thorpej Exp $");
38 1.1 riastrad
39 1.1 riastrad #include <sys/types.h>
40 1.1 riastrad #include <sys/param.h>
41 1.1 riastrad #include <sys/device.h>
42 1.1 riastrad #include <sys/errno.h>
43 1.1 riastrad #include <sys/kmem.h>
44 1.2 riastrad #include <sys/module.h>
45 1.1 riastrad #include <sys/mutex.h>
46 1.2 riastrad #include <sys/rwlock.h>
47 1.1 riastrad #if 0 /* XXX sysctl */
48 1.1 riastrad #include <sys/sysctl.h>
49 1.1 riastrad #endif
50 1.1 riastrad #include <sys/systm.h>
51 1.1 riastrad
52 1.1 riastrad #include <dev/ic/apple_smc.h>
53 1.1 riastrad #include <dev/ic/apple_smcreg.h>
54 1.1 riastrad #include <dev/ic/apple_smcvar.h>
55 1.1 riastrad
56 1.3 riastrad /* Must match the config(5) name. */
57 1.2 riastrad #define APPLE_SMC_BUS "applesmcbus"
58 1.2 riastrad
59 1.3 riastrad static int apple_smc_search(device_t, cfdata_t, const int *, void *);
60 1.1 riastrad static uint8_t apple_smc_bus_read_1(struct apple_smc_tag *, bus_size_t);
61 1.1 riastrad static void apple_smc_bus_write_1(struct apple_smc_tag *, bus_size_t,
62 1.1 riastrad uint8_t);
63 1.1 riastrad static int apple_smc_read_data(struct apple_smc_tag *, uint8_t *);
64 1.1 riastrad static int apple_smc_write(struct apple_smc_tag *, bus_size_t, uint8_t);
65 1.1 riastrad static int apple_smc_write_cmd(struct apple_smc_tag *, uint8_t);
66 1.1 riastrad static int apple_smc_write_data(struct apple_smc_tag *, uint8_t);
67 1.1 riastrad static int apple_smc_begin(struct apple_smc_tag *, uint8_t,
68 1.1 riastrad const char *, uint8_t);
69 1.1 riastrad static int apple_smc_input(struct apple_smc_tag *, uint8_t,
70 1.1 riastrad const char *, void *, uint8_t);
71 1.1 riastrad static int apple_smc_output(struct apple_smc_tag *, uint8_t,
72 1.1 riastrad const char *, const void *, uint8_t);
73 1.2 riastrad
74 1.2 riastrad void
76 1.1 riastrad apple_smc_attach(struct apple_smc_tag *smc)
77 1.1 riastrad {
78 1.4 riastrad
79 1.2 riastrad mutex_init(&smc->smc_io_lock, MUTEX_DEFAULT, IPL_NONE);
80 1.2 riastrad #if 0 /* XXX sysctl */
81 1.2 riastrad apple_smc_sysctl_setup(smc);
82 1.2 riastrad #endif
83 1.4 riastrad
84 1.3 riastrad /* Attach any children. */
85 1.1 riastrad (void)apple_smc_rescan(smc, APPLE_SMC_BUS, NULL);
86 1.1 riastrad }
87 1.1 riastrad
88 1.1 riastrad int
89 1.1 riastrad apple_smc_detach(struct apple_smc_tag *smc, int flags)
90 1.1 riastrad {
91 1.1 riastrad int error;
92 1.4 riastrad
93 1.1 riastrad /* Fail if we can't detach all our children. */
94 1.1 riastrad error = config_detach_children(smc->smc_dev, flags);
95 1.1 riastrad if (error)
96 1.1 riastrad return error;
97 1.1 riastrad
98 1.1 riastrad #if 0 /* XXX sysctl */
99 1.1 riastrad sysctl_teardown(&smc->smc_log);
100 1.4 riastrad #endif
101 1.1 riastrad mutex_destroy(&smc->smc_io_lock);
102 1.1 riastrad
103 1.1 riastrad return 0;
104 1.1 riastrad }
105 1.2 riastrad
106 1.2 riastrad int
107 1.3 riastrad apple_smc_rescan(struct apple_smc_tag *smc, const char *ifattr,
108 1.2 riastrad const int *locators)
109 1.2 riastrad {
110 1.4 riastrad
111 1.6.50.1 thorpej /* Let autoconf(9) do the work of finding new children. */
112 1.6.50.1 thorpej config_search(smc->smc_dev, smc,
113 1.6.50.1 thorpej CFARG_SUBMATCH, apple_smc_search,
114 1.6.50.1 thorpej CFARG_IATTR, APPLE_SMC_BUS,
115 1.6.50.1 thorpej CFARG_LOCATORS, locators,
116 1.3 riastrad CFARG_EOL);
117 1.3 riastrad return 0;
118 1.2 riastrad }
119 1.3 riastrad
120 1.3 riastrad static int
121 1.3 riastrad apple_smc_search(device_t parent, cfdata_t cf, const int *locators, void *aux)
122 1.3 riastrad {
123 1.3 riastrad struct apple_smc_tag *const smc = aux;
124 1.3 riastrad static const struct apple_smc_attach_args zero_asa;
125 1.3 riastrad struct apple_smc_attach_args asa = zero_asa;
126 1.3 riastrad device_t dev;
127 1.3 riastrad deviter_t di;
128 1.2 riastrad bool attached = false;
129 1.2 riastrad
130 1.3 riastrad /*
131 1.2 riastrad * If this device has already attached, don't attach it again.
132 1.3 riastrad *
133 1.3 riastrad * XXX This is a pretty silly way to query the children, but
134 1.2 riastrad * struct device doesn't seem to list its children.
135 1.3 riastrad */
136 1.3 riastrad for (dev = deviter_first(&di, DEVITER_F_LEAVES_FIRST);
137 1.3 riastrad dev != NULL;
138 1.3 riastrad dev = deviter_next(&di)) {
139 1.2 riastrad if (device_parent(dev) != parent)
140 1.3 riastrad continue;
141 1.2 riastrad if (!device_is_a(dev, cf->cf_name))
142 1.3 riastrad continue;
143 1.3 riastrad attached = true;
144 1.3 riastrad break;
145 1.3 riastrad }
146 1.3 riastrad deviter_release(&di);
147 1.3 riastrad if (attached)
148 1.2 riastrad return 0;
149 1.3 riastrad
150 1.3 riastrad /* If this device doesn't match, don't attach it. */
151 1.3 riastrad if (!config_match(parent, cf, aux))
152 1.2 riastrad return 0;
153 1.3 riastrad
154 1.3 riastrad /* Looks hunky-dory. Attach. */
155 1.3 riastrad asa.asa_smc = smc;
156 1.3 riastrad (void)config_attach_loc(parent, cf, locators, &asa, NULL);
157 1.2 riastrad return 0;
158 1.2 riastrad }
159 1.2 riastrad
160 1.3 riastrad void
161 1.3 riastrad apple_smc_child_detached(struct apple_smc_tag *smc __unused,
162 1.2 riastrad device_t child __unused)
163 1.4 riastrad {
164 1.2 riastrad /* We keep no books about our children. */
165 1.2 riastrad }
166 1.1 riastrad
167 1.1 riastrad static uint8_t
169 1.1 riastrad apple_smc_bus_read_1(struct apple_smc_tag *smc, bus_size_t reg)
170 1.1 riastrad {
171 1.1 riastrad
172 1.1 riastrad return bus_space_read_1(smc->smc_bst, smc->smc_bsh, reg);
173 1.1 riastrad }
174 1.1 riastrad
175 1.1 riastrad static void
176 1.1 riastrad apple_smc_bus_write_1(struct apple_smc_tag *smc, bus_size_t reg, uint8_t v)
177 1.1 riastrad {
178 1.1 riastrad
179 1.1 riastrad bus_space_write_1(smc->smc_bst, smc->smc_bsh, reg, v);
180 1.1 riastrad }
181 1.1 riastrad
182 1.1 riastrad /*
183 1.1 riastrad * XXX These delays are pretty randomly chosen. Wait in 100 us
184 1.1 riastrad * increments, up to a total of 1 ms.
185 1.1 riastrad */
186 1.1 riastrad
187 1.1 riastrad static int
188 1.1 riastrad apple_smc_read_data(struct apple_smc_tag *smc, uint8_t *byte)
189 1.1 riastrad {
190 1.1 riastrad uint8_t status;
191 1.4 riastrad unsigned int i;
192 1.1 riastrad
193 1.4 riastrad KASSERT(mutex_owned(&smc->smc_io_lock));
194 1.4 riastrad
195 1.4 riastrad /*
196 1.4 riastrad * Wait until the status register says there's data to read and
197 1.1 riastrad * read it.
198 1.1 riastrad */
199 1.1 riastrad for (i = 0; i < 100; i++) {
200 1.1 riastrad status = apple_smc_bus_read_1(smc, APPLE_SMC_CSR);
201 1.1 riastrad if (status & APPLE_SMC_STATUS_READ_READY) {
202 1.1 riastrad *byte = apple_smc_bus_read_1(smc, APPLE_SMC_DATA);
203 1.1 riastrad return 0;
204 1.1 riastrad }
205 1.1 riastrad DELAY(100);
206 1.1 riastrad }
207 1.1 riastrad
208 1.1 riastrad return ETIMEDOUT;
209 1.1 riastrad }
210 1.1 riastrad
211 1.1 riastrad static int
212 1.1 riastrad apple_smc_write(struct apple_smc_tag *smc, bus_size_t reg, uint8_t byte)
213 1.1 riastrad {
214 1.1 riastrad uint8_t status;
215 1.4 riastrad unsigned int i;
216 1.1 riastrad
217 1.4 riastrad KASSERT(mutex_owned(&smc->smc_io_lock));
218 1.4 riastrad
219 1.4 riastrad /*
220 1.4 riastrad * Write the byte and then wait until the status register says
221 1.1 riastrad * it has been accepted.
222 1.1 riastrad */
223 1.1 riastrad apple_smc_bus_write_1(smc, reg, byte);
224 1.1 riastrad for (i = 0; i < 100; i++) {
225 1.1 riastrad status = apple_smc_bus_read_1(smc, APPLE_SMC_CSR);
226 1.1 riastrad if (status & APPLE_SMC_STATUS_WRITE_ACCEPTED)
227 1.4 riastrad return 0;
228 1.4 riastrad DELAY(100);
229 1.1 riastrad
230 1.1 riastrad /* Write again if it hasn't been acknowledged at all. */
231 1.1 riastrad if (!(status & APPLE_SMC_STATUS_WRITE_PENDING))
232 1.1 riastrad apple_smc_bus_write_1(smc, reg, byte);
233 1.1 riastrad }
234 1.1 riastrad
235 1.1 riastrad return ETIMEDOUT;
236 1.1 riastrad }
237 1.1 riastrad
238 1.1 riastrad static int
239 1.1 riastrad apple_smc_write_cmd(struct apple_smc_tag *smc, uint8_t cmd)
240 1.1 riastrad {
241 1.1 riastrad
242 1.1 riastrad return apple_smc_write(smc, APPLE_SMC_CSR, cmd);
243 1.1 riastrad }
244 1.1 riastrad
245 1.1 riastrad static int
246 1.1 riastrad apple_smc_write_data(struct apple_smc_tag *smc, uint8_t data)
247 1.1 riastrad {
248 1.1 riastrad
249 1.2 riastrad return apple_smc_write(smc, APPLE_SMC_DATA, data);
250 1.1 riastrad }
251 1.1 riastrad
252 1.1 riastrad static int
254 1.1 riastrad apple_smc_begin(struct apple_smc_tag *smc, uint8_t cmd, const char *key,
255 1.1 riastrad uint8_t size)
256 1.1 riastrad {
257 1.4 riastrad unsigned int i;
258 1.1 riastrad int error;
259 1.4 riastrad
260 1.1 riastrad KASSERT(mutex_owned(&smc->smc_io_lock));
261 1.1 riastrad
262 1.1 riastrad /* Write the command first. */
263 1.1 riastrad error = apple_smc_write_cmd(smc, cmd);
264 1.4 riastrad if (error)
265 1.1 riastrad return error;
266 1.1 riastrad
267 1.1 riastrad /* Write the key next. */
268 1.1 riastrad for (i = 0; i < 4; i++) {
269 1.1 riastrad error = apple_smc_write_data(smc, key[i]);
270 1.1 riastrad if (error)
271 1.4 riastrad return error;
272 1.1 riastrad }
273 1.1 riastrad
274 1.1 riastrad /* Finally, report how many bytes of data we want to send/receive. */
275 1.1 riastrad error = apple_smc_write_data(smc, size);
276 1.1 riastrad if (error)
277 1.1 riastrad return error;
278 1.1 riastrad
279 1.1 riastrad return 0;
280 1.1 riastrad }
281 1.1 riastrad
282 1.1 riastrad static int
283 1.1 riastrad apple_smc_input(struct apple_smc_tag *smc, uint8_t cmd, const char *key,
284 1.1 riastrad void *buffer, uint8_t size)
285 1.1 riastrad {
286 1.1 riastrad uint8_t *bytes = buffer;
287 1.4 riastrad uint8_t i;
288 1.4 riastrad int error;
289 1.4 riastrad
290 1.4 riastrad /* Grab the SMC I/O lock. */
291 1.1 riastrad mutex_enter(&smc->smc_io_lock);
292 1.1 riastrad
293 1.1 riastrad /* Initiate the command with this key. */
294 1.1 riastrad error = apple_smc_begin(smc, cmd, key, size);
295 1.4 riastrad if (error)
296 1.1 riastrad goto out;
297 1.1 riastrad
298 1.1 riastrad /* Read each byte of data in sequence. */
299 1.1 riastrad for (i = 0; i < size; i++) {
300 1.1 riastrad error = apple_smc_read_data(smc, &bytes[i]);
301 1.1 riastrad if (error)
302 1.1 riastrad goto out;
303 1.1 riastrad }
304 1.1 riastrad
305 1.4 riastrad /* Success! */
306 1.1 riastrad error = 0;
307 1.1 riastrad
308 1.1 riastrad out: mutex_exit(&smc->smc_io_lock);
309 1.1 riastrad return error;
310 1.1 riastrad }
311 1.1 riastrad
312 1.1 riastrad static int
313 1.1 riastrad apple_smc_output(struct apple_smc_tag *smc, uint8_t cmd, const char *key,
314 1.1 riastrad const void *buffer, uint8_t size)
315 1.1 riastrad {
316 1.1 riastrad const uint8_t *bytes = buffer;
317 1.4 riastrad uint8_t i;
318 1.4 riastrad int error;
319 1.4 riastrad
320 1.4 riastrad /* Grab the SMC I/O lock. */
321 1.1 riastrad mutex_enter(&smc->smc_io_lock);
322 1.1 riastrad
323 1.1 riastrad /* Initiate the command with this key. */
324 1.1 riastrad error = apple_smc_begin(smc, cmd, key, size);
325 1.4 riastrad if (error)
326 1.1 riastrad goto out;
327 1.1 riastrad
328 1.1 riastrad /* Write each byte of data in sequence. */
329 1.1 riastrad for (i = 0; i < size; i++) {
330 1.1 riastrad error = apple_smc_write_data(smc, bytes[i]);
331 1.1 riastrad if (error)
332 1.4 riastrad goto out;
333 1.4 riastrad }
334 1.4 riastrad
335 1.4 riastrad /* Success! */
336 1.1 riastrad error = 0;
337 1.1 riastrad
338 1.2 riastrad out: mutex_exit(&smc->smc_io_lock);
339 1.2 riastrad return error;
340 1.2 riastrad }
341 1.2 riastrad
342 1.2 riastrad struct apple_smc_key {
344 1.2 riastrad char ask_name[4 + 1];
345 1.2 riastrad struct apple_smc_desc ask_desc;
346 1.1 riastrad #ifdef DIAGNOSTIC
347 1.1 riastrad struct apple_smc_tag *ask_smc;
348 1.1 riastrad #endif
349 1.1 riastrad };
350 1.1 riastrad
351 1.1 riastrad const char *
352 1.1 riastrad apple_smc_key_name(const struct apple_smc_key *key)
353 1.1 riastrad {
354 1.1 riastrad
355 1.1 riastrad return key->ask_name;
356 1.1 riastrad }
357 1.1 riastrad
358 1.1 riastrad const struct apple_smc_desc *
359 1.1 riastrad apple_smc_key_desc(const struct apple_smc_key *key)
360 1.1 riastrad {
361 1.1 riastrad
362 1.1 riastrad return &key->ask_desc;
363 1.1 riastrad }
364 1.1 riastrad
365 1.1 riastrad uint32_t
366 1.1 riastrad apple_smc_nkeys(struct apple_smc_tag *smc)
367 1.1 riastrad {
368 1.1 riastrad
369 1.1 riastrad return smc->smc_nkeys;
370 1.1 riastrad }
371 1.1 riastrad
372 1.1 riastrad int
373 1.1 riastrad apple_smc_nth_key(struct apple_smc_tag *smc, uint32_t index,
374 1.1 riastrad const char type[4 + 1], struct apple_smc_key **keyp)
375 1.1 riastrad {
376 1.4 riastrad union { uint32_t u32; char name[4]; } index_be;
377 1.1 riastrad struct apple_smc_key *key;
378 1.1 riastrad int error;
379 1.1 riastrad
380 1.4 riastrad /* Paranoia: type must be NULL or 4 non-null characters long. */
381 1.1 riastrad if ((type != NULL) && (strlen(type) != 4))
382 1.1 riastrad return EINVAL;
383 1.1 riastrad
384 1.1 riastrad /* Create a new key. XXX Consider caching these. */
385 1.1 riastrad key = kmem_alloc(sizeof(*key), KM_SLEEP);
386 1.4 riastrad #ifdef DIAGNOSTIC
387 1.1 riastrad key->ask_smc = smc;
388 1.1 riastrad #endif
389 1.1 riastrad
390 1.1 riastrad /* Ask the SMC what the name of the key by this number is. */
391 1.1 riastrad index_be.u32 = htobe32(index);
392 1.4 riastrad error = apple_smc_input(smc, APPLE_SMC_CMD_NTH_KEY, index_be.name,
393 1.4 riastrad key->ask_name, 4);
394 1.1 riastrad if (error)
395 1.1 riastrad goto fail;
396 1.4 riastrad
397 1.1 riastrad /* Null-terminate the name. */
398 1.1 riastrad key->ask_name[4] = '\0';
399 1.1 riastrad
400 1.1 riastrad /* Ask the SMC for a description of this key by name. */
401 1.1 riastrad CTASSERT(sizeof(key->ask_desc) == 6);
402 1.1 riastrad error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name,
403 1.4 riastrad &key->ask_desc, 6);
404 1.1 riastrad if (error)
405 1.1 riastrad goto fail;
406 1.1 riastrad
407 1.1 riastrad /* Fail with EINVAL if the types don't match. */
408 1.1 riastrad if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) {
409 1.1 riastrad error = EINVAL;
410 1.1 riastrad goto fail;
411 1.1 riastrad }
412 1.1 riastrad
413 1.4 riastrad /* Success! */
414 1.1 riastrad *keyp = key;
415 1.1 riastrad return 0;
416 1.1 riastrad
417 1.1 riastrad fail: kmem_free(key, sizeof(*key));
418 1.1 riastrad return error;
419 1.1 riastrad }
420 1.1 riastrad
421 1.1 riastrad int
422 1.1 riastrad apple_smc_named_key(struct apple_smc_tag *smc, const char name[4 + 1],
423 1.1 riastrad const char type[4 + 1], struct apple_smc_key **keyp)
424 1.4 riastrad {
425 1.1 riastrad struct apple_smc_key *key;
426 1.1 riastrad int error;
427 1.1 riastrad
428 1.1 riastrad /* Paranoia: name must be 4 non-null characters long. */
429 1.4 riastrad KASSERT(name != NULL);
430 1.1 riastrad if (strlen(name) != 4)
431 1.1 riastrad return EINVAL;
432 1.1 riastrad
433 1.4 riastrad /* Paranoia: type must be NULL or 4 non-null characters long. */
434 1.1 riastrad if ((type != NULL) && (strlen(type) != 4))
435 1.1 riastrad return EINVAL;
436 1.1 riastrad
437 1.1 riastrad /* Create a new key. XXX Consider caching these. */
438 1.4 riastrad key = kmem_alloc(sizeof(*key), KM_SLEEP);
439 1.4 riastrad #ifdef DIAGNOSTIC
440 1.1 riastrad key->ask_smc = smc;
441 1.1 riastrad #endif
442 1.1 riastrad
443 1.4 riastrad /* Use the specified name, and make sure it's null-terminated. */
444 1.1 riastrad (void)memcpy(key->ask_name, name, 4);
445 1.1 riastrad key->ask_name[4] = '\0';
446 1.1 riastrad
447 1.1 riastrad /* Ask the SMC for a description of this key by name. */
448 1.1 riastrad CTASSERT(sizeof(key->ask_desc) == 6);
449 1.1 riastrad error = apple_smc_input(smc, APPLE_SMC_CMD_KEY_DESC, key->ask_name,
450 1.4 riastrad &key->ask_desc, 6);
451 1.1 riastrad if (error)
452 1.1 riastrad goto fail;
453 1.1 riastrad
454 1.1 riastrad /* Fail with EINVAL if the types don't match. */
455 1.1 riastrad if ((type != NULL) && (0 != memcmp(key->ask_desc.asd_type, type, 4))) {
456 1.4 riastrad error = EINVAL;
457 1.1 riastrad goto fail;
458 1.1 riastrad }
459 1.1 riastrad
460 1.4 riastrad /* Success! */
461 1.1 riastrad *keyp = key;
462 1.1 riastrad return 0;
463 1.1 riastrad
464 1.1 riastrad fail: kmem_free(key, sizeof(*key));
465 1.1 riastrad return error;
466 1.1 riastrad }
467 1.1 riastrad
468 1.1 riastrad void
469 1.4 riastrad apple_smc_release_key(struct apple_smc_tag *smc, struct apple_smc_key *key)
470 1.1 riastrad {
471 1.1 riastrad
472 1.1 riastrad #ifdef DIAGNOSTIC
473 1.1 riastrad /* Make sure the caller didn't mix up SMC tags. */
474 1.1 riastrad if (key->ask_smc != smc)
475 1.4 riastrad aprint_error_dev(smc->smc_dev,
476 1.4 riastrad "releasing key with wrong tag: %p != %p",
477 1.1 riastrad smc, key->ask_smc);
478 1.1 riastrad #endif
479 1.1 riastrad
480 1.1 riastrad /* Nothing to do but free the key's memory. */
481 1.1 riastrad kmem_free(key, sizeof(*key));
482 1.1 riastrad }
483 1.1 riastrad
484 1.1 riastrad int
485 1.1 riastrad apple_smc_key_search(struct apple_smc_tag *smc, const char *name,
486 1.6 riastrad uint32_t *result)
487 1.1 riastrad {
488 1.1 riastrad struct apple_smc_key *key;
489 1.4 riastrad uint32_t start = 0, end = apple_smc_nkeys(smc), median;
490 1.1 riastrad int cmp;
491 1.1 riastrad int error;
492 1.1 riastrad
493 1.1 riastrad /* Do a binary search on the SMC's key space. */
494 1.1 riastrad while (start < end) {
495 1.1 riastrad median = (start + ((end - start) / 2));
496 1.6 riastrad error = apple_smc_nth_key(smc, median, NULL, &key);
497 1.6 riastrad if (error)
498 1.1 riastrad return error;
499 1.6 riastrad
500 1.6 riastrad cmp = memcmp(name, apple_smc_key_name(key), 4);
501 1.1 riastrad if (cmp < 0)
502 1.6 riastrad end = median;
503 1.6 riastrad else if (cmp > 0)
504 1.1 riastrad start = (median + 1);
505 1.1 riastrad else
506 1.1 riastrad start = end = median; /* stop here */
507 1.4 riastrad
508 1.1 riastrad apple_smc_release_key(smc, key);
509 1.1 riastrad }
510 1.1 riastrad
511 1.2 riastrad /* Success! */
512 1.1 riastrad *result = start;
513 1.1 riastrad return 0;
514 1.1 riastrad }
515 1.1 riastrad
516 1.1 riastrad int
518 1.1 riastrad apple_smc_read_key(struct apple_smc_tag *smc, const struct apple_smc_key *key,
519 1.1 riastrad void *buffer, uint8_t size)
520 1.4 riastrad {
521 1.4 riastrad
522 1.1 riastrad /* Refuse if software and hardware disagree on the key's size. */
523 1.1 riastrad if (key->ask_desc.asd_size != size)
524 1.1 riastrad return EINVAL;
525 1.4 riastrad
526 1.1 riastrad /* Refuse if the hardware doesn't want us to read it. */
527 1.1 riastrad if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_READ))
528 1.1 riastrad return EACCES;
529 1.1 riastrad
530 1.1 riastrad /* Looks good. Try reading it from the hardware. */
531 1.1 riastrad return apple_smc_input(smc, APPLE_SMC_CMD_READ_KEY, key->ask_name,
532 1.1 riastrad buffer, size);
533 1.1 riastrad }
534 1.1 riastrad
535 1.1 riastrad int
536 1.1 riastrad apple_smc_read_key_1(struct apple_smc_tag *smc,
537 1.1 riastrad const struct apple_smc_key *key, uint8_t *p)
538 1.1 riastrad {
539 1.1 riastrad
540 1.1 riastrad return apple_smc_read_key(smc, key, p, 1);
541 1.1 riastrad }
542 1.1 riastrad
543 1.1 riastrad int
544 1.1 riastrad apple_smc_read_key_2(struct apple_smc_tag *smc,
545 1.4 riastrad const struct apple_smc_key *key, uint16_t *p)
546 1.1 riastrad {
547 1.1 riastrad uint16_t be;
548 1.1 riastrad int error;
549 1.1 riastrad
550 1.4 riastrad /* Read a big-endian quantity from the hardware. */
551 1.1 riastrad error = apple_smc_read_key(smc, key, &be, 2);
552 1.4 riastrad if (error)
553 1.4 riastrad return error;
554 1.1 riastrad
555 1.1 riastrad /* Convert it to host order. */
556 1.1 riastrad *p = be16toh(be);
557 1.1 riastrad
558 1.1 riastrad /* Success! */
559 1.1 riastrad return 0;
560 1.1 riastrad }
561 1.1 riastrad
562 1.1 riastrad int
563 1.1 riastrad apple_smc_read_key_4(struct apple_smc_tag *smc,
564 1.4 riastrad const struct apple_smc_key *key, uint32_t *p)
565 1.1 riastrad {
566 1.1 riastrad uint32_t be;
567 1.1 riastrad int error;
568 1.1 riastrad
569 1.4 riastrad /* Read a big-endian quantity from the hardware. */
570 1.1 riastrad error = apple_smc_read_key(smc, key, &be, 4);
571 1.4 riastrad if (error)
572 1.4 riastrad return error;
573 1.1 riastrad
574 1.1 riastrad /* Convert it to host order. */
575 1.1 riastrad *p = be32toh(be);
576 1.1 riastrad
577 1.1 riastrad /* Success! */
578 1.1 riastrad return 0;
579 1.1 riastrad }
580 1.1 riastrad
581 1.4 riastrad int
582 1.1 riastrad apple_smc_write_key(struct apple_smc_tag *smc, const struct apple_smc_key *key,
583 1.1 riastrad const void *buffer, uint8_t size)
584 1.4 riastrad {
585 1.4 riastrad
586 1.1 riastrad /* Refuse if software and hardware disagree on the key's size. */
587 1.1 riastrad if (key->ask_desc.asd_size != size)
588 1.1 riastrad return EINVAL;
589 1.4 riastrad
590 1.1 riastrad /* Refuse if the hardware doesn't want us to write it. */
591 1.1 riastrad if (!(key->ask_desc.asd_flags & APPLE_SMC_FLAG_WRITE))
592 1.1 riastrad return EACCES;
593 1.1 riastrad
594 1.1 riastrad /* Looks good. Try writing it to the hardware. */
595 1.1 riastrad return apple_smc_output(smc, APPLE_SMC_CMD_WRITE_KEY, key->ask_name,
596 1.1 riastrad buffer, size);
597 1.1 riastrad }
598 1.1 riastrad
599 1.1 riastrad int
600 1.1 riastrad apple_smc_write_key_1(struct apple_smc_tag *smc,
601 1.1 riastrad const struct apple_smc_key *key, uint8_t v)
602 1.1 riastrad {
603 1.1 riastrad
604 1.1 riastrad return apple_smc_write_key(smc, key, &v, 1);
605 1.1 riastrad }
606 1.4 riastrad
607 1.1 riastrad int
608 1.1 riastrad apple_smc_write_key_2(struct apple_smc_tag *smc,
609 1.4 riastrad const struct apple_smc_key *key, uint16_t v)
610 1.1 riastrad {
611 1.1 riastrad /* Convert the quantity from host to big-endian byte order. */
612 1.1 riastrad const uint16_t v_be = htobe16(v);
613 1.1 riastrad
614 1.1 riastrad /* Write the big-endian quantity to the hardware. */
615 1.1 riastrad return apple_smc_write_key(smc, key, &v_be, 2);
616 1.1 riastrad }
617 1.4 riastrad
618 1.5 riastrad int
619 1.1 riastrad apple_smc_write_key_4(struct apple_smc_tag *smc,
620 1.4 riastrad const struct apple_smc_key *key, uint32_t v)
621 1.1 riastrad {
622 1.1 riastrad /* Convert the quantity from host to big-endian byte order. */
623 1.2 riastrad const uint32_t v_be = htobe32(v);
624 1.2 riastrad
625 1.2 riastrad /* Write the big-endian quantity to the hardware. */
626 1.2 riastrad return apple_smc_write_key(smc, key, &v_be, 4);
627 1.2 riastrad }
628 1.2 riastrad
629 1.2 riastrad MODULE(MODULE_CLASS_MISC, apple_smc, NULL)
631 1.2 riastrad
632 1.2 riastrad static int
633 1.3 riastrad apple_smc_modcmd(modcmd_t cmd, void *data __unused)
634 1.2 riastrad {
635 1.2 riastrad
636 1.3 riastrad /* Nothing to do for now to set up or tear down the module. */
637 1.2 riastrad switch (cmd) {
638 1.2 riastrad case MODULE_CMD_INIT:
639 1.2 riastrad return 0;
640 1.2 riastrad
641 1.2 riastrad case MODULE_CMD_FINI:
642 return 0;
643
644 default:
645 return ENOTTY;
646 }
647 }
648