]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/powerpc/powermac/pmu.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / powerpc / powermac / pmu.c
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  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/module.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/kernel.h>
38 #include <sys/sysctl.h>
39
40 #include <dev/ofw/ofw_bus.h>
41 #include <dev/ofw/openfirm.h>
42 #include <dev/led/led.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 u_int    pmu_poll(device_t dev);
72
73 static void     pmu_set_sleepled(void *xsc, int onoff);
74 static int      pmu_server_mode(SYSCTL_HANDLER_ARGS);
75 static int      pmu_acline_state(SYSCTL_HANDLER_ARGS);
76 static int      pmu_query_battery(struct pmu_softc *sc, int batt, 
77                     struct pmu_battstate *info);
78 static int      pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS);
79
80 /*
81  * List of battery-related sysctls we might ask for
82  */
83
84 enum {
85         PMU_BATSYSCTL_PRESENT   = 1 << 8,
86         PMU_BATSYSCTL_CHARGING  = 2 << 8,
87         PMU_BATSYSCTL_CHARGE    = 3 << 8,
88         PMU_BATSYSCTL_MAXCHARGE = 4 << 8,
89         PMU_BATSYSCTL_CURRENT   = 5 << 8,
90         PMU_BATSYSCTL_VOLTAGE   = 6 << 8,
91         PMU_BATSYSCTL_TIME      = 7 << 8,
92         PMU_BATSYSCTL_LIFE      = 8 << 8
93 };
94
95 static device_method_t  pmu_methods[] = {
96         /* Device interface */
97         DEVMETHOD(device_probe,         pmu_probe),
98         DEVMETHOD(device_attach,        pmu_attach),
99         DEVMETHOD(device_detach,        pmu_detach),
100         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
101         DEVMETHOD(device_suspend,       bus_generic_suspend),
102         DEVMETHOD(device_resume,        bus_generic_resume),
103
104         /* bus interface, for ADB root */
105         DEVMETHOD(bus_print_child,      bus_generic_print_child),
106         DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
107
108         /* ADB bus interface */
109         DEVMETHOD(adb_hb_send_raw_packet,   pmu_adb_send),
110         DEVMETHOD(adb_hb_controller_poll,   pmu_poll),
111         DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll),
112
113         { 0, 0 },
114 };
115
116 static driver_t pmu_driver = {
117         "pmu",
118         pmu_methods,
119         sizeof(struct pmu_softc),
120 };
121
122 static devclass_t pmu_devclass;
123
124 DRIVER_MODULE(pmu, macio, pmu_driver, pmu_devclass, 0, 0);
125 DRIVER_MODULE(adb, pmu, adb_driver, adb_devclass, 0, 0);
126
127 static int      pmuextint_probe(device_t);
128 static int      pmuextint_attach(device_t);
129
130 static device_method_t  pmuextint_methods[] = {
131         /* Device interface */
132         DEVMETHOD(device_probe,         pmuextint_probe),
133         DEVMETHOD(device_attach,        pmuextint_attach),
134         
135         {0,0}
136 };
137
138 static driver_t pmuextint_driver = {
139         "pmuextint",
140         pmuextint_methods,
141         0
142 };
143
144 static devclass_t pmuextint_devclass;
145
146 DRIVER_MODULE(pmuextint, macgpio, pmuextint_driver, pmuextint_devclass, 0, 0);
147
148 /* Make sure uhid is loaded, as it turns off some of the ADB emulation */
149 MODULE_DEPEND(pmu, usb, 1, 1, 1);
150
151 static void pmu_intr(void *arg);
152 static void pmu_in(struct pmu_softc *sc);
153 static void pmu_out(struct pmu_softc *sc);
154 static void pmu_ack_on(struct pmu_softc *sc);
155 static void pmu_ack_off(struct pmu_softc *sc);
156 static int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg,
157         int rlen, uint8_t *out_msg);
158 static uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset);
159 static void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value);
160 static int pmu_intr_state(struct pmu_softc *);
161
162 /* these values shows that number of data returned after 'send' cmd is sent */
163 static signed char pm_send_cmd_type[] = {
164           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
165           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
166         0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
167         0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
168           -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
169         0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
170         0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
171         0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
172         0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
173         0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
174         0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
175         0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
176         0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
177         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
178         0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
179         0x00, 0x00,   -1,   -1,   -1,   -1, 0x04, 0x04,
180         0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
181         0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
182         0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
183         0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
184         0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
185         0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
186           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
187           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
188           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
189           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
190         0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
191         0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
192           -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
193         0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
194           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
195           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
196 };
197
198 /* these values shows that number of data returned after 'receive' cmd is sent */
199 static signed char pm_receive_cmd_type[] = {
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         0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
204         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
205           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
206         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
207         0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
208         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
209         0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
210         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
211         0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
212         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
213         0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
214         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
215           -1,   -1,   -1,   -1,   -1,   -1, 0x01, 0x01,
216         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217         0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
218         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219         0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
220         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
221         0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
222         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
224         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
226         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227         0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
228         0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
229           -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
230         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
232 };
233
234 /* We only have one of each device, so globals are safe */
235 static device_t pmu = NULL;
236 static device_t pmu_extint = NULL;
237
238 static int
239 pmuextint_probe(device_t dev)
240 {
241         const char *type = ofw_bus_get_type(dev);
242
243         if (strcmp(type, "extint-gpio1") != 0)
244                 return (ENXIO);
245
246         device_set_desc(dev, "Apple PMU99 External Interrupt");
247         return (0);
248 }
249
250 static int
251 pmu_probe(device_t dev)
252 {
253         const char *type = ofw_bus_get_type(dev);
254
255         if (strcmp(type, "via-pmu") != 0)
256                 return (ENXIO);
257
258         device_set_desc(dev, "Apple PMU99 Controller");
259         return (0);
260 }
261
262
263 static int
264 setup_pmu_intr(device_t dev, device_t extint)
265 {
266         struct pmu_softc *sc;
267         sc = device_get_softc(dev);
268
269         sc->sc_irqrid = 0;
270         sc->sc_irq = bus_alloc_resource_any(extint, SYS_RES_IRQ, &sc->sc_irqrid,
271                 RF_ACTIVE);
272         if (sc->sc_irq == NULL) {
273                 device_printf(dev, "could not allocate interrupt\n");
274                 return (ENXIO);
275         }
276
277         if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE 
278             | INTR_ENTROPY, NULL, pmu_intr, dev, &sc->sc_ih) != 0) {
279                 device_printf(dev, "could not setup interrupt\n");
280                 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
281                     sc->sc_irq);
282                 return (ENXIO);
283         }
284
285         return (0);
286 }
287
288 static int
289 pmuextint_attach(device_t dev)
290 {
291         pmu_extint = dev;
292         if (pmu)
293                 return (setup_pmu_intr(pmu,dev));
294
295         return (0);
296 }
297
298 static int
299 pmu_attach(device_t dev)
300 {
301         struct pmu_softc *sc;
302
303         int i;
304         uint8_t reg;
305         uint8_t cmd[2] = {2, 0};
306         uint8_t resp[16];
307         phandle_t node,child;
308         struct sysctl_ctx_list *ctx;
309         struct sysctl_oid *tree;
310         
311         sc = device_get_softc(dev);
312         sc->sc_dev = dev;
313         
314         sc->sc_memrid = 0;
315         sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 
316                           &sc->sc_memrid, RF_ACTIVE);
317
318         mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE);
319
320         if (sc->sc_memr == NULL) {
321                 device_printf(dev, "Could not alloc mem resource!\n");
322                 return (ENXIO);
323         }
324
325         /*
326          * Our interrupt is attached to a GPIO pin. Depending on probe order,
327          * we may not have found it yet. If we haven't, it will find us, and
328          * attach our interrupt then.
329          */
330         pmu = dev;
331         if (pmu_extint != NULL) {
332                 if (setup_pmu_intr(dev,pmu_extint) != 0)
333                         return (ENXIO);
334         }
335
336         sc->sc_autopoll = 0;
337         sc->sc_batteries = 0;
338         sc->adb_bus = NULL;
339         sc->sc_leddev = NULL;
340
341         /* Init PMU */
342
343         reg = PMU_INT_TICK | PMU_INT_ADB | PMU_INT_PCEJECT | PMU_INT_SNDBRT;
344         reg |= PMU_INT_BATTERY;
345         reg |= PMU_INT_ENVIRONMENT;
346         pmu_send(sc, PMU_SET_IMASK, 1, &reg, 16, resp);
347
348         pmu_write_reg(sc, vIER, 0x90); /* make sure VIA interrupts are on */
349
350         pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp);
351         pmu_send(sc, PMU_GET_VERSION, 1, cmd, 16, resp);
352
353         /* Initialize child buses (ADB) */
354         node = ofw_bus_get_node(dev);
355
356         for (child = OF_child(node); child != 0; child = OF_peer(child)) {
357                 char name[32];
358
359                 memset(name, 0, sizeof(name));
360                 OF_getprop(child, "name", name, sizeof(name));
361
362                 if (bootverbose)
363                         device_printf(dev, "PMU child <%s>\n",name);
364
365                 if (strncmp(name, "adb", 4) == 0) {
366                         sc->adb_bus = device_add_child(dev,"adb",-1);
367                 }
368
369                 if (strncmp(name, "power-mgt", 9) == 0) {
370                         uint32_t prim_info[9];
371
372                         if (OF_getprop(child, "prim-info", prim_info, 
373                             sizeof(prim_info)) >= 7) 
374                                 sc->sc_batteries = (prim_info[6] >> 16) & 0xff;
375
376                         if (bootverbose && sc->sc_batteries > 0)
377                                 device_printf(dev, "%d batteries detected\n",
378                                     sc->sc_batteries);
379                 }
380         }
381
382         /*
383          * Set up sysctls
384          */
385
386         ctx = device_get_sysctl_ctx(dev);
387         tree = device_get_sysctl_tree(dev);
388
389         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
390             "server_mode", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
391             pmu_server_mode, "I", "Enable reboot after power failure");
392
393         if (sc->sc_batteries > 0) {
394                 struct sysctl_oid *oid, *battroot;
395                 char battnum[2];
396
397                 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
398                     "acline", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
399                     pmu_acline_state, "I", "AC Line Status");
400
401                 battroot = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
402                     "batteries", CTLFLAG_RD, 0, "Battery Information");
403
404                 for (i = 0; i < sc->sc_batteries; i++) {
405                         battnum[0] = i + '0';
406                         battnum[1] = '\0';
407
408                         oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(battroot),
409                             OID_AUTO, battnum, CTLFLAG_RD, 0, 
410                             "Battery Information");
411                 
412                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
413                             "present", CTLTYPE_INT | CTLFLAG_RD, sc, 
414                             PMU_BATSYSCTL_PRESENT | i, pmu_battquery_sysctl, 
415                             "I", "Battery present");
416                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
417                             "charging", CTLTYPE_INT | CTLFLAG_RD, sc,
418                             PMU_BATSYSCTL_CHARGING | i, pmu_battquery_sysctl, 
419                             "I", "Battery charging");
420                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
421                             "charge", CTLTYPE_INT | CTLFLAG_RD, sc,
422                             PMU_BATSYSCTL_CHARGE | i, pmu_battquery_sysctl, 
423                             "I", "Battery charge (mAh)");
424                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
425                             "maxcharge", CTLTYPE_INT | CTLFLAG_RD, sc,
426                             PMU_BATSYSCTL_MAXCHARGE | i, pmu_battquery_sysctl, 
427                             "I", "Maximum battery capacity (mAh)");
428                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
429                             "rate", CTLTYPE_INT | CTLFLAG_RD, sc,
430                             PMU_BATSYSCTL_CURRENT | i, pmu_battquery_sysctl, 
431                             "I", "Battery discharge rate (mA)");
432                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
433                             "voltage", CTLTYPE_INT | CTLFLAG_RD, sc,
434                             PMU_BATSYSCTL_VOLTAGE | i, pmu_battquery_sysctl, 
435                             "I", "Battery voltage (mV)");
436
437                         /* Knobs for mental compatibility with ACPI */
438
439                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
440                             "time", CTLTYPE_INT | CTLFLAG_RD, sc,
441                             PMU_BATSYSCTL_TIME | i, pmu_battquery_sysctl, 
442                             "I", "Time Remaining (minutes)");
443                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
444                             "life", CTLTYPE_INT | CTLFLAG_RD, sc,
445                             PMU_BATSYSCTL_LIFE | i, pmu_battquery_sysctl, 
446                             "I", "Capacity remaining (percent)");
447                 }
448         }
449
450         /*
451          * Set up LED interface
452          */
453
454         sc->sc_leddev = led_create(pmu_set_sleepled, sc, "sleepled");
455
456         return (bus_generic_attach(dev));
457 }
458
459 static int 
460 pmu_detach(device_t dev) 
461 {
462         struct pmu_softc *sc;
463
464         sc = device_get_softc(dev);
465
466         if (sc->sc_leddev != NULL)
467                 led_destroy(sc->sc_leddev);
468
469         bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
470         bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq);
471         bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr);
472         mtx_destroy(&sc->sc_mutex);
473
474         return (bus_generic_detach(dev));
475 }
476
477 static uint8_t
478 pmu_read_reg(struct pmu_softc *sc, u_int offset) 
479 {
480         return (bus_read_1(sc->sc_memr, offset));
481 }
482
483 static void
484 pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value) 
485 {
486         bus_write_1(sc->sc_memr, offset, value);
487 }
488
489 static int
490 pmu_send_byte(struct pmu_softc *sc, uint8_t data)
491 {
492
493         pmu_out(sc);
494         pmu_write_reg(sc, vSR, data);
495         pmu_ack_off(sc);
496         /* wait for intr to come up */
497         /* XXX should add a timeout and bail if it expires */
498         do {} while (pmu_intr_state(sc) == 0);
499         pmu_ack_on(sc);
500         do {} while (pmu_intr_state(sc));
501         pmu_ack_on(sc);
502         return 0;
503 }
504
505 static inline int
506 pmu_read_byte(struct pmu_softc *sc, uint8_t *data)
507 {
508         volatile uint8_t scratch;
509         pmu_in(sc);
510         scratch = pmu_read_reg(sc, vSR);
511         pmu_ack_off(sc);
512         /* wait for intr to come up */
513         do {} while (pmu_intr_state(sc) == 0);
514         pmu_ack_on(sc);
515         do {} while (pmu_intr_state(sc));
516         *data = pmu_read_reg(sc, vSR);
517         return 0;
518 }
519
520 static int
521 pmu_intr_state(struct pmu_softc *sc)
522 {
523         return ((pmu_read_reg(sc, vBufB) & vPB3) == 0);
524 }
525
526 static int
527 pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen,
528     uint8_t *out_msg)
529 {
530         struct pmu_softc *sc = cookie;
531         int i, rcv_len = -1;
532         uint8_t out_len, intreg;
533
534         intreg = pmu_read_reg(sc, vIER);
535         intreg &= 0x10;
536         pmu_write_reg(sc, vIER, intreg);
537
538         /* wait idle */
539         do {} while (pmu_intr_state(sc));
540
541         /* send command */
542         pmu_send_byte(sc, cmd);
543
544         /* send length if necessary */
545         if (pm_send_cmd_type[cmd] < 0) {
546                 pmu_send_byte(sc, length);
547         }
548
549         for (i = 0; i < length; i++) {
550                 pmu_send_byte(sc, in_msg[i]);
551         }
552
553         /* see if there's data to read */
554         rcv_len = pm_receive_cmd_type[cmd];
555         if (rcv_len == 0) 
556                 goto done;
557
558         /* read command */
559         if (rcv_len == 1) {
560                 pmu_read_byte(sc, out_msg);
561                 goto done;
562         } else
563                 out_msg[0] = cmd;
564         if (rcv_len < 0) {
565                 pmu_read_byte(sc, &out_len);
566                 rcv_len = out_len + 1;
567         }
568         for (i = 1; i < min(rcv_len, rlen); i++)
569                 pmu_read_byte(sc, &out_msg[i]);
570
571 done:
572         pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90);
573
574         return rcv_len;
575 }
576
577
578 static u_int
579 pmu_poll(device_t dev)
580 {
581         pmu_intr(dev);
582         return (0);
583 }
584
585 static void
586 pmu_in(struct pmu_softc *sc)
587 {
588         uint8_t reg;
589
590         reg = pmu_read_reg(sc, vACR);
591         reg &= ~vSR_OUT;
592         reg |= 0x0c;
593         pmu_write_reg(sc, vACR, reg);
594 }
595
596 static void
597 pmu_out(struct pmu_softc *sc)
598 {
599         uint8_t reg;
600
601         reg = pmu_read_reg(sc, vACR);
602         reg |= vSR_OUT;
603         reg |= 0x0c;
604         pmu_write_reg(sc, vACR, reg);
605 }
606
607 static void
608 pmu_ack_off(struct pmu_softc *sc)
609 {
610         uint8_t reg;
611
612         reg = pmu_read_reg(sc, vBufB);
613         reg &= ~vPB4;
614         pmu_write_reg(sc, vBufB, reg);
615 }
616
617 static void
618 pmu_ack_on(struct pmu_softc *sc)
619 {
620         uint8_t reg;
621
622         reg = pmu_read_reg(sc, vBufB);
623         reg |= vPB4;
624         pmu_write_reg(sc, vBufB, reg);
625 }
626
627 static void
628 pmu_intr(void *arg)
629 {
630         device_t        dev;
631         struct pmu_softc *sc;
632
633         unsigned int len;
634         uint8_t resp[16];
635         uint8_t junk[16];
636
637         dev = (device_t)arg;
638         sc = device_get_softc(dev);
639
640         mtx_lock(&sc->sc_mutex);
641
642         pmu_write_reg(sc, vIFR, 0x90);  /* Clear 'em */
643         len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp);
644
645         mtx_unlock(&sc->sc_mutex);
646
647         if ((len < 1) || (resp[1] == 0)) {
648                 return;
649         }
650
651         if (resp[1] & PMU_INT_ADB) {
652                 /*
653                  * the PMU will turn off autopolling after each command that
654                  * it did not issue, so we assume any but TALK R0 is ours and
655                  * re-enable autopoll here whenever we receive an ACK for a
656                  * non TR0 command.
657                  */
658                 mtx_lock(&sc->sc_mutex);
659
660                 if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) {
661                         if (sc->sc_autopoll) {
662                                 uint8_t cmd[] = {0, PMU_SET_POLL_MASK, 
663                                     (sc->sc_autopoll >> 8) & 0xff, 
664                                     sc->sc_autopoll & 0xff};
665
666                                 pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk);
667                         }
668                 }       
669
670                 mtx_unlock(&sc->sc_mutex);
671
672                 adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2],
673                         len - 3,&resp[3]);
674         }
675 }
676
677 static u_int
678 pmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data, 
679     u_char poll)
680 {
681         struct pmu_softc *sc = device_get_softc(dev);
682         int i,replen;
683         uint8_t packet[16], resp[16];
684
685         /* construct an ADB command packet and send it */
686
687         packet[0] = command_byte;
688
689         packet[1] = 0;
690         packet[2] = len;
691         for (i = 0; i < len; i++)
692                 packet[i + 3] = data[i];
693
694         mtx_lock(&sc->sc_mutex);
695         replen = pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp);
696         mtx_unlock(&sc->sc_mutex);
697
698         if (poll)
699                 pmu_poll(dev);
700
701         return 0;
702 }
703
704 static u_int 
705 pmu_adb_autopoll(device_t dev, uint16_t mask) 
706 {
707         struct pmu_softc *sc = device_get_softc(dev);
708
709         /* magical incantation to re-enable autopolling */
710         uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff};
711         uint8_t resp[16];
712
713         mtx_lock(&sc->sc_mutex);
714
715         if (sc->sc_autopoll == mask) {
716                 mtx_unlock(&sc->sc_mutex);
717                 return 0;
718         }
719
720         sc->sc_autopoll = mask & 0xffff;
721
722         if (mask)
723                 pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp);
724         else
725                 pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp);
726
727         mtx_unlock(&sc->sc_mutex);
728         
729         return 0;
730 }
731
732 static void
733 pmu_set_sleepled(void *xsc, int onoff)
734 {
735         struct pmu_softc *sc = xsc;
736         uint8_t cmd[] = {4, 0, 0};
737
738         cmd[2] = onoff;
739         
740         mtx_lock(&sc->sc_mutex);
741         pmu_send(sc, PMU_SET_SLEEPLED, 3, cmd, 0, NULL);
742         mtx_unlock(&sc->sc_mutex);
743 }
744
745 static int
746 pmu_server_mode(SYSCTL_HANDLER_ARGS)
747 {
748         struct pmu_softc *sc = arg1;
749         
750         u_int server_mode = 0;
751         uint8_t getcmd[] = {PMU_PWR_GET_POWERUP_EVENTS};
752         uint8_t setcmd[] = {0, 0, PMU_PWR_WAKEUP_AC_INSERT};
753         uint8_t resp[3];
754         int error, len;
755
756         mtx_lock(&sc->sc_mutex);
757         len = pmu_send(sc, PMU_POWER_EVENTS, 1, getcmd, 3, resp);
758         mtx_unlock(&sc->sc_mutex);
759
760         if (len == 3)
761                 server_mode = (resp[2] & PMU_PWR_WAKEUP_AC_INSERT) ? 1 : 0;
762
763         error = sysctl_handle_int(oidp, &server_mode, 0, req);
764
765         if (len != 3)
766                 return (EINVAL);
767
768         if (error || !req->newptr)
769                 return (error);
770
771         if (server_mode == 1)
772                 setcmd[0] = PMU_PWR_SET_POWERUP_EVENTS;
773         else if (server_mode == 0)
774                 setcmd[0] = PMU_PWR_CLR_POWERUP_EVENTS;
775         else
776                 return (EINVAL);
777
778         setcmd[1] = resp[1];
779
780         mtx_lock(&sc->sc_mutex);
781         pmu_send(sc, PMU_POWER_EVENTS, 3, setcmd, 2, resp);
782         mtx_unlock(&sc->sc_mutex);
783
784         return (0);
785 }
786
787 static int
788 pmu_query_battery(struct pmu_softc *sc, int batt, struct pmu_battstate *info)
789 {
790         uint8_t reg;
791         uint8_t resp[16];
792         int len;
793
794         reg = batt + 1;
795
796         mtx_lock(&sc->sc_mutex);
797         len = pmu_send(sc, PMU_SMART_BATTERY_STATE, 1, &reg, 16, resp);
798         mtx_unlock(&sc->sc_mutex);
799
800         if (len < 3)
801                 return (-1);
802
803         /* All PMU battery info replies share a common header:
804          * Byte 1       Payload Format
805          * Byte 2       Battery Flags
806          */
807
808         info->state = resp[2];
809
810         switch (resp[1]) {
811         case 3:
812         case 4: 
813                 /*
814                  * Formats 3 and 4 appear to be the same:
815                  * Byte 3       Charge
816                  * Byte 4       Max Charge
817                  * Byte 5       Current
818                  * Byte 6       Voltage
819                  */
820
821                 info->charge = resp[3];
822                 info->maxcharge = resp[4];
823                 /* Current can be positive or negative */
824                 info->current = (int8_t)resp[5];
825                 info->voltage = resp[6];
826                 break;
827         case 5:
828                 /*
829                  * Formats 5 is a wider version of formats 3 and 4
830                  * Byte 3-4     Charge
831                  * Byte 5-6     Max Charge
832                  * Byte 7-8     Current
833                  * Byte 9-10    Voltage
834                  */
835
836                 info->charge = (resp[3] << 8) | resp[4];
837                 info->maxcharge = (resp[5] << 8) | resp[6];
838                 /* Current can be positive or negative */
839                 info->current = (int16_t)((resp[7] << 8) | resp[8]);
840                 info->voltage = (resp[9] << 8) | resp[10];
841                 break;
842         default:
843                 device_printf(sc->sc_dev, "Unknown battery info format (%d)!\n",
844                     resp[1]);
845                 return (-1);
846         }
847
848         return (0);
849 }
850
851 static int
852 pmu_acline_state(SYSCTL_HANDLER_ARGS)
853 {
854         struct pmu_softc *sc;
855         struct pmu_battstate batt;
856         int error, result;
857
858         sc = arg1;
859
860         /* The PMU treats the AC line status as a property of the battery */
861         error = pmu_query_battery(sc, 0, &batt);
862
863         if (error != 0)
864                 return (error);
865         
866         result = (batt.state & PMU_PWR_AC_PRESENT) ? 1 : 0;
867         error = sysctl_handle_int(oidp, &result, 0, req);
868
869         return (error);
870 }
871
872 static int
873 pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS)
874 {
875         struct pmu_softc *sc;
876         struct pmu_battstate batt;
877         int error, result;
878
879         sc = arg1;
880
881         error = pmu_query_battery(sc, arg2 & 0x00ff, &batt);
882
883         if (error != 0)
884                 return (error);
885
886         switch (arg2 & 0xff00) {
887         case PMU_BATSYSCTL_PRESENT:
888                 result = (batt.state & PMU_PWR_BATT_PRESENT) ? 1 : 0;
889                 break;
890         case PMU_BATSYSCTL_CHARGING:
891                 result = (batt.state & PMU_PWR_BATT_CHARGING) ? 1 : 0;
892                 break;
893         case PMU_BATSYSCTL_CHARGE:
894                 result = batt.charge;
895                 break;
896         case PMU_BATSYSCTL_MAXCHARGE:
897                 result = batt.maxcharge;
898                 break;
899         case PMU_BATSYSCTL_CURRENT:
900                 result = batt.current;
901                 break;
902         case PMU_BATSYSCTL_VOLTAGE:
903                 result = batt.voltage;
904                 break;
905         case PMU_BATSYSCTL_TIME:
906                 /* Time remaining until full charge/discharge, in minutes */
907
908                 if (batt.current >= 0)
909                         result = (batt.maxcharge - batt.charge) /* mAh */ * 60 
910                             / batt.current /* mA */;
911                 else
912                         result = (batt.charge /* mAh */ * 60) 
913                             / (-batt.current /* mA */);
914                 break;
915         case PMU_BATSYSCTL_LIFE:
916                 /* Battery charge fraction, in percent */
917                 result = (batt.charge * 100) / batt.maxcharge;
918                 break;
919         default:
920                 /* This should never happen */
921                 result = -1;
922         };
923
924         error = sysctl_handle_int(oidp, &result, 0, req);
925
926         return (error);
927 }
928