1 /* $NetBSD: j720pwr.c,v 1.4 2008/04/28 20:23:21 martin Exp $ */
4 * Copyright (c) 2002, 2006 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Emmanuel Dreyfus and Peter Postma.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 /* Jornada 720 power management. */
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: j720pwr.c,v 1.4 2008/04/28 20:23:21 martin Exp $");
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
41 #include <sys/device.h>
43 #include <dev/apm/apmbios.h>
45 #include <machine/config_hook.h>
46 #include <machine/platid.h>
47 #include <machine/platid_mask.h>
49 #include <arm/sa11x0/sa11x0_var.h>
50 #include <arm/sa11x0/sa11x0_gpioreg.h>
51 #include <arm/sa11x0/sa11x0_ppcreg.h>
52 #include <arm/sa11x0/sa11x0_sspreg.h>
54 #include <hpcarm/dev/j720sspvar.h>
57 #define DPRINTF(arg) aprint_normal arg
59 #define DPRINTF(arg) /* nothing */
62 #define arraysize(ary) (sizeof(ary) / sizeof(ary[0]))
64 struct j720pwr_softc
{
67 struct j720ssp_softc
*sc_ssp
;
69 volatile int sc_state
;
70 #define J720PWR_POWEROFF 0x01
71 #define J720PWR_SLEEPING 0x02
74 static int j720pwr_match(device_t
, cfdata_t
, void *);
75 static void j720pwr_attach(device_t
, device_t
, void *);
77 static void j720pwr_sleep(void *);
78 static int j720pwr_suspend_hook(void *, int, long, void *);
79 static int j720pwr_event_hook(void *, int, long, void *);
80 static int j720pwr_apm_getpower_hook(void *, int, long, void *);
81 static int j720pwr_get_battery(struct j720pwr_softc
*);
82 static int j720pwr_get_ac_status(struct j720pwr_softc
*);
83 static int j720pwr_get_charge_status(struct j720pwr_softc
*);
90 { 100, 670, APM_BATT_FLAG_HIGH
},
91 { 90, 660, APM_BATT_FLAG_HIGH
},
92 { 80, 650, APM_BATT_FLAG_HIGH
},
93 { 70, 640, APM_BATT_FLAG_HIGH
},
94 { 60, 630, APM_BATT_FLAG_HIGH
},
95 { 50, 620, APM_BATT_FLAG_HIGH
},
96 { 40, 605, APM_BATT_FLAG_LOW
},
97 { 30, 580, APM_BATT_FLAG_LOW
},
98 { 20, 545, APM_BATT_FLAG_LOW
},
99 { 10, 500, APM_BATT_FLAG_CRITICAL
},
100 { 0, 430, APM_BATT_FLAG_CRITICAL
},
103 CFATTACH_DECL_NEW(j720pwr
, sizeof(struct j720pwr_softc
),
104 j720pwr_match
, j720pwr_attach
, NULL
, NULL
);
108 j720pwr_match(device_t parent
, cfdata_t cf
, void *aux
)
111 if (!platid_match(&platid
, &platid_mask_MACH_HP_JORNADA_7XX
))
113 if (strcmp(cf
->cf_name
, "j720pwr") != 0)
120 j720pwr_attach(device_t parent
, device_t self
, void *aux
)
122 struct j720pwr_softc
*sc
= device_private(self
);
123 extern void (*__sleep_func
)(void *);
124 extern void *__sleep_ctx
;
129 sc
->sc_ssp
= device_private(parent
);
132 /* Register apm sleep function. */
133 __sleep_func
= j720pwr_sleep
;
136 /* Battery status query hook. */
137 config_hook(CONFIG_HOOK_GET
, CONFIG_HOOK_BATTERYVAL
,
138 CONFIG_HOOK_EXCLUSIVE
, j720pwr_apm_getpower_hook
, sc
);
140 /* Battery charge status query hook. */
141 config_hook(CONFIG_HOOK_GET
, CONFIG_HOOK_CHARGE
,
142 CONFIG_HOOK_EXCLUSIVE
, j720pwr_apm_getpower_hook
, sc
);
144 /* AC status query hook. */
145 config_hook(CONFIG_HOOK_GET
, CONFIG_HOOK_ACADAPTER
,
146 CONFIG_HOOK_EXCLUSIVE
, j720pwr_apm_getpower_hook
, sc
);
148 /* Suspend/resume button hook. */
149 config_hook(CONFIG_HOOK_BUTTONEVENT
,
150 CONFIG_HOOK_BUTTONEVENT_POWER
,
151 CONFIG_HOOK_SHARE
, j720pwr_suspend_hook
, sc
);
153 /* Receive suspend/resume events. */
154 config_hook(CONFIG_HOOK_PMEVENT
,
155 CONFIG_HOOK_PMEVENT_HARDPOWER
,
156 CONFIG_HOOK_SHARE
, j720pwr_event_hook
, sc
);
159 config_found_ia(self
, "hpcapmif", NULL
, NULL
);
163 j720pwr_sleep(void *ctx
)
165 struct j720pwr_softc
*sc
= ctx
;
166 struct j720ssp_softc
*ssp
= sc
->sc_ssp
;
169 /* Disable falling-edge detect on all GPIO ports, except keyboard. */
170 oldfer
= bus_space_read_4(ssp
->sc_iot
, ssp
->sc_gpioh
, SAGPIO_FER
);
171 bus_space_write_4(ssp
->sc_iot
, ssp
->sc_gpioh
, SAGPIO_FER
, 1 << 0);
173 while (sc
->sc_state
& J720PWR_POWEROFF
) {
175 * Just sleep here until the poweroff bit gets unset.
176 * We need to wait here because when machine_sleep() returns
177 * hpcapm(4) assumes that we are "resuming".
179 (void)tsleep(&sc
->sc_state
, PWAIT
, "j720slp", 0);
182 /* Restore previous FER value. */
183 bus_space_write_4(ssp
->sc_iot
, ssp
->sc_gpioh
, SAGPIO_FER
, oldfer
);
187 j720pwr_suspend_hook(void *ctx
, int type
, long id
, void *msg
)
189 struct j720pwr_softc
*sc
= ctx
;
191 if (type
!= CONFIG_HOOK_BUTTONEVENT
||
192 id
!= CONFIG_HOOK_BUTTONEVENT_POWER
)
195 if ((sc
->sc_state
& (J720PWR_POWEROFF
| J720PWR_SLEEPING
)) == 0) {
196 sc
->sc_state
|= J720PWR_POWEROFF
;
197 } else if ((sc
->sc_state
& (J720PWR_POWEROFF
| J720PWR_SLEEPING
)) ==
198 (J720PWR_POWEROFF
| J720PWR_SLEEPING
)) {
199 sc
->sc_state
&= ~J720PWR_POWEROFF
;
200 wakeup(&sc
->sc_state
);
202 DPRINTF(("j720pwr_suspend_hook: busy\n"));
206 config_hook_call(CONFIG_HOOK_PMEVENT
,
207 CONFIG_HOOK_PMEVENT_SUSPENDREQ
, NULL
);
213 j720pwr_event_hook(void *ctx
, int type
, long id
, void *msg
)
215 struct j720pwr_softc
*sc
= ctx
;
216 int event
= (int)msg
;
218 if (type
!= CONFIG_HOOK_PMEVENT
||
219 id
!= CONFIG_HOOK_PMEVENT_HARDPOWER
)
224 sc
->sc_state
|= (J720PWR_SLEEPING
| J720PWR_POWEROFF
);
227 sc
->sc_state
&= ~(J720PWR_SLEEPING
| J720PWR_POWEROFF
);
237 j720pwr_apm_getpower_hook(void *ctx
, int type
, long id
, void *msg
)
239 int * const pval
= msg
;
240 int val
, tmp
, i
, state
= 0;
242 if (type
!= CONFIG_HOOK_GET
)
246 case CONFIG_HOOK_BATTERYVAL
:
247 val
= j720pwr_get_battery(ctx
);
250 for (i
= 0; i
< arraysize(battery_table
); i
++)
251 if (val
> battery_table
[i
].value
)
254 /* Battery charge status is at maximum. */
258 * Use linear interpolation to calculate
259 * the estimated charge status.
261 tmp
= ((val
- battery_table
[i
].value
) * 100) /
262 (battery_table
[i
- 1].value
-
263 battery_table
[i
].value
);
264 *pval
= battery_table
[i
].percent
+
265 ((battery_table
[i
- 1].percent
-
266 battery_table
[i
].percent
) * tmp
) / 100;
269 /* Battery is absent. */
275 case CONFIG_HOOK_CHARGE
:
276 val
= j720pwr_get_battery(ctx
);
279 for (i
= 1; i
< arraysize(battery_table
); i
++) {
280 if (val
> battery_table
[i
].value
) {
281 state
= battery_table
[i
- 1].state
;
286 if (j720pwr_get_charge_status(ctx
) == 0)
287 state
|= APM_BATT_FLAG_CHARGING
;
289 state
= APM_BATT_FLAG_NO_SYSTEM_BATTERY
;
295 case CONFIG_HOOK_ACADAPTER
:
296 *pval
= j720pwr_get_ac_status(ctx
) ? APM_AC_OFF
: APM_AC_ON
;
305 j720pwr_get_battery(struct j720pwr_softc
*sc
)
307 struct j720ssp_softc
*ssp
= sc
->sc_ssp
;
308 int data
, i
, pmdata
[3];
310 bus_space_write_4(ssp
->sc_iot
, ssp
->sc_gpioh
, SAGPIO_PCR
, 0x2000000);
312 if (j720ssp_readwrite(ssp
, 1, 0xc0, &data
, 500) < 0 || data
!= 0x11) {
313 DPRINTF(("j720pwr_get_battery: no dummy received\n"));
317 for (i
= 0; i
< 3; i
++) {
318 if (j720ssp_readwrite(ssp
, 0, 0x11, &pmdata
[i
], 100) < 0)
322 bus_space_write_4(ssp
->sc_iot
, ssp
->sc_gpioh
, SAGPIO_PSR
, 0x2000000);
324 pmdata
[0] |= (pmdata
[2] & 0x3) << 8; /* Main battery. */
325 pmdata
[1] |= (pmdata
[2] & 0xc) << 6; /* Backup battery (unused). */
327 DPRINTF(("j720pwr_get_battery: data[0]=%d data[1]=%d data[2]=%d\n",
328 pmdata
[0], pmdata
[1], pmdata
[2]));
330 /* If bit 0 and 1 are both set, the main battery is absent. */
331 if ((pmdata
[2] & 3) == 3)
337 bus_space_write_4(ssp
->sc_iot
, ssp
->sc_gpioh
, SAGPIO_PSR
, 0x2000000);
340 bus_space_write_4(ssp
->sc_iot
, ssp
->sc_ssph
, SASSP_CR0
, 0x307);
342 bus_space_write_4(ssp
->sc_iot
, ssp
->sc_ssph
, SASSP_CR0
, 0x387);
344 DPRINTF(("j720pwr_get_battery: error %x\n", data
));
349 j720pwr_get_ac_status(struct j720pwr_softc
*sc
)
351 struct j720ssp_softc
*ssp
= sc
->sc_ssp
;
354 status
= bus_space_read_4(ssp
->sc_iot
, ssp
->sc_gpioh
, SAGPIO_PLR
);
356 return status
& (1 << 4);
360 j720pwr_get_charge_status(struct j720pwr_softc
*sc
)
362 struct j720ssp_softc
*ssp
= sc
->sc_ssp
;
365 status
= bus_space_read_4(ssp
->sc_iot
, ssp
->sc_gpioh
, SAGPIO_PLR
);
367 return status
& (1 << 26);