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