pmu.c revision 185754
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 * 3. The name of the author may not be used to endorse or promote products
15184299Snwhitehorn *    derived from this software without specific prior written permission.
16184299Snwhitehorn *
17184299Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18184299Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19184299Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20184299Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21184299Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22184299Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23184299Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24184299Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25184299Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26184299Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27184299Snwhitehorn * SUCH DAMAGE.
28184299Snwhitehorn *
29184299Snwhitehorn */
30184299Snwhitehorn
31184299Snwhitehorn#include <sys/cdefs.h>
32184299Snwhitehorn__FBSDID("$FreeBSD: head/sys/powerpc/powermac/pmu.c 185754 2008-12-08 02:37:08Z nwhitehorn $");
33184299Snwhitehorn
34184299Snwhitehorn#include <sys/param.h>
35184299Snwhitehorn#include <sys/systm.h>
36184299Snwhitehorn#include <sys/module.h>
37184299Snwhitehorn#include <sys/bus.h>
38184299Snwhitehorn#include <sys/conf.h>
39184299Snwhitehorn#include <sys/kernel.h>
40185727Snwhitehorn#include <sys/sysctl.h>
41184299Snwhitehorn
42184299Snwhitehorn#include <dev/ofw/ofw_bus.h>
43184299Snwhitehorn#include <dev/ofw/openfirm.h>
44184299Snwhitehorn
45184299Snwhitehorn#include <machine/bus.h>
46184299Snwhitehorn#include <machine/intr.h>
47184299Snwhitehorn#include <machine/intr_machdep.h>
48184299Snwhitehorn#include <machine/md_var.h>
49184299Snwhitehorn#include <machine/pio.h>
50184299Snwhitehorn#include <machine/resource.h>
51184299Snwhitehorn
52184299Snwhitehorn#include <vm/vm.h>
53184299Snwhitehorn#include <vm/pmap.h>
54184299Snwhitehorn
55184299Snwhitehorn#include <sys/rman.h>
56184299Snwhitehorn
57184299Snwhitehorn#include <dev/adb/adb.h>
58184299Snwhitehorn
59184299Snwhitehorn#include "pmuvar.h"
60184299Snwhitehorn#include "viareg.h"
61184299Snwhitehorn
62184299Snwhitehorn/*
63184299Snwhitehorn * MacIO interface
64184299Snwhitehorn */
65184299Snwhitehornstatic int	pmu_probe(device_t);
66184299Snwhitehornstatic int	pmu_attach(device_t);
67184299Snwhitehornstatic int	pmu_detach(device_t);
68184299Snwhitehorn
69185727Snwhitehornstatic u_int	pmu_adb_send(device_t dev, u_char command_byte, int len,
70185754Snwhitehorn		    u_char *data, u_char poll);
71185727Snwhitehornstatic u_int	pmu_adb_autopoll(device_t dev, uint16_t mask);
72185727Snwhitehornstatic void	pmu_poll(device_t dev);
73185754Snwhitehorn
74185727Snwhitehornstatic int	pmu_server_mode(SYSCTL_HANDLER_ARGS);
75185754Snwhitehornstatic int	pmu_query_battery(struct pmu_softc *sc, int batt,
76185754Snwhitehorn		    struct pmu_battstate *info);
77185754Snwhitehornstatic int	pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS);
78184299Snwhitehorn
79185754Snwhitehorn/*
80185754Snwhitehorn * List of battery-related sysctls we might ask for
81185754Snwhitehorn */
82185754Snwhitehorn
83185754Snwhitehornenum {
84185754Snwhitehorn	PMU_BATSYSCTL_PRESENT	= 1 << 8,
85185754Snwhitehorn	PMU_BATSYSCTL_CHARGING	= 2 << 8,
86185754Snwhitehorn	PMU_BATSYSCTL_CHARGE	= 3 << 8,
87185754Snwhitehorn	PMU_BATSYSCTL_MAXCHARGE = 4 << 8,
88185754Snwhitehorn	PMU_BATSYSCTL_CURRENT	= 5 << 8,
89185754Snwhitehorn	PMU_BATSYSCTL_VOLTAGE	= 6 << 8,
90185754Snwhitehorn	PMU_BATSYSCTL_TIME	= 7 << 8,
91185754Snwhitehorn	PMU_BATSYSCTL_LIFE	= 8 << 8
92185754Snwhitehorn};
93185754Snwhitehorn
94184299Snwhitehornstatic device_method_t  pmu_methods[] = {
95184299Snwhitehorn	/* Device interface */
96184299Snwhitehorn	DEVMETHOD(device_probe,		pmu_probe),
97184299Snwhitehorn	DEVMETHOD(device_attach,	pmu_attach),
98184299Snwhitehorn        DEVMETHOD(device_detach,        pmu_detach),
99184299Snwhitehorn        DEVMETHOD(device_shutdown,      bus_generic_shutdown),
100184299Snwhitehorn        DEVMETHOD(device_suspend,       bus_generic_suspend),
101184299Snwhitehorn        DEVMETHOD(device_resume,        bus_generic_resume),
102184299Snwhitehorn
103184299Snwhitehorn	/* bus interface, for ADB root */
104184299Snwhitehorn        DEVMETHOD(bus_print_child,      bus_generic_print_child),
105184299Snwhitehorn        DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
106184299Snwhitehorn
107184299Snwhitehorn	/* ADB bus interface */
108184299Snwhitehorn	DEVMETHOD(adb_hb_send_raw_packet,   pmu_adb_send),
109184299Snwhitehorn	DEVMETHOD(adb_hb_controller_poll,   pmu_poll),
110184299Snwhitehorn	DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll),
111184299Snwhitehorn
112184299Snwhitehorn	{ 0, 0 },
113184299Snwhitehorn};
114184299Snwhitehorn
115184299Snwhitehornstatic driver_t pmu_driver = {
116184299Snwhitehorn	"pmu",
117184299Snwhitehorn	pmu_methods,
118184299Snwhitehorn	sizeof(struct pmu_softc),
119184299Snwhitehorn};
120184299Snwhitehorn
121184299Snwhitehornstatic devclass_t pmu_devclass;
122184299Snwhitehorn
123184299SnwhitehornDRIVER_MODULE(pmu, macio, pmu_driver, pmu_devclass, 0, 0);
124184299SnwhitehornDRIVER_MODULE(adb, pmu, adb_driver, adb_devclass, 0, 0);
125184299Snwhitehorn
126184299Snwhitehornstatic int	pmuextint_probe(device_t);
127184299Snwhitehornstatic int	pmuextint_attach(device_t);
128184299Snwhitehorn
129184299Snwhitehornstatic device_method_t  pmuextint_methods[] = {
130184299Snwhitehorn	/* Device interface */
131184299Snwhitehorn	DEVMETHOD(device_probe,		pmuextint_probe),
132184299Snwhitehorn	DEVMETHOD(device_attach,	pmuextint_attach),
133184299Snwhitehorn
134184299Snwhitehorn	{0,0}
135184299Snwhitehorn};
136184299Snwhitehorn
137184299Snwhitehornstatic driver_t pmuextint_driver = {
138184299Snwhitehorn	"pmuextint",
139184299Snwhitehorn	pmuextint_methods,
140184299Snwhitehorn	0
141184299Snwhitehorn};
142184299Snwhitehorn
143184299Snwhitehornstatic devclass_t pmuextint_devclass;
144184299Snwhitehorn
145184299SnwhitehornDRIVER_MODULE(pmuextint, macgpio, pmuextint_driver, pmuextint_devclass, 0, 0);
146184299Snwhitehorn
147184299Snwhitehorn/* Make sure uhid is loaded, as it turns off some of the ADB emulation */
148184299SnwhitehornMODULE_DEPEND(pmu, usb, 1, 1, 1);
149184299Snwhitehorn
150184299Snwhitehornstatic void pmu_intr(void *arg);
151184299Snwhitehornstatic void pmu_in(struct pmu_softc *sc);
152184299Snwhitehornstatic void pmu_out(struct pmu_softc *sc);
153184299Snwhitehornstatic void pmu_ack_on(struct pmu_softc *sc);
154184299Snwhitehornstatic void pmu_ack_off(struct pmu_softc *sc);
155184299Snwhitehornstatic int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg,
156184299Snwhitehorn	int rlen, uint8_t *out_msg);
157184299Snwhitehornstatic uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset);
158184299Snwhitehornstatic void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value);
159184299Snwhitehornstatic int pmu_intr_state(struct pmu_softc *);
160184299Snwhitehorn
161184299Snwhitehorn/* these values shows that number of data returned after 'send' cmd is sent */
162184299Snwhitehornstatic signed char pm_send_cmd_type[] = {
163184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
164184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
165184299Snwhitehorn	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
166184299Snwhitehorn	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
167184299Snwhitehorn	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
168184299Snwhitehorn	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
169184299Snwhitehorn	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
170184299Snwhitehorn	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
171184299Snwhitehorn	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
172184299Snwhitehorn	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
173184299Snwhitehorn	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
174184299Snwhitehorn	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
175184299Snwhitehorn	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
176184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
177184299Snwhitehorn	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
178184299Snwhitehorn	0x00, 0x00,   -1,   -1,   -1,   -1, 0x04, 0x04,
179184299Snwhitehorn	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
180184299Snwhitehorn	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
181184299Snwhitehorn	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
182184299Snwhitehorn	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
183184299Snwhitehorn	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
184184299Snwhitehorn	0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
185184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
186184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
187184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
188184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
189184299Snwhitehorn	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
190184299Snwhitehorn	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
191184299Snwhitehorn	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
192184299Snwhitehorn	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
193184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
194184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
195184299Snwhitehorn};
196184299Snwhitehorn
197184299Snwhitehorn/* these values shows that number of data returned after 'receive' cmd is sent */
198184299Snwhitehornstatic signed char pm_receive_cmd_type[] = {
199184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
201184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
202184299Snwhitehorn	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
203184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
204184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
205184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
206184299Snwhitehorn	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
207184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
208184299Snwhitehorn	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
209184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
210184299Snwhitehorn	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
211184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
212184299Snwhitehorn	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
213184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
214184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1, 0x01, 0x01,
215184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
216184299Snwhitehorn	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
217184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218184299Snwhitehorn	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
219184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
220184299Snwhitehorn	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
221184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
222184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
223184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
225184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226184299Snwhitehorn	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
227184299Snwhitehorn	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
228184299Snwhitehorn	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
229184299Snwhitehorn	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230184299Snwhitehorn	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
231184299Snwhitehorn};
232184299Snwhitehorn
233184299Snwhitehorn/* We only have one of each device, so globals are safe */
234184299Snwhitehornstatic device_t pmu = NULL;
235184299Snwhitehornstatic device_t pmu_extint = NULL;
236184299Snwhitehorn
237184299Snwhitehornstatic int
238184299Snwhitehornpmuextint_probe(device_t dev)
239184299Snwhitehorn{
240184299Snwhitehorn	const char *type = ofw_bus_get_type(dev);
241184299Snwhitehorn
242184299Snwhitehorn	if (strcmp(type, "extint-gpio1") != 0)
243184299Snwhitehorn                return (ENXIO);
244184299Snwhitehorn
245184299Snwhitehorn	device_set_desc(dev, "Apple PMU99 External Interrupt");
246184299Snwhitehorn	return (0);
247184299Snwhitehorn}
248184299Snwhitehorn
249184299Snwhitehornstatic int
250184299Snwhitehornpmu_probe(device_t dev)
251184299Snwhitehorn{
252184299Snwhitehorn	const char *type = ofw_bus_get_type(dev);
253184299Snwhitehorn
254184299Snwhitehorn	if (strcmp(type, "via-pmu") != 0)
255184299Snwhitehorn                return (ENXIO);
256184299Snwhitehorn
257184299Snwhitehorn	device_set_desc(dev, "Apple PMU99 Controller");
258184299Snwhitehorn	return (0);
259184299Snwhitehorn}
260184299Snwhitehorn
261184299Snwhitehorn
262184299Snwhitehornstatic int
263184299Snwhitehornsetup_pmu_intr(device_t dev, device_t extint)
264184299Snwhitehorn{
265184299Snwhitehorn	struct pmu_softc *sc;
266184299Snwhitehorn	sc = device_get_softc(dev);
267184299Snwhitehorn
268184299Snwhitehorn	sc->sc_irqrid = 0;
269184299Snwhitehorn	sc->sc_irq = bus_alloc_resource_any(extint, SYS_RES_IRQ, &sc->sc_irqrid,
270184299Snwhitehorn           	RF_ACTIVE);
271184299Snwhitehorn        if (sc->sc_irq == NULL) {
272184299Snwhitehorn                device_printf(dev, "could not allocate interrupt\n");
273184299Snwhitehorn                return (ENXIO);
274184299Snwhitehorn        }
275184299Snwhitehorn
276184299Snwhitehorn	if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE
277184299Snwhitehorn	    | INTR_ENTROPY, NULL, pmu_intr, dev, &sc->sc_ih) != 0) {
278184299Snwhitehorn                device_printf(dev, "could not setup interrupt\n");
279184299Snwhitehorn                bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
280184299Snwhitehorn                    sc->sc_irq);
281184299Snwhitehorn                return (ENXIO);
282184299Snwhitehorn        }
283184299Snwhitehorn
284184299Snwhitehorn	return (0);
285184299Snwhitehorn}
286184299Snwhitehorn
287184299Snwhitehornstatic int
288184299Snwhitehornpmuextint_attach(device_t dev)
289184299Snwhitehorn{
290184299Snwhitehorn	pmu_extint = dev;
291184299Snwhitehorn	if (pmu)
292184299Snwhitehorn		return (setup_pmu_intr(pmu,dev));
293184299Snwhitehorn
294184299Snwhitehorn	return (0);
295184299Snwhitehorn}
296184299Snwhitehorn
297184299Snwhitehornstatic int
298184299Snwhitehornpmu_attach(device_t dev)
299184299Snwhitehorn{
300184299Snwhitehorn	struct pmu_softc *sc;
301184299Snwhitehorn
302185754Snwhitehorn	int i;
303184299Snwhitehorn	uint8_t reg;
304184299Snwhitehorn	uint8_t cmd[2] = {2, 0};
305184299Snwhitehorn	uint8_t resp[16];
306184299Snwhitehorn	phandle_t node,child;
307185727Snwhitehorn	struct sysctl_ctx_list *ctx;
308185727Snwhitehorn	struct sysctl_oid *tree;
309184299Snwhitehorn
310184299Snwhitehorn	sc = device_get_softc(dev);
311184299Snwhitehorn	sc->sc_dev = dev;
312184299Snwhitehorn
313184299Snwhitehorn	sc->sc_memrid = 0;
314184299Snwhitehorn	sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
315184299Snwhitehorn		          &sc->sc_memrid, RF_ACTIVE);
316184299Snwhitehorn
317184299Snwhitehorn	mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE);
318184299Snwhitehorn
319184299Snwhitehorn	if (sc->sc_memr == NULL) {
320184299Snwhitehorn		device_printf(dev, "Could not alloc mem resource!\n");
321184299Snwhitehorn		return (ENXIO);
322184299Snwhitehorn	}
323184299Snwhitehorn
324184299Snwhitehorn	/*
325184299Snwhitehorn	 * Our interrupt is attached to a GPIO pin. Depending on probe order,
326184299Snwhitehorn	 * we may not have found it yet. If we haven't, it will find us, and
327184299Snwhitehorn	 * attach our interrupt then.
328184299Snwhitehorn	 */
329184299Snwhitehorn	pmu = dev;
330184299Snwhitehorn	if (pmu_extint != NULL) {
331184299Snwhitehorn		if (setup_pmu_intr(dev,pmu_extint) != 0)
332184299Snwhitehorn			return (ENXIO);
333184299Snwhitehorn	}
334184299Snwhitehorn
335184299Snwhitehorn	sc->sc_autopoll = 0;
336184299Snwhitehorn
337184299Snwhitehorn	/* Init PMU */
338184299Snwhitehorn
339184299Snwhitehorn	reg = PMU_INT_TICK | PMU_INT_ADB | PMU_INT_PCEJECT | PMU_INT_SNDBRT;
340184299Snwhitehorn	reg |= PMU_INT_BATTERY;
341184299Snwhitehorn	reg |= PMU_INT_ENVIRONMENT;
342184299Snwhitehorn	pmu_send(sc, PMU_SET_IMASK, 1, &reg, 16, resp);
343184299Snwhitehorn
344184299Snwhitehorn	pmu_write_reg(sc, vIER, 0x90); /* make sure VIA interrupts are on */
345184299Snwhitehorn
346184299Snwhitehorn	pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp);
347184299Snwhitehorn	pmu_send(sc, PMU_GET_VERSION, 1, cmd, 16, resp);
348184299Snwhitehorn
349184299Snwhitehorn	/* Initialize child buses (ADB) */
350184299Snwhitehorn	node = ofw_bus_get_node(dev);
351184299Snwhitehorn
352184299Snwhitehorn	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
353184299Snwhitehorn		char name[32];
354184299Snwhitehorn
355184299Snwhitehorn		memset(name, 0, sizeof(name));
356184299Snwhitehorn		OF_getprop(child, "name", name, sizeof(name));
357184299Snwhitehorn
358184299Snwhitehorn		if (bootverbose)
359184299Snwhitehorn			device_printf(dev, "PMU child <%s>\n",name);
360184299Snwhitehorn
361184299Snwhitehorn		if (strncmp(name, "adb", 4) == 0) {
362184299Snwhitehorn			sc->adb_bus = device_add_child(dev,"adb",-1);
363184299Snwhitehorn		}
364185754Snwhitehorn
365185754Snwhitehorn		if (strncmp(name, "power-mgt", 9) == 0) {
366185754Snwhitehorn			uint32_t prim_info[9];
367185754Snwhitehorn
368185754Snwhitehorn			if (OF_getprop(child, "prim-info", prim_info,
369185754Snwhitehorn			    sizeof(prim_info)) >= 7)
370185754Snwhitehorn				sc->sc_batteries = (prim_info[6] >> 16) & 0xff;
371185754Snwhitehorn
372185754Snwhitehorn			if (bootverbose && sc->sc_batteries > 0)
373185754Snwhitehorn				device_printf(dev, "%d batteries detected\n",
374185754Snwhitehorn				    sc->sc_batteries);
375185754Snwhitehorn		}
376184299Snwhitehorn	}
377184299Snwhitehorn
378185727Snwhitehorn	/*
379185727Snwhitehorn	 * Set up sysctls
380185727Snwhitehorn	 */
381185727Snwhitehorn
382185727Snwhitehorn	ctx = device_get_sysctl_ctx(dev);
383185727Snwhitehorn	tree = device_get_sysctl_tree(dev);
384185727Snwhitehorn
385185727Snwhitehorn	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
386185754Snwhitehorn	    "server_mode", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
387185754Snwhitehorn	    pmu_server_mode, "I", "Enable reboot after power failure");
388185727Snwhitehorn
389185754Snwhitehorn	if (sc->sc_batteries > 0) {
390185754Snwhitehorn		struct sysctl_oid *oid, *battroot;
391185754Snwhitehorn		char battnum[2];
392185754Snwhitehorn
393185754Snwhitehorn		battroot = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
394185754Snwhitehorn		    "batteries", CTLFLAG_RD, 0, "Battery Information");
395185754Snwhitehorn
396185754Snwhitehorn		for (i = 0; i < sc->sc_batteries; i++) {
397185754Snwhitehorn			battnum[0] = i + '0';
398185754Snwhitehorn			battnum[1] = '\0';
399185754Snwhitehorn
400185754Snwhitehorn			oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(battroot),
401185754Snwhitehorn			    OID_AUTO, battnum, CTLFLAG_RD, 0,
402185754Snwhitehorn			    "Battery Information");
403185754Snwhitehorn
404185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
405185754Snwhitehorn			    "present", CTLTYPE_INT | CTLFLAG_RD, sc,
406185754Snwhitehorn			    PMU_BATSYSCTL_PRESENT | i, pmu_battquery_sysctl,
407185754Snwhitehorn			    "I", "Battery present");
408185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
409185754Snwhitehorn			    "charging", CTLTYPE_INT | CTLFLAG_RD, sc,
410185754Snwhitehorn			    PMU_BATSYSCTL_CHARGING | i, pmu_battquery_sysctl,
411185754Snwhitehorn			    "I", "Battery charging");
412185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
413185754Snwhitehorn			    "charge", CTLTYPE_INT | CTLFLAG_RD, sc,
414185754Snwhitehorn			    PMU_BATSYSCTL_CHARGE | i, pmu_battquery_sysctl,
415185754Snwhitehorn			    "I", "Battery charge (mAh)");
416185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
417185754Snwhitehorn			    "maxcharge", CTLTYPE_INT | CTLFLAG_RD, sc,
418185754Snwhitehorn			    PMU_BATSYSCTL_MAXCHARGE | i, pmu_battquery_sysctl,
419185754Snwhitehorn			    "I", "Maximum battery capacity (mAh)");
420185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
421185754Snwhitehorn			    "rate", CTLTYPE_INT | CTLFLAG_RD, sc,
422185754Snwhitehorn			    PMU_BATSYSCTL_CURRENT | i, pmu_battquery_sysctl,
423185754Snwhitehorn			    "I", "Battery discharge rate (mA)");
424185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
425185754Snwhitehorn			    "voltage", CTLTYPE_INT | CTLFLAG_RD, sc,
426185754Snwhitehorn			    PMU_BATSYSCTL_VOLTAGE | i, pmu_battquery_sysctl,
427185754Snwhitehorn			    "I", "Battery voltage (mV)");
428185754Snwhitehorn
429185754Snwhitehorn			/* Knobs for mental compatibility with ACPI */
430185754Snwhitehorn
431185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
432185754Snwhitehorn			    "time", CTLTYPE_INT | CTLFLAG_RD, sc,
433185754Snwhitehorn			    PMU_BATSYSCTL_TIME | i, pmu_battquery_sysctl,
434185754Snwhitehorn			    "I", "Time Remaining (minutes)");
435185754Snwhitehorn			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
436185754Snwhitehorn			    "life", CTLTYPE_INT | CTLFLAG_RD, sc,
437185754Snwhitehorn			    PMU_BATSYSCTL_LIFE | i, pmu_battquery_sysctl,
438185754Snwhitehorn			    "I", "Capacity remaining (percent)");
439185754Snwhitehorn		}
440185754Snwhitehorn	}
441185754Snwhitehorn
442184299Snwhitehorn	return (bus_generic_attach(dev));
443184299Snwhitehorn}
444184299Snwhitehorn
445184299Snwhitehornstatic int
446184299Snwhitehornpmu_detach(device_t dev)
447184299Snwhitehorn{
448184299Snwhitehorn	struct pmu_softc *sc;
449184299Snwhitehorn
450184299Snwhitehorn	sc = device_get_softc(dev);
451184299Snwhitehorn
452184299Snwhitehorn	bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
453184299Snwhitehorn	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq);
454184299Snwhitehorn	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr);
455184299Snwhitehorn	mtx_destroy(&sc->sc_mutex);
456184299Snwhitehorn
457184299Snwhitehorn	return (bus_generic_detach(dev));
458184299Snwhitehorn}
459184299Snwhitehorn
460184299Snwhitehornstatic uint8_t
461184299Snwhitehornpmu_read_reg(struct pmu_softc *sc, u_int offset)
462184299Snwhitehorn{
463184299Snwhitehorn	return (bus_read_1(sc->sc_memr, offset));
464184299Snwhitehorn}
465184299Snwhitehorn
466184299Snwhitehornstatic void
467184299Snwhitehornpmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value)
468184299Snwhitehorn{
469184299Snwhitehorn	bus_write_1(sc->sc_memr, offset, value);
470184299Snwhitehorn}
471184299Snwhitehorn
472184299Snwhitehornstatic int
473184299Snwhitehornpmu_send_byte(struct pmu_softc *sc, uint8_t data)
474184299Snwhitehorn{
475184299Snwhitehorn
476184299Snwhitehorn	pmu_out(sc);
477184299Snwhitehorn	pmu_write_reg(sc, vSR, data);
478184299Snwhitehorn	pmu_ack_off(sc);
479184299Snwhitehorn	/* wait for intr to come up */
480184299Snwhitehorn	/* XXX should add a timeout and bail if it expires */
481184299Snwhitehorn	do {} while (pmu_intr_state(sc) == 0);
482184299Snwhitehorn	pmu_ack_on(sc);
483184299Snwhitehorn	do {} while (pmu_intr_state(sc));
484184299Snwhitehorn	pmu_ack_on(sc);
485184299Snwhitehorn	return 0;
486184299Snwhitehorn}
487184299Snwhitehorn
488184299Snwhitehornstatic inline int
489184299Snwhitehornpmu_read_byte(struct pmu_softc *sc, uint8_t *data)
490184299Snwhitehorn{
491184299Snwhitehorn	volatile uint8_t scratch;
492184299Snwhitehorn	pmu_in(sc);
493184299Snwhitehorn	scratch = pmu_read_reg(sc, vSR);
494184299Snwhitehorn	pmu_ack_off(sc);
495184299Snwhitehorn	/* wait for intr to come up */
496184299Snwhitehorn	do {} while (pmu_intr_state(sc) == 0);
497184299Snwhitehorn	pmu_ack_on(sc);
498184299Snwhitehorn	do {} while (pmu_intr_state(sc));
499184299Snwhitehorn	*data = pmu_read_reg(sc, vSR);
500184299Snwhitehorn	return 0;
501184299Snwhitehorn}
502184299Snwhitehorn
503184299Snwhitehornstatic int
504184299Snwhitehornpmu_intr_state(struct pmu_softc *sc)
505184299Snwhitehorn{
506184299Snwhitehorn	return ((pmu_read_reg(sc, vBufB) & vPB3) == 0);
507184299Snwhitehorn}
508184299Snwhitehorn
509184299Snwhitehornstatic int
510184299Snwhitehornpmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen,
511184299Snwhitehorn    uint8_t *out_msg)
512184299Snwhitehorn{
513184299Snwhitehorn	struct pmu_softc *sc = cookie;
514184299Snwhitehorn	int i, rcv_len = -1;
515184299Snwhitehorn	uint8_t out_len, intreg;
516184299Snwhitehorn
517184299Snwhitehorn	intreg = pmu_read_reg(sc, vIER);
518184299Snwhitehorn	intreg &= 0x10;
519184299Snwhitehorn	pmu_write_reg(sc, vIER, intreg);
520184299Snwhitehorn
521184299Snwhitehorn	/* wait idle */
522184299Snwhitehorn	do {} while (pmu_intr_state(sc));
523184299Snwhitehorn
524184299Snwhitehorn	/* send command */
525184299Snwhitehorn	pmu_send_byte(sc, cmd);
526184299Snwhitehorn
527184299Snwhitehorn	/* send length if necessary */
528184299Snwhitehorn	if (pm_send_cmd_type[cmd] < 0) {
529184299Snwhitehorn		pmu_send_byte(sc, length);
530184299Snwhitehorn	}
531184299Snwhitehorn
532184299Snwhitehorn	for (i = 0; i < length; i++) {
533184299Snwhitehorn		pmu_send_byte(sc, in_msg[i]);
534184299Snwhitehorn	}
535184299Snwhitehorn
536184299Snwhitehorn	/* see if there's data to read */
537184299Snwhitehorn	rcv_len = pm_receive_cmd_type[cmd];
538184299Snwhitehorn	if (rcv_len == 0)
539184299Snwhitehorn		goto done;
540184299Snwhitehorn
541184299Snwhitehorn	/* read command */
542184299Snwhitehorn	if (rcv_len == 1) {
543184299Snwhitehorn		pmu_read_byte(sc, out_msg);
544184299Snwhitehorn		goto done;
545184299Snwhitehorn	} else
546184299Snwhitehorn		out_msg[0] = cmd;
547184299Snwhitehorn	if (rcv_len < 0) {
548184299Snwhitehorn		pmu_read_byte(sc, &out_len);
549184299Snwhitehorn		rcv_len = out_len + 1;
550184299Snwhitehorn	}
551184299Snwhitehorn	for (i = 1; i < min(rcv_len, rlen); i++)
552184299Snwhitehorn		pmu_read_byte(sc, &out_msg[i]);
553184299Snwhitehorn
554184299Snwhitehorndone:
555184299Snwhitehorn	pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90);
556184299Snwhitehorn
557184299Snwhitehorn	return rcv_len;
558184299Snwhitehorn}
559184299Snwhitehorn
560184299Snwhitehorn
561184299Snwhitehornstatic void
562184299Snwhitehornpmu_poll(device_t dev)
563184299Snwhitehorn{
564184299Snwhitehorn	pmu_intr(dev);
565184299Snwhitehorn}
566184299Snwhitehorn
567184299Snwhitehornstatic void
568184299Snwhitehornpmu_in(struct pmu_softc *sc)
569184299Snwhitehorn{
570184299Snwhitehorn	uint8_t reg;
571184299Snwhitehorn
572184299Snwhitehorn	reg = pmu_read_reg(sc, vACR);
573184299Snwhitehorn	reg &= ~vSR_OUT;
574184299Snwhitehorn	reg |= 0x0c;
575184299Snwhitehorn	pmu_write_reg(sc, vACR, reg);
576184299Snwhitehorn}
577184299Snwhitehorn
578184299Snwhitehornstatic void
579184299Snwhitehornpmu_out(struct pmu_softc *sc)
580184299Snwhitehorn{
581184299Snwhitehorn	uint8_t reg;
582184299Snwhitehorn
583184299Snwhitehorn	reg = pmu_read_reg(sc, vACR);
584184299Snwhitehorn	reg |= vSR_OUT;
585184299Snwhitehorn	reg |= 0x0c;
586184299Snwhitehorn	pmu_write_reg(sc, vACR, reg);
587184299Snwhitehorn}
588184299Snwhitehorn
589184299Snwhitehornstatic void
590184299Snwhitehornpmu_ack_off(struct pmu_softc *sc)
591184299Snwhitehorn{
592184299Snwhitehorn	uint8_t reg;
593184299Snwhitehorn
594184299Snwhitehorn	reg = pmu_read_reg(sc, vBufB);
595184299Snwhitehorn	reg &= ~vPB4;
596184299Snwhitehorn	pmu_write_reg(sc, vBufB, reg);
597184299Snwhitehorn}
598184299Snwhitehorn
599184299Snwhitehornstatic void
600184299Snwhitehornpmu_ack_on(struct pmu_softc *sc)
601184299Snwhitehorn{
602184299Snwhitehorn	uint8_t reg;
603184299Snwhitehorn
604184299Snwhitehorn	reg = pmu_read_reg(sc, vBufB);
605184299Snwhitehorn	reg |= vPB4;
606184299Snwhitehorn	pmu_write_reg(sc, vBufB, reg);
607184299Snwhitehorn}
608184299Snwhitehorn
609184299Snwhitehornstatic void
610184299Snwhitehornpmu_intr(void *arg)
611184299Snwhitehorn{
612184299Snwhitehorn	device_t        dev;
613184299Snwhitehorn	struct pmu_softc *sc;
614184299Snwhitehorn
615184299Snwhitehorn	unsigned int len;
616184299Snwhitehorn	uint8_t resp[16];
617184299Snwhitehorn	uint8_t junk[16];
618184299Snwhitehorn
619184299Snwhitehorn        dev = (device_t)arg;
620184299Snwhitehorn	sc = device_get_softc(dev);
621184299Snwhitehorn
622184299Snwhitehorn	mtx_lock(&sc->sc_mutex);
623184299Snwhitehorn
624184299Snwhitehorn	pmu_write_reg(sc, vIFR, 0x90);	/* Clear 'em */
625184299Snwhitehorn	len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp);
626184299Snwhitehorn
627184299Snwhitehorn	mtx_unlock(&sc->sc_mutex);
628184299Snwhitehorn
629184299Snwhitehorn	if ((len < 1) || (resp[1] == 0)) {
630184299Snwhitehorn		return;
631184299Snwhitehorn	}
632184299Snwhitehorn
633184299Snwhitehorn	if (resp[1] & PMU_INT_ADB) {
634184299Snwhitehorn		/*
635184299Snwhitehorn		 * the PMU will turn off autopolling after each command that
636184299Snwhitehorn		 * it did not issue, so we assume any but TALK R0 is ours and
637184299Snwhitehorn		 * re-enable autopoll here whenever we receive an ACK for a
638184299Snwhitehorn		 * non TR0 command.
639184299Snwhitehorn		 */
640184299Snwhitehorn		mtx_lock(&sc->sc_mutex);
641184299Snwhitehorn
642184299Snwhitehorn		if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) {
643184299Snwhitehorn			if (sc->sc_autopoll) {
644184299Snwhitehorn				uint8_t cmd[] = {0, PMU_SET_POLL_MASK,
645184299Snwhitehorn				    (sc->sc_autopoll >> 8) & 0xff,
646184299Snwhitehorn				    sc->sc_autopoll & 0xff};
647184299Snwhitehorn
648184299Snwhitehorn				pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk);
649184299Snwhitehorn			}
650184299Snwhitehorn		}
651184299Snwhitehorn
652184299Snwhitehorn		mtx_unlock(&sc->sc_mutex);
653184299Snwhitehorn
654184299Snwhitehorn		adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2],
655184299Snwhitehorn			len - 3,&resp[3]);
656184299Snwhitehorn	}
657184299Snwhitehorn}
658184299Snwhitehorn
659184299Snwhitehornstatic u_int
660184299Snwhitehornpmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data,
661184299Snwhitehorn    u_char poll)
662184299Snwhitehorn{
663184299Snwhitehorn	struct pmu_softc *sc = device_get_softc(dev);
664184299Snwhitehorn	int i,replen;
665184299Snwhitehorn	uint8_t packet[16], resp[16];
666184299Snwhitehorn
667184299Snwhitehorn	/* construct an ADB command packet and send it */
668184299Snwhitehorn
669184299Snwhitehorn	packet[0] = command_byte;
670184299Snwhitehorn
671184299Snwhitehorn	packet[1] = 0;
672184299Snwhitehorn	packet[2] = len;
673184299Snwhitehorn	for (i = 0; i < len; i++)
674184299Snwhitehorn		packet[i + 3] = data[i];
675184299Snwhitehorn
676184299Snwhitehorn	mtx_lock(&sc->sc_mutex);
677184299Snwhitehorn	replen = pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp);
678184299Snwhitehorn	mtx_unlock(&sc->sc_mutex);
679184299Snwhitehorn
680184299Snwhitehorn	if (poll)
681184299Snwhitehorn		pmu_poll(dev);
682184299Snwhitehorn
683184299Snwhitehorn	return 0;
684184299Snwhitehorn}
685184299Snwhitehorn
686184299Snwhitehornstatic u_int
687184299Snwhitehornpmu_adb_autopoll(device_t dev, uint16_t mask)
688184299Snwhitehorn{
689184299Snwhitehorn	struct pmu_softc *sc = device_get_softc(dev);
690184299Snwhitehorn
691184299Snwhitehorn	/* magical incantation to re-enable autopolling */
692184299Snwhitehorn	uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff};
693184299Snwhitehorn	uint8_t resp[16];
694184299Snwhitehorn
695184299Snwhitehorn	mtx_lock(&sc->sc_mutex);
696184299Snwhitehorn
697184299Snwhitehorn	if (sc->sc_autopoll == mask) {
698184299Snwhitehorn		mtx_unlock(&sc->sc_mutex);
699184299Snwhitehorn		return 0;
700184299Snwhitehorn	}
701184299Snwhitehorn
702184299Snwhitehorn	sc->sc_autopoll = mask & 0xffff;
703184299Snwhitehorn
704184299Snwhitehorn	if (mask)
705184299Snwhitehorn		pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp);
706184299Snwhitehorn	else
707184299Snwhitehorn		pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp);
708184299Snwhitehorn
709184299Snwhitehorn	mtx_unlock(&sc->sc_mutex);
710184299Snwhitehorn
711184299Snwhitehorn	return 0;
712184299Snwhitehorn}
713185727Snwhitehorn
714185727Snwhitehornstatic int
715185727Snwhitehornpmu_server_mode(SYSCTL_HANDLER_ARGS)
716185727Snwhitehorn{
717185727Snwhitehorn	struct pmu_softc *sc = arg1;
718185727Snwhitehorn
719185727Snwhitehorn	u_int server_mode = 0;
720185727Snwhitehorn	uint8_t getcmd[] = {PMU_PWR_GET_POWERUP_EVENTS};
721185727Snwhitehorn	uint8_t setcmd[] = {0, 0, PMU_PWR_WAKEUP_AC_INSERT};
722185727Snwhitehorn	uint8_t resp[3];
723185727Snwhitehorn	int error, len;
724185727Snwhitehorn
725185727Snwhitehorn	mtx_lock(&sc->sc_mutex);
726185727Snwhitehorn	len = pmu_send(sc, PMU_POWER_EVENTS, 1, getcmd, 3, resp);
727185727Snwhitehorn	mtx_unlock(&sc->sc_mutex);
728185727Snwhitehorn
729185727Snwhitehorn	if (len == 3)
730185727Snwhitehorn		server_mode = (resp[2] & PMU_PWR_WAKEUP_AC_INSERT) ? 1 : 0;
731185727Snwhitehorn
732185727Snwhitehorn	error = sysctl_handle_int(oidp, &server_mode, 0, req);
733185727Snwhitehorn
734185727Snwhitehorn	if (len != 3)
735185727Snwhitehorn		return (EINVAL);
736185727Snwhitehorn
737185727Snwhitehorn	if (error || !req->newptr)
738185727Snwhitehorn		return (error);
739185727Snwhitehorn
740185727Snwhitehorn	if (server_mode == 1)
741185727Snwhitehorn		setcmd[0] = PMU_PWR_SET_POWERUP_EVENTS;
742185727Snwhitehorn	else if (server_mode == 0)
743185727Snwhitehorn		setcmd[0] = PMU_PWR_CLR_POWERUP_EVENTS;
744185727Snwhitehorn	else
745185727Snwhitehorn		return (EINVAL);
746185727Snwhitehorn
747185727Snwhitehorn	setcmd[1] = resp[1];
748185727Snwhitehorn
749185727Snwhitehorn	mtx_lock(&sc->sc_mutex);
750185727Snwhitehorn	pmu_send(sc, PMU_POWER_EVENTS, 3, setcmd, 2, resp);
751185727Snwhitehorn	mtx_unlock(&sc->sc_mutex);
752185727Snwhitehorn
753185727Snwhitehorn	return (0);
754185727Snwhitehorn}
755185727Snwhitehorn
756185754Snwhitehornstatic int
757185754Snwhitehornpmu_query_battery(struct pmu_softc *sc, int batt, struct pmu_battstate *info)
758185754Snwhitehorn{
759185754Snwhitehorn	uint8_t reg;
760185754Snwhitehorn	uint8_t resp[16];
761185754Snwhitehorn	int len;
762185754Snwhitehorn
763185754Snwhitehorn	reg = batt + 1;
764185754Snwhitehorn
765185754Snwhitehorn	mtx_lock(&sc->sc_mutex);
766185754Snwhitehorn	len = pmu_send(sc, PMU_SMART_BATTERY_STATE, 1, &reg, 16, resp);
767185754Snwhitehorn	mtx_unlock(&sc->sc_mutex);
768185754Snwhitehorn
769185754Snwhitehorn	if (len < 3)
770185754Snwhitehorn		return (-1);
771185754Snwhitehorn
772185754Snwhitehorn	/* All PMU battery info replies share a common header:
773185754Snwhitehorn	 * Byte 1	Payload Format
774185754Snwhitehorn	 * Byte 2	Battery Flags
775185754Snwhitehorn	 */
776185754Snwhitehorn
777185754Snwhitehorn	info->state = resp[2];
778185754Snwhitehorn
779185754Snwhitehorn	switch (resp[1]) {
780185754Snwhitehorn	case 3:
781185754Snwhitehorn	case 4:
782185754Snwhitehorn		/*
783185754Snwhitehorn		 * Formats 3 and 4 appear to be the same:
784185754Snwhitehorn		 * Byte 3	Charge
785185754Snwhitehorn		 * Byte 4	Max Charge
786185754Snwhitehorn		 * Byte 5	Current
787185754Snwhitehorn		 * Byte 6	Voltage
788185754Snwhitehorn		 */
789185754Snwhitehorn
790185754Snwhitehorn		info->charge = resp[3];
791185754Snwhitehorn		info->maxcharge = resp[4];
792185754Snwhitehorn		/* Current can be positive or negative */
793185754Snwhitehorn		info->current = (int8_t)resp[5];
794185754Snwhitehorn		info->voltage = resp[6];
795185754Snwhitehorn		break;
796185754Snwhitehorn	case 5:
797185754Snwhitehorn		/*
798185754Snwhitehorn		 * Formats 5 is a wider version of formats 3 and 4
799185754Snwhitehorn		 * Byte 3-4	Charge
800185754Snwhitehorn		 * Byte 5-6	Max Charge
801185754Snwhitehorn		 * Byte 7-8	Current
802185754Snwhitehorn		 * Byte 9-10	Voltage
803185754Snwhitehorn		 */
804185754Snwhitehorn
805185754Snwhitehorn		info->charge = (resp[3] << 8) | resp[4];
806185754Snwhitehorn		info->maxcharge = (resp[5] << 8) | resp[6];
807185754Snwhitehorn		/* Current can be positive or negative */
808185754Snwhitehorn		info->current = (int16_t)((resp[7] << 8) | resp[8]);
809185754Snwhitehorn		info->voltage = (resp[9] << 8) | resp[10];
810185754Snwhitehorn		break;
811185754Snwhitehorn	default:
812185754Snwhitehorn		device_printf(sc->sc_dev, "Unknown battery info format (%d)!\n",
813185754Snwhitehorn		    resp[1]);
814185754Snwhitehorn		return (-1);
815185754Snwhitehorn	}
816185754Snwhitehorn
817185754Snwhitehorn	return (0);
818185754Snwhitehorn}
819185754Snwhitehorn
820185754Snwhitehornstatic int
821185754Snwhitehornpmu_battquery_sysctl(SYSCTL_HANDLER_ARGS)
822185754Snwhitehorn{
823185754Snwhitehorn	struct pmu_softc *sc;
824185754Snwhitehorn	struct pmu_battstate batt;
825185754Snwhitehorn	int error, result;
826185754Snwhitehorn
827185754Snwhitehorn	sc = arg1;
828185754Snwhitehorn
829185754Snwhitehorn	error = pmu_query_battery(sc, arg2 & 0x00ff, &batt);
830185754Snwhitehorn
831185754Snwhitehorn	if (error != 0)
832185754Snwhitehorn		return (error);
833185754Snwhitehorn
834185754Snwhitehorn	switch (arg2 & 0xff00) {
835185754Snwhitehorn	case PMU_BATSYSCTL_PRESENT:
836185754Snwhitehorn		result = (batt.state & PMU_PWR_BATT_PRESENT) ? 1 : 0;
837185754Snwhitehorn		break;
838185754Snwhitehorn	case PMU_BATSYSCTL_CHARGING:
839185754Snwhitehorn		result = (batt.state & PMU_PWR_BATT_CHARGING) ? 1 : 0;
840185754Snwhitehorn		break;
841185754Snwhitehorn	case PMU_BATSYSCTL_CHARGE:
842185754Snwhitehorn		result = batt.charge;
843185754Snwhitehorn		break;
844185754Snwhitehorn	case PMU_BATSYSCTL_MAXCHARGE:
845185754Snwhitehorn		result = batt.maxcharge;
846185754Snwhitehorn		break;
847185754Snwhitehorn	case PMU_BATSYSCTL_CURRENT:
848185754Snwhitehorn		result = batt.current;
849185754Snwhitehorn		break;
850185754Snwhitehorn	case PMU_BATSYSCTL_VOLTAGE:
851185754Snwhitehorn		result = batt.voltage;
852185754Snwhitehorn		break;
853185754Snwhitehorn	case PMU_BATSYSCTL_TIME:
854185754Snwhitehorn		/* Time remaining until full charge/discharge, in minutes */
855185754Snwhitehorn
856185754Snwhitehorn		if (batt.current >= 0)
857185754Snwhitehorn			result = (batt.maxcharge - batt.charge) /* mAh */ * 60
858185754Snwhitehorn			    / batt.current /* mA */;
859185754Snwhitehorn		else
860185754Snwhitehorn			result = (batt.charge /* mAh */ * 60)
861185754Snwhitehorn			    / (-batt.current /* mA */);
862185754Snwhitehorn		break;
863185754Snwhitehorn	case PMU_BATSYSCTL_LIFE:
864185754Snwhitehorn		/* Battery charge fraction, in percent */
865185754Snwhitehorn		result = (batt.charge * 100) / batt.maxcharge;
866185754Snwhitehorn		break;
867185754Snwhitehorn	default:
868185754Snwhitehorn		/* This should never happen */
869185754Snwhitehorn		result = -1;
870185754Snwhitehorn	};
871185754Snwhitehorn
872185754Snwhitehorn	error = sysctl_handle_int(oidp, &result, 0, req);
873185754Snwhitehorn
874185754Snwhitehorn	return (error);
875185754Snwhitehorn}
876185754Snwhitehorn
877