1164010Smarcel/*	$NetBSD: zapm.c,v 1.11 2011/06/19 16:20:09 nonaka Exp $	*/
2164010Smarcel/*	$OpenBSD: zaurus_apm.c,v 1.13 2006/12/12 23:14:28 dim Exp $	*/
3164010Smarcel
4164010Smarcel/*
5164010Smarcel * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de>
6164010Smarcel *
7164010Smarcel * Permission to use, copy, modify, and distribute this software for any
8164010Smarcel * purpose with or without fee is hereby granted, provided that the above
9164010Smarcel * copyright notice and this permission notice appear in all copies.
10164010Smarcel *
11164010Smarcel * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12164010Smarcel * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13164010Smarcel * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14164010Smarcel * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15164010Smarcel * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16164010Smarcel * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17164010Smarcel * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18164010Smarcel */
19164010Smarcel
20164010Smarcel#include <sys/cdefs.h>
21164010Smarcel__KERNEL_RCSID(0, "$NetBSD: zapm.c,v 1.11 2011/06/19 16:20:09 nonaka Exp $");
22164010Smarcel
23164010Smarcel#include <sys/param.h>
24164010Smarcel#include <sys/systm.h>
25164010Smarcel#include <sys/kernel.h>
26164010Smarcel#include <sys/callout.h>
27164010Smarcel#include <sys/selinfo.h> /* XXX: for apm_softc that is exposed here */
28164010Smarcel#include <sys/device.h>
29164010Smarcel
30164010Smarcel#include <dev/hpc/apm/apmvar.h>
31164010Smarcel
32164010Smarcel#include <arm/xscale/pxa2x0reg.h>
33164010Smarcel#include <arm/xscale/pxa2x0var.h>
34220313Smarcel#include <arm/xscale/pxa2x0cpu.h>
35164010Smarcel#include <arm/xscale/pxa2x0_gpio.h>
36220313Smarcel
37219691Smarcel#include <machine/config_hook.h>
38164010Smarcel
39164010Smarcel#include <zaurus/dev/scoopvar.h>
40164010Smarcel#include <zaurus/dev/zsspvar.h>
41164010Smarcel#include <zaurus/zaurus/zaurus_reg.h>
42219691Smarcel#include <zaurus/zaurus/zaurus_var.h>
43219691Smarcel
44219691Smarcel#ifdef APMDEBUG
45219691Smarcel#define DPRINTF(x)	printf x
46164010Smarcel#else
47164010Smarcel#define	DPRINTF(x)	do { } while (/*CONSTCOND*/0)
48164010Smarcel#endif
49164010Smarcel
50164010Smarcelstruct zapm_softc {
51220313Smarcel	device_t sc_dev;
52220313Smarcel	void *sc_apmdev;
53219691Smarcel	kmutex_t sc_mtx;
54219691Smarcel
55164010Smarcel	struct callout sc_cyclic_poll;
56219691Smarcel	struct callout sc_discharge_poll;
57219691Smarcel	struct timeval sc_lastbattchk;
58220313Smarcel	volatile int suspended;
59220313Smarcel	volatile int charging;
60220313Smarcel	volatile int discharging;
61220313Smarcel	int battery_volt;
62220313Smarcel	int battery_full_cnt;
63220313Smarcel
64164010Smarcel	/* GPIO pin */
65219691Smarcel	int sc_ac_detect_pin;
66219691Smarcel	int sc_batt_cover_pin;
67222798Smarcel	int sc_charge_comp_pin;
68219691Smarcel
69220313Smarcel	/* machine-independent part */
70164010Smarcel	volatile u_int events;
71219691Smarcel	volatile int power_state;
72219691Smarcel	volatile int battery_state;
73219691Smarcel	volatile int ac_state;
74164010Smarcel	config_hook_tag sc_standby_hook;
75164010Smarcel	config_hook_tag sc_suspend_hook;
76	config_hook_tag sc_battery_hook;
77	config_hook_tag sc_ac_hook;
78	int battery_life;
79	int minutes_left;
80};
81
82static int	zapm_match(device_t, cfdata_t, void *);
83static void	zapm_attach(device_t, device_t, void *);
84
85CFATTACH_DECL_NEW(zapm, sizeof(struct zapm_softc),
86    zapm_match, zapm_attach, NULL, NULL);
87
88static int	zapm_hook(void *, int, long, void *);
89static void     zapm_disconnect(void *);
90static void     zapm_enable(void *, int);
91static int      zapm_set_powstate(void *, u_int, u_int);
92static int      zapm_get_powstat(void *, u_int, struct apm_power_info *);
93static int      zapm_get_event(void *, u_int *, u_int *);
94static void     zapm_cpu_busy(void *);
95static void     zapm_cpu_idle(void *);
96static void     zapm_get_capabilities(void *, u_int *, u_int *);
97
98static struct apm_accessops zapm_accessops = {
99	zapm_disconnect,
100	zapm_enable,
101	zapm_set_powstate,
102	zapm_get_powstat,
103	zapm_get_event,
104	zapm_cpu_busy,
105	zapm_cpu_idle,
106	zapm_get_capabilities,
107};
108
109static int	zapm_acintr(void *);
110static int	zapm_bcintr(void *);
111static void	zapm_cyclic(void *);
112static void	zapm_poll(void *);
113static void	zapm_poll1(void *, int);
114
115/* battery-related GPIO pins */
116#define GPIO_AC_IN_C3000	115	/* 0=AC connected */
117#define GPIO_CHRG_CO_C3000	101	/* 1=battery full */
118#define GPIO_BATT_COVER_C3000	90	/* 0=unlocked */
119
120/* Cyclic timer value */
121#define	CYCLIC_TIME	(60 * hz)	/* 60s */
122
123static int
124zapm_match(device_t parent, cfdata_t cf, void *aux)
125{
126
127	if (ZAURUS_ISC1000 || ZAURUS_ISC3000)
128		return 1;
129	return 0;
130}
131
132static void
133zapm_attach(device_t parent, device_t self, void *aux)
134{
135	struct zapm_softc *sc = device_private(self);
136	struct apmdev_attach_args aaa;
137
138	sc->sc_dev = self;
139
140	aprint_normal(": pseudo power management module\n");
141	aprint_naive("\n");
142
143	/* machine-depent part */
144	callout_init(&sc->sc_cyclic_poll, 0);
145	callout_setfunc(&sc->sc_cyclic_poll, zapm_cyclic, sc);
146	callout_init(&sc->sc_discharge_poll, 0);
147	callout_setfunc(&sc->sc_discharge_poll, zapm_poll, sc);
148	mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE);
149
150	if (ZAURUS_ISC1000 || ZAURUS_ISC3000) {
151		sc->sc_ac_detect_pin = GPIO_AC_IN_C3000;
152		sc->sc_batt_cover_pin = GPIO_BATT_COVER_C3000;
153		sc->sc_charge_comp_pin = GPIO_CHRG_CO_C3000;
154	} else {
155		/* XXX */
156		return;
157	}
158
159	pxa2x0_gpio_set_function(sc->sc_ac_detect_pin, GPIO_IN);
160	pxa2x0_gpio_set_function(sc->sc_charge_comp_pin, GPIO_IN);
161	pxa2x0_gpio_set_function(sc->sc_batt_cover_pin, GPIO_IN);
162
163	(void)pxa2x0_gpio_intr_establish(sc->sc_ac_detect_pin,
164	    IST_EDGE_BOTH, IPL_BIO, zapm_acintr, sc);
165	(void)pxa2x0_gpio_intr_establish(sc->sc_charge_comp_pin,
166	    IST_EDGE_BOTH, IPL_BIO, zapm_bcintr, sc);
167
168	/* machine-independent part */
169	sc->events = 0;
170	sc->power_state = APM_SYS_READY;
171	sc->battery_state = APM_BATT_FLAG_UNKNOWN;
172	sc->ac_state = APM_AC_UNKNOWN;
173	sc->battery_life = APM_BATT_LIFE_UNKNOWN;
174	sc->minutes_left = 0;
175	sc->sc_standby_hook = config_hook(CONFIG_HOOK_PMEVENT,
176					  CONFIG_HOOK_PMEVENT_STANDBYREQ,
177					  CONFIG_HOOK_EXCLUSIVE,
178					  zapm_hook, sc);
179	sc->sc_suspend_hook = config_hook(CONFIG_HOOK_PMEVENT,
180					  CONFIG_HOOK_PMEVENT_SUSPENDREQ,
181					  CONFIG_HOOK_EXCLUSIVE,
182					  zapm_hook, sc);
183
184	sc->sc_battery_hook = config_hook(CONFIG_HOOK_PMEVENT,
185					  CONFIG_HOOK_PMEVENT_BATTERY,
186					  CONFIG_HOOK_SHARE,
187					  zapm_hook, sc);
188
189	sc->sc_ac_hook = config_hook(CONFIG_HOOK_PMEVENT,
190				     CONFIG_HOOK_PMEVENT_AC,
191				     CONFIG_HOOK_SHARE,
192				     zapm_hook, sc);
193
194	aaa.accessops = &zapm_accessops;
195	aaa.accesscookie = sc;
196	aaa.apm_detail = 0x0102;
197
198	sc->sc_apmdev = config_found_ia(self, "apmdevif", &aaa, apmprint);
199	if (sc->sc_apmdev != NULL) {
200		zapm_poll1(sc, 0);
201		callout_schedule(&sc->sc_cyclic_poll, CYCLIC_TIME);
202	}
203}
204
205static int
206zapm_hook(void *v, int type, long id, void *msg)
207{
208	struct zapm_softc *sc = (struct zapm_softc *)v;
209	int charge;
210	int message;
211	int s;
212
213	if (type != CONFIG_HOOK_PMEVENT)
214		return 1;
215
216	if (CONFIG_HOOK_VALUEP(msg))
217		message = (int)msg;
218	else
219		message = *(int *)msg;
220
221	s = splhigh();
222
223	switch (id) {
224	case CONFIG_HOOK_PMEVENT_STANDBYREQ:
225		if (sc->power_state != APM_SYS_STANDBY) {
226			sc->events |= (1 << APM_USER_STANDBY_REQ);
227		} else {
228			sc->events |= (1 << APM_NORMAL_RESUME);
229		}
230		break;
231	case CONFIG_HOOK_PMEVENT_SUSPENDREQ:
232		if (sc->power_state != APM_SYS_SUSPEND) {
233			DPRINTF(("zapm: suspend request\n"));
234			sc->events |= (1 << APM_USER_SUSPEND_REQ);
235		} else {
236			sc->events |= (1 << APM_NORMAL_RESUME);
237		}
238		break;
239	case CONFIG_HOOK_PMEVENT_BATTERY:
240		switch (message) {
241		case CONFIG_HOOK_BATT_CRITICAL:
242			DPRINTF(("zapm: battery state critical\n"));
243			charge = sc->battery_state & APM_BATT_FLAG_CHARGING;
244			sc->battery_state = APM_BATT_FLAG_CRITICAL;
245			sc->battery_state |= charge;
246			sc->battery_life = 0;
247			break;
248		case CONFIG_HOOK_BATT_LOW:
249			DPRINTF(("zapm: battery state low\n"));
250			charge = sc->battery_state & APM_BATT_FLAG_CHARGING;
251			sc->battery_state = APM_BATT_FLAG_LOW;
252			sc->battery_state |= charge;
253			break;
254		case CONFIG_HOOK_BATT_HIGH:
255			DPRINTF(("zapm: battery state high\n"));
256			charge = sc->battery_state & APM_BATT_FLAG_CHARGING;
257			sc->battery_state = APM_BATT_FLAG_HIGH;
258			sc->battery_state |= charge;
259			break;
260		case CONFIG_HOOK_BATT_10P:
261			DPRINTF(("zapm: battery life 10%%\n"));
262			sc->battery_life = 10;
263			break;
264		case CONFIG_HOOK_BATT_20P:
265			DPRINTF(("zapm: battery life 20%%\n"));
266			sc->battery_life = 20;
267			break;
268		case CONFIG_HOOK_BATT_30P:
269			DPRINTF(("zapm: battery life 30%%\n"));
270			sc->battery_life = 30;
271			break;
272		case CONFIG_HOOK_BATT_40P:
273			DPRINTF(("zapm: battery life 40%%\n"));
274			sc->battery_life = 40;
275			break;
276		case CONFIG_HOOK_BATT_50P:
277			DPRINTF(("zapm: battery life 50%%\n"));
278			sc->battery_life = 50;
279			break;
280		case CONFIG_HOOK_BATT_60P:
281			DPRINTF(("zapm: battery life 60%%\n"));
282			sc->battery_life = 60;
283			break;
284		case CONFIG_HOOK_BATT_70P:
285			DPRINTF(("zapm: battery life 70%%\n"));
286			sc->battery_life = 70;
287			break;
288		case CONFIG_HOOK_BATT_80P:
289			DPRINTF(("zapm: battery life 80%%\n"));
290			sc->battery_life = 80;
291			break;
292		case CONFIG_HOOK_BATT_90P:
293			DPRINTF(("zapm: battery life 90%%\n"));
294			sc->battery_life = 90;
295			break;
296		case CONFIG_HOOK_BATT_100P:
297			DPRINTF(("zapm: battery life 100%%\n"));
298			sc->battery_life = 100;
299			break;
300		case CONFIG_HOOK_BATT_UNKNOWN:
301			DPRINTF(("zapm: battery state unknown\n"));
302			sc->battery_state = APM_BATT_FLAG_UNKNOWN;
303			sc->battery_life = APM_BATT_LIFE_UNKNOWN;
304			break;
305		case CONFIG_HOOK_BATT_NO_SYSTEM_BATTERY:
306			DPRINTF(("zapm: battery state no system battery?\n"));
307			sc->battery_state = APM_BATT_FLAG_NO_SYSTEM_BATTERY;
308			sc->battery_life = APM_BATT_LIFE_UNKNOWN;
309			break;
310		}
311		break;
312	case CONFIG_HOOK_PMEVENT_AC:
313		switch (message) {
314		case CONFIG_HOOK_AC_OFF:
315			DPRINTF(("zapm: ac not connected\n"));
316			sc->battery_state &= ~APM_BATT_FLAG_CHARGING;
317			sc->ac_state = APM_AC_OFF;
318			break;
319		case CONFIG_HOOK_AC_ON_CHARGE:
320			DPRINTF(("zapm: charging\n"));
321			sc->battery_state |= APM_BATT_FLAG_CHARGING;
322			sc->ac_state = APM_AC_ON;
323			break;
324		case CONFIG_HOOK_AC_ON_NOCHARGE:
325			DPRINTF(("zapm: ac connected\n"));
326			sc->battery_state &= ~APM_BATT_FLAG_CHARGING;
327			sc->ac_state = APM_AC_ON;
328			break;
329		case CONFIG_HOOK_AC_UNKNOWN:
330			sc->ac_state = APM_AC_UNKNOWN;
331			break;
332		}
333		break;
334	}
335
336	splx(s);
337
338	return 0;
339}
340
341static void
342zapm_disconnect(void *v)
343{
344#if 0
345	struct zapm_softc *sc = (struct zapm_softc *)v;
346#endif
347}
348
349static void
350zapm_enable(void *v, int onoff)
351{
352#if 0
353	struct zapm_softc *sc = (struct zapm_softc *)v;
354#endif
355}
356
357static int
358zapm_set_powstate(void *v, u_int devid, u_int powstat)
359{
360	struct zapm_softc *sc = (struct zapm_softc *)v;
361
362	if (devid != APM_DEV_ALLDEVS)
363		return APM_ERR_UNRECOG_DEV;
364
365	switch (powstat) {
366	case APM_SYS_READY:
367		DPRINTF(("zapm: set power state READY\n"));
368		sc->power_state = APM_SYS_READY;
369		break;
370	case APM_SYS_STANDBY:
371		DPRINTF(("zapm: set power state STANDBY\n"));
372		/* XXX */
373		DPRINTF(("zapm: resume\n"));
374		break;
375	case APM_SYS_SUSPEND:
376		DPRINTF(("zapm: set power state SUSPEND...\n"));
377		/* XXX */
378		DPRINTF(("zapm: resume\n"));
379		break;
380	case APM_SYS_OFF:
381		DPRINTF(("zapm: set power state OFF\n"));
382		sc->power_state = APM_SYS_OFF;
383		break;
384	case APM_LASTREQ_INPROG:
385		/*DPRINTF(("zapm: set power state INPROG\n"));*/
386		break;
387	case APM_LASTREQ_REJECTED:
388		DPRINTF(("zapm: set power state REJECTED\n"));
389		break;
390	}
391
392	return 0;
393}
394
395static int
396zapm_get_powstat(void *v, u_int batteryid, struct apm_power_info *pinfo)
397{
398	struct zapm_softc *sc = (struct zapm_softc *)v;
399	int val;
400
401	if (config_hook_call(CONFIG_HOOK_GET,
402			     CONFIG_HOOK_ACADAPTER, &val) != -1)
403		pinfo->ac_state = val;
404	else
405		pinfo->ac_state = sc->ac_state;
406	DPRINTF(("zapm: pinfo->ac_state: %d\n", pinfo->ac_state));
407
408	if (config_hook_call(CONFIG_HOOK_GET,
409			     CONFIG_HOOK_CHARGE, &val) != -1)
410		pinfo->battery_state = val;
411	else {
412		DPRINTF(("zapm: sc->battery_state: %#x\n", sc->battery_state));
413		if (sc->battery_state & APM_BATT_FLAG_CHARGING)
414			pinfo->battery_flags = APM_BATT_FLAG_CHARGING;
415		else if (sc->battery_state & APM_BATT_FLAG_CRITICAL)
416			pinfo->battery_flags = APM_BATT_FLAG_CRITICAL;
417		else if (sc->battery_state & APM_BATT_FLAG_LOW)
418			pinfo->battery_flags = APM_BATT_FLAG_LOW;
419		else if (sc->battery_state & APM_BATT_FLAG_HIGH)
420			pinfo->battery_flags = APM_BATT_FLAG_HIGH;
421		else
422			pinfo->battery_flags = APM_BATT_FLAG_UNKNOWN;
423	}
424	DPRINTF(("zapm: pinfo->battery_flags: %#x\n", pinfo->battery_flags));
425
426	if (config_hook_call(CONFIG_HOOK_GET,
427			     CONFIG_HOOK_BATTERYVAL, &val) != -1)
428		pinfo->battery_life = val;
429	else
430		pinfo->battery_life = sc->battery_life;
431	DPRINTF(("zapm: pinfo->battery_life: %d\n", pinfo->battery_life));
432
433	return 0;
434}
435
436static int
437zapm_get_event(void *v, u_int *event_type, u_int *event_info)
438{
439	struct zapm_softc *sc = (struct zapm_softc *)v;
440	u_int ev;
441	int s;
442
443	s = splhigh();
444	for (ev = APM_STANDBY_REQ; ev <= APM_CAP_CHANGE; ev++) {
445		if (sc->events & (1 << ev)) {
446			sc->events &= ~(1 << ev);
447			*event_type = ev;
448			if (*event_type == APM_NORMAL_RESUME ||
449			    *event_type == APM_CRIT_RESUME) {
450				/* pccard power off in the suspend state */
451				*event_info = 1;
452				sc->power_state = APM_SYS_READY;
453			} else {
454				*event_info = 0;
455			}
456			splx(s);
457
458			return 0;
459		}
460	}
461	splx(s);
462
463	return APM_ERR_NOEVENTS;
464}
465
466static void
467zapm_cpu_busy(void *v)
468{
469#if 0
470	struct zapm_softc *sc = (struct zapm_softc *)v;
471#endif
472}
473
474static void
475zapm_cpu_idle(void *v)
476{
477#if 0
478	struct zapm_softc *sc = (struct zapm_softc *)v;
479#endif
480}
481
482static void
483zapm_get_capabilities(void *v, u_int *numbatts, u_int *capflags)
484{
485#if 0
486	struct zapm_softc *sc = (struct zapm_softc *)v;
487#endif
488
489	*numbatts = 1;
490	*capflags = 0 /* | APM_GLOBAL_STANDBY | APM_GLOBAL_SUSPEND */;
491}
492
493/*-----------------------------------------------------------------------------
494 * zaurus depent part
495 */
496/* MAX1111 command word */
497#define MAXCTRL_PD0		(1<<0)
498#define MAXCTRL_PD1		(1<<1)
499#define MAXCTRL_SGL		(1<<2)
500#define MAXCTRL_UNI		(1<<3)
501#define MAXCTRL_SEL_SHIFT	4
502#define MAXCTRL_STR		(1<<7)
503
504/* MAX1111 ADC channels */
505#define	BATT_THM		2
506#define	BATT_AD			4
507#define JK_VAD			6
508
509/*
510 * Battery-specific information
511 */
512struct battery_threshold {
513	int	percent;
514	int	value;
515	int	state;
516};
517
518struct battery_info {
519	const struct battery_threshold *bi_thres;
520};
521
522static const struct battery_threshold zaurus_battery_life_c3000[] = {
523	{ 100,	212,	CONFIG_HOOK_BATT_HIGH		},
524	{  98,	212,	CONFIG_HOOK_BATT_HIGH		},
525	{  95,	211,	CONFIG_HOOK_BATT_HIGH		},
526	{  93,	210,	CONFIG_HOOK_BATT_HIGH		},
527	{  90,	209,	CONFIG_HOOK_BATT_HIGH		},
528	{  88,	208,	CONFIG_HOOK_BATT_HIGH		},
529	{  85,	207,	CONFIG_HOOK_BATT_HIGH		},
530	{  83,	206,	CONFIG_HOOK_BATT_HIGH		},
531	{  80,	205,	CONFIG_HOOK_BATT_HIGH		},
532	{  78,	204,	CONFIG_HOOK_BATT_HIGH		},
533	{  75,	203,	CONFIG_HOOK_BATT_HIGH		},
534	{  73,	202,	CONFIG_HOOK_BATT_HIGH		},
535	{  70,	201,	CONFIG_HOOK_BATT_HIGH		},
536	{  68,	200,	CONFIG_HOOK_BATT_HIGH		},
537	{  65,	199,	CONFIG_HOOK_BATT_HIGH		},
538	{  63,	198,	CONFIG_HOOK_BATT_HIGH		},
539	{  60,	197,	CONFIG_HOOK_BATT_HIGH		},
540	{  58,	196,	CONFIG_HOOK_BATT_HIGH		},
541	{  55,	195,	CONFIG_HOOK_BATT_HIGH		},
542	{  53,	194,	CONFIG_HOOK_BATT_HIGH		},
543	{  50,	193,	CONFIG_HOOK_BATT_HIGH		},
544	{  48,	192,	CONFIG_HOOK_BATT_HIGH		},
545	{  45,	192,	CONFIG_HOOK_BATT_HIGH		},
546	{  43,	191,	CONFIG_HOOK_BATT_HIGH		},
547	{  40,	191,	CONFIG_HOOK_BATT_HIGH		},
548	{  38,	190,	CONFIG_HOOK_BATT_HIGH		},
549	{  35,	190,	CONFIG_HOOK_BATT_HIGH		},
550	{  33,	189,	CONFIG_HOOK_BATT_HIGH		},
551	{  30,	188,	CONFIG_HOOK_BATT_HIGH		},
552	{  28,	187,	CONFIG_HOOK_BATT_LOW		},
553	{  25,	186,	CONFIG_HOOK_BATT_LOW		},
554	{  23,	185,	CONFIG_HOOK_BATT_LOW		},
555	{  20,	184,	CONFIG_HOOK_BATT_LOW		},
556	{  18,	183,	CONFIG_HOOK_BATT_LOW		},
557	{  15,	182,	CONFIG_HOOK_BATT_LOW		},
558	{  13,	181,	CONFIG_HOOK_BATT_LOW		},
559	{  10,	180,	CONFIG_HOOK_BATT_LOW		},
560	{   8,	179,	CONFIG_HOOK_BATT_LOW		},
561	{   5,	178,	CONFIG_HOOK_BATT_LOW		},
562	{   0,	  0,	CONFIG_HOOK_BATT_CRITICAL	}
563};
564
565static const struct battery_info zaurus_battery_c3000 = {
566	zaurus_battery_life_c3000
567};
568
569static const struct battery_info *zaurus_main_battery = &zaurus_battery_c3000;
570
571/* Restart charging this many times before accepting BATT_FULL. */
572#define	MIN_BATT_FULL		2
573
574/* Discharge 100 ms before reading the voltage if AC is connected. */
575#define	DISCHARGE_TIMEOUT	(hz / 10)
576
577/* Check battery voltage and "kick charging" every minute. */
578static const struct timeval zapm_battchkrate = { 60, 0 };
579
580static int	zapm_get_ac_state(struct zapm_softc *);
581static int	zapm_get_battery_compartment_state(struct zapm_softc *);
582static int	zapm_get_charge_complete_state(struct zapm_softc *);
583static void	zapm_set_charging(struct zapm_softc *, int);
584static int	zapm_charge_complete(struct zapm_softc *);
585static int	max1111_adc_value_avg(int chan, int pause);
586static int	zapm_get_battery_volt(void);
587static int	zapm_battery_state(int volt);
588static int	zapm_battery_life(int volt);
589
590static int
591zapm_acintr(void *v)
592{
593
594	zapm_poll1(v, 1);
595
596	return 1;
597}
598
599static int
600zapm_bcintr(void *v)
601{
602
603	zapm_poll1(v, 1);
604
605	return 1;
606}
607
608static void
609zapm_cyclic(void *v)
610{
611	struct zapm_softc *sc = (struct zapm_softc *)v;
612
613	zapm_poll1(sc, 1);
614
615	callout_schedule(&sc->sc_cyclic_poll, CYCLIC_TIME);
616}
617
618static void
619zapm_poll(void *v)
620{
621
622	zapm_poll1(v, 1);
623}
624
625static int
626zapm_get_ac_state(struct zapm_softc *sc)
627{
628
629	if (!pxa2x0_gpio_get_bit(sc->sc_ac_detect_pin))
630		return APM_AC_ON;
631	return APM_AC_OFF;
632}
633
634static int
635zapm_get_battery_compartment_state(struct zapm_softc *sc)
636{
637
638	return pxa2x0_gpio_get_bit(sc->sc_batt_cover_pin);
639}
640
641static int
642zapm_get_charge_complete_state(struct zapm_softc *sc)
643{
644
645	return pxa2x0_gpio_get_bit(sc->sc_charge_comp_pin);
646}
647
648static void
649zapm_set_charging(struct zapm_softc *sc, int enable)
650{
651
652	if (ZAURUS_ISC1000 || ZAURUS_ISC3000) {
653		scoop_discharge_battery(0);
654		scoop_charge_battery(enable, 0);
655		scoop_led_set(SCOOP_LED_ORANGE, enable);
656	}
657}
658
659/*
660 * Return non-zero if the charge complete signal indicates that the
661 * battery is fully charged.  Restart charging to clear this signal.
662 */
663static int
664zapm_charge_complete(struct zapm_softc *sc)
665{
666
667	if (sc->charging && sc->battery_full_cnt < MIN_BATT_FULL) {
668		if (zapm_get_charge_complete_state(sc)) {
669			sc->battery_full_cnt++;
670			if (sc->battery_full_cnt < MIN_BATT_FULL) {
671				DPRINTF(("battery almost full\n"));
672				zapm_set_charging(sc, 0);
673				delay(15000);
674				zapm_set_charging(sc, 1);
675			}
676		} else if (sc->battery_full_cnt > 0) {
677			/* false alarm */
678			sc->battery_full_cnt = 0;
679			zapm_set_charging(sc, 0);
680			delay(15000);
681			zapm_set_charging(sc, 1);
682		}
683	}
684
685	return (sc->battery_full_cnt >= MIN_BATT_FULL);
686}
687
688static int
689max1111_adc_value(int chan)
690{
691
692	return ((int)zssp_ic_send(ZSSP_IC_MAX1111, MAXCTRL_PD0 |
693	    MAXCTRL_PD1 | MAXCTRL_SGL | MAXCTRL_UNI |
694	    (chan << MAXCTRL_SEL_SHIFT) | MAXCTRL_STR));
695}
696
697/* XXX simplify */
698static int
699max1111_adc_value_avg(int chan, int pause)
700{
701	int val[5];
702	int sum;
703	int minv, maxv, v;
704	int i;
705
706	DPRINTF(("max1111_adc_value_avg: chan = %d, pause = %d\n",
707	    chan, pause));
708
709	for (i = 0; i < 5; i++) {
710		val[i] = max1111_adc_value(chan);
711		if (i != 4)
712			delay(pause * 1000);
713		DPRINTF(("max1111_adc_value_avg: chan[%d] = %d\n", i, val[i]));
714	}
715
716	/* get max value */
717	v = val[0];
718	minv = 0;
719	for (i = 1; i < 5; i++) {
720		if (v < val[i]) {
721			v = val[i];
722			minv = i;
723		}
724	}
725
726	/* get min value */
727	v = val[4];
728	maxv = 4;
729	for (i = 3; i >= 0; i--) {
730		if (v > val[i]) {
731			v = val[i];
732			maxv = i;
733		}
734	}
735
736	DPRINTF(("max1111_adc_value_avg: minv = %d, maxv = %d\n", minv, maxv));
737	sum = 0;
738	for (i = 0; i < 5; i++) {
739		if (i == minv || i == maxv)
740			continue;
741		sum += val[i];
742	}
743
744	DPRINTF(("max1111_adc_value_avg: sum = %d, sum / 3 = %d\n",
745	    sum, sum / 3));
746
747	return sum / 3;
748}
749
750static int
751zapm_get_battery_volt(void)
752{
753
754	return max1111_adc_value_avg(BATT_AD, 10);
755}
756
757static int
758zapm_battery_state(int volt)
759{
760	const struct battery_threshold *bthr;
761	int i;
762
763	bthr = zaurus_main_battery->bi_thres;
764
765	for (i = 0; bthr[i].value > 0; i++)
766		if (bthr[i].value <= volt)
767			break;
768
769	return bthr[i].state;
770}
771
772static int
773zapm_battery_life(int volt)
774{
775	const struct battery_threshold *bthr;
776	int i;
777
778	bthr = zaurus_main_battery->bi_thres;
779
780	for (i = 0; bthr[i].value > 0; i++)
781		if (bthr[i].value <= volt)
782			break;
783
784	if (i == 0)
785		return bthr[0].percent;
786
787	return (bthr[i].percent +
788	    ((volt - bthr[i].value) * 100) /
789	    (bthr[i-1].value - bthr[i].value) *
790	    (bthr[i-1].percent - bthr[i].percent) / 100);
791}
792
793/*
794 * Poll power-management related GPIO inputs, update battery life
795 * in softc, and/or control battery charging.
796 */
797static void
798zapm_poll1(void *v, int do_suspend)
799{
800	struct zapm_softc *sc = (struct zapm_softc *)v;
801	int ac_state;
802	int bc_lock;
803	int charging;
804	int volt;
805
806	if (!mutex_tryenter(&sc->sc_mtx))
807		return;
808
809	ac_state = zapm_get_ac_state(sc);
810	bc_lock = zapm_get_battery_compartment_state(sc);
811
812	/* Stop discharging. */
813	if (sc->discharging) {
814		sc->discharging = 0;
815		charging = 0;
816		volt = zapm_get_battery_volt();
817		DPRINTF(("zapm_poll: discharge off volt %d\n", volt));
818	} else {
819		charging = sc->battery_state & APM_BATT_FLAG_CHARGING;
820		volt = sc->battery_volt;
821	}
822
823	/* Start or stop charging as necessary. */
824	if (ac_state && bc_lock) {
825		int charge_completed = zapm_charge_complete(sc);
826		if (charging) {
827			if (charge_completed) {
828				DPRINTF(("zapm_poll: battery is full\n"));
829				charging = 0;
830				zapm_set_charging(sc, 0);
831			}
832		} else if (!charge_completed) {
833			charging = APM_BATT_FLAG_CHARGING;
834			volt = zapm_get_battery_volt();
835			zapm_set_charging(sc, 1);
836			DPRINTF(("zapm_poll: start charging volt %d\n", volt));
837		}
838	} else {
839		if (charging) {
840			charging = 0;
841			zapm_set_charging(sc, 0);
842			timerclear(&sc->sc_lastbattchk);
843			DPRINTF(("zapm_poll: stop charging\n"));
844		}
845		sc->battery_full_cnt = 0;
846	}
847
848	/*
849	 * Restart charging once in a while.  Discharge a few milliseconds
850	 * before updating the voltage in our softc if A/C is connected.
851	 */
852	if (bc_lock && ratecheck(&sc->sc_lastbattchk, &zapm_battchkrate)) {
853		if (do_suspend && sc->suspended) {
854			/* XXX */
855#if 0
856			DPRINTF(("zapm_poll: suspended %lu %lu\n",
857			    sc->lastbattchk.tv_sec,
858			    pxa2x0_rtc_getsecs()));
859			if (charging) {
860				zapm_set_charging(sc, 0);
861				delay(15000);
862				zapm_set_charging(sc, 1);
863				pxa2x0_rtc_setalarm(pxa2x0_rtc_getsecs() +
864				    zapm_battchkrate.tv_sec + 1);
865			}
866#endif
867		} else if (ac_state && sc->battery_full_cnt == 0) {
868			DPRINTF(("zapm_poll: discharge on\n"));
869			if (charging)
870				zapm_set_charging(sc, 0);
871			sc->discharging = 1;
872			if (ZAURUS_ISC1000 || ZAURUS_ISC3000)
873				scoop_discharge_battery(1);
874			callout_schedule(&sc->sc_discharge_poll,
875			    DISCHARGE_TIMEOUT);
876		} else if (!ac_state) {
877			volt = zapm_get_battery_volt();
878			DPRINTF(("zapm_poll: volt %d\n", volt));
879		}
880	}
881
882	/* Update the cached power state in our softc. */
883	if ((ac_state != sc->ac_state)
884	 || (charging != (sc->battery_state & APM_BATT_FLAG_CHARGING))) {
885		config_hook_call(CONFIG_HOOK_PMEVENT,
886		    CONFIG_HOOK_PMEVENT_AC,
887		    (void *)((ac_state == APM_AC_OFF)
888		        ? CONFIG_HOOK_AC_OFF
889		        : (charging ? CONFIG_HOOK_AC_ON_CHARGE
890		                    : CONFIG_HOOK_AC_ON_NOCHARGE)));
891	}
892	if (volt != sc->battery_volt) {
893		sc->battery_volt = volt;
894		sc->battery_life = zapm_battery_life(volt);
895		config_hook_call(CONFIG_HOOK_PMEVENT,
896		    CONFIG_HOOK_PMEVENT_BATTERY,
897		    (void *)zapm_battery_state(volt));
898	}
899
900	mutex_exit(&sc->sc_mtx);
901}
902