xref: /freebsd-src/sys/powerpc/powermac/pmu.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
1b4dbc599SNathan Whitehorn /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
371e3c308SPedro F. Giffuni  *
4b4dbc599SNathan Whitehorn  * Copyright (c) 2006 Michael Lorenz
5b4dbc599SNathan Whitehorn  * Copyright 2008 by Nathan Whitehorn
6b4dbc599SNathan Whitehorn  * All rights reserved.
7b4dbc599SNathan Whitehorn  *
8b4dbc599SNathan Whitehorn  * Redistribution and use in source and binary forms, with or without
9b4dbc599SNathan Whitehorn  * modification, are permitted provided that the following conditions
10b4dbc599SNathan Whitehorn  * are met:
11b4dbc599SNathan Whitehorn  * 1. Redistributions of source code must retain the above copyright
12b4dbc599SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer.
13b4dbc599SNathan Whitehorn  * 2. Redistributions in binary form must reproduce the above copyright
14b4dbc599SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer in the
15b4dbc599SNathan Whitehorn  *    documentation and/or other materials provided with the distribution.
16b4dbc599SNathan Whitehorn  *
17b4dbc599SNathan Whitehorn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18b4dbc599SNathan Whitehorn  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19b4dbc599SNathan Whitehorn  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20b4dbc599SNathan Whitehorn  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21b4dbc599SNathan Whitehorn  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22b4dbc599SNathan Whitehorn  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23b4dbc599SNathan Whitehorn  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24b4dbc599SNathan Whitehorn  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25b4dbc599SNathan Whitehorn  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26b4dbc599SNathan Whitehorn  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27b4dbc599SNathan Whitehorn  * SUCH DAMAGE.
28b4dbc599SNathan Whitehorn  *
29b4dbc599SNathan Whitehorn  */
30b4dbc599SNathan Whitehorn 
31b4dbc599SNathan Whitehorn #include <sys/param.h>
32b4dbc599SNathan Whitehorn #include <sys/systm.h>
33b4dbc599SNathan Whitehorn #include <sys/module.h>
34b4dbc599SNathan Whitehorn #include <sys/bus.h>
35b4dbc599SNathan Whitehorn #include <sys/conf.h>
36e2e050c8SConrad Meyer #include <sys/eventhandler.h>
37b4dbc599SNathan Whitehorn #include <sys/kernel.h>
38d49c6f02SJustin Hibbits #include <sys/kthread.h>
39e2e050c8SConrad Meyer #include <sys/lock.h>
40e2e050c8SConrad Meyer #include <sys/mutex.h>
413df9e037SNathan Whitehorn #include <sys/clock.h>
424702d987SJustin Hibbits #include <sys/proc.h>
43b2a237beSNathan Whitehorn #include <sys/reboot.h>
441af2e191SNathan Whitehorn #include <sys/sysctl.h>
45b4dbc599SNathan Whitehorn 
46b4dbc599SNathan Whitehorn #include <dev/ofw/ofw_bus.h>
47b4dbc599SNathan Whitehorn #include <dev/ofw/openfirm.h>
48a228f5cdSNathan Whitehorn #include <dev/led/led.h>
49b4dbc599SNathan Whitehorn 
504702d987SJustin Hibbits #include <machine/_inttypes.h>
51b4dbc599SNathan Whitehorn #include <machine/bus.h>
524702d987SJustin Hibbits #include <machine/cpu.h>
534702d987SJustin Hibbits #include <machine/hid.h>
54b4dbc599SNathan Whitehorn #include <machine/intr_machdep.h>
55b4dbc599SNathan Whitehorn #include <machine/md_var.h>
564702d987SJustin Hibbits #include <machine/pcb.h>
57b4dbc599SNathan Whitehorn #include <machine/pio.h>
58b4dbc599SNathan Whitehorn #include <machine/resource.h>
59b4dbc599SNathan Whitehorn 
60b4dbc599SNathan Whitehorn #include <vm/vm.h>
61b4dbc599SNathan Whitehorn #include <vm/pmap.h>
62b4dbc599SNathan Whitehorn 
63b4dbc599SNathan Whitehorn #include <sys/rman.h>
64b4dbc599SNathan Whitehorn 
65b4dbc599SNathan Whitehorn #include <dev/adb/adb.h>
66b4dbc599SNathan Whitehorn 
673df9e037SNathan Whitehorn #include "clock_if.h"
68b4dbc599SNathan Whitehorn #include "pmuvar.h"
69b4dbc599SNathan Whitehorn #include "viareg.h"
704702d987SJustin Hibbits #include "uninorthvar.h"	/* For unin_chip_sleep()/unin_chip_wake() */
714702d987SJustin Hibbits 
724702d987SJustin Hibbits #define PMU_DEFAULTS	PMU_INT_TICK | PMU_INT_ADB | \
734702d987SJustin Hibbits 	PMU_INT_PCEJECT | PMU_INT_SNDBRT | \
744702d987SJustin Hibbits 	PMU_INT_BATTERY | PMU_INT_ENVIRONMENT
75b4dbc599SNathan Whitehorn 
76b4dbc599SNathan Whitehorn /*
773df9e037SNathan Whitehorn  * Bus interface
78b4dbc599SNathan Whitehorn  */
79b4dbc599SNathan Whitehorn static int	pmu_probe(device_t);
80b4dbc599SNathan Whitehorn static int	pmu_attach(device_t);
81b4dbc599SNathan Whitehorn static int	pmu_detach(device_t);
82b4dbc599SNathan Whitehorn 
833df9e037SNathan Whitehorn /*
843df9e037SNathan Whitehorn  * Clock interface
853df9e037SNathan Whitehorn  */
863df9e037SNathan Whitehorn static int	pmu_gettime(device_t dev, struct timespec *ts);
873df9e037SNathan Whitehorn static int	pmu_settime(device_t dev, struct timespec *ts);
883df9e037SNathan Whitehorn 
893df9e037SNathan Whitehorn /*
903df9e037SNathan Whitehorn  * ADB Interface
913df9e037SNathan Whitehorn  */
923df9e037SNathan Whitehorn 
93b4dbc599SNathan Whitehorn static u_int	pmu_adb_send(device_t dev, u_char command_byte, int len,
94b4dbc599SNathan Whitehorn 		    u_char *data, u_char poll);
95b4dbc599SNathan Whitehorn static u_int	pmu_adb_autopoll(device_t dev, uint16_t mask);
9665f44679SAndriy Gapon static u_int	pmu_poll(device_t dev);
97eff47708SNathan Whitehorn 
98b2a237beSNathan Whitehorn /*
99b2a237beSNathan Whitehorn  * Power interface
100b2a237beSNathan Whitehorn  */
101b2a237beSNathan Whitehorn 
102b2a237beSNathan Whitehorn static void	pmu_shutdown(void *xsc, int howto);
103a228f5cdSNathan Whitehorn static void	pmu_set_sleepled(void *xsc, int onoff);
1041af2e191SNathan Whitehorn static int	pmu_server_mode(SYSCTL_HANDLER_ARGS);
1051165ddc2SNathan Whitehorn static int	pmu_acline_state(SYSCTL_HANDLER_ARGS);
106eff47708SNathan Whitehorn static int	pmu_query_battery(struct pmu_softc *sc, int batt,
107eff47708SNathan Whitehorn 		    struct pmu_battstate *info);
108eff47708SNathan Whitehorn static int	pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS);
1093cd55688SJustin Hibbits static int	pmu_battmon(SYSCTL_HANDLER_ARGS);
1103cd55688SJustin Hibbits static void	pmu_battquery_proc(void);
1113cd55688SJustin Hibbits static void	pmu_battery_notify(struct pmu_battstate *batt,
1123cd55688SJustin Hibbits 		    struct pmu_battstate *old);
113eff47708SNathan Whitehorn 
114eff47708SNathan Whitehorn /*
115eff47708SNathan Whitehorn  * List of battery-related sysctls we might ask for
116eff47708SNathan Whitehorn  */
117eff47708SNathan Whitehorn 
118eff47708SNathan Whitehorn enum {
119eff47708SNathan Whitehorn 	PMU_BATSYSCTL_PRESENT	= 1 << 8,
120eff47708SNathan Whitehorn 	PMU_BATSYSCTL_CHARGING	= 2 << 8,
121eff47708SNathan Whitehorn 	PMU_BATSYSCTL_CHARGE	= 3 << 8,
122eff47708SNathan Whitehorn 	PMU_BATSYSCTL_MAXCHARGE = 4 << 8,
123eff47708SNathan Whitehorn 	PMU_BATSYSCTL_CURRENT	= 5 << 8,
124eff47708SNathan Whitehorn 	PMU_BATSYSCTL_VOLTAGE	= 6 << 8,
125eff47708SNathan Whitehorn 	PMU_BATSYSCTL_TIME	= 7 << 8,
126eff47708SNathan Whitehorn 	PMU_BATSYSCTL_LIFE	= 8 << 8
127eff47708SNathan Whitehorn };
128b4dbc599SNathan Whitehorn 
129b4dbc599SNathan Whitehorn static device_method_t  pmu_methods[] = {
130b4dbc599SNathan Whitehorn 	/* Device interface */
131b4dbc599SNathan Whitehorn 	DEVMETHOD(device_probe,		pmu_probe),
132b4dbc599SNathan Whitehorn 	DEVMETHOD(device_attach,	pmu_attach),
133b4dbc599SNathan Whitehorn         DEVMETHOD(device_detach,        pmu_detach),
134b4dbc599SNathan Whitehorn         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
135b4dbc599SNathan Whitehorn 
136b4dbc599SNathan Whitehorn 	/* ADB bus interface */
137b4dbc599SNathan Whitehorn 	DEVMETHOD(adb_hb_send_raw_packet,   pmu_adb_send),
138b4dbc599SNathan Whitehorn 	DEVMETHOD(adb_hb_controller_poll,   pmu_poll),
139b4dbc599SNathan Whitehorn 	DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll),
140b4dbc599SNathan Whitehorn 
1413df9e037SNathan Whitehorn 	/* Clock interface */
1423df9e037SNathan Whitehorn 	DEVMETHOD(clock_gettime,	pmu_gettime),
1433df9e037SNathan Whitehorn 	DEVMETHOD(clock_settime,	pmu_settime),
1443df9e037SNathan Whitehorn 
1454b7ec270SMarius Strobl 	DEVMETHOD_END
146b4dbc599SNathan Whitehorn };
147b4dbc599SNathan Whitehorn 
148b4dbc599SNathan Whitehorn static driver_t pmu_driver = {
149b4dbc599SNathan Whitehorn 	"pmu",
150b4dbc599SNathan Whitehorn 	pmu_methods,
151b4dbc599SNathan Whitehorn 	sizeof(struct pmu_softc),
152b4dbc599SNathan Whitehorn };
153b4dbc599SNathan Whitehorn 
154992ae60bSJohn Baldwin EARLY_DRIVER_MODULE(pmu, macio, pmu_driver, 0, 0, BUS_PASS_RESOURCE);
15542f777fcSJohn Baldwin DRIVER_MODULE(adb, pmu, adb_driver, 0, 0);
156b4dbc599SNathan Whitehorn 
157b4dbc599SNathan Whitehorn static int	pmuextint_probe(device_t);
158b4dbc599SNathan Whitehorn static int	pmuextint_attach(device_t);
159b4dbc599SNathan Whitehorn 
160b4dbc599SNathan Whitehorn static device_method_t  pmuextint_methods[] = {
161b4dbc599SNathan Whitehorn 	/* Device interface */
162b4dbc599SNathan Whitehorn 	DEVMETHOD(device_probe,		pmuextint_probe),
163b4dbc599SNathan Whitehorn 	DEVMETHOD(device_attach,	pmuextint_attach),
164b4dbc599SNathan Whitehorn 	{0,0}
165b4dbc599SNathan Whitehorn };
166b4dbc599SNathan Whitehorn 
167b4dbc599SNathan Whitehorn static driver_t pmuextint_driver = {
168b4dbc599SNathan Whitehorn 	"pmuextint",
169b4dbc599SNathan Whitehorn 	pmuextint_methods,
170b4dbc599SNathan Whitehorn 	0
171b4dbc599SNathan Whitehorn };
172b4dbc599SNathan Whitehorn 
173992ae60bSJohn Baldwin EARLY_DRIVER_MODULE(pmuextint, macgpio, pmuextint_driver, 0, 0,
174992ae60bSJohn Baldwin     BUS_PASS_RESOURCE);
175b4dbc599SNathan Whitehorn 
176b4dbc599SNathan Whitehorn /* Make sure uhid is loaded, as it turns off some of the ADB emulation */
177b4dbc599SNathan Whitehorn MODULE_DEPEND(pmu, usb, 1, 1, 1);
178b4dbc599SNathan Whitehorn 
179b4dbc599SNathan Whitehorn static void pmu_intr(void *arg);
180b4dbc599SNathan Whitehorn static void pmu_in(struct pmu_softc *sc);
181b4dbc599SNathan Whitehorn static void pmu_out(struct pmu_softc *sc);
182b4dbc599SNathan Whitehorn static void pmu_ack_on(struct pmu_softc *sc);
183b4dbc599SNathan Whitehorn static void pmu_ack_off(struct pmu_softc *sc);
184b4dbc599SNathan Whitehorn static int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg,
185b4dbc599SNathan Whitehorn 	int rlen, uint8_t *out_msg);
186b4dbc599SNathan Whitehorn static uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset);
187b4dbc599SNathan Whitehorn static void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value);
188b4dbc599SNathan Whitehorn static int pmu_intr_state(struct pmu_softc *);
189b4dbc599SNathan Whitehorn 
190b4dbc599SNathan Whitehorn /* these values shows that number of data returned after 'send' cmd is sent */
191b4dbc599SNathan Whitehorn static signed char pm_send_cmd_type[] = {
192b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
193b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
194b4dbc599SNathan Whitehorn 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
195b4dbc599SNathan Whitehorn 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
196b4dbc599SNathan Whitehorn 	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
197b4dbc599SNathan Whitehorn 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
198b4dbc599SNathan Whitehorn 	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
199b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
200b4dbc599SNathan Whitehorn 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
201b4dbc599SNathan Whitehorn 	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
202b4dbc599SNathan Whitehorn 	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
203b4dbc599SNathan Whitehorn 	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
204b4dbc599SNathan Whitehorn 	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
205b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
206b4dbc599SNathan Whitehorn 	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
2074702d987SJustin Hibbits 	0x00, 0x00,   -1,   -1,   -1, 0x05, 0x04, 0x04,
208b4dbc599SNathan Whitehorn 	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
209b4dbc599SNathan Whitehorn 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
210b4dbc599SNathan Whitehorn 	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
211b4dbc599SNathan Whitehorn 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
212b4dbc599SNathan Whitehorn 	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
213b4dbc599SNathan Whitehorn 	0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
214b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
215b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
216b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
217b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
218b4dbc599SNathan Whitehorn 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
219b4dbc599SNathan Whitehorn 	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
220b4dbc599SNathan Whitehorn 	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
221b4dbc599SNathan Whitehorn 	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
222b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
223b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
224b4dbc599SNathan Whitehorn };
225b4dbc599SNathan Whitehorn 
226b4dbc599SNathan Whitehorn /* these values shows that number of data returned after 'receive' cmd is sent */
227b4dbc599SNathan Whitehorn static signed char pm_receive_cmd_type[] = {
228b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
230b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231b4dbc599SNathan Whitehorn 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
232b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
234b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235b4dbc599SNathan Whitehorn 	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
236b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237b4dbc599SNathan Whitehorn 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
238b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239b4dbc599SNathan Whitehorn 	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
240b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241b4dbc599SNathan Whitehorn 	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
242b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2434702d987SJustin Hibbits 	  -1,   -1,   -1,   -1,   -1, 0x01, 0x01, 0x01,
244b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245b4dbc599SNathan Whitehorn 	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
246b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247b4dbc599SNathan Whitehorn 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
248b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249b4dbc599SNathan Whitehorn 	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
250b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
252b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
253b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
254b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
255b4dbc599SNathan Whitehorn 	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
256b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
257b4dbc599SNathan Whitehorn 	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
258b4dbc599SNathan Whitehorn 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
259b4dbc599SNathan Whitehorn 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
260b4dbc599SNathan Whitehorn };
261b4dbc599SNathan Whitehorn 
2623cd55688SJustin Hibbits static int pmu_battmon_enabled = 1;
263d49c6f02SJustin Hibbits static struct proc *pmubattproc;
264d49c6f02SJustin Hibbits static struct kproc_desc pmu_batt_kp = {
265d49c6f02SJustin Hibbits 	"pmu_batt",
266d49c6f02SJustin Hibbits 	pmu_battquery_proc,
267d49c6f02SJustin Hibbits 	&pmubattproc
268d49c6f02SJustin Hibbits };
269d49c6f02SJustin Hibbits 
270b4dbc599SNathan Whitehorn /* We only have one of each device, so globals are safe */
271b4dbc599SNathan Whitehorn static device_t pmu = NULL;
272b4dbc599SNathan Whitehorn static device_t pmu_extint = NULL;
273b4dbc599SNathan Whitehorn 
274b4dbc599SNathan Whitehorn static int
275b4dbc599SNathan Whitehorn pmuextint_probe(device_t dev)
276b4dbc599SNathan Whitehorn {
277b4dbc599SNathan Whitehorn 	const char *type = ofw_bus_get_type(dev);
278b4dbc599SNathan Whitehorn 
279b4dbc599SNathan Whitehorn 	if (strcmp(type, "extint-gpio1") != 0)
280b4dbc599SNathan Whitehorn                 return (ENXIO);
281b4dbc599SNathan Whitehorn 
282b4dbc599SNathan Whitehorn 	device_set_desc(dev, "Apple PMU99 External Interrupt");
283b4dbc599SNathan Whitehorn 	return (0);
284b4dbc599SNathan Whitehorn }
285b4dbc599SNathan Whitehorn 
286b4dbc599SNathan Whitehorn static int
287b4dbc599SNathan Whitehorn pmu_probe(device_t dev)
288b4dbc599SNathan Whitehorn {
289b4dbc599SNathan Whitehorn 	const char *type = ofw_bus_get_type(dev);
290b4dbc599SNathan Whitehorn 
291b4dbc599SNathan Whitehorn 	if (strcmp(type, "via-pmu") != 0)
292b4dbc599SNathan Whitehorn                 return (ENXIO);
293b4dbc599SNathan Whitehorn 
294b4dbc599SNathan Whitehorn 	device_set_desc(dev, "Apple PMU99 Controller");
295b4dbc599SNathan Whitehorn 	return (0);
296b4dbc599SNathan Whitehorn }
297b4dbc599SNathan Whitehorn 
298b4dbc599SNathan Whitehorn static int
299b4dbc599SNathan Whitehorn setup_pmu_intr(device_t dev, device_t extint)
300b4dbc599SNathan Whitehorn {
301b4dbc599SNathan Whitehorn 	struct pmu_softc *sc;
302b4dbc599SNathan Whitehorn 	sc = device_get_softc(dev);
303b4dbc599SNathan Whitehorn 
304b4dbc599SNathan Whitehorn 	sc->sc_irqrid = 0;
305b4dbc599SNathan Whitehorn 	sc->sc_irq = bus_alloc_resource_any(extint, SYS_RES_IRQ, &sc->sc_irqrid,
306b4dbc599SNathan Whitehorn            	RF_ACTIVE);
307b4dbc599SNathan Whitehorn         if (sc->sc_irq == NULL) {
308b4dbc599SNathan Whitehorn                 device_printf(dev, "could not allocate interrupt\n");
309b4dbc599SNathan Whitehorn                 return (ENXIO);
310b4dbc599SNathan Whitehorn         }
311b4dbc599SNathan Whitehorn 
312b4dbc599SNathan Whitehorn 	if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE
313b4dbc599SNathan Whitehorn 	    | INTR_ENTROPY, NULL, pmu_intr, dev, &sc->sc_ih) != 0) {
314b4dbc599SNathan Whitehorn                 device_printf(dev, "could not setup interrupt\n");
315b4dbc599SNathan Whitehorn                 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
316b4dbc599SNathan Whitehorn                     sc->sc_irq);
317b4dbc599SNathan Whitehorn                 return (ENXIO);
318b4dbc599SNathan Whitehorn         }
319b4dbc599SNathan Whitehorn 
320b4dbc599SNathan Whitehorn 	return (0);
321b4dbc599SNathan Whitehorn }
322b4dbc599SNathan Whitehorn 
323b4dbc599SNathan Whitehorn static int
324b4dbc599SNathan Whitehorn pmuextint_attach(device_t dev)
325b4dbc599SNathan Whitehorn {
326b4dbc599SNathan Whitehorn 	pmu_extint = dev;
327b4dbc599SNathan Whitehorn 	if (pmu)
328b4dbc599SNathan Whitehorn 		return (setup_pmu_intr(pmu,dev));
329b4dbc599SNathan Whitehorn 
330b4dbc599SNathan Whitehorn 	return (0);
331b4dbc599SNathan Whitehorn }
332b4dbc599SNathan Whitehorn 
333b4dbc599SNathan Whitehorn static int
334b4dbc599SNathan Whitehorn pmu_attach(device_t dev)
335b4dbc599SNathan Whitehorn {
336b4dbc599SNathan Whitehorn 	struct pmu_softc *sc;
337b4dbc599SNathan Whitehorn 
338eff47708SNathan Whitehorn 	int i;
339b4dbc599SNathan Whitehorn 	uint8_t reg;
340b4dbc599SNathan Whitehorn 	uint8_t cmd[2] = {2, 0};
341b4dbc599SNathan Whitehorn 	uint8_t resp[16];
342b4dbc599SNathan Whitehorn 	phandle_t node,child;
3431af2e191SNathan Whitehorn 	struct sysctl_ctx_list *ctx;
3441af2e191SNathan Whitehorn 	struct sysctl_oid *tree;
345b4dbc599SNathan Whitehorn 
346b4dbc599SNathan Whitehorn 	sc = device_get_softc(dev);
347b4dbc599SNathan Whitehorn 	sc->sc_dev = dev;
348b4dbc599SNathan Whitehorn 
349b4dbc599SNathan Whitehorn 	sc->sc_memrid = 0;
350b4dbc599SNathan Whitehorn 	sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
351b4dbc599SNathan Whitehorn 		          &sc->sc_memrid, RF_ACTIVE);
352b4dbc599SNathan Whitehorn 
353b4dbc599SNathan Whitehorn 	mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE);
354b4dbc599SNathan Whitehorn 
355b4dbc599SNathan Whitehorn 	if (sc->sc_memr == NULL) {
356b4dbc599SNathan Whitehorn 		device_printf(dev, "Could not alloc mem resource!\n");
357b4dbc599SNathan Whitehorn 		return (ENXIO);
358b4dbc599SNathan Whitehorn 	}
359b4dbc599SNathan Whitehorn 
360b4dbc599SNathan Whitehorn 	/*
361b4dbc599SNathan Whitehorn 	 * Our interrupt is attached to a GPIO pin. Depending on probe order,
362b4dbc599SNathan Whitehorn 	 * we may not have found it yet. If we haven't, it will find us, and
363b4dbc599SNathan Whitehorn 	 * attach our interrupt then.
364b4dbc599SNathan Whitehorn 	 */
365b4dbc599SNathan Whitehorn 	pmu = dev;
366b4dbc599SNathan Whitehorn 	if (pmu_extint != NULL) {
367b4dbc599SNathan Whitehorn 		if (setup_pmu_intr(dev,pmu_extint) != 0)
368b4dbc599SNathan Whitehorn 			return (ENXIO);
369b4dbc599SNathan Whitehorn 	}
370b4dbc599SNathan Whitehorn 
371b4dbc599SNathan Whitehorn 	sc->sc_autopoll = 0;
372a228f5cdSNathan Whitehorn 	sc->sc_batteries = 0;
373a228f5cdSNathan Whitehorn 	sc->adb_bus = NULL;
374a228f5cdSNathan Whitehorn 	sc->sc_leddev = NULL;
375b4dbc599SNathan Whitehorn 
376b4dbc599SNathan Whitehorn 	/* Init PMU */
377b4dbc599SNathan Whitehorn 
3784702d987SJustin Hibbits 	pmu_write_reg(sc, vBufB, pmu_read_reg(sc, vBufB) | vPB4);
3794702d987SJustin Hibbits 	pmu_write_reg(sc, vDirB, (pmu_read_reg(sc, vDirB) | vPB4) & ~vPB3);
3804702d987SJustin Hibbits 
3814702d987SJustin Hibbits 	reg = PMU_DEFAULTS;
382b4dbc599SNathan Whitehorn 	pmu_send(sc, PMU_SET_IMASK, 1, &reg, 16, resp);
383b4dbc599SNathan Whitehorn 
3844702d987SJustin Hibbits 	pmu_write_reg(sc, vIER, 0x94); /* make sure VIA interrupts are on */
385b4dbc599SNathan Whitehorn 
386b4dbc599SNathan Whitehorn 	pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp);
387feb05383SJustin Hibbits 	pmu_send(sc, PMU_GET_VERSION, 0, cmd, 16, resp);
388b4dbc599SNathan Whitehorn 
389b4dbc599SNathan Whitehorn 	/* Initialize child buses (ADB) */
390b4dbc599SNathan Whitehorn 	node = ofw_bus_get_node(dev);
391b4dbc599SNathan Whitehorn 
392b4dbc599SNathan Whitehorn 	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
393b4dbc599SNathan Whitehorn 		char name[32];
394b4dbc599SNathan Whitehorn 
395b4dbc599SNathan Whitehorn 		memset(name, 0, sizeof(name));
396b4dbc599SNathan Whitehorn 		OF_getprop(child, "name", name, sizeof(name));
397b4dbc599SNathan Whitehorn 
398b4dbc599SNathan Whitehorn 		if (bootverbose)
399b4dbc599SNathan Whitehorn 			device_printf(dev, "PMU child <%s>\n",name);
400b4dbc599SNathan Whitehorn 
401b4dbc599SNathan Whitehorn 		if (strncmp(name, "adb", 4) == 0) {
4025b56413dSWarner Losh 			sc->adb_bus = device_add_child(dev,"adb",DEVICE_UNIT_ANY);
403b4dbc599SNathan Whitehorn 		}
404eff47708SNathan Whitehorn 
405eff47708SNathan Whitehorn 		if (strncmp(name, "power-mgt", 9) == 0) {
406eff47708SNathan Whitehorn 			uint32_t prim_info[9];
407eff47708SNathan Whitehorn 
408eff47708SNathan Whitehorn 			if (OF_getprop(child, "prim-info", prim_info,
409eff47708SNathan Whitehorn 			    sizeof(prim_info)) >= 7)
410eff47708SNathan Whitehorn 				sc->sc_batteries = (prim_info[6] >> 16) & 0xff;
411eff47708SNathan Whitehorn 
412eff47708SNathan Whitehorn 			if (bootverbose && sc->sc_batteries > 0)
413eff47708SNathan Whitehorn 				device_printf(dev, "%d batteries detected\n",
414eff47708SNathan Whitehorn 				    sc->sc_batteries);
415eff47708SNathan Whitehorn 		}
416b4dbc599SNathan Whitehorn 	}
417b4dbc599SNathan Whitehorn 
4181af2e191SNathan Whitehorn 	/*
4191af2e191SNathan Whitehorn 	 * Set up sysctls
4201af2e191SNathan Whitehorn 	 */
4211af2e191SNathan Whitehorn 
4221af2e191SNathan Whitehorn 	ctx = device_get_sysctl_ctx(dev);
4231af2e191SNathan Whitehorn 	tree = device_get_sysctl_tree(dev);
4241af2e191SNathan Whitehorn 
4251af2e191SNathan Whitehorn 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
4267029da5cSPawel Biernacki 	    "server_mode", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
4271af2e191SNathan Whitehorn 	    pmu_server_mode, "I", "Enable reboot after power failure");
4281af2e191SNathan Whitehorn 
429eff47708SNathan Whitehorn 	if (sc->sc_batteries > 0) {
430eff47708SNathan Whitehorn 		struct sysctl_oid *oid, *battroot;
431eff47708SNathan Whitehorn 		char battnum[2];
432eff47708SNathan Whitehorn 
433d49c6f02SJustin Hibbits 		/* Only start the battery monitor if we have a battery. */
434d49c6f02SJustin Hibbits 		kproc_start(&pmu_batt_kp);
4351165ddc2SNathan Whitehorn 		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
4367029da5cSPawel Biernacki 		    "monitor_batteries",
4377029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
4383cd55688SJustin Hibbits 		    pmu_battmon, "I", "Post battery events to devd");
4393cd55688SJustin Hibbits 
4403cd55688SJustin Hibbits 		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
4417029da5cSPawel Biernacki 		    "acline", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
4427029da5cSPawel Biernacki 		    0, pmu_acline_state, "I", "AC Line Status");
4431165ddc2SNathan Whitehorn 
444eff47708SNathan Whitehorn 		battroot = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
4457029da5cSPawel Biernacki 		    "batteries", CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
4467029da5cSPawel Biernacki 		    "Battery Information");
447eff47708SNathan Whitehorn 
448eff47708SNathan Whitehorn 		for (i = 0; i < sc->sc_batteries; i++) {
449eff47708SNathan Whitehorn 			battnum[0] = i + '0';
450eff47708SNathan Whitehorn 			battnum[1] = '\0';
451eff47708SNathan Whitehorn 
452eff47708SNathan Whitehorn 			oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(battroot),
4537029da5cSPawel Biernacki 			    OID_AUTO, battnum, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
454eff47708SNathan Whitehorn 			    "Battery Information");
455eff47708SNathan Whitehorn 
456eff47708SNathan Whitehorn 			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
4577029da5cSPawel Biernacki 			    "present",
4587029da5cSPawel Biernacki 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
459eff47708SNathan Whitehorn 			    PMU_BATSYSCTL_PRESENT | i, pmu_battquery_sysctl,
460eff47708SNathan Whitehorn 			    "I", "Battery present");
461eff47708SNathan Whitehorn 			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
4627029da5cSPawel Biernacki 			    "charging",
4637029da5cSPawel Biernacki 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
464eff47708SNathan Whitehorn 			    PMU_BATSYSCTL_CHARGING | i, pmu_battquery_sysctl,
465eff47708SNathan Whitehorn 			    "I", "Battery charging");
466eff47708SNathan Whitehorn 			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
4677029da5cSPawel Biernacki 			    "charge",
4687029da5cSPawel Biernacki 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
469eff47708SNathan Whitehorn 			    PMU_BATSYSCTL_CHARGE | i, pmu_battquery_sysctl,
470eff47708SNathan Whitehorn 			    "I", "Battery charge (mAh)");
471eff47708SNathan Whitehorn 			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
4727029da5cSPawel Biernacki 			    "maxcharge",
4737029da5cSPawel Biernacki 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
474eff47708SNathan Whitehorn 			    PMU_BATSYSCTL_MAXCHARGE | i, pmu_battquery_sysctl,
475eff47708SNathan Whitehorn 			    "I", "Maximum battery capacity (mAh)");
476eff47708SNathan Whitehorn 			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
4777029da5cSPawel Biernacki 			    "rate",
4787029da5cSPawel Biernacki 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
479eff47708SNathan Whitehorn 			    PMU_BATSYSCTL_CURRENT | i, pmu_battquery_sysctl,
480eff47708SNathan Whitehorn 			    "I", "Battery discharge rate (mA)");
481eff47708SNathan Whitehorn 			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
4827029da5cSPawel Biernacki 			    "voltage",
4837029da5cSPawel Biernacki 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
484eff47708SNathan Whitehorn 			    PMU_BATSYSCTL_VOLTAGE | i, pmu_battquery_sysctl,
485eff47708SNathan Whitehorn 			    "I", "Battery voltage (mV)");
486eff47708SNathan Whitehorn 
487eff47708SNathan Whitehorn 			/* Knobs for mental compatibility with ACPI */
488eff47708SNathan Whitehorn 
489eff47708SNathan Whitehorn 			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
4907029da5cSPawel Biernacki 			    "time",
4917029da5cSPawel Biernacki 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
492eff47708SNathan Whitehorn 			    PMU_BATSYSCTL_TIME | i, pmu_battquery_sysctl,
493eff47708SNathan Whitehorn 			    "I", "Time Remaining (minutes)");
494eff47708SNathan Whitehorn 			SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
4957029da5cSPawel Biernacki 			    "life",
4967029da5cSPawel Biernacki 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
497eff47708SNathan Whitehorn 			    PMU_BATSYSCTL_LIFE | i, pmu_battquery_sysctl,
498eff47708SNathan Whitehorn 			    "I", "Capacity remaining (percent)");
499eff47708SNathan Whitehorn 		}
500eff47708SNathan Whitehorn 	}
501eff47708SNathan Whitehorn 
502a228f5cdSNathan Whitehorn 	/*
503a228f5cdSNathan Whitehorn 	 * Set up LED interface
504a228f5cdSNathan Whitehorn 	 */
505a228f5cdSNathan Whitehorn 
506a228f5cdSNathan Whitehorn 	sc->sc_leddev = led_create(pmu_set_sleepled, sc, "sleepled");
507a228f5cdSNathan Whitehorn 
5083df9e037SNathan Whitehorn 	/*
5093df9e037SNathan Whitehorn 	 * Register RTC
5103df9e037SNathan Whitehorn 	 */
5113df9e037SNathan Whitehorn 
5123df9e037SNathan Whitehorn 	clock_register(dev, 1000);
5133df9e037SNathan Whitehorn 
514b2a237beSNathan Whitehorn 	/*
515b2a237beSNathan Whitehorn 	 * Register power control handler
516b2a237beSNathan Whitehorn 	 */
517b2a237beSNathan Whitehorn 	EVENTHANDLER_REGISTER(shutdown_final, pmu_shutdown, sc,
518b2a237beSNathan Whitehorn 	    SHUTDOWN_PRI_LAST);
519b2a237beSNathan Whitehorn 
520*18250ec6SJohn Baldwin 	bus_attach_children(dev);
521*18250ec6SJohn Baldwin 	return (0);
522b4dbc599SNathan Whitehorn }
523b4dbc599SNathan Whitehorn 
524b4dbc599SNathan Whitehorn static int
525b4dbc599SNathan Whitehorn pmu_detach(device_t dev)
526b4dbc599SNathan Whitehorn {
527b4dbc599SNathan Whitehorn 	struct pmu_softc *sc;
528d412c076SJohn Baldwin 	int error;
529d412c076SJohn Baldwin 
530d412c076SJohn Baldwin 	error = bus_generic_detach(dev);
531d412c076SJohn Baldwin 	if (error != 0)
532d412c076SJohn Baldwin 		return (error);
533b4dbc599SNathan Whitehorn 
534b4dbc599SNathan Whitehorn 	sc = device_get_softc(dev);
535b4dbc599SNathan Whitehorn 
536a228f5cdSNathan Whitehorn 	if (sc->sc_leddev != NULL)
537a228f5cdSNathan Whitehorn 		led_destroy(sc->sc_leddev);
538a228f5cdSNathan Whitehorn 
539b4dbc599SNathan Whitehorn 	bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
540b4dbc599SNathan Whitehorn 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq);
541b4dbc599SNathan Whitehorn 	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr);
542b4dbc599SNathan Whitehorn 	mtx_destroy(&sc->sc_mutex);
543b4dbc599SNathan Whitehorn 
544d412c076SJohn Baldwin 	return (0);
545b4dbc599SNathan Whitehorn }
546b4dbc599SNathan Whitehorn 
547b4dbc599SNathan Whitehorn static uint8_t
548b4dbc599SNathan Whitehorn pmu_read_reg(struct pmu_softc *sc, u_int offset)
549b4dbc599SNathan Whitehorn {
550b4dbc599SNathan Whitehorn 	return (bus_read_1(sc->sc_memr, offset));
551b4dbc599SNathan Whitehorn }
552b4dbc599SNathan Whitehorn 
553b4dbc599SNathan Whitehorn static void
554b4dbc599SNathan Whitehorn pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value)
555b4dbc599SNathan Whitehorn {
556b4dbc599SNathan Whitehorn 	bus_write_1(sc->sc_memr, offset, value);
557b4dbc599SNathan Whitehorn }
558b4dbc599SNathan Whitehorn 
559b4dbc599SNathan Whitehorn static int
560b4dbc599SNathan Whitehorn pmu_send_byte(struct pmu_softc *sc, uint8_t data)
561b4dbc599SNathan Whitehorn {
562b4dbc599SNathan Whitehorn 
563b4dbc599SNathan Whitehorn 	pmu_out(sc);
564b4dbc599SNathan Whitehorn 	pmu_write_reg(sc, vSR, data);
565b4dbc599SNathan Whitehorn 	pmu_ack_off(sc);
566b4dbc599SNathan Whitehorn 	/* wait for intr to come up */
567b4dbc599SNathan Whitehorn 	/* XXX should add a timeout and bail if it expires */
568b4dbc599SNathan Whitehorn 	do {} while (pmu_intr_state(sc) == 0);
569b4dbc599SNathan Whitehorn 	pmu_ack_on(sc);
570b4dbc599SNathan Whitehorn 	do {} while (pmu_intr_state(sc));
571b4dbc599SNathan Whitehorn 	pmu_ack_on(sc);
572b4dbc599SNathan Whitehorn 	return 0;
573b4dbc599SNathan Whitehorn }
574b4dbc599SNathan Whitehorn 
575b4dbc599SNathan Whitehorn static inline int
576b4dbc599SNathan Whitehorn pmu_read_byte(struct pmu_softc *sc, uint8_t *data)
577b4dbc599SNathan Whitehorn {
578b4dbc599SNathan Whitehorn 	pmu_in(sc);
579b7fe00faSJohn Baldwin 	(void)pmu_read_reg(sc, vSR);
580b4dbc599SNathan Whitehorn 	pmu_ack_off(sc);
581b4dbc599SNathan Whitehorn 	/* wait for intr to come up */
582b4dbc599SNathan Whitehorn 	do {} while (pmu_intr_state(sc) == 0);
583b4dbc599SNathan Whitehorn 	pmu_ack_on(sc);
584b4dbc599SNathan Whitehorn 	do {} while (pmu_intr_state(sc));
585b4dbc599SNathan Whitehorn 	*data = pmu_read_reg(sc, vSR);
586b4dbc599SNathan Whitehorn 	return 0;
587b4dbc599SNathan Whitehorn }
588b4dbc599SNathan Whitehorn 
589b4dbc599SNathan Whitehorn static int
590b4dbc599SNathan Whitehorn pmu_intr_state(struct pmu_softc *sc)
591b4dbc599SNathan Whitehorn {
592b4dbc599SNathan Whitehorn 	return ((pmu_read_reg(sc, vBufB) & vPB3) == 0);
593b4dbc599SNathan Whitehorn }
594b4dbc599SNathan Whitehorn 
595b4dbc599SNathan Whitehorn static int
596b4dbc599SNathan Whitehorn pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen,
597b4dbc599SNathan Whitehorn     uint8_t *out_msg)
598b4dbc599SNathan Whitehorn {
599b4dbc599SNathan Whitehorn 	struct pmu_softc *sc = cookie;
600b4dbc599SNathan Whitehorn 	int i, rcv_len = -1;
601b4dbc599SNathan Whitehorn 	uint8_t out_len, intreg;
602b4dbc599SNathan Whitehorn 
603b4dbc599SNathan Whitehorn 	intreg = pmu_read_reg(sc, vIER);
604b4dbc599SNathan Whitehorn 	intreg &= 0x10;
605b4dbc599SNathan Whitehorn 	pmu_write_reg(sc, vIER, intreg);
606b4dbc599SNathan Whitehorn 
607b4dbc599SNathan Whitehorn 	/* wait idle */
608b4dbc599SNathan Whitehorn 	do {} while (pmu_intr_state(sc));
609b4dbc599SNathan Whitehorn 
610b4dbc599SNathan Whitehorn 	/* send command */
611b4dbc599SNathan Whitehorn 	pmu_send_byte(sc, cmd);
612b4dbc599SNathan Whitehorn 
613b4dbc599SNathan Whitehorn 	/* send length if necessary */
614b4dbc599SNathan Whitehorn 	if (pm_send_cmd_type[cmd] < 0) {
615b4dbc599SNathan Whitehorn 		pmu_send_byte(sc, length);
616b4dbc599SNathan Whitehorn 	}
617b4dbc599SNathan Whitehorn 
618b4dbc599SNathan Whitehorn 	for (i = 0; i < length; i++) {
619b4dbc599SNathan Whitehorn 		pmu_send_byte(sc, in_msg[i]);
620b4dbc599SNathan Whitehorn 	}
621b4dbc599SNathan Whitehorn 
622b4dbc599SNathan Whitehorn 	/* see if there's data to read */
623b4dbc599SNathan Whitehorn 	rcv_len = pm_receive_cmd_type[cmd];
624b4dbc599SNathan Whitehorn 	if (rcv_len == 0)
625b4dbc599SNathan Whitehorn 		goto done;
626b4dbc599SNathan Whitehorn 
627b4dbc599SNathan Whitehorn 	/* read command */
628b4dbc599SNathan Whitehorn 	if (rcv_len == 1) {
629b4dbc599SNathan Whitehorn 		pmu_read_byte(sc, out_msg);
630b4dbc599SNathan Whitehorn 		goto done;
631b4dbc599SNathan Whitehorn 	} else
632b4dbc599SNathan Whitehorn 		out_msg[0] = cmd;
633b4dbc599SNathan Whitehorn 	if (rcv_len < 0) {
634b4dbc599SNathan Whitehorn 		pmu_read_byte(sc, &out_len);
635b4dbc599SNathan Whitehorn 		rcv_len = out_len + 1;
636b4dbc599SNathan Whitehorn 	}
637b4dbc599SNathan Whitehorn 	for (i = 1; i < min(rcv_len, rlen); i++)
638b4dbc599SNathan Whitehorn 		pmu_read_byte(sc, &out_msg[i]);
639b4dbc599SNathan Whitehorn 
640b4dbc599SNathan Whitehorn done:
641b4dbc599SNathan Whitehorn 	pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90);
642b4dbc599SNathan Whitehorn 
643b4dbc599SNathan Whitehorn 	return rcv_len;
644b4dbc599SNathan Whitehorn }
645b4dbc599SNathan Whitehorn 
64665f44679SAndriy Gapon static u_int
647b4dbc599SNathan Whitehorn pmu_poll(device_t dev)
648b4dbc599SNathan Whitehorn {
649b4dbc599SNathan Whitehorn 	pmu_intr(dev);
65065f44679SAndriy Gapon 	return (0);
651b4dbc599SNathan Whitehorn }
652b4dbc599SNathan Whitehorn 
653b4dbc599SNathan Whitehorn static void
654b4dbc599SNathan Whitehorn pmu_in(struct pmu_softc *sc)
655b4dbc599SNathan Whitehorn {
656b4dbc599SNathan Whitehorn 	uint8_t reg;
657b4dbc599SNathan Whitehorn 
658b4dbc599SNathan Whitehorn 	reg = pmu_read_reg(sc, vACR);
659b4dbc599SNathan Whitehorn 	reg &= ~vSR_OUT;
660b4dbc599SNathan Whitehorn 	reg |= 0x0c;
661b4dbc599SNathan Whitehorn 	pmu_write_reg(sc, vACR, reg);
662b4dbc599SNathan Whitehorn }
663b4dbc599SNathan Whitehorn 
664b4dbc599SNathan Whitehorn static void
665b4dbc599SNathan Whitehorn pmu_out(struct pmu_softc *sc)
666b4dbc599SNathan Whitehorn {
667b4dbc599SNathan Whitehorn 	uint8_t reg;
668b4dbc599SNathan Whitehorn 
669b4dbc599SNathan Whitehorn 	reg = pmu_read_reg(sc, vACR);
670b4dbc599SNathan Whitehorn 	reg |= vSR_OUT;
671b4dbc599SNathan Whitehorn 	reg |= 0x0c;
672b4dbc599SNathan Whitehorn 	pmu_write_reg(sc, vACR, reg);
673b4dbc599SNathan Whitehorn }
674b4dbc599SNathan Whitehorn 
675b4dbc599SNathan Whitehorn static void
676b4dbc599SNathan Whitehorn pmu_ack_off(struct pmu_softc *sc)
677b4dbc599SNathan Whitehorn {
678b4dbc599SNathan Whitehorn 	uint8_t reg;
679b4dbc599SNathan Whitehorn 
680b4dbc599SNathan Whitehorn 	reg = pmu_read_reg(sc, vBufB);
681b4dbc599SNathan Whitehorn 	reg &= ~vPB4;
682b4dbc599SNathan Whitehorn 	pmu_write_reg(sc, vBufB, reg);
683b4dbc599SNathan Whitehorn }
684b4dbc599SNathan Whitehorn 
685b4dbc599SNathan Whitehorn static void
686b4dbc599SNathan Whitehorn pmu_ack_on(struct pmu_softc *sc)
687b4dbc599SNathan Whitehorn {
688b4dbc599SNathan Whitehorn 	uint8_t reg;
689b4dbc599SNathan Whitehorn 
690b4dbc599SNathan Whitehorn 	reg = pmu_read_reg(sc, vBufB);
691b4dbc599SNathan Whitehorn 	reg |= vPB4;
692b4dbc599SNathan Whitehorn 	pmu_write_reg(sc, vBufB, reg);
693b4dbc599SNathan Whitehorn }
694b4dbc599SNathan Whitehorn 
695b4dbc599SNathan Whitehorn static void
696b4dbc599SNathan Whitehorn pmu_intr(void *arg)
697b4dbc599SNathan Whitehorn {
698b4dbc599SNathan Whitehorn 	device_t        dev;
699b4dbc599SNathan Whitehorn 	struct pmu_softc *sc;
700b4dbc599SNathan Whitehorn 
701b4dbc599SNathan Whitehorn 	unsigned int len;
702b4dbc599SNathan Whitehorn 	uint8_t resp[16];
703b4dbc599SNathan Whitehorn 	uint8_t junk[16];
704b4dbc599SNathan Whitehorn 
705b4dbc599SNathan Whitehorn         dev = (device_t)arg;
706b4dbc599SNathan Whitehorn 	sc = device_get_softc(dev);
707b4dbc599SNathan Whitehorn 
708b4dbc599SNathan Whitehorn 	mtx_lock(&sc->sc_mutex);
709b4dbc599SNathan Whitehorn 
710b4dbc599SNathan Whitehorn 	pmu_write_reg(sc, vIFR, 0x90);	/* Clear 'em */
711b4dbc599SNathan Whitehorn 	len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp);
712b4dbc599SNathan Whitehorn 
713b4dbc599SNathan Whitehorn 	mtx_unlock(&sc->sc_mutex);
714b4dbc599SNathan Whitehorn 
715b4dbc599SNathan Whitehorn 	if ((len < 1) || (resp[1] == 0)) {
716b4dbc599SNathan Whitehorn 		return;
717b4dbc599SNathan Whitehorn 	}
718b4dbc599SNathan Whitehorn 
719b4dbc599SNathan Whitehorn 	if (resp[1] & PMU_INT_ADB) {
720b4dbc599SNathan Whitehorn 		/*
721b4dbc599SNathan Whitehorn 		 * the PMU will turn off autopolling after each command that
722b4dbc599SNathan Whitehorn 		 * it did not issue, so we assume any but TALK R0 is ours and
723b4dbc599SNathan Whitehorn 		 * re-enable autopoll here whenever we receive an ACK for a
724b4dbc599SNathan Whitehorn 		 * non TR0 command.
725b4dbc599SNathan Whitehorn 		 */
726b4dbc599SNathan Whitehorn 		mtx_lock(&sc->sc_mutex);
727b4dbc599SNathan Whitehorn 
728b4dbc599SNathan Whitehorn 		if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) {
729b4dbc599SNathan Whitehorn 			if (sc->sc_autopoll) {
730b4dbc599SNathan Whitehorn 				uint8_t cmd[] = {0, PMU_SET_POLL_MASK,
731b4dbc599SNathan Whitehorn 				    (sc->sc_autopoll >> 8) & 0xff,
732b4dbc599SNathan Whitehorn 				    sc->sc_autopoll & 0xff};
733b4dbc599SNathan Whitehorn 
734b4dbc599SNathan Whitehorn 				pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk);
735b4dbc599SNathan Whitehorn 			}
736b4dbc599SNathan Whitehorn 		}
737b4dbc599SNathan Whitehorn 
738b4dbc599SNathan Whitehorn 		mtx_unlock(&sc->sc_mutex);
739b4dbc599SNathan Whitehorn 
740b4dbc599SNathan Whitehorn 		adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2],
741b4dbc599SNathan Whitehorn 			len - 3,&resp[3]);
742b4dbc599SNathan Whitehorn 	}
7436431ede3SJustin Hibbits 	if (resp[1] & PMU_INT_ENVIRONMENT) {
74423ab37cbSJustin Hibbits 		/* if the lid was just closed, notify devd. */
7456431ede3SJustin Hibbits 		if ((resp[2] & PMU_ENV_LID_CLOSED) && (!sc->lid_closed)) {
7466431ede3SJustin Hibbits 			sc->lid_closed = 1;
7476431ede3SJustin Hibbits 			devctl_notify("PMU", "lid", "close", NULL);
7486431ede3SJustin Hibbits 		}
7496431ede3SJustin Hibbits 		else if (!(resp[2] & PMU_ENV_LID_CLOSED) && (sc->lid_closed)) {
75023ab37cbSJustin Hibbits 			/* if the lid was just opened, notify devd. */
7516431ede3SJustin Hibbits 			sc->lid_closed = 0;
752a1afe0bfSJustin Hibbits 			devctl_notify("PMU", "lid", "open", NULL);
7536431ede3SJustin Hibbits 		}
754a1afe0bfSJustin Hibbits 		if (resp[2] & PMU_ENV_POWER)
755a1afe0bfSJustin Hibbits 			devctl_notify("PMU", "Button", "pressed", NULL);
7566431ede3SJustin Hibbits 	}
757b4dbc599SNathan Whitehorn }
758b4dbc599SNathan Whitehorn 
759b4dbc599SNathan Whitehorn static u_int
760b4dbc599SNathan Whitehorn pmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data,
761b4dbc599SNathan Whitehorn     u_char poll)
762b4dbc599SNathan Whitehorn {
763b4dbc599SNathan Whitehorn 	struct pmu_softc *sc = device_get_softc(dev);
764b7fe00faSJohn Baldwin 	int i;
765b4dbc599SNathan Whitehorn 	uint8_t packet[16], resp[16];
766b4dbc599SNathan Whitehorn 
767b4dbc599SNathan Whitehorn 	/* construct an ADB command packet and send it */
768b4dbc599SNathan Whitehorn 
769b4dbc599SNathan Whitehorn 	packet[0] = command_byte;
770b4dbc599SNathan Whitehorn 
771b4dbc599SNathan Whitehorn 	packet[1] = 0;
772b4dbc599SNathan Whitehorn 	packet[2] = len;
773b4dbc599SNathan Whitehorn 	for (i = 0; i < len; i++)
774b4dbc599SNathan Whitehorn 		packet[i + 3] = data[i];
775b4dbc599SNathan Whitehorn 
776b4dbc599SNathan Whitehorn 	mtx_lock(&sc->sc_mutex);
777b7fe00faSJohn Baldwin 	pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp);
778b4dbc599SNathan Whitehorn 	mtx_unlock(&sc->sc_mutex);
779b4dbc599SNathan Whitehorn 
780b4dbc599SNathan Whitehorn 	if (poll)
781b4dbc599SNathan Whitehorn 		pmu_poll(dev);
782b4dbc599SNathan Whitehorn 
783b4dbc599SNathan Whitehorn 	return 0;
784b4dbc599SNathan Whitehorn }
785b4dbc599SNathan Whitehorn 
786b4dbc599SNathan Whitehorn static u_int
787b4dbc599SNathan Whitehorn pmu_adb_autopoll(device_t dev, uint16_t mask)
788b4dbc599SNathan Whitehorn {
789b4dbc599SNathan Whitehorn 	struct pmu_softc *sc = device_get_softc(dev);
790b4dbc599SNathan Whitehorn 
791b4dbc599SNathan Whitehorn 	/* magical incantation to re-enable autopolling */
792b4dbc599SNathan Whitehorn 	uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff};
793b4dbc599SNathan Whitehorn 	uint8_t resp[16];
794b4dbc599SNathan Whitehorn 
795b4dbc599SNathan Whitehorn 	mtx_lock(&sc->sc_mutex);
796b4dbc599SNathan Whitehorn 
797b4dbc599SNathan Whitehorn 	if (sc->sc_autopoll == mask) {
798b4dbc599SNathan Whitehorn 		mtx_unlock(&sc->sc_mutex);
799b4dbc599SNathan Whitehorn 		return 0;
800b4dbc599SNathan Whitehorn 	}
801b4dbc599SNathan Whitehorn 
802b4dbc599SNathan Whitehorn 	sc->sc_autopoll = mask & 0xffff;
803b4dbc599SNathan Whitehorn 
804b4dbc599SNathan Whitehorn 	if (mask)
805b4dbc599SNathan Whitehorn 		pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp);
806b4dbc599SNathan Whitehorn 	else
807b4dbc599SNathan Whitehorn 		pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp);
808b4dbc599SNathan Whitehorn 
809b4dbc599SNathan Whitehorn 	mtx_unlock(&sc->sc_mutex);
810b4dbc599SNathan Whitehorn 
811b4dbc599SNathan Whitehorn 	return 0;
812b4dbc599SNathan Whitehorn }
8131af2e191SNathan Whitehorn 
814a228f5cdSNathan Whitehorn static void
815b2a237beSNathan Whitehorn pmu_shutdown(void *xsc, int howto)
816b2a237beSNathan Whitehorn {
817b2a237beSNathan Whitehorn 	struct pmu_softc *sc = xsc;
818b2a237beSNathan Whitehorn 	uint8_t cmd[] = {'M', 'A', 'T', 'T'};
819b2a237beSNathan Whitehorn 
82041e26e82SMitchell Horne 	if ((howto & RB_POWEROFF) != 0)
821b2a237beSNathan Whitehorn 		pmu_send(sc, PMU_POWER_OFF, 4, cmd, 0, NULL);
82241e26e82SMitchell Horne 	else if ((howto & RB_HALT) == 0)
823b2a237beSNathan Whitehorn 		pmu_send(sc, PMU_RESET_CPU, 0, NULL, 0, NULL);
82441e26e82SMitchell Horne 	else
82541e26e82SMitchell Horne 		return;
826b2a237beSNathan Whitehorn 
827b2a237beSNathan Whitehorn 	for (;;);
828b2a237beSNathan Whitehorn }
829b2a237beSNathan Whitehorn 
830b2a237beSNathan Whitehorn static void
831a228f5cdSNathan Whitehorn pmu_set_sleepled(void *xsc, int onoff)
832a228f5cdSNathan Whitehorn {
833a228f5cdSNathan Whitehorn 	struct pmu_softc *sc = xsc;
834a228f5cdSNathan Whitehorn 	uint8_t cmd[] = {4, 0, 0};
835a228f5cdSNathan Whitehorn 
836a228f5cdSNathan Whitehorn 	cmd[2] = onoff;
837a228f5cdSNathan Whitehorn 
838a228f5cdSNathan Whitehorn 	mtx_lock(&sc->sc_mutex);
839a228f5cdSNathan Whitehorn 	pmu_send(sc, PMU_SET_SLEEPLED, 3, cmd, 0, NULL);
840a228f5cdSNathan Whitehorn 	mtx_unlock(&sc->sc_mutex);
841a228f5cdSNathan Whitehorn }
842a228f5cdSNathan Whitehorn 
8431af2e191SNathan Whitehorn static int
8441af2e191SNathan Whitehorn pmu_server_mode(SYSCTL_HANDLER_ARGS)
8451af2e191SNathan Whitehorn {
8461af2e191SNathan Whitehorn 	struct pmu_softc *sc = arg1;
8471af2e191SNathan Whitehorn 
8481af2e191SNathan Whitehorn 	u_int server_mode = 0;
8491af2e191SNathan Whitehorn 	uint8_t getcmd[] = {PMU_PWR_GET_POWERUP_EVENTS};
8501af2e191SNathan Whitehorn 	uint8_t setcmd[] = {0, 0, PMU_PWR_WAKEUP_AC_INSERT};
8511af2e191SNathan Whitehorn 	uint8_t resp[3];
8521af2e191SNathan Whitehorn 	int error, len;
8531af2e191SNathan Whitehorn 
8541af2e191SNathan Whitehorn 	mtx_lock(&sc->sc_mutex);
8551af2e191SNathan Whitehorn 	len = pmu_send(sc, PMU_POWER_EVENTS, 1, getcmd, 3, resp);
8561af2e191SNathan Whitehorn 	mtx_unlock(&sc->sc_mutex);
8571af2e191SNathan Whitehorn 
8581af2e191SNathan Whitehorn 	if (len == 3)
8591af2e191SNathan Whitehorn 		server_mode = (resp[2] & PMU_PWR_WAKEUP_AC_INSERT) ? 1 : 0;
8601af2e191SNathan Whitehorn 
8611af2e191SNathan Whitehorn 	error = sysctl_handle_int(oidp, &server_mode, 0, req);
8621af2e191SNathan Whitehorn 
8631af2e191SNathan Whitehorn 	if (len != 3)
8641af2e191SNathan Whitehorn 		return (EINVAL);
8651af2e191SNathan Whitehorn 
8661af2e191SNathan Whitehorn 	if (error || !req->newptr)
8671af2e191SNathan Whitehorn 		return (error);
8681af2e191SNathan Whitehorn 
8691af2e191SNathan Whitehorn 	if (server_mode == 1)
8701af2e191SNathan Whitehorn 		setcmd[0] = PMU_PWR_SET_POWERUP_EVENTS;
8711af2e191SNathan Whitehorn 	else if (server_mode == 0)
8721af2e191SNathan Whitehorn 		setcmd[0] = PMU_PWR_CLR_POWERUP_EVENTS;
8731af2e191SNathan Whitehorn 	else
8741af2e191SNathan Whitehorn 		return (EINVAL);
8751af2e191SNathan Whitehorn 
8761af2e191SNathan Whitehorn 	setcmd[1] = resp[1];
8771af2e191SNathan Whitehorn 
8781af2e191SNathan Whitehorn 	mtx_lock(&sc->sc_mutex);
8791af2e191SNathan Whitehorn 	pmu_send(sc, PMU_POWER_EVENTS, 3, setcmd, 2, resp);
8801af2e191SNathan Whitehorn 	mtx_unlock(&sc->sc_mutex);
8811af2e191SNathan Whitehorn 
8821af2e191SNathan Whitehorn 	return (0);
8831af2e191SNathan Whitehorn }
8841af2e191SNathan Whitehorn 
885eff47708SNathan Whitehorn static int
886eff47708SNathan Whitehorn pmu_query_battery(struct pmu_softc *sc, int batt, struct pmu_battstate *info)
887eff47708SNathan Whitehorn {
888eff47708SNathan Whitehorn 	uint8_t reg;
889eff47708SNathan Whitehorn 	uint8_t resp[16];
890eff47708SNathan Whitehorn 	int len;
891eff47708SNathan Whitehorn 
892eff47708SNathan Whitehorn 	reg = batt + 1;
893eff47708SNathan Whitehorn 
894eff47708SNathan Whitehorn 	mtx_lock(&sc->sc_mutex);
895eff47708SNathan Whitehorn 	len = pmu_send(sc, PMU_SMART_BATTERY_STATE, 1, &reg, 16, resp);
896eff47708SNathan Whitehorn 	mtx_unlock(&sc->sc_mutex);
897eff47708SNathan Whitehorn 
898eff47708SNathan Whitehorn 	if (len < 3)
899eff47708SNathan Whitehorn 		return (-1);
900eff47708SNathan Whitehorn 
901eff47708SNathan Whitehorn 	/* All PMU battery info replies share a common header:
902eff47708SNathan Whitehorn 	 * Byte 1	Payload Format
903eff47708SNathan Whitehorn 	 * Byte 2	Battery Flags
904eff47708SNathan Whitehorn 	 */
905eff47708SNathan Whitehorn 
906eff47708SNathan Whitehorn 	info->state = resp[2];
907eff47708SNathan Whitehorn 
908eff47708SNathan Whitehorn 	switch (resp[1]) {
909eff47708SNathan Whitehorn 	case 3:
910eff47708SNathan Whitehorn 	case 4:
911eff47708SNathan Whitehorn 		/*
912eff47708SNathan Whitehorn 		 * Formats 3 and 4 appear to be the same:
913eff47708SNathan Whitehorn 		 * Byte 3	Charge
914eff47708SNathan Whitehorn 		 * Byte 4	Max Charge
915eff47708SNathan Whitehorn 		 * Byte 5	Current
916eff47708SNathan Whitehorn 		 * Byte 6	Voltage
917eff47708SNathan Whitehorn 		 */
918eff47708SNathan Whitehorn 
919eff47708SNathan Whitehorn 		info->charge = resp[3];
920eff47708SNathan Whitehorn 		info->maxcharge = resp[4];
921eff47708SNathan Whitehorn 		/* Current can be positive or negative */
922eff47708SNathan Whitehorn 		info->current = (int8_t)resp[5];
923eff47708SNathan Whitehorn 		info->voltage = resp[6];
924eff47708SNathan Whitehorn 		break;
925eff47708SNathan Whitehorn 	case 5:
926eff47708SNathan Whitehorn 		/*
927eff47708SNathan Whitehorn 		 * Formats 5 is a wider version of formats 3 and 4
928eff47708SNathan Whitehorn 		 * Byte 3-4	Charge
929eff47708SNathan Whitehorn 		 * Byte 5-6	Max Charge
930eff47708SNathan Whitehorn 		 * Byte 7-8	Current
931eff47708SNathan Whitehorn 		 * Byte 9-10	Voltage
932eff47708SNathan Whitehorn 		 */
933eff47708SNathan Whitehorn 
934eff47708SNathan Whitehorn 		info->charge = (resp[3] << 8) | resp[4];
935eff47708SNathan Whitehorn 		info->maxcharge = (resp[5] << 8) | resp[6];
936eff47708SNathan Whitehorn 		/* Current can be positive or negative */
937eff47708SNathan Whitehorn 		info->current = (int16_t)((resp[7] << 8) | resp[8]);
938eff47708SNathan Whitehorn 		info->voltage = (resp[9] << 8) | resp[10];
939eff47708SNathan Whitehorn 		break;
940eff47708SNathan Whitehorn 	default:
941eff47708SNathan Whitehorn 		device_printf(sc->sc_dev, "Unknown battery info format (%d)!\n",
942eff47708SNathan Whitehorn 		    resp[1]);
943eff47708SNathan Whitehorn 		return (-1);
944eff47708SNathan Whitehorn 	}
945eff47708SNathan Whitehorn 
946eff47708SNathan Whitehorn 	return (0);
947eff47708SNathan Whitehorn }
948eff47708SNathan Whitehorn 
949d49c6f02SJustin Hibbits static void
950d49c6f02SJustin Hibbits pmu_battery_notify(struct pmu_battstate *batt, struct pmu_battstate *old)
951d49c6f02SJustin Hibbits {
952d49c6f02SJustin Hibbits 	char notify_buf[16];
9533cd55688SJustin Hibbits 	int new_acline, old_acline;
954d49c6f02SJustin Hibbits 
9553cd55688SJustin Hibbits 	new_acline = (batt->state & PMU_PWR_AC_PRESENT) ? 1 : 0;
9563cd55688SJustin Hibbits 	old_acline = (old->state & PMU_PWR_AC_PRESENT) ? 1 : 0;
9573cd55688SJustin Hibbits 
9583cd55688SJustin Hibbits 	if (new_acline != old_acline) {
959d49c6f02SJustin Hibbits 		snprintf(notify_buf, sizeof(notify_buf),
9603cd55688SJustin Hibbits 		    "notify=0x%02x", new_acline);
961d49c6f02SJustin Hibbits 		devctl_notify("PMU", "POWER", "ACLINE", notify_buf);
962d49c6f02SJustin Hibbits 	}
963d49c6f02SJustin Hibbits }
964d49c6f02SJustin Hibbits 
965d49c6f02SJustin Hibbits static void
9665f2995aaSJohn Baldwin pmu_battquery_proc(void)
967d49c6f02SJustin Hibbits {
968d49c6f02SJustin Hibbits 	struct pmu_softc *sc;
969d49c6f02SJustin Hibbits 	struct pmu_battstate batt;
970d49c6f02SJustin Hibbits 	struct pmu_battstate cur_batt;
971d49c6f02SJustin Hibbits 	int error;
972d49c6f02SJustin Hibbits 
973d49c6f02SJustin Hibbits 	sc = device_get_softc(pmu);
974d49c6f02SJustin Hibbits 
9753cd55688SJustin Hibbits 	bzero(&cur_batt, sizeof(cur_batt));
976d49c6f02SJustin Hibbits 	while (1) {
9773cd55688SJustin Hibbits 		kproc_suspend_check(curproc);
978d49c6f02SJustin Hibbits 		error = pmu_query_battery(sc, 0, &batt);
9795f2995aaSJohn Baldwin 		if (error == 0) {
980d49c6f02SJustin Hibbits 			pmu_battery_notify(&batt, &cur_batt);
981d49c6f02SJustin Hibbits 			cur_batt = batt;
9825f2995aaSJohn Baldwin 		}
983d49c6f02SJustin Hibbits 		pause("pmu_batt", hz);
984d49c6f02SJustin Hibbits 	}
985d49c6f02SJustin Hibbits }
986d49c6f02SJustin Hibbits 
987eff47708SNathan Whitehorn static int
9883cd55688SJustin Hibbits pmu_battmon(SYSCTL_HANDLER_ARGS)
9893cd55688SJustin Hibbits {
9903cd55688SJustin Hibbits 	int error, result;
9913cd55688SJustin Hibbits 
9923cd55688SJustin Hibbits 	result = pmu_battmon_enabled;
9933cd55688SJustin Hibbits 
9943cd55688SJustin Hibbits 	error = sysctl_handle_int(oidp, &result, 0, req);
9953cd55688SJustin Hibbits 
9963cd55688SJustin Hibbits 	if (error || !req->newptr)
9973cd55688SJustin Hibbits 		return (error);
9983cd55688SJustin Hibbits 
9993cd55688SJustin Hibbits 	if (!result && pmu_battmon_enabled)
10003cd55688SJustin Hibbits 		error = kproc_suspend(pmubattproc, hz);
10013cd55688SJustin Hibbits 	else if (result && pmu_battmon_enabled == 0)
10023cd55688SJustin Hibbits 		error = kproc_resume(pmubattproc);
10033cd55688SJustin Hibbits 	pmu_battmon_enabled = (result != 0);
10043cd55688SJustin Hibbits 
10053cd55688SJustin Hibbits 	return (error);
10063cd55688SJustin Hibbits }
10073cd55688SJustin Hibbits 
10083cd55688SJustin Hibbits static int
10091165ddc2SNathan Whitehorn pmu_acline_state(SYSCTL_HANDLER_ARGS)
10101165ddc2SNathan Whitehorn {
10111165ddc2SNathan Whitehorn 	struct pmu_softc *sc;
10121165ddc2SNathan Whitehorn 	struct pmu_battstate batt;
10131165ddc2SNathan Whitehorn 	int error, result;
10141165ddc2SNathan Whitehorn 
10151165ddc2SNathan Whitehorn 	sc = arg1;
10161165ddc2SNathan Whitehorn 
10171165ddc2SNathan Whitehorn 	/* The PMU treats the AC line status as a property of the battery */
10181165ddc2SNathan Whitehorn 	error = pmu_query_battery(sc, 0, &batt);
10191165ddc2SNathan Whitehorn 
10201165ddc2SNathan Whitehorn 	if (error != 0)
10211165ddc2SNathan Whitehorn 		return (error);
10221165ddc2SNathan Whitehorn 
10231165ddc2SNathan Whitehorn 	result = (batt.state & PMU_PWR_AC_PRESENT) ? 1 : 0;
10241165ddc2SNathan Whitehorn 	error = sysctl_handle_int(oidp, &result, 0, req);
10251165ddc2SNathan Whitehorn 
10261165ddc2SNathan Whitehorn 	return (error);
10271165ddc2SNathan Whitehorn }
10281165ddc2SNathan Whitehorn 
10291165ddc2SNathan Whitehorn static int
1030eff47708SNathan Whitehorn pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS)
1031eff47708SNathan Whitehorn {
1032eff47708SNathan Whitehorn 	struct pmu_softc *sc;
1033eff47708SNathan Whitehorn 	struct pmu_battstate batt;
1034eff47708SNathan Whitehorn 	int error, result;
1035eff47708SNathan Whitehorn 
1036eff47708SNathan Whitehorn 	sc = arg1;
1037eff47708SNathan Whitehorn 
1038eff47708SNathan Whitehorn 	error = pmu_query_battery(sc, arg2 & 0x00ff, &batt);
1039eff47708SNathan Whitehorn 
1040eff47708SNathan Whitehorn 	if (error != 0)
1041eff47708SNathan Whitehorn 		return (error);
1042eff47708SNathan Whitehorn 
1043eff47708SNathan Whitehorn 	switch (arg2 & 0xff00) {
1044eff47708SNathan Whitehorn 	case PMU_BATSYSCTL_PRESENT:
1045eff47708SNathan Whitehorn 		result = (batt.state & PMU_PWR_BATT_PRESENT) ? 1 : 0;
1046eff47708SNathan Whitehorn 		break;
1047eff47708SNathan Whitehorn 	case PMU_BATSYSCTL_CHARGING:
1048eff47708SNathan Whitehorn 		result = (batt.state & PMU_PWR_BATT_CHARGING) ? 1 : 0;
1049eff47708SNathan Whitehorn 		break;
1050eff47708SNathan Whitehorn 	case PMU_BATSYSCTL_CHARGE:
1051eff47708SNathan Whitehorn 		result = batt.charge;
1052eff47708SNathan Whitehorn 		break;
1053eff47708SNathan Whitehorn 	case PMU_BATSYSCTL_MAXCHARGE:
1054eff47708SNathan Whitehorn 		result = batt.maxcharge;
1055eff47708SNathan Whitehorn 		break;
1056eff47708SNathan Whitehorn 	case PMU_BATSYSCTL_CURRENT:
1057eff47708SNathan Whitehorn 		result = batt.current;
1058eff47708SNathan Whitehorn 		break;
1059eff47708SNathan Whitehorn 	case PMU_BATSYSCTL_VOLTAGE:
1060eff47708SNathan Whitehorn 		result = batt.voltage;
1061eff47708SNathan Whitehorn 		break;
1062eff47708SNathan Whitehorn 	case PMU_BATSYSCTL_TIME:
1063eff47708SNathan Whitehorn 		/* Time remaining until full charge/discharge, in minutes */
1064eff47708SNathan Whitehorn 
1065eff47708SNathan Whitehorn 		if (batt.current >= 0)
1066eff47708SNathan Whitehorn 			result = (batt.maxcharge - batt.charge) /* mAh */ * 60
1067eff47708SNathan Whitehorn 			    / batt.current /* mA */;
1068eff47708SNathan Whitehorn 		else
1069eff47708SNathan Whitehorn 			result = (batt.charge /* mAh */ * 60)
1070eff47708SNathan Whitehorn 			    / (-batt.current /* mA */);
1071eff47708SNathan Whitehorn 		break;
1072eff47708SNathan Whitehorn 	case PMU_BATSYSCTL_LIFE:
1073eff47708SNathan Whitehorn 		/* Battery charge fraction, in percent */
1074eff47708SNathan Whitehorn 		result = (batt.charge * 100) / batt.maxcharge;
1075eff47708SNathan Whitehorn 		break;
1076eff47708SNathan Whitehorn 	default:
1077eff47708SNathan Whitehorn 		/* This should never happen */
1078eff47708SNathan Whitehorn 		result = -1;
107974b8d63dSPedro F. Giffuni 	}
1080eff47708SNathan Whitehorn 
1081eff47708SNathan Whitehorn 	error = sysctl_handle_int(oidp, &result, 0, req);
1082eff47708SNathan Whitehorn 
1083eff47708SNathan Whitehorn 	return (error);
1084eff47708SNathan Whitehorn }
1085eff47708SNathan Whitehorn 
10863df9e037SNathan Whitehorn #define DIFF19041970	2082844800
10873df9e037SNathan Whitehorn 
10883df9e037SNathan Whitehorn static int
10893df9e037SNathan Whitehorn pmu_gettime(device_t dev, struct timespec *ts)
10903df9e037SNathan Whitehorn {
10913df9e037SNathan Whitehorn 	struct pmu_softc *sc = device_get_softc(dev);
10923df9e037SNathan Whitehorn 	uint8_t resp[16];
10933df9e037SNathan Whitehorn 	uint32_t sec;
10943df9e037SNathan Whitehorn 
10953df9e037SNathan Whitehorn 	mtx_lock(&sc->sc_mutex);
10963df9e037SNathan Whitehorn 	pmu_send(sc, PMU_READ_RTC, 0, NULL, 16, resp);
10973df9e037SNathan Whitehorn 	mtx_unlock(&sc->sc_mutex);
10983df9e037SNathan Whitehorn 
10993df9e037SNathan Whitehorn 	memcpy(&sec, &resp[1], 4);
11003df9e037SNathan Whitehorn 	ts->tv_sec = sec - DIFF19041970;
11013df9e037SNathan Whitehorn 	ts->tv_nsec = 0;
11023df9e037SNathan Whitehorn 
11033df9e037SNathan Whitehorn 	return (0);
11043df9e037SNathan Whitehorn }
11053df9e037SNathan Whitehorn 
11063df9e037SNathan Whitehorn static int
11073df9e037SNathan Whitehorn pmu_settime(device_t dev, struct timespec *ts)
11083df9e037SNathan Whitehorn {
11093df9e037SNathan Whitehorn 	struct pmu_softc *sc = device_get_softc(dev);
11103df9e037SNathan Whitehorn 	uint32_t sec;
11113df9e037SNathan Whitehorn 
11123df9e037SNathan Whitehorn 	sec = ts->tv_sec + DIFF19041970;
11133df9e037SNathan Whitehorn 
11143df9e037SNathan Whitehorn 	mtx_lock(&sc->sc_mutex);
11153df9e037SNathan Whitehorn 	pmu_send(sc, PMU_SET_RTC, sizeof(sec), (uint8_t *)&sec, 0, NULL);
11163df9e037SNathan Whitehorn 	mtx_unlock(&sc->sc_mutex);
11173df9e037SNathan Whitehorn 
11183df9e037SNathan Whitehorn 	return (0);
11193df9e037SNathan Whitehorn }
11203df9e037SNathan Whitehorn 
11214702d987SJustin Hibbits int
11224702d987SJustin Hibbits pmu_set_speed(int low_speed)
11234702d987SJustin Hibbits {
11244702d987SJustin Hibbits 	struct pmu_softc *sc;
11254702d987SJustin Hibbits 	uint8_t sleepcmd[] = {'W', 'O', 'O', 'F', 0};
11264702d987SJustin Hibbits 	uint8_t resp[16];
11274702d987SJustin Hibbits 
11284702d987SJustin Hibbits 	sc = device_get_softc(pmu);
11294702d987SJustin Hibbits 	pmu_write_reg(sc, vIER, 0x10);
11304702d987SJustin Hibbits 	spinlock_enter();
11314702d987SJustin Hibbits 	mtdec(0x7fffffff);
11324702d987SJustin Hibbits 	mb();
11334702d987SJustin Hibbits 	mtdec(0x7fffffff);
11344702d987SJustin Hibbits 
11354702d987SJustin Hibbits 	sleepcmd[4] = low_speed;
11364702d987SJustin Hibbits 	pmu_send(sc, PMU_CPU_SPEED, 5, sleepcmd, 16, resp);
11374702d987SJustin Hibbits 	unin_chip_sleep(NULL, 1);
1138e1c161e7SJustin Hibbits 	platform_sleep();
11394702d987SJustin Hibbits 	unin_chip_wake(NULL);
11404702d987SJustin Hibbits 
11414702d987SJustin Hibbits 	mtdec(1);	/* Force a decrementer exception */
11424702d987SJustin Hibbits 	spinlock_exit();
11434702d987SJustin Hibbits 	pmu_write_reg(sc, vIER, 0x90);
11444702d987SJustin Hibbits 
11454702d987SJustin Hibbits 	return (0);
11464702d987SJustin Hibbits }
1147