xref: /freebsd/sys/powerpc/powermac/pmu.c (revision a9148abd9da5db2f1c682fb17bed791845fc41c9)
1 /*-
2  * Copyright (c) 2006 Michael Lorenz
3  * Copyright 2008 by Nathan Whitehorn
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/module.h>
37 #include <sys/bus.h>
38 #include <sys/conf.h>
39 #include <sys/kernel.h>
40 
41 #include <dev/ofw/ofw_bus.h>
42 #include <dev/ofw/openfirm.h>
43 
44 #include <machine/bus.h>
45 #include <machine/intr.h>
46 #include <machine/intr_machdep.h>
47 #include <machine/md_var.h>
48 #include <machine/pio.h>
49 #include <machine/resource.h>
50 
51 #include <vm/vm.h>
52 #include <vm/pmap.h>
53 
54 #include <sys/rman.h>
55 
56 #include <dev/adb/adb.h>
57 
58 #include "pmuvar.h"
59 #include "viareg.h"
60 
61 /*
62  * MacIO interface
63  */
64 static int	pmu_probe(device_t);
65 static int	pmu_attach(device_t);
66 static int	pmu_detach(device_t);
67 
68 static u_int pmu_adb_send(device_t dev, u_char command_byte, int len,
69     u_char *data, u_char poll);
70 static u_int pmu_adb_autopoll(device_t dev, uint16_t mask);
71 static void pmu_poll(device_t dev);
72 
73 static device_method_t  pmu_methods[] = {
74 	/* Device interface */
75 	DEVMETHOD(device_probe,		pmu_probe),
76 	DEVMETHOD(device_attach,	pmu_attach),
77         DEVMETHOD(device_detach,        pmu_detach),
78         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
79         DEVMETHOD(device_suspend,       bus_generic_suspend),
80         DEVMETHOD(device_resume,        bus_generic_resume),
81 
82 	/* bus interface, for ADB root */
83         DEVMETHOD(bus_print_child,      bus_generic_print_child),
84         DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
85 
86 	/* ADB bus interface */
87 	DEVMETHOD(adb_hb_send_raw_packet,   pmu_adb_send),
88 	DEVMETHOD(adb_hb_controller_poll,   pmu_poll),
89 	DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll),
90 
91 	{ 0, 0 },
92 };
93 
94 static driver_t pmu_driver = {
95 	"pmu",
96 	pmu_methods,
97 	sizeof(struct pmu_softc),
98 };
99 
100 static devclass_t pmu_devclass;
101 
102 DRIVER_MODULE(pmu, macio, pmu_driver, pmu_devclass, 0, 0);
103 DRIVER_MODULE(adb, pmu, adb_driver, adb_devclass, 0, 0);
104 
105 static int	pmuextint_probe(device_t);
106 static int	pmuextint_attach(device_t);
107 
108 static device_method_t  pmuextint_methods[] = {
109 	/* Device interface */
110 	DEVMETHOD(device_probe,		pmuextint_probe),
111 	DEVMETHOD(device_attach,	pmuextint_attach),
112 
113 	{0,0}
114 };
115 
116 static driver_t pmuextint_driver = {
117 	"pmuextint",
118 	pmuextint_methods,
119 	0
120 };
121 
122 static devclass_t pmuextint_devclass;
123 
124 DRIVER_MODULE(pmuextint, macgpio, pmuextint_driver, pmuextint_devclass, 0, 0);
125 
126 /* Make sure uhid is loaded, as it turns off some of the ADB emulation */
127 MODULE_DEPEND(pmu, usb, 1, 1, 1);
128 
129 static void pmu_intr(void *arg);
130 static void pmu_in(struct pmu_softc *sc);
131 static void pmu_out(struct pmu_softc *sc);
132 static void pmu_ack_on(struct pmu_softc *sc);
133 static void pmu_ack_off(struct pmu_softc *sc);
134 static int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg,
135 	int rlen, uint8_t *out_msg);
136 static uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset);
137 static void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value);
138 static int pmu_intr_state(struct pmu_softc *);
139 
140 /* these values shows that number of data returned after 'send' cmd is sent */
141 static signed char pm_send_cmd_type[] = {
142 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
143 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
144 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
145 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
146 	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
147 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
148 	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
149 	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
150 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
151 	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
152 	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
153 	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
154 	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
155 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
156 	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
157 	0x00, 0x00,   -1,   -1,   -1,   -1, 0x04, 0x04,
158 	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
159 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
160 	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
161 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
162 	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
163 	0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
164 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
165 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
166 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
167 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
168 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
169 	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
170 	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
171 	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
172 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
173 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
174 };
175 
176 /* these values shows that number of data returned after 'receive' cmd is sent */
177 static signed char pm_receive_cmd_type[] = {
178 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
180 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
182 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
184 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
185 	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
186 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
187 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
188 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
189 	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
190 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
191 	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
192 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
193 	  -1,   -1,   -1,   -1,   -1,   -1, 0x01, 0x01,
194 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195 	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
196 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
197 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
198 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
199 	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
200 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
201 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
202 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
203 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
204 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
205 	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
206 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
207 	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
208 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
209 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
210 };
211 
212 /* We only have one of each device, so globals are safe */
213 static device_t pmu = NULL;
214 static device_t pmu_extint = NULL;
215 
216 static int
217 pmuextint_probe(device_t dev)
218 {
219 	const char *type = ofw_bus_get_type(dev);
220 
221 	if (strcmp(type, "extint-gpio1") != 0)
222                 return (ENXIO);
223 
224 	device_set_desc(dev, "Apple PMU99 External Interrupt");
225 	return (0);
226 }
227 
228 static int
229 pmu_probe(device_t dev)
230 {
231 	const char *type = ofw_bus_get_type(dev);
232 
233 	if (strcmp(type, "via-pmu") != 0)
234                 return (ENXIO);
235 
236 	device_set_desc(dev, "Apple PMU99 Controller");
237 	return (0);
238 }
239 
240 
241 static int
242 setup_pmu_intr(device_t dev, device_t extint)
243 {
244 	struct pmu_softc *sc;
245 	sc = device_get_softc(dev);
246 
247 	sc->sc_irqrid = 0;
248 	sc->sc_irq = bus_alloc_resource_any(extint, SYS_RES_IRQ, &sc->sc_irqrid,
249            	RF_ACTIVE);
250         if (sc->sc_irq == NULL) {
251                 device_printf(dev, "could not allocate interrupt\n");
252                 return (ENXIO);
253         }
254 
255 	if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE
256 	    | INTR_ENTROPY, NULL, pmu_intr, dev, &sc->sc_ih) != 0) {
257                 device_printf(dev, "could not setup interrupt\n");
258                 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
259                     sc->sc_irq);
260                 return (ENXIO);
261         }
262 
263 	return (0);
264 }
265 
266 static int
267 pmuextint_attach(device_t dev)
268 {
269 	pmu_extint = dev;
270 	if (pmu)
271 		return (setup_pmu_intr(pmu,dev));
272 
273 	return (0);
274 }
275 
276 static int
277 pmu_attach(device_t dev)
278 {
279 	struct pmu_softc *sc;
280 
281 	uint8_t reg;
282 	uint8_t cmd[2] = {2, 0};
283 	uint8_t resp[16];
284 	phandle_t node,child;
285 
286 	sc = device_get_softc(dev);
287 	sc->sc_dev = dev;
288 
289 	sc->sc_memrid = 0;
290 	sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
291 		          &sc->sc_memrid, RF_ACTIVE);
292 
293 	mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE);
294 
295 	if (sc->sc_memr == NULL) {
296 		device_printf(dev, "Could not alloc mem resource!\n");
297 		return (ENXIO);
298 	}
299 
300 	/*
301 	 * Our interrupt is attached to a GPIO pin. Depending on probe order,
302 	 * we may not have found it yet. If we haven't, it will find us, and
303 	 * attach our interrupt then.
304 	 */
305 	pmu = dev;
306 	if (pmu_extint != NULL) {
307 		if (setup_pmu_intr(dev,pmu_extint) != 0)
308 			return (ENXIO);
309 	}
310 
311 	sc->sc_error = 0;
312 	sc->sc_polling = 0;
313 	sc->sc_autopoll = 0;
314 
315 	/* Init PMU */
316 
317 	reg = PMU_INT_TICK | PMU_INT_ADB | PMU_INT_PCEJECT | PMU_INT_SNDBRT;
318 	reg |= PMU_INT_BATTERY;
319 	reg |= PMU_INT_ENVIRONMENT;
320 	pmu_send(sc, PMU_SET_IMASK, 1, &reg, 16, resp);
321 
322 	pmu_write_reg(sc, vIER, 0x90); /* make sure VIA interrupts are on */
323 
324 	pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp);
325 	pmu_send(sc, PMU_GET_VERSION, 1, cmd, 16, resp);
326 
327 	/* Initialize child buses (ADB) */
328 	node = ofw_bus_get_node(dev);
329 
330 	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
331 		char name[32];
332 
333 		memset(name, 0, sizeof(name));
334 		OF_getprop(child, "name", name, sizeof(name));
335 
336 		if (bootverbose)
337 			device_printf(dev, "PMU child <%s>\n",name);
338 
339 		if (strncmp(name, "adb", 4) == 0) {
340 			sc->adb_bus = device_add_child(dev,"adb",-1);
341 		}
342 	}
343 
344 	return (bus_generic_attach(dev));
345 }
346 
347 static int
348 pmu_detach(device_t dev)
349 {
350 	struct pmu_softc *sc;
351 
352 	sc = device_get_softc(dev);
353 
354 	bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
355 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq);
356 	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr);
357 	mtx_destroy(&sc->sc_mutex);
358 
359 	return (bus_generic_detach(dev));
360 }
361 
362 static uint8_t
363 pmu_read_reg(struct pmu_softc *sc, u_int offset)
364 {
365 	return (bus_read_1(sc->sc_memr, offset));
366 }
367 
368 static void
369 pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value)
370 {
371 	bus_write_1(sc->sc_memr, offset, value);
372 }
373 
374 static int
375 pmu_send_byte(struct pmu_softc *sc, uint8_t data)
376 {
377 
378 	pmu_out(sc);
379 	pmu_write_reg(sc, vSR, data);
380 	pmu_ack_off(sc);
381 	/* wait for intr to come up */
382 	/* XXX should add a timeout and bail if it expires */
383 	do {} while (pmu_intr_state(sc) == 0);
384 	pmu_ack_on(sc);
385 	do {} while (pmu_intr_state(sc));
386 	pmu_ack_on(sc);
387 	return 0;
388 }
389 
390 static inline int
391 pmu_read_byte(struct pmu_softc *sc, uint8_t *data)
392 {
393 	volatile uint8_t scratch;
394 	pmu_in(sc);
395 	scratch = pmu_read_reg(sc, vSR);
396 	pmu_ack_off(sc);
397 	/* wait for intr to come up */
398 	do {} while (pmu_intr_state(sc) == 0);
399 	pmu_ack_on(sc);
400 	do {} while (pmu_intr_state(sc));
401 	*data = pmu_read_reg(sc, vSR);
402 	return 0;
403 }
404 
405 static int
406 pmu_intr_state(struct pmu_softc *sc)
407 {
408 	return ((pmu_read_reg(sc, vBufB) & vPB3) == 0);
409 }
410 
411 static int
412 pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen,
413     uint8_t *out_msg)
414 {
415 	struct pmu_softc *sc = cookie;
416 	int i, rcv_len = -1;
417 	uint8_t out_len, intreg;
418 
419 	intreg = pmu_read_reg(sc, vIER);
420 	intreg &= 0x10;
421 	pmu_write_reg(sc, vIER, intreg);
422 
423 	/* wait idle */
424 	do {} while (pmu_intr_state(sc));
425 	sc->sc_error = 0;
426 
427 	/* send command */
428 	pmu_send_byte(sc, cmd);
429 
430 	/* send length if necessary */
431 	if (pm_send_cmd_type[cmd] < 0) {
432 		pmu_send_byte(sc, length);
433 	}
434 
435 	for (i = 0; i < length; i++) {
436 		pmu_send_byte(sc, in_msg[i]);
437 	}
438 
439 	/* see if there's data to read */
440 	rcv_len = pm_receive_cmd_type[cmd];
441 	if (rcv_len == 0)
442 		goto done;
443 
444 	/* read command */
445 	if (rcv_len == 1) {
446 		pmu_read_byte(sc, out_msg);
447 		goto done;
448 	} else
449 		out_msg[0] = cmd;
450 	if (rcv_len < 0) {
451 		pmu_read_byte(sc, &out_len);
452 		rcv_len = out_len + 1;
453 	}
454 	for (i = 1; i < min(rcv_len, rlen); i++)
455 		pmu_read_byte(sc, &out_msg[i]);
456 
457 done:
458 	pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90);
459 
460 	return rcv_len;
461 }
462 
463 
464 static void
465 pmu_poll(device_t dev)
466 {
467 	pmu_intr(dev);
468 }
469 
470 static void
471 pmu_in(struct pmu_softc *sc)
472 {
473 	uint8_t reg;
474 
475 	reg = pmu_read_reg(sc, vACR);
476 	reg &= ~vSR_OUT;
477 	reg |= 0x0c;
478 	pmu_write_reg(sc, vACR, reg);
479 }
480 
481 static void
482 pmu_out(struct pmu_softc *sc)
483 {
484 	uint8_t reg;
485 
486 	reg = pmu_read_reg(sc, vACR);
487 	reg |= vSR_OUT;
488 	reg |= 0x0c;
489 	pmu_write_reg(sc, vACR, reg);
490 }
491 
492 static void
493 pmu_ack_off(struct pmu_softc *sc)
494 {
495 	uint8_t reg;
496 
497 	reg = pmu_read_reg(sc, vBufB);
498 	reg &= ~vPB4;
499 	pmu_write_reg(sc, vBufB, reg);
500 }
501 
502 static void
503 pmu_ack_on(struct pmu_softc *sc)
504 {
505 	uint8_t reg;
506 
507 	reg = pmu_read_reg(sc, vBufB);
508 	reg |= vPB4;
509 	pmu_write_reg(sc, vBufB, reg);
510 }
511 
512 static void
513 pmu_intr(void *arg)
514 {
515 	device_t        dev;
516 	struct pmu_softc *sc;
517 
518 	unsigned int len;
519 	uint8_t resp[16];
520 	uint8_t junk[16];
521 
522         dev = (device_t)arg;
523 	sc = device_get_softc(dev);
524 
525 	mtx_lock(&sc->sc_mutex);
526 
527 	pmu_write_reg(sc, vIFR, 0x90);	/* Clear 'em */
528 	len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp);
529 
530 	mtx_unlock(&sc->sc_mutex);
531 
532 	if ((len < 1) || (resp[1] == 0)) {
533 		return;
534 	}
535 
536 	if (resp[1] & PMU_INT_ADB) {
537 		/*
538 		 * the PMU will turn off autopolling after each command that
539 		 * it did not issue, so we assume any but TALK R0 is ours and
540 		 * re-enable autopoll here whenever we receive an ACK for a
541 		 * non TR0 command.
542 		 */
543 		mtx_lock(&sc->sc_mutex);
544 
545 		if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) {
546 			if (sc->sc_autopoll) {
547 				uint8_t cmd[] = {0, PMU_SET_POLL_MASK,
548 				    (sc->sc_autopoll >> 8) & 0xff,
549 				    sc->sc_autopoll & 0xff};
550 
551 				pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk);
552 			}
553 		}
554 
555 		mtx_unlock(&sc->sc_mutex);
556 
557 		adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2],
558 			len - 3,&resp[3]);
559 	}
560 }
561 
562 static u_int
563 pmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data,
564     u_char poll)
565 {
566 	struct pmu_softc *sc = device_get_softc(dev);
567 	int i,replen;
568 	uint8_t packet[16], resp[16];
569 
570 	/* construct an ADB command packet and send it */
571 
572 	packet[0] = command_byte;
573 
574 	packet[1] = 0;
575 	packet[2] = len;
576 	for (i = 0; i < len; i++)
577 		packet[i + 3] = data[i];
578 
579 	mtx_lock(&sc->sc_mutex);
580 	replen = pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp);
581 	mtx_unlock(&sc->sc_mutex);
582 
583 	if (poll)
584 		pmu_poll(dev);
585 
586 	return 0;
587 }
588 
589 static u_int
590 pmu_adb_autopoll(device_t dev, uint16_t mask)
591 {
592 	struct pmu_softc *sc = device_get_softc(dev);
593 
594 	/* magical incantation to re-enable autopolling */
595 	uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff};
596 	uint8_t resp[16];
597 
598 	mtx_lock(&sc->sc_mutex);
599 
600 	if (sc->sc_autopoll == mask) {
601 		mtx_unlock(&sc->sc_mutex);
602 		return 0;
603 	}
604 
605 	sc->sc_autopoll = mask & 0xffff;
606 
607 	if (mask)
608 		pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp);
609 	else
610 		pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp);
611 
612 	mtx_unlock(&sc->sc_mutex);
613 
614 	return 0;
615 }
616