pmu.c revision 273009
1184299Snwhitehorn/*-
2184299Snwhitehorn * Copyright (c) 2006 Michael Lorenz
3184299Snwhitehorn * Copyright 2008 by Nathan Whitehorn
4184299Snwhitehorn * All rights reserved.
5184299Snwhitehorn *
6184299Snwhitehorn * Redistribution and use in source and binary forms, with or without
7184299Snwhitehorn * modification, are permitted provided that the following conditions
8184299Snwhitehorn * are met:
9184299Snwhitehorn * 1. Redistributions of source code must retain the above copyright
10184299Snwhitehorn *    notice, this list of conditions and the following disclaimer.
11184299Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
12184299Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
13184299Snwhitehorn *    documentation and/or other materials provided with the distribution.
14184299Snwhitehorn *
15184299Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16184299Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17184299Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18184299Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19184299Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20184299Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21184299Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22184299Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23184299Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24184299Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25184299Snwhitehorn * SUCH DAMAGE.
26184299Snwhitehorn *
27184299Snwhitehorn */
28184299Snwhitehorn
29184299Snwhitehorn#include <sys/cdefs.h>
30184299Snwhitehorn__FBSDID("$FreeBSD: head/sys/powerpc/powermac/pmu.c 273009 2014-10-12 19:12:48Z jhibbits $");
31184299Snwhitehorn
32184299Snwhitehorn#include <sys/param.h>
33184299Snwhitehorn#include <sys/systm.h>
34184299Snwhitehorn#include <sys/module.h>
35184299Snwhitehorn#include <sys/bus.h>
36184299Snwhitehorn#include <sys/conf.h>
37184299Snwhitehorn#include <sys/kernel.h>
38273009Sjhibbits#include <sys/kthread.h>
39205506Snwhitehorn#include <sys/clock.h>
40259284Sjhibbits#include <sys/proc.h>
41212054Snwhitehorn#include <sys/reboot.h>
42185727Snwhitehorn#include <sys/sysctl.h>
43184299Snwhitehorn
44184299Snwhitehorn#include <dev/ofw/ofw_bus.h>
45184299Snwhitehorn#include <dev/ofw/openfirm.h>
46185782Snwhitehorn#include <dev/led/led.h>
47184299Snwhitehorn
48259284Sjhibbits#include <machine/_inttypes.h>
49184299Snwhitehorn#include <machine/bus.h>
50259284Sjhibbits#include <machine/cpu.h>
51259284Sjhibbits#include <machine/hid.h>
52184299Snwhitehorn#include <machine/intr_machdep.h>
53184299Snwhitehorn#include <machine/md_var.h>
54259284Sjhibbits#include <machine/pcb.h>
55184299Snwhitehorn#include <machine/pio.h>
56184299Snwhitehorn#include <machine/resource.h>
57184299Snwhitehorn
58184299Snwhitehorn#include <vm/vm.h>
59184299Snwhitehorn#include <vm/pmap.h>
60184299Snwhitehorn
61184299Snwhitehorn#include <sys/rman.h>
62184299Snwhitehorn
63184299Snwhitehorn#include <dev/adb/adb.h>
64184299Snwhitehorn
65205506Snwhitehorn#include "clock_if.h"
66184299Snwhitehorn#include "pmuvar.h"
67184299Snwhitehorn#include "viareg.h"
68259284Sjhibbits#include "uninorthvar.h"	/* For unin_chip_sleep()/unin_chip_wake() */
69184299Snwhitehorn
70259284Sjhibbits#define PMU_DEFAULTS	PMU_INT_TICK | PMU_INT_ADB | \
71259284Sjhibbits	PMU_INT_PCEJECT | PMU_INT_SNDBRT | \
72259284Sjhibbits	PMU_INT_BATTERY | PMU_INT_ENVIRONMENT
73259284Sjhibbits
74184299Snwhitehorn/*
75205506Snwhitehorn * Bus interface
76184299Snwhitehorn */
77184299Snwhitehornstatic int	pmu_probe(device_t);
78184299Snwhitehornstatic int	pmu_attach(device_t);
79184299Snwhitehornstatic int	pmu_detach(device_t);
80184299Snwhitehorn
81205506Snwhitehorn/*
82205506Snwhitehorn * Clock interface
83205506Snwhitehorn */
84205506Snwhitehornstatic int	pmu_gettime(device_t dev, struct timespec *ts);
85205506Snwhitehornstatic int	pmu_settime(device_t dev, struct timespec *ts);
86205506Snwhitehorn
87205506Snwhitehorn/*
88205506Snwhitehorn * ADB Interface
89205506Snwhitehorn */
90205506Snwhitehorn
91185727Snwhitehornstatic u_int	pmu_adb_send(device_t dev, u_char command_byte, int len,
92185754Snwhitehorn		    u_char *data, u_char poll);
93185727Snwhitehornstatic u_int	pmu_adb_autopoll(device_t dev, uint16_t mask);
94194027Savgstatic u_int	pmu_poll(device_t dev);
95185754Snwhitehorn
96212054Snwhitehorn/*
97212054Snwhitehorn * Power interface
98212054Snwhitehorn */
99212054Snwhitehorn
100212054Snwhitehornstatic void	pmu_shutdown(void *xsc, int howto);
101185782Snwhitehornstatic void	pmu_set_sleepled(void *xsc, int onoff);
102185727Snwhitehornstatic int	pmu_server_mode(SYSCTL_HANDLER_ARGS);
103193159Snwhitehornstatic int	pmu_acline_state(SYSCTL_HANDLER_ARGS);
104185754Snwhitehornstatic int	pmu_query_battery(struct pmu_softc *sc, int batt,
105185754Snwhitehorn		    struct pmu_battstate *info);
106185754Snwhitehornstatic int	pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS);
107184299Snwhitehorn
108185754Snwhitehorn/*
109185754Snwhitehorn * List of battery-related sysctls we might ask for
110185754Snwhitehorn */
111185754Snwhitehorn
112185754Snwhitehornenum {
113185754Snwhitehorn	PMU_BATSYSCTL_PRESENT	= 1 << 8,
114185754Snwhitehorn	PMU_BATSYSCTL_CHARGING	= 2 << 8,
115185754Snwhitehorn	PMU_BATSYSCTL_CHARGE	= 3 << 8,
116185754Snwhitehorn	PMU_BATSYSCTL_MAXCHARGE = 4 << 8,
117185754Snwhitehorn	PMU_BATSYSCTL_CURRENT	= 5 << 8,
118185754Snwhitehorn	PMU_BATSYSCTL_VOLTAGE	= 6 << 8,
119185754Snwhitehorn	PMU_BATSYSCTL_TIME	= 7 << 8,
120185754Snwhitehorn	PMU_BATSYSCTL_LIFE	= 8 << 8
121185754Snwhitehorn};
122185754Snwhitehorn
123184299Snwhitehornstatic device_method_t  pmu_methods[] = {
124184299Snwhitehorn	/* Device interface */
125184299Snwhitehorn	DEVMETHOD(device_probe,		pmu_probe),
126184299Snwhitehorn	DEVMETHOD(device_attach,	pmu_attach),
127184299Snwhitehorn        DEVMETHOD(device_detach,        pmu_detach),
128184299Snwhitehorn        DEVMETHOD(device_shutdown,      bus_generic_shutdown),
129184299Snwhitehorn
130184299Snwhitehorn	/* ADB bus interface */
131184299Snwhitehorn	DEVMETHOD(adb_hb_send_raw_packet,   pmu_adb_send),
132184299Snwhitehorn	DEVMETHOD(adb_hb_controller_poll,   pmu_poll),
133184299Snwhitehorn	DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll),
134184299Snwhitehorn
135205506Snwhitehorn	/* Clock interface */
136205506Snwhitehorn	DEVMETHOD(clock_gettime,	pmu_gettime),
137205506Snwhitehorn	DEVMETHOD(clock_settime,	pmu_settime),
138205506Snwhitehorn
139227843Smarius	DEVMETHOD_END
140184299Snwhitehorn};
141184299Snwhitehorn
142184299Snwhitehornstatic driver_t pmu_driver = {
143184299Snwhitehorn	"pmu",
144184299Snwhitehorn	pmu_methods,
145184299Snwhitehorn	sizeof(struct pmu_softc),
146184299Snwhitehorn};
147184299Snwhitehorn
148184299Snwhitehornstatic devclass_t pmu_devclass;
149184299Snwhitehorn
150184299SnwhitehornDRIVER_MODULE(pmu, macio, pmu_driver, pmu_devclass, 0, 0);
151184299SnwhitehornDRIVER_MODULE(adb, pmu, adb_driver, adb_devclass, 0, 0);
152184299Snwhitehorn
153184299Snwhitehornstatic int	pmuextint_probe(device_t);
154184299Snwhitehornstatic int	pmuextint_attach(device_t);
155184299Snwhitehorn
156184299Snwhitehornstatic device_method_t  pmuextint_methods[] = {
157184299Snwhitehorn	/* Device interface */
158184299Snwhitehorn	DEVMETHOD(device_probe,		pmuextint_probe),
159184299Snwhitehorn	DEVMETHOD(device_attach,	pmuextint_attach),
160184299Snwhitehorn
161184299Snwhitehorn	{0,0}
162184299Snwhitehorn};
163184299Snwhitehorn
164184299Snwhitehornstatic driver_t pmuextint_driver = {
165184299Snwhitehorn	"pmuextint",
166184299Snwhitehorn	pmuextint_methods,
167184299Snwhitehorn	0
168184299Snwhitehorn};
169184299Snwhitehorn
170184299Snwhitehornstatic devclass_t pmuextint_devclass;
171184299Snwhitehorn
172184299SnwhitehornDRIVER_MODULE(pmuextint, macgpio, pmuextint_driver, pmuextint_devclass, 0, 0);
173184299Snwhitehorn
174184299Snwhitehorn/* Make sure uhid is loaded, as it turns off some of the ADB emulation */
175184299SnwhitehornMODULE_DEPEND(pmu, usb, 1, 1, 1);
176184299Snwhitehorn
177184299Snwhitehornstatic void pmu_intr(void *arg);
178184299Snwhitehornstatic void pmu_in(struct pmu_softc *sc);
179184299Snwhitehornstatic void pmu_out(struct pmu_softc *sc);
180184299Snwhitehornstatic void pmu_ack_on(struct pmu_softc *sc);
181184299Snwhitehornstatic void pmu_ack_off(struct pmu_softc *sc);
182184299Snwhitehornstatic int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg,
183184299Snwhitehorn	int rlen, uint8_t *out_msg);
184184299Snwhitehornstatic uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset);
185184299Snwhitehornstatic void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value);
186184299Snwhitehornstatic int pmu_intr_state(struct pmu_softc *);
187273009Sjhibbitsstatic void pmu_battquery_proc(void);
188273009Sjhibbitsstatic void pmu_battery_notify(struct pmu_battstate *batt,
189273009Sjhibbits	struct pmu_battstate *old);
190184299Snwhitehorn
191184299Snwhitehorn/* these values shows that number of data returned after 'send' cmd is sent */
192184299Snwhitehornstatic signed char pm_send_cmd_type[] = {
193184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
194184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
195184299Snwhitehorn	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
196184299Snwhitehorn	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
197184299Snwhitehorn	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
198184299Snwhitehorn	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
199184299Snwhitehorn	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
200184299Snwhitehorn	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
201184299Snwhitehorn	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
202184299Snwhitehorn	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
203184299Snwhitehorn	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
204184299Snwhitehorn	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
205184299Snwhitehorn	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
206184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
207184299Snwhitehorn	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
208259284Sjhibbits	0x00, 0x00,   -1,   -1,   -1, 0x05, 0x04, 0x04,
209184299Snwhitehorn	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
210184299Snwhitehorn	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
211184299Snwhitehorn	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
212184299Snwhitehorn	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
213184299Snwhitehorn	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
214184299Snwhitehorn	0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
215184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
216184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
217184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
218184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
219184299Snwhitehorn	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
220184299Snwhitehorn	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
221184299Snwhitehorn	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
222184299Snwhitehorn	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
223184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
224184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
225184299Snwhitehorn};
226184299Snwhitehorn
227184299Snwhitehorn/* these values shows that number of data returned after 'receive' cmd is sent */
228184299Snwhitehornstatic signed char pm_receive_cmd_type[] = {
229184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
231184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232184299Snwhitehorn	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
233184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
235184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236184299Snwhitehorn	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
237184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238184299Snwhitehorn	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
239184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240184299Snwhitehorn	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
241184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242184299Snwhitehorn	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
243184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244259284Sjhibbits	  -1,   -1,   -1,   -1,   -1, 0x01, 0x01, 0x01,
245184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246184299Snwhitehorn	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
247184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
248184299Snwhitehorn	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
249184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250184299Snwhitehorn	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
251184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
252184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
253184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
254184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
255184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
256184299Snwhitehorn	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
257184299Snwhitehorn	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
258184299Snwhitehorn	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
259184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
260184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
261184299Snwhitehorn};
262184299Snwhitehorn
263273009Sjhibbitsstatic struct proc *pmubattproc;
264273009Sjhibbitsstatic struct kproc_desc pmu_batt_kp = {
265273009Sjhibbits	"pmu_batt",
266273009Sjhibbits	pmu_battquery_proc,
267273009Sjhibbits	&pmubattproc
268273009Sjhibbits};
269273009Sjhibbits
270184299Snwhitehorn/* We only have one of each device, so globals are safe */
271184299Snwhitehornstatic device_t pmu = NULL;
272184299Snwhitehornstatic device_t pmu_extint = NULL;
273184299Snwhitehorn
274184299Snwhitehornstatic int
275184299Snwhitehornpmuextint_probe(device_t dev)
276184299Snwhitehorn{
277184299Snwhitehorn	const char *type = ofw_bus_get_type(dev);
278184299Snwhitehorn
279184299Snwhitehorn	if (strcmp(type, "extint-gpio1") != 0)
280184299Snwhitehorn                return (ENXIO);
281184299Snwhitehorn
282184299Snwhitehorn	device_set_desc(dev, "Apple PMU99 External Interrupt");
283184299Snwhitehorn	return (0);
284184299Snwhitehorn}
285184299Snwhitehorn
286184299Snwhitehornstatic int
287184299Snwhitehornpmu_probe(device_t dev)
288184299Snwhitehorn{
289184299Snwhitehorn	const char *type = ofw_bus_get_type(dev);
290184299Snwhitehorn
291184299Snwhitehorn	if (strcmp(type, "via-pmu") != 0)
292184299Snwhitehorn                return (ENXIO);
293184299Snwhitehorn
294184299Snwhitehorn	device_set_desc(dev, "Apple PMU99 Controller");
295184299Snwhitehorn	return (0);
296184299Snwhitehorn}
297184299Snwhitehorn
298184299Snwhitehorn
299184299Snwhitehornstatic int
300184299Snwhitehornsetup_pmu_intr(device_t dev, device_t extint)
301184299Snwhitehorn{
302184299Snwhitehorn	struct pmu_softc *sc;
303184299Snwhitehorn	sc = device_get_softc(dev);
304184299Snwhitehorn
305184299Snwhitehorn	sc->sc_irqrid = 0;
306184299Snwhitehorn	sc->sc_irq = bus_alloc_resource_any(extint, SYS_RES_IRQ, &sc->sc_irqrid,
307184299Snwhitehorn           	RF_ACTIVE);
308184299Snwhitehorn        if (sc->sc_irq == NULL) {
309184299Snwhitehorn                device_printf(dev, "could not allocate interrupt\n");
310184299Snwhitehorn                return (ENXIO);
311184299Snwhitehorn        }
312184299Snwhitehorn
313184299Snwhitehorn	if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE
314184299Snwhitehorn	    | INTR_ENTROPY, NULL, pmu_intr, dev, &sc->sc_ih) != 0) {
315184299Snwhitehorn                device_printf(dev, "could not setup interrupt\n");
316184299Snwhitehorn                bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
317184299Snwhitehorn                    sc->sc_irq);
318184299Snwhitehorn                return (ENXIO);
319184299Snwhitehorn        }
320184299Snwhitehorn
321184299Snwhitehorn	return (0);
322184299Snwhitehorn}
323184299Snwhitehorn
324184299Snwhitehornstatic int
325184299Snwhitehornpmuextint_attach(device_t dev)
326184299Snwhitehorn{
327184299Snwhitehorn	pmu_extint = dev;
328184299Snwhitehorn	if (pmu)
329184299Snwhitehorn		return (setup_pmu_intr(pmu,dev));
330184299Snwhitehorn
331184299Snwhitehorn	return (0);
332184299Snwhitehorn}
333184299Snwhitehorn
334184299Snwhitehornstatic int
335184299Snwhitehornpmu_attach(device_t dev)
336184299Snwhitehorn{
337184299Snwhitehorn	struct pmu_softc *sc;
338184299Snwhitehorn
339185754Snwhitehorn	int i;
340184299Snwhitehorn	uint8_t reg;
341184299Snwhitehorn	uint8_t cmd[2] = {2, 0};
342184299Snwhitehorn	uint8_t resp[16];
343184299Snwhitehorn	phandle_t node,child;
344185727Snwhitehorn	struct sysctl_ctx_list *ctx;
345185727Snwhitehorn	struct sysctl_oid *tree;
346184299Snwhitehorn
347184299Snwhitehorn	sc = device_get_softc(dev);
348184299Snwhitehorn	sc->sc_dev = dev;
349184299Snwhitehorn
350184299Snwhitehorn	sc->sc_memrid = 0;
351184299Snwhitehorn	sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
352184299Snwhitehorn		          &sc->sc_memrid, RF_ACTIVE);
353184299Snwhitehorn
354184299Snwhitehorn	mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE);
355184299Snwhitehorn
356184299Snwhitehorn	if (sc->sc_memr == NULL) {
357184299Snwhitehorn		device_printf(dev, "Could not alloc mem resource!\n");
358184299Snwhitehorn		return (ENXIO);
359184299Snwhitehorn	}
360184299Snwhitehorn
361184299Snwhitehorn	/*
362184299Snwhitehorn	 * Our interrupt is attached to a GPIO pin. Depending on probe order,
363184299Snwhitehorn	 * we may not have found it yet. If we haven't, it will find us, and
364184299Snwhitehorn	 * attach our interrupt then.
365184299Snwhitehorn	 */
366184299Snwhitehorn	pmu = dev;
367184299Snwhitehorn	if (pmu_extint != NULL) {
368184299Snwhitehorn		if (setup_pmu_intr(dev,pmu_extint) != 0)
369184299Snwhitehorn			return (ENXIO);
370184299Snwhitehorn	}
371184299Snwhitehorn
372184299Snwhitehorn	sc->sc_autopoll = 0;
373185782Snwhitehorn	sc->sc_batteries = 0;
374185782Snwhitehorn	sc->adb_bus = NULL;
375185782Snwhitehorn	sc->sc_leddev = NULL;
376184299Snwhitehorn
377184299Snwhitehorn	/* Init PMU */
378184299Snwhitehorn
379259284Sjhibbits	pmu_write_reg(sc, vBufB, pmu_read_reg(sc, vBufB) | vPB4);
380259284Sjhibbits	pmu_write_reg(sc, vDirB, (pmu_read_reg(sc, vDirB) | vPB4) & ~vPB3);
381259284Sjhibbits
382259284Sjhibbits	reg = PMU_DEFAULTS;
383184299Snwhitehorn	pmu_send(sc, PMU_SET_IMASK, 1, &reg, 16, resp);
384184299Snwhitehorn
385259284Sjhibbits	pmu_write_reg(sc, vIER, 0x94); /* make sure VIA interrupts are on */
386184299Snwhitehorn
387184299Snwhitehorn	pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp);
388260872Sjhibbits	pmu_send(sc, PMU_GET_VERSION, 0, cmd, 16, resp);
389184299Snwhitehorn
390184299Snwhitehorn	/* Initialize child buses (ADB) */
391184299Snwhitehorn	node = ofw_bus_get_node(dev);
392184299Snwhitehorn
393184299Snwhitehorn	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
394184299Snwhitehorn		char name[32];
395184299Snwhitehorn
396184299Snwhitehorn		memset(name, 0, sizeof(name));
397184299Snwhitehorn		OF_getprop(child, "name", name, sizeof(name));
398184299Snwhitehorn
399184299Snwhitehorn		if (bootverbose)
400184299Snwhitehorn			device_printf(dev, "PMU child <%s>\n",name);
401184299Snwhitehorn
402184299Snwhitehorn		if (strncmp(name, "adb", 4) == 0) {
403184299Snwhitehorn			sc->adb_bus = device_add_child(dev,"adb",-1);
404184299Snwhitehorn		}
405185754Snwhitehorn
406185754Snwhitehorn		if (strncmp(name, "power-mgt", 9) == 0) {
407185754Snwhitehorn			uint32_t prim_info[9];
408185754Snwhitehorn
409185754Snwhitehorn			if (OF_getprop(child, "prim-info", prim_info,
410185754Snwhitehorn			    sizeof(prim_info)) >= 7)
411185754Snwhitehorn				sc->sc_batteries = (prim_info[6] >> 16) & 0xff;
412185754Snwhitehorn
413185754Snwhitehorn			if (bootverbose && sc->sc_batteries > 0)
414185754Snwhitehorn				device_printf(dev, "%d batteries detected\n",
415185754Snwhitehorn				    sc->sc_batteries);
416185754Snwhitehorn		}
417184299Snwhitehorn	}
418184299Snwhitehorn
419185727Snwhitehorn	/*
420185727Snwhitehorn	 * Set up sysctls
421185727Snwhitehorn	 */
422185727Snwhitehorn
423185727Snwhitehorn	ctx = device_get_sysctl_ctx(dev);
424185727Snwhitehorn	tree = device_get_sysctl_tree(dev);
425185727Snwhitehorn
426185727Snwhitehorn	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
427185754Snwhitehorn	    "server_mode", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
428185754Snwhitehorn	    pmu_server_mode, "I", "Enable reboot after power failure");
429185727Snwhitehorn
430185754Snwhitehorn	if (sc->sc_batteries > 0) {
431185754Snwhitehorn		struct sysctl_oid *oid, *battroot;
432185754Snwhitehorn		char battnum[2];
433185754Snwhitehorn
434273009Sjhibbits		/* Only start the battery monitor if we have a battery. */
435273009Sjhibbits		kproc_start(&pmu_batt_kp);
436193159Snwhitehorn		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
437193159Snwhitehorn		    "acline", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
438193159Snwhitehorn		    pmu_acline_state, "I", "AC Line Status");
439193159Snwhitehorn
440185754Snwhitehorn		battroot = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
441185754Snwhitehorn		    "batteries", CTLFLAG_RD, 0, "Battery Information");
442185754Snwhitehorn
443185754Snwhitehorn		for (i = 0; i < sc->sc_batteries; i++) {
444185754Snwhitehorn			battnum[0] = i + '0';
445185754Snwhitehorn			battnum[1] = '\0';
446185754Snwhitehorn
447185754Snwhitehorn			oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(battroot),
448185754Snwhitehorn			    OID_AUTO, battnum, CTLFLAG_RD, 0,
449185754Snwhitehorn			    "Battery Information");
450185754Snwhitehorn
451185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
452185754Snwhitehorn			    "present", CTLTYPE_INT | CTLFLAG_RD, sc,
453185754Snwhitehorn			    PMU_BATSYSCTL_PRESENT | i, pmu_battquery_sysctl,
454185754Snwhitehorn			    "I", "Battery present");
455185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
456185754Snwhitehorn			    "charging", CTLTYPE_INT | CTLFLAG_RD, sc,
457185754Snwhitehorn			    PMU_BATSYSCTL_CHARGING | i, pmu_battquery_sysctl,
458185754Snwhitehorn			    "I", "Battery charging");
459185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
460185754Snwhitehorn			    "charge", CTLTYPE_INT | CTLFLAG_RD, sc,
461185754Snwhitehorn			    PMU_BATSYSCTL_CHARGE | i, pmu_battquery_sysctl,
462185754Snwhitehorn			    "I", "Battery charge (mAh)");
463185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
464185754Snwhitehorn			    "maxcharge", CTLTYPE_INT | CTLFLAG_RD, sc,
465185754Snwhitehorn			    PMU_BATSYSCTL_MAXCHARGE | i, pmu_battquery_sysctl,
466185754Snwhitehorn			    "I", "Maximum battery capacity (mAh)");
467185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
468185754Snwhitehorn			    "rate", CTLTYPE_INT | CTLFLAG_RD, sc,
469185754Snwhitehorn			    PMU_BATSYSCTL_CURRENT | i, pmu_battquery_sysctl,
470185754Snwhitehorn			    "I", "Battery discharge rate (mA)");
471185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
472185754Snwhitehorn			    "voltage", CTLTYPE_INT | CTLFLAG_RD, sc,
473185754Snwhitehorn			    PMU_BATSYSCTL_VOLTAGE | i, pmu_battquery_sysctl,
474185754Snwhitehorn			    "I", "Battery voltage (mV)");
475185754Snwhitehorn
476185754Snwhitehorn			/* Knobs for mental compatibility with ACPI */
477185754Snwhitehorn
478185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
479185754Snwhitehorn			    "time", CTLTYPE_INT | CTLFLAG_RD, sc,
480185754Snwhitehorn			    PMU_BATSYSCTL_TIME | i, pmu_battquery_sysctl,
481185754Snwhitehorn			    "I", "Time Remaining (minutes)");
482185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
483185754Snwhitehorn			    "life", CTLTYPE_INT | CTLFLAG_RD, sc,
484185754Snwhitehorn			    PMU_BATSYSCTL_LIFE | i, pmu_battquery_sysctl,
485185754Snwhitehorn			    "I", "Capacity remaining (percent)");
486185754Snwhitehorn		}
487185754Snwhitehorn	}
488185754Snwhitehorn
489185782Snwhitehorn	/*
490185782Snwhitehorn	 * Set up LED interface
491185782Snwhitehorn	 */
492185782Snwhitehorn
493185782Snwhitehorn	sc->sc_leddev = led_create(pmu_set_sleepled, sc, "sleepled");
494185782Snwhitehorn
495205506Snwhitehorn	/*
496205506Snwhitehorn	 * Register RTC
497205506Snwhitehorn	 */
498205506Snwhitehorn
499205506Snwhitehorn	clock_register(dev, 1000);
500205506Snwhitehorn
501212054Snwhitehorn	/*
502212054Snwhitehorn	 * Register power control handler
503212054Snwhitehorn	 */
504212054Snwhitehorn	EVENTHANDLER_REGISTER(shutdown_final, pmu_shutdown, sc,
505212054Snwhitehorn	    SHUTDOWN_PRI_LAST);
506212054Snwhitehorn
507184299Snwhitehorn	return (bus_generic_attach(dev));
508184299Snwhitehorn}
509184299Snwhitehorn
510184299Snwhitehornstatic int
511184299Snwhitehornpmu_detach(device_t dev)
512184299Snwhitehorn{
513184299Snwhitehorn	struct pmu_softc *sc;
514184299Snwhitehorn
515184299Snwhitehorn	sc = device_get_softc(dev);
516184299Snwhitehorn
517185782Snwhitehorn	if (sc->sc_leddev != NULL)
518185782Snwhitehorn		led_destroy(sc->sc_leddev);
519185782Snwhitehorn
520184299Snwhitehorn	bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
521184299Snwhitehorn	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq);
522184299Snwhitehorn	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr);
523184299Snwhitehorn	mtx_destroy(&sc->sc_mutex);
524184299Snwhitehorn
525184299Snwhitehorn	return (bus_generic_detach(dev));
526184299Snwhitehorn}
527184299Snwhitehorn
528184299Snwhitehornstatic uint8_t
529184299Snwhitehornpmu_read_reg(struct pmu_softc *sc, u_int offset)
530184299Snwhitehorn{
531184299Snwhitehorn	return (bus_read_1(sc->sc_memr, offset));
532184299Snwhitehorn}
533184299Snwhitehorn
534184299Snwhitehornstatic void
535184299Snwhitehornpmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value)
536184299Snwhitehorn{
537184299Snwhitehorn	bus_write_1(sc->sc_memr, offset, value);
538184299Snwhitehorn}
539184299Snwhitehorn
540184299Snwhitehornstatic int
541184299Snwhitehornpmu_send_byte(struct pmu_softc *sc, uint8_t data)
542184299Snwhitehorn{
543184299Snwhitehorn
544184299Snwhitehorn	pmu_out(sc);
545184299Snwhitehorn	pmu_write_reg(sc, vSR, data);
546184299Snwhitehorn	pmu_ack_off(sc);
547184299Snwhitehorn	/* wait for intr to come up */
548184299Snwhitehorn	/* XXX should add a timeout and bail if it expires */
549184299Snwhitehorn	do {} while (pmu_intr_state(sc) == 0);
550184299Snwhitehorn	pmu_ack_on(sc);
551184299Snwhitehorn	do {} while (pmu_intr_state(sc));
552184299Snwhitehorn	pmu_ack_on(sc);
553184299Snwhitehorn	return 0;
554184299Snwhitehorn}
555184299Snwhitehorn
556184299Snwhitehornstatic inline int
557184299Snwhitehornpmu_read_byte(struct pmu_softc *sc, uint8_t *data)
558184299Snwhitehorn{
559184299Snwhitehorn	volatile uint8_t scratch;
560184299Snwhitehorn	pmu_in(sc);
561184299Snwhitehorn	scratch = pmu_read_reg(sc, vSR);
562184299Snwhitehorn	pmu_ack_off(sc);
563184299Snwhitehorn	/* wait for intr to come up */
564184299Snwhitehorn	do {} while (pmu_intr_state(sc) == 0);
565184299Snwhitehorn	pmu_ack_on(sc);
566184299Snwhitehorn	do {} while (pmu_intr_state(sc));
567184299Snwhitehorn	*data = pmu_read_reg(sc, vSR);
568184299Snwhitehorn	return 0;
569184299Snwhitehorn}
570184299Snwhitehorn
571184299Snwhitehornstatic int
572184299Snwhitehornpmu_intr_state(struct pmu_softc *sc)
573184299Snwhitehorn{
574184299Snwhitehorn	return ((pmu_read_reg(sc, vBufB) & vPB3) == 0);
575184299Snwhitehorn}
576184299Snwhitehorn
577184299Snwhitehornstatic int
578184299Snwhitehornpmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen,
579184299Snwhitehorn    uint8_t *out_msg)
580184299Snwhitehorn{
581184299Snwhitehorn	struct pmu_softc *sc = cookie;
582184299Snwhitehorn	int i, rcv_len = -1;
583184299Snwhitehorn	uint8_t out_len, intreg;
584184299Snwhitehorn
585184299Snwhitehorn	intreg = pmu_read_reg(sc, vIER);
586184299Snwhitehorn	intreg &= 0x10;
587184299Snwhitehorn	pmu_write_reg(sc, vIER, intreg);
588184299Snwhitehorn
589184299Snwhitehorn	/* wait idle */
590184299Snwhitehorn	do {} while (pmu_intr_state(sc));
591184299Snwhitehorn
592184299Snwhitehorn	/* send command */
593184299Snwhitehorn	pmu_send_byte(sc, cmd);
594184299Snwhitehorn
595184299Snwhitehorn	/* send length if necessary */
596184299Snwhitehorn	if (pm_send_cmd_type[cmd] < 0) {
597184299Snwhitehorn		pmu_send_byte(sc, length);
598184299Snwhitehorn	}
599184299Snwhitehorn
600184299Snwhitehorn	for (i = 0; i < length; i++) {
601184299Snwhitehorn		pmu_send_byte(sc, in_msg[i]);
602184299Snwhitehorn	}
603184299Snwhitehorn
604184299Snwhitehorn	/* see if there's data to read */
605184299Snwhitehorn	rcv_len = pm_receive_cmd_type[cmd];
606184299Snwhitehorn	if (rcv_len == 0)
607184299Snwhitehorn		goto done;
608184299Snwhitehorn
609184299Snwhitehorn	/* read command */
610184299Snwhitehorn	if (rcv_len == 1) {
611184299Snwhitehorn		pmu_read_byte(sc, out_msg);
612184299Snwhitehorn		goto done;
613184299Snwhitehorn	} else
614184299Snwhitehorn		out_msg[0] = cmd;
615184299Snwhitehorn	if (rcv_len < 0) {
616184299Snwhitehorn		pmu_read_byte(sc, &out_len);
617184299Snwhitehorn		rcv_len = out_len + 1;
618184299Snwhitehorn	}
619184299Snwhitehorn	for (i = 1; i < min(rcv_len, rlen); i++)
620184299Snwhitehorn		pmu_read_byte(sc, &out_msg[i]);
621184299Snwhitehorn
622184299Snwhitehorndone:
623184299Snwhitehorn	pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90);
624184299Snwhitehorn
625184299Snwhitehorn	return rcv_len;
626184299Snwhitehorn}
627184299Snwhitehorn
628184299Snwhitehorn
629194027Savgstatic u_int
630184299Snwhitehornpmu_poll(device_t dev)
631184299Snwhitehorn{
632184299Snwhitehorn	pmu_intr(dev);
633194027Savg	return (0);
634184299Snwhitehorn}
635184299Snwhitehorn
636184299Snwhitehornstatic void
637184299Snwhitehornpmu_in(struct pmu_softc *sc)
638184299Snwhitehorn{
639184299Snwhitehorn	uint8_t reg;
640184299Snwhitehorn
641184299Snwhitehorn	reg = pmu_read_reg(sc, vACR);
642184299Snwhitehorn	reg &= ~vSR_OUT;
643184299Snwhitehorn	reg |= 0x0c;
644184299Snwhitehorn	pmu_write_reg(sc, vACR, reg);
645184299Snwhitehorn}
646184299Snwhitehorn
647184299Snwhitehornstatic void
648184299Snwhitehornpmu_out(struct pmu_softc *sc)
649184299Snwhitehorn{
650184299Snwhitehorn	uint8_t reg;
651184299Snwhitehorn
652184299Snwhitehorn	reg = pmu_read_reg(sc, vACR);
653184299Snwhitehorn	reg |= vSR_OUT;
654184299Snwhitehorn	reg |= 0x0c;
655184299Snwhitehorn	pmu_write_reg(sc, vACR, reg);
656184299Snwhitehorn}
657184299Snwhitehorn
658184299Snwhitehornstatic void
659184299Snwhitehornpmu_ack_off(struct pmu_softc *sc)
660184299Snwhitehorn{
661184299Snwhitehorn	uint8_t reg;
662184299Snwhitehorn
663184299Snwhitehorn	reg = pmu_read_reg(sc, vBufB);
664184299Snwhitehorn	reg &= ~vPB4;
665184299Snwhitehorn	pmu_write_reg(sc, vBufB, reg);
666184299Snwhitehorn}
667184299Snwhitehorn
668184299Snwhitehornstatic void
669184299Snwhitehornpmu_ack_on(struct pmu_softc *sc)
670184299Snwhitehorn{
671184299Snwhitehorn	uint8_t reg;
672184299Snwhitehorn
673184299Snwhitehorn	reg = pmu_read_reg(sc, vBufB);
674184299Snwhitehorn	reg |= vPB4;
675184299Snwhitehorn	pmu_write_reg(sc, vBufB, reg);
676184299Snwhitehorn}
677184299Snwhitehorn
678184299Snwhitehornstatic void
679184299Snwhitehornpmu_intr(void *arg)
680184299Snwhitehorn{
681184299Snwhitehorn	device_t        dev;
682184299Snwhitehorn	struct pmu_softc *sc;
683184299Snwhitehorn
684184299Snwhitehorn	unsigned int len;
685184299Snwhitehorn	uint8_t resp[16];
686184299Snwhitehorn	uint8_t junk[16];
687184299Snwhitehorn
688184299Snwhitehorn        dev = (device_t)arg;
689184299Snwhitehorn	sc = device_get_softc(dev);
690184299Snwhitehorn
691184299Snwhitehorn	mtx_lock(&sc->sc_mutex);
692184299Snwhitehorn
693184299Snwhitehorn	pmu_write_reg(sc, vIFR, 0x90);	/* Clear 'em */
694184299Snwhitehorn	len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp);
695184299Snwhitehorn
696184299Snwhitehorn	mtx_unlock(&sc->sc_mutex);
697184299Snwhitehorn
698184299Snwhitehorn	if ((len < 1) || (resp[1] == 0)) {
699184299Snwhitehorn		return;
700184299Snwhitehorn	}
701184299Snwhitehorn
702184299Snwhitehorn	if (resp[1] & PMU_INT_ADB) {
703184299Snwhitehorn		/*
704184299Snwhitehorn		 * the PMU will turn off autopolling after each command that
705184299Snwhitehorn		 * it did not issue, so we assume any but TALK R0 is ours and
706184299Snwhitehorn		 * re-enable autopoll here whenever we receive an ACK for a
707184299Snwhitehorn		 * non TR0 command.
708184299Snwhitehorn		 */
709184299Snwhitehorn		mtx_lock(&sc->sc_mutex);
710184299Snwhitehorn
711184299Snwhitehorn		if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) {
712184299Snwhitehorn			if (sc->sc_autopoll) {
713184299Snwhitehorn				uint8_t cmd[] = {0, PMU_SET_POLL_MASK,
714184299Snwhitehorn				    (sc->sc_autopoll >> 8) & 0xff,
715184299Snwhitehorn				    sc->sc_autopoll & 0xff};
716184299Snwhitehorn
717184299Snwhitehorn				pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk);
718184299Snwhitehorn			}
719184299Snwhitehorn		}
720184299Snwhitehorn
721184299Snwhitehorn		mtx_unlock(&sc->sc_mutex);
722184299Snwhitehorn
723184299Snwhitehorn		adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2],
724184299Snwhitehorn			len - 3,&resp[3]);
725184299Snwhitehorn	}
726228270Sjhibbits	if (resp[1] & PMU_INT_ENVIRONMENT) {
727228277Sjhibbits		/* if the lid was just closed, notify devd. */
728228270Sjhibbits		if ((resp[2] & PMU_ENV_LID_CLOSED) && (!sc->lid_closed)) {
729228270Sjhibbits			sc->lid_closed = 1;
730228270Sjhibbits			if (devctl_process_running())
731228270Sjhibbits				devctl_notify("PMU", "lid", "close", NULL);
732228270Sjhibbits		}
733228270Sjhibbits		else if (!(resp[2] & PMU_ENV_LID_CLOSED) && (sc->lid_closed)) {
734228277Sjhibbits			/* if the lid was just opened, notify devd. */
735228270Sjhibbits			if (devctl_process_running())
736228270Sjhibbits				devctl_notify("PMU", "lid", "open", NULL);
737228270Sjhibbits			sc->lid_closed = 0;
738228270Sjhibbits		}
739228270Sjhibbits	}
740184299Snwhitehorn}
741184299Snwhitehorn
742184299Snwhitehornstatic u_int
743184299Snwhitehornpmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data,
744184299Snwhitehorn    u_char poll)
745184299Snwhitehorn{
746184299Snwhitehorn	struct pmu_softc *sc = device_get_softc(dev);
747184299Snwhitehorn	int i,replen;
748184299Snwhitehorn	uint8_t packet[16], resp[16];
749184299Snwhitehorn
750184299Snwhitehorn	/* construct an ADB command packet and send it */
751184299Snwhitehorn
752184299Snwhitehorn	packet[0] = command_byte;
753184299Snwhitehorn
754184299Snwhitehorn	packet[1] = 0;
755184299Snwhitehorn	packet[2] = len;
756184299Snwhitehorn	for (i = 0; i < len; i++)
757184299Snwhitehorn		packet[i + 3] = data[i];
758184299Snwhitehorn
759184299Snwhitehorn	mtx_lock(&sc->sc_mutex);
760184299Snwhitehorn	replen = pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp);
761184299Snwhitehorn	mtx_unlock(&sc->sc_mutex);
762184299Snwhitehorn
763184299Snwhitehorn	if (poll)
764184299Snwhitehorn		pmu_poll(dev);
765184299Snwhitehorn
766184299Snwhitehorn	return 0;
767184299Snwhitehorn}
768184299Snwhitehorn
769184299Snwhitehornstatic u_int
770184299Snwhitehornpmu_adb_autopoll(device_t dev, uint16_t mask)
771184299Snwhitehorn{
772184299Snwhitehorn	struct pmu_softc *sc = device_get_softc(dev);
773184299Snwhitehorn
774184299Snwhitehorn	/* magical incantation to re-enable autopolling */
775184299Snwhitehorn	uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff};
776184299Snwhitehorn	uint8_t resp[16];
777184299Snwhitehorn
778184299Snwhitehorn	mtx_lock(&sc->sc_mutex);
779184299Snwhitehorn
780184299Snwhitehorn	if (sc->sc_autopoll == mask) {
781184299Snwhitehorn		mtx_unlock(&sc->sc_mutex);
782184299Snwhitehorn		return 0;
783184299Snwhitehorn	}
784184299Snwhitehorn
785184299Snwhitehorn	sc->sc_autopoll = mask & 0xffff;
786184299Snwhitehorn
787184299Snwhitehorn	if (mask)
788184299Snwhitehorn		pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp);
789184299Snwhitehorn	else
790184299Snwhitehorn		pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp);
791184299Snwhitehorn
792184299Snwhitehorn	mtx_unlock(&sc->sc_mutex);
793184299Snwhitehorn
794184299Snwhitehorn	return 0;
795184299Snwhitehorn}
796185727Snwhitehorn
797185782Snwhitehornstatic void
798212054Snwhitehornpmu_shutdown(void *xsc, int howto)
799212054Snwhitehorn{
800212054Snwhitehorn	struct pmu_softc *sc = xsc;
801212054Snwhitehorn	uint8_t cmd[] = {'M', 'A', 'T', 'T'};
802212054Snwhitehorn
803212054Snwhitehorn	if (howto & RB_HALT)
804212054Snwhitehorn		pmu_send(sc, PMU_POWER_OFF, 4, cmd, 0, NULL);
805212054Snwhitehorn	else
806212054Snwhitehorn		pmu_send(sc, PMU_RESET_CPU, 0, NULL, 0, NULL);
807212054Snwhitehorn
808212054Snwhitehorn	for (;;);
809212054Snwhitehorn}
810212054Snwhitehorn
811212054Snwhitehornstatic void
812185782Snwhitehornpmu_set_sleepled(void *xsc, int onoff)
813185782Snwhitehorn{
814185782Snwhitehorn	struct pmu_softc *sc = xsc;
815185782Snwhitehorn	uint8_t cmd[] = {4, 0, 0};
816185782Snwhitehorn
817185782Snwhitehorn	cmd[2] = onoff;
818185782Snwhitehorn
819185782Snwhitehorn	mtx_lock(&sc->sc_mutex);
820185782Snwhitehorn	pmu_send(sc, PMU_SET_SLEEPLED, 3, cmd, 0, NULL);
821185782Snwhitehorn	mtx_unlock(&sc->sc_mutex);
822185782Snwhitehorn}
823185782Snwhitehorn
824185727Snwhitehornstatic int
825185727Snwhitehornpmu_server_mode(SYSCTL_HANDLER_ARGS)
826185727Snwhitehorn{
827185727Snwhitehorn	struct pmu_softc *sc = arg1;
828185727Snwhitehorn
829185727Snwhitehorn	u_int server_mode = 0;
830185727Snwhitehorn	uint8_t getcmd[] = {PMU_PWR_GET_POWERUP_EVENTS};
831185727Snwhitehorn	uint8_t setcmd[] = {0, 0, PMU_PWR_WAKEUP_AC_INSERT};
832185727Snwhitehorn	uint8_t resp[3];
833185727Snwhitehorn	int error, len;
834185727Snwhitehorn
835185727Snwhitehorn	mtx_lock(&sc->sc_mutex);
836185727Snwhitehorn	len = pmu_send(sc, PMU_POWER_EVENTS, 1, getcmd, 3, resp);
837185727Snwhitehorn	mtx_unlock(&sc->sc_mutex);
838185727Snwhitehorn
839185727Snwhitehorn	if (len == 3)
840185727Snwhitehorn		server_mode = (resp[2] & PMU_PWR_WAKEUP_AC_INSERT) ? 1 : 0;
841185727Snwhitehorn
842185727Snwhitehorn	error = sysctl_handle_int(oidp, &server_mode, 0, req);
843185727Snwhitehorn
844185727Snwhitehorn	if (len != 3)
845185727Snwhitehorn		return (EINVAL);
846185727Snwhitehorn
847185727Snwhitehorn	if (error || !req->newptr)
848185727Snwhitehorn		return (error);
849185727Snwhitehorn
850185727Snwhitehorn	if (server_mode == 1)
851185727Snwhitehorn		setcmd[0] = PMU_PWR_SET_POWERUP_EVENTS;
852185727Snwhitehorn	else if (server_mode == 0)
853185727Snwhitehorn		setcmd[0] = PMU_PWR_CLR_POWERUP_EVENTS;
854185727Snwhitehorn	else
855185727Snwhitehorn		return (EINVAL);
856185727Snwhitehorn
857185727Snwhitehorn	setcmd[1] = resp[1];
858185727Snwhitehorn
859185727Snwhitehorn	mtx_lock(&sc->sc_mutex);
860185727Snwhitehorn	pmu_send(sc, PMU_POWER_EVENTS, 3, setcmd, 2, resp);
861185727Snwhitehorn	mtx_unlock(&sc->sc_mutex);
862185727Snwhitehorn
863185727Snwhitehorn	return (0);
864185727Snwhitehorn}
865185727Snwhitehorn
866185754Snwhitehornstatic int
867185754Snwhitehornpmu_query_battery(struct pmu_softc *sc, int batt, struct pmu_battstate *info)
868185754Snwhitehorn{
869185754Snwhitehorn	uint8_t reg;
870185754Snwhitehorn	uint8_t resp[16];
871185754Snwhitehorn	int len;
872185754Snwhitehorn
873185754Snwhitehorn	reg = batt + 1;
874185754Snwhitehorn
875185754Snwhitehorn	mtx_lock(&sc->sc_mutex);
876185754Snwhitehorn	len = pmu_send(sc, PMU_SMART_BATTERY_STATE, 1, &reg, 16, resp);
877185754Snwhitehorn	mtx_unlock(&sc->sc_mutex);
878185754Snwhitehorn
879185754Snwhitehorn	if (len < 3)
880185754Snwhitehorn		return (-1);
881185754Snwhitehorn
882185754Snwhitehorn	/* All PMU battery info replies share a common header:
883185754Snwhitehorn	 * Byte 1	Payload Format
884185754Snwhitehorn	 * Byte 2	Battery Flags
885185754Snwhitehorn	 */
886185754Snwhitehorn
887185754Snwhitehorn	info->state = resp[2];
888185754Snwhitehorn
889185754Snwhitehorn	switch (resp[1]) {
890185754Snwhitehorn	case 3:
891185754Snwhitehorn	case 4:
892185754Snwhitehorn		/*
893185754Snwhitehorn		 * Formats 3 and 4 appear to be the same:
894185754Snwhitehorn		 * Byte 3	Charge
895185754Snwhitehorn		 * Byte 4	Max Charge
896185754Snwhitehorn		 * Byte 5	Current
897185754Snwhitehorn		 * Byte 6	Voltage
898185754Snwhitehorn		 */
899185754Snwhitehorn
900185754Snwhitehorn		info->charge = resp[3];
901185754Snwhitehorn		info->maxcharge = resp[4];
902185754Snwhitehorn		/* Current can be positive or negative */
903185754Snwhitehorn		info->current = (int8_t)resp[5];
904185754Snwhitehorn		info->voltage = resp[6];
905185754Snwhitehorn		break;
906185754Snwhitehorn	case 5:
907185754Snwhitehorn		/*
908185754Snwhitehorn		 * Formats 5 is a wider version of formats 3 and 4
909185754Snwhitehorn		 * Byte 3-4	Charge
910185754Snwhitehorn		 * Byte 5-6	Max Charge
911185754Snwhitehorn		 * Byte 7-8	Current
912185754Snwhitehorn		 * Byte 9-10	Voltage
913185754Snwhitehorn		 */
914185754Snwhitehorn
915185754Snwhitehorn		info->charge = (resp[3] << 8) | resp[4];
916185754Snwhitehorn		info->maxcharge = (resp[5] << 8) | resp[6];
917185754Snwhitehorn		/* Current can be positive or negative */
918185754Snwhitehorn		info->current = (int16_t)((resp[7] << 8) | resp[8]);
919185754Snwhitehorn		info->voltage = (resp[9] << 8) | resp[10];
920185754Snwhitehorn		break;
921185754Snwhitehorn	default:
922185754Snwhitehorn		device_printf(sc->sc_dev, "Unknown battery info format (%d)!\n",
923185754Snwhitehorn		    resp[1]);
924185754Snwhitehorn		return (-1);
925185754Snwhitehorn	}
926185754Snwhitehorn
927185754Snwhitehorn	return (0);
928185754Snwhitehorn}
929185754Snwhitehorn
930273009Sjhibbitsstatic void
931273009Sjhibbitspmu_battery_notify(struct pmu_battstate *batt, struct pmu_battstate *old)
932273009Sjhibbits{
933273009Sjhibbits	char notify_buf[16];
934273009Sjhibbits	int acline;
935273009Sjhibbits
936273009Sjhibbits	acline = (batt->state & PMU_PWR_AC_PRESENT) ? 1 : 0;
937273009Sjhibbits	if (acline != (old->state & PMU_PWR_AC_PRESENT)) {
938273009Sjhibbits		snprintf(notify_buf, sizeof(notify_buf),
939273009Sjhibbits		    "notify=0x%02x", acline);
940273009Sjhibbits		devctl_notify("PMU", "POWER", "ACLINE", notify_buf);
941273009Sjhibbits	}
942273009Sjhibbits}
943273009Sjhibbits
944273009Sjhibbitsstatic void
945273009Sjhibbitspmu_battquery_proc()
946273009Sjhibbits{
947273009Sjhibbits	struct pmu_softc *sc;
948273009Sjhibbits	struct pmu_battstate batt;
949273009Sjhibbits	struct pmu_battstate cur_batt;
950273009Sjhibbits	int error;
951273009Sjhibbits
952273009Sjhibbits	sc = device_get_softc(pmu);
953273009Sjhibbits
954273009Sjhibbits	error = pmu_query_battery(sc, 0, &cur_batt);
955273009Sjhibbits	while (1) {
956273009Sjhibbits		error = pmu_query_battery(sc, 0, &batt);
957273009Sjhibbits		pmu_battery_notify(&batt, &cur_batt);
958273009Sjhibbits		cur_batt = batt;
959273009Sjhibbits		pause("pmu_batt", hz);
960273009Sjhibbits	}
961273009Sjhibbits}
962273009Sjhibbits
963185754Snwhitehornstatic int
964193159Snwhitehornpmu_acline_state(SYSCTL_HANDLER_ARGS)
965193159Snwhitehorn{
966193159Snwhitehorn	struct pmu_softc *sc;
967193159Snwhitehorn	struct pmu_battstate batt;
968193159Snwhitehorn	int error, result;
969193159Snwhitehorn
970193159Snwhitehorn	sc = arg1;
971193159Snwhitehorn
972193159Snwhitehorn	/* The PMU treats the AC line status as a property of the battery */
973193159Snwhitehorn	error = pmu_query_battery(sc, 0, &batt);
974193159Snwhitehorn
975193159Snwhitehorn	if (error != 0)
976193159Snwhitehorn		return (error);
977193159Snwhitehorn
978193159Snwhitehorn	result = (batt.state & PMU_PWR_AC_PRESENT) ? 1 : 0;
979193159Snwhitehorn	error = sysctl_handle_int(oidp, &result, 0, req);
980193159Snwhitehorn
981193159Snwhitehorn	return (error);
982193159Snwhitehorn}
983193159Snwhitehorn
984193159Snwhitehornstatic int
985185754Snwhitehornpmu_battquery_sysctl(SYSCTL_HANDLER_ARGS)
986185754Snwhitehorn{
987185754Snwhitehorn	struct pmu_softc *sc;
988185754Snwhitehorn	struct pmu_battstate batt;
989185754Snwhitehorn	int error, result;
990185754Snwhitehorn
991185754Snwhitehorn	sc = arg1;
992185754Snwhitehorn
993185754Snwhitehorn	error = pmu_query_battery(sc, arg2 & 0x00ff, &batt);
994185754Snwhitehorn
995185754Snwhitehorn	if (error != 0)
996185754Snwhitehorn		return (error);
997185754Snwhitehorn
998185754Snwhitehorn	switch (arg2 & 0xff00) {
999185754Snwhitehorn	case PMU_BATSYSCTL_PRESENT:
1000185754Snwhitehorn		result = (batt.state & PMU_PWR_BATT_PRESENT) ? 1 : 0;
1001185754Snwhitehorn		break;
1002185754Snwhitehorn	case PMU_BATSYSCTL_CHARGING:
1003185754Snwhitehorn		result = (batt.state & PMU_PWR_BATT_CHARGING) ? 1 : 0;
1004185754Snwhitehorn		break;
1005185754Snwhitehorn	case PMU_BATSYSCTL_CHARGE:
1006185754Snwhitehorn		result = batt.charge;
1007185754Snwhitehorn		break;
1008185754Snwhitehorn	case PMU_BATSYSCTL_MAXCHARGE:
1009185754Snwhitehorn		result = batt.maxcharge;
1010185754Snwhitehorn		break;
1011185754Snwhitehorn	case PMU_BATSYSCTL_CURRENT:
1012185754Snwhitehorn		result = batt.current;
1013185754Snwhitehorn		break;
1014185754Snwhitehorn	case PMU_BATSYSCTL_VOLTAGE:
1015185754Snwhitehorn		result = batt.voltage;
1016185754Snwhitehorn		break;
1017185754Snwhitehorn	case PMU_BATSYSCTL_TIME:
1018185754Snwhitehorn		/* Time remaining until full charge/discharge, in minutes */
1019185754Snwhitehorn
1020185754Snwhitehorn		if (batt.current >= 0)
1021185754Snwhitehorn			result = (batt.maxcharge - batt.charge) /* mAh */ * 60
1022185754Snwhitehorn			    / batt.current /* mA */;
1023185754Snwhitehorn		else
1024185754Snwhitehorn			result = (batt.charge /* mAh */ * 60)
1025185754Snwhitehorn			    / (-batt.current /* mA */);
1026185754Snwhitehorn		break;
1027185754Snwhitehorn	case PMU_BATSYSCTL_LIFE:
1028185754Snwhitehorn		/* Battery charge fraction, in percent */
1029185754Snwhitehorn		result = (batt.charge * 100) / batt.maxcharge;
1030185754Snwhitehorn		break;
1031185754Snwhitehorn	default:
1032185754Snwhitehorn		/* This should never happen */
1033185754Snwhitehorn		result = -1;
1034185754Snwhitehorn	};
1035185754Snwhitehorn
1036185754Snwhitehorn	error = sysctl_handle_int(oidp, &result, 0, req);
1037185754Snwhitehorn
1038185754Snwhitehorn	return (error);
1039185754Snwhitehorn}
1040185754Snwhitehorn
1041205506Snwhitehorn#define DIFF19041970	2082844800
1042205506Snwhitehorn
1043205506Snwhitehornstatic int
1044205506Snwhitehornpmu_gettime(device_t dev, struct timespec *ts)
1045205506Snwhitehorn{
1046205506Snwhitehorn	struct pmu_softc *sc = device_get_softc(dev);
1047205506Snwhitehorn	uint8_t resp[16];
1048205506Snwhitehorn	uint32_t sec;
1049205506Snwhitehorn
1050205506Snwhitehorn	mtx_lock(&sc->sc_mutex);
1051205506Snwhitehorn	pmu_send(sc, PMU_READ_RTC, 0, NULL, 16, resp);
1052205506Snwhitehorn	mtx_unlock(&sc->sc_mutex);
1053205506Snwhitehorn
1054205506Snwhitehorn	memcpy(&sec, &resp[1], 4);
1055205506Snwhitehorn	ts->tv_sec = sec - DIFF19041970;
1056205506Snwhitehorn	ts->tv_nsec = 0;
1057205506Snwhitehorn
1058205506Snwhitehorn	return (0);
1059205506Snwhitehorn}
1060205506Snwhitehorn
1061205506Snwhitehornstatic int
1062205506Snwhitehornpmu_settime(device_t dev, struct timespec *ts)
1063205506Snwhitehorn{
1064205506Snwhitehorn	struct pmu_softc *sc = device_get_softc(dev);
1065205506Snwhitehorn	uint32_t sec;
1066205506Snwhitehorn
1067205506Snwhitehorn	sec = ts->tv_sec + DIFF19041970;
1068205506Snwhitehorn
1069205506Snwhitehorn	mtx_lock(&sc->sc_mutex);
1070205506Snwhitehorn	pmu_send(sc, PMU_SET_RTC, sizeof(sec), (uint8_t *)&sec, 0, NULL);
1071205506Snwhitehorn	mtx_unlock(&sc->sc_mutex);
1072205506Snwhitehorn
1073205506Snwhitehorn	return (0);
1074205506Snwhitehorn}
1075205506Snwhitehorn
1076259284Sjhibbitsint
1077259284Sjhibbitspmu_set_speed(int low_speed)
1078259284Sjhibbits{
1079259284Sjhibbits	struct pmu_softc *sc;
1080259284Sjhibbits	uint8_t sleepcmd[] = {'W', 'O', 'O', 'F', 0};
1081259284Sjhibbits	uint8_t resp[16];
1082259284Sjhibbits
1083259284Sjhibbits	sc = device_get_softc(pmu);
1084259284Sjhibbits	pmu_write_reg(sc, vIER, 0x10);
1085259284Sjhibbits	spinlock_enter();
1086259284Sjhibbits	mtdec(0x7fffffff);
1087259284Sjhibbits	mb();
1088259284Sjhibbits	mtdec(0x7fffffff);
1089259284Sjhibbits
1090259284Sjhibbits	sleepcmd[4] = low_speed;
1091259284Sjhibbits	pmu_send(sc, PMU_CPU_SPEED, 5, sleepcmd, 16, resp);
1092259284Sjhibbits	unin_chip_sleep(NULL, 1);
1093261309Sjhibbits	platform_sleep();
1094259284Sjhibbits	unin_chip_wake(NULL);
1095259284Sjhibbits
1096259284Sjhibbits	mtdec(1);	/* Force a decrementer exception */
1097259284Sjhibbits	spinlock_exit();
1098259284Sjhibbits	pmu_write_reg(sc, vIER, 0x90);
1099259284Sjhibbits
1100259284Sjhibbits	return (0);
1101259284Sjhibbits}
1102