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