diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/zaurus/dev/zaurus_apm.c | 340 |
1 files changed, 208 insertions, 132 deletions
diff --git a/sys/arch/zaurus/dev/zaurus_apm.c b/sys/arch/zaurus/dev/zaurus_apm.c index 4adaaed2936..87ba0303e7e 100644 --- a/sys/arch/zaurus/dev/zaurus_apm.c +++ b/sys/arch/zaurus/dev/zaurus_apm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: zaurus_apm.c,v 1.3 2005/03/13 05:13:15 uwe Exp $ */ +/* $OpenBSD: zaurus_apm.c,v 1.4 2005/03/30 21:44:08 uwe Exp $ */ /* * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de> @@ -18,6 +18,8 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/timeout.h> #include <sys/conf.h> #include <arm/xscale/pxa2x0reg.h> @@ -27,6 +29,12 @@ #include <zaurus/dev/zaurus_scoopvar.h> #include <zaurus/dev/zaurus_sspvar.h> +#if defined(APMDEBUG) +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) /**/ +#endif + int apm_match(struct device *, void *, void *); void apm_attach(struct device *, struct device *, void *); @@ -52,21 +60,6 @@ struct cfattach apm_pxaip_ca = { #define GPIO_CHRG_FULL_C3000 101 #define GPIO_BATT_COVER_C3000 90 /* active low */ -/* battery state */ -#define BATT_RESET 0 -#define BATT_ABSENT 1 -#define BATT_NOT_CHARGING 2 -#define BATT_CHARGING 3 -#define BATT_FULL 4 - -#ifdef APMDEBUG -const char *zaurus_batt_state_names[5] = { - "reset", "absent", "not charging", "charging", "full" -}; -#endif - -int zaurus_batt_state = BATT_RESET; - struct battery_threshold { int bt_volt; int bt_life; @@ -91,30 +84,44 @@ const struct battery_threshold zaurus_battery_life_c3000[] = { }; const struct battery_info zaurus_battery_c3000 = { - 360 /* minutes */, + 180 /* minutes; pessimistic estimate */, zaurus_battery_life_c3000 }; const struct battery_info *zaurus_main_battery = &zaurus_battery_c3000; #if 0 -void zaurus_shutdownhook(void *); +void zapm_shutdown(void *); +int zapm_acintr(void *); #endif +int zapm_ac_on(void); int max1111_adc_value(int); int max1111_adc_value_avg(int, int); #if 0 -int zaurus_jkvad_voltage(void); -int zaurus_battery_temp(void); +int zapm_jkvad_voltage(void); +int zapm_batt_temp(void); #endif -int zaurus_battery_voltage(void); -int zaurus_battery_state(int); -int zaurus_battery_life(int); -int zaurus_minutes_left(int); -int zaurus_ac_present(void); -int zaurus_charge_complete(void); -void zaurus_charge_control(int); -void zaurus_power_check(struct pxa2x0_apm_softc *); -void zaurus_power_info(struct pxa2x0_apm_softc *, +int zapm_batt_volt(void); +int zapm_batt_state(int); +int zapm_batt_life(int); +int zapm_batt_minutes(int); +int zapm_batt_full(void); + +int zapm_curbattvolt; /* updated periodically when A/C is on */ +int zapm_battcharging; +int zapm_battfullcount; + +struct timeout zapm_charge_off_to; +struct timeout zapm_charge_on_to; + +void zapm_charge_enable(void); +void zapm_charge_disable(void); +void zapm_charge_restart(void); +void zapm_charge_off(void *); +void zapm_charge_on(void *); + +void zapm_power_check(struct pxa2x0_apm_softc *); +void zapm_power_info(struct pxa2x0_apm_softc *, struct apm_power_info *); int @@ -134,38 +141,64 @@ apm_attach(struct device *parent, struct device *self, void *aux) #if 0 (void)pxa2x0_gpio_intr_establish(GPIO_AC_IN_C3000, IST_EDGE_BOTH, - IPL_BIO, apm_intr, sc, "apm_ac"); + IPL_BIO, zapm_acintr, sc, "apm_ac"); #endif - sc->sc_periodic_check = zaurus_power_check; - sc->sc_power_info = zaurus_power_info; + sc->sc_periodic_check = zapm_power_check; + sc->sc_power_info = zapm_power_info; + + timeout_set(&zapm_charge_off_to, &zapm_charge_off, NULL); + timeout_set(&zapm_charge_on_to, &zapm_charge_on, NULL); - /* Initialize the battery status before APM is enabled. */ - zaurus_power_check(sc); + zapm_charge_disable(); + zapm_battcharging = 0; + zapm_battfullcount = 0; + + /* C3000: discharge 100 ms when AC is on. */ + if (zapm_ac_on()) { + scoop_discharge_battery(1); + delay(100000); + } + + zapm_curbattvolt = zapm_batt_volt(); + scoop_discharge_battery(0); + + zapm_power_check(sc); pxa2x0_apm_attach_sub(sc); #if 0 - (void)shutdownhook_establish(zaurus_shutdownhook, NULL); + (void)shutdownhook_establish(zapm_shutdown, NULL); #endif } #if 0 void -zaurus_shutdownhook(void *v) +zapm_shutdown(void *v) +{ + zapm_charge_disable(); +} + +int +zapm_acintr(void *v) { - /* XXX */ - zaurus_charge_control(BATT_NOT_CHARGING); + return 1; } #endif int +zapm_ac_on(void) +{ + return (!pxa2x0_gpio_get_bit(GPIO_AC_IN_C3000)); +} + +int max1111_adc_value(int chan) { - return zssp_read_max1111(MAXCTRL_PD0 | MAXCTRL_PD1 | + return (zssp_read_max1111(MAXCTRL_PD0 | MAXCTRL_PD1 | MAXCTRL_SGL | MAXCTRL_UNI | (chan << MAXCTRL_SEL_SHIFT) | - MAXCTRL_STR); + MAXCTRL_STR)); } /* XXX simplify */ @@ -215,14 +248,14 @@ max1111_adc_value_avg(int chan, int pause) * unless A/C power is connected. */ int -zaurus_jkvad_voltage(void) +zapm_jkvad_voltage(void) { - return max1111_adc_value_avg(JK_VAD, 10); + return (max1111_adc_value_avg(JK_VAD, 10)); } int -zaurus_battery_temp(void) +zapm_batt_temp(void) { int temp; @@ -231,19 +264,19 @@ zaurus_battery_temp(void) temp = max1111_adc_value_avg(BATT_THM, 1); scoop_battery_temp_adc(0); - return temp; + return (temp); } #endif int -zaurus_battery_voltage(void) +zapm_batt_volt(void) { - return max1111_adc_value_avg(BATT_AD, 10); + return (max1111_adc_value_avg(BATT_AD, 10)); } int -zaurus_battery_state(int volt) +zapm_batt_state(int volt) { const struct battery_threshold *bthr; int i; @@ -258,7 +291,7 @@ zaurus_battery_state(int volt) } int -zaurus_battery_life(int volt) +zapm_batt_life(int volt) { const struct battery_threshold *bthr; int i; @@ -273,132 +306,175 @@ zaurus_battery_life(int volt) return (bthr[i].bt_life); return (bthr[i].bt_life + - ((bthr[i-1].bt_volt - volt) * 100) / + ((volt - bthr[i].bt_volt) * 100) / (bthr[i-1].bt_volt - bthr[i].bt_volt) * (bthr[i-1].bt_life - bthr[i].bt_life) / 100); } int -zaurus_minutes_left(int life) +zapm_batt_minutes(int life) { return (zaurus_main_battery->bi_minutes * life / 100); } +/* + * Return non-zero if the charge complete signal is set. This signal + * becomes valid after charging has been stopped and restarted. + */ int -zaurus_ac_present(void) +zapm_batt_full(void) { - return !pxa2x0_gpio_get_bit(GPIO_AC_IN_C3000); + return (pxa2x0_gpio_get_bit(GPIO_CHRG_FULL_C3000) ? 1 : 0); } -/* - * Return non-zero if the charge complete signal is set. This signal - * is valid only after charging is restarted. - */ -int -zaurus_charge_complete(void) +void +zapm_charge_enable(void) { - return pxa2x0_gpio_get_bit(GPIO_CHRG_FULL_C3000); + timeout_del(&zapm_charge_off_to); + timeout_del(&zapm_charge_on_to); + + scoop_charge_battery(1, 0); + scoop_discharge_battery(0); + scoop_led_set(SCOOP_LED_ORANGE, 1); + + /* Restart charging and updating curbattvolt. */ + timeout_add(&zapm_charge_off_to, hz * 60); +} + +void +zapm_charge_disable(void) +{ + + timeout_del(&zapm_charge_off_to); + timeout_del(&zapm_charge_on_to); + + scoop_discharge_battery(0); + scoop_charge_battery(0, 0); + scoop_led_set(SCOOP_LED_ORANGE, 0); } void -zaurus_charge_control(int state) +zapm_charge_restart(void) { - switch (state) { - case BATT_RESET: - case BATT_ABSENT: - case BATT_NOT_CHARGING: - case BATT_FULL: - scoop_charge_battery(0, 0); - scoop_led_set(SCOOP_LED_ORANGE, 0); - /* Always force a 15 ms delay before charging again. */ - delay(15000); - break; - case BATT_CHARGING: - scoop_charge_battery(1, 0); - scoop_led_set(SCOOP_LED_ORANGE, 1); - break; - default: - printf("zaurus_charge_control: bad state %d\n", state); - break; + zapm_charge_disable(); + delay(15000); + zapm_charge_enable(); +} + +void +zapm_charge_off(void *v) +{ + + if (zapm_battcharging) + zapm_charge_disable(); + + /* Discharge 100 ms before updating curbattvolt. */ + if (zapm_ac_on()) { + scoop_discharge_battery(1); + timeout_add(&zapm_charge_on_to, hz / 10); } } +void +zapm_charge_on(void *v) +{ + + /* + * Read battery voltage while the battery is still discharging, + * then restart charging or schedule the next curbattvolt update. + */ + if (zapm_ac_on()) { + zapm_curbattvolt = zapm_batt_volt(); + if (zapm_battcharging) + zapm_charge_enable(); + else + timeout_add(&zapm_charge_off_to, hz * 60); + } + + scoop_discharge_battery(0); +} + /* * Check A/C power and control battery charging. This gets called once * from apm_attach(), and once per second from the APM kernel thread. */ void -zaurus_power_check(struct pxa2x0_apm_softc *sc) +zapm_power_check(struct pxa2x0_apm_softc *sc) { - int state = zaurus_batt_state; - - switch (state) { - case BATT_RESET: - zaurus_charge_control(state); - /* FALLTHROUGH */ - case BATT_ABSENT: - state = BATT_NOT_CHARGING; - /* FALLTHROUGH */ - case BATT_NOT_CHARGING: - if (zaurus_ac_present()) - state = BATT_CHARGING; - break; - - case BATT_CHARGING: - if (!zaurus_ac_present()) - state = BATT_NOT_CHARGING; - else if (zaurus_charge_complete()) - state = BATT_FULL; - break; - - case BATT_FULL: - if (!zaurus_ac_present()) - state = BATT_NOT_CHARGING; - break; - - default: - printf("zaurus_power_check: bad state %d\n", state); - break; + int s; + + s = splsoftclock(); + + if (zapm_ac_on()) { + if (zapm_battcharging) { + /* + * Read BATT_FULL once per second until it + * stablizes; restart charging between reads. + */ + if (zapm_batt_full()) { + if (++zapm_battfullcount >= 2) { + /* battery full; stop charging. */ + DPRINTF(("zapm_power_check: battery full\n")); + zapm_battcharging = 0; + zapm_charge_disable(); + zapm_charge_off(NULL); + } else + zapm_charge_restart(); + } else if (zapm_battfullcount > 0) { + /* Ignore BATT_FULL glitch. */ + DPRINTF(("zapm_power_check: battery almost full?\n")); + zapm_battfullcount = 0; + zapm_charge_restart(); + } + } else if (zapm_battfullcount == 0) { + /* Start charging and updating curbattvolt. */ + DPRINTF(("zapm_power_check: start charging\n")); + zapm_battcharging = 1; + zapm_charge_off(NULL); + } + } else if (zapm_battcharging || zapm_battfullcount != 0) { + /* Stop charging and updating curbattvolt. */ + DPRINTF(("zapm_power_check: stop charging\n")); + zapm_battcharging = 0; + zapm_battfullcount = 0; + zapm_charge_disable(); + } else { + /* Running on battery. */ + /* XXX detect battery low condition and take measures. */ } - if (zaurus_batt_state != state) { -#ifdef APMDEBUG - printf("%s: battery state %s -> %s volt %d\n", - sc->sc_dev.dv_xname, - zaurus_batt_state_names[zaurus_batt_state], - zaurus_batt_state_names[state], - zaurus_battery_voltage()); -#endif - zaurus_charge_control(state); - zaurus_batt_state = state; - } + splx(s); } /* * Report A/C and battery state in response to a request from apmd. */ void -zaurus_power_info(struct pxa2x0_apm_softc *sc, +zapm_power_info(struct pxa2x0_apm_softc *sc, struct apm_power_info *power) { + int s; int volt; + int charging; + + s = splsoftclock(); + volt = zapm_curbattvolt; + charging = zapm_battcharging; + splx(s); + + power->ac_state = zapm_ac_on() ? APM_AC_ON : APM_AC_OFF; + if (power->ac_state == APM_AC_OFF) + volt = zapm_batt_volt(); - if (zaurus_batt_state == BATT_CHARGING) { - power->ac_state = APM_AC_ON; + if (charging) power->battery_state = APM_BATT_CHARGING; - power->battery_life = 100; - power->minutes_left = zaurus_main_battery->bi_minutes; - } else { - power->ac_state = zaurus_ac_present() ? APM_AC_ON : - APM_AC_OFF; - volt = zaurus_battery_voltage(); - power->battery_state = zaurus_battery_state(volt); - power->battery_life = zaurus_battery_life(volt); - power->minutes_left = - zaurus_minutes_left(power->battery_life); - } + else + power->battery_state = zapm_batt_state(volt); + + power->battery_life = zapm_batt_life(volt); + power->minutes_left = zapm_batt_minutes(power->battery_life); } |