Index: apps/debug_menu.c =================================================================== --- apps/debug_menu.c (revision 19141) +++ apps/debug_menu.c (working copy) @@ -1652,12 +1652,45 @@ y = battery_adc_charge_current(); x = y < 0 ? '-' : ' '; y = abs(y); - snprintf(buf, 30, "I Charge:%c%d.%03d A", (char)x, y / 1000, y % 1000); + snprintf(buf, 30, "I Charge:%c%d.%03d A", + (char)x, y / 1000, y % 1000); lcd_puts(0, 4, buf); + y = battery_charge_current(); + x = y < 0 ? '-' : ' '; + y = abs(y); + snprintf(buf, 30, "I Filtered:%c%d.%03d A", + (char)x, y / 1000, y % 1000); + lcd_puts(0, 5, buf); + y = battery_adc_temp(); - snprintf(buf, 30, "T Battery: %dC (%dF)", y, (9*y + 160) / 5); - lcd_puts(0, 5, buf); + snprintf(buf, 30, "T Battery: %dC (%dF)", y, + (9*y + 160) / 5); + lcd_puts(0, 6, buf); + + static const unsigned char * const + strings[CHARGING_NUM_STATES] = + { + [0 ... CHARGING_NUM_STATES-1] = + "", + [CHARGE_STATE_DISABLED-CHARGING_STATE_FIRST] = + "Disabled", + [CHARGE_STATE_ERROR-CHARGING_STATE_FIRST] = + "Error", + [DISCHARGING-CHARGING_STATE_FIRST] = + "Discharging", + [TRICKLE-CHARGING_STATE_FIRST] = + "Precharge", + [TOPOFF-CHARGING_STATE_FIRST] = + "Constant Voltage", + [CHARGING-CHARGING_STATE_FIRST] = + "Constant Current", + }; + + snprintf(buf, 30, "State: %s", + strings[charge_state-CHARGING_STATE_FIRST]); + lcd_puts(0, 7, buf); + #endif /* defined TOSHIBA_GIGABEAT_S */ #endif /* defined IPOD_NANO || defined IPOD_VIDEO */ #endif /* CONFIG_CHARGING != CHARGING_CONTROL */ Index: firmware/export/powermgmt.h =================================================================== --- firmware/export/powermgmt.h (revision 19141) +++ firmware/export/powermgmt.h (working copy) @@ -30,17 +30,30 @@ #define CHARGE_END_LONGD 50 /* stop when N minutes have passed with * avg delta being < -0.02 V */ +typedef enum { /* sorted by increasing charging current */ #if CONFIG_CHARGING >= CHARGING_MONITOR -typedef enum { /* sorted by increasing charging current */ + CHARGE_STATE_DISABLED = -2, /* Disable charger use */ + CHARGE_STATE_ERROR = -1, /* Some error occurred that should not allow + further attempts without user intervention */ +#endif DISCHARGING = 0, +#if CONFIG_CHARGING >= CHARGING_MONITOR TRICKLE, /* Can occur for CONFIG_CHARGING >= CHARGING_MONITOR */ + /* For LiIon, the low-current pre-charge mode if battery + was very low (0.1C) */ TOPOFF, /* Can occur for CONFIG_CHARGING == CHARGING_CONTROL */ - CHARGING /* Can occur for all CONFIG_CHARGING options */ + /* For LiIon, constant voltage phase */ + CHARGING, /* Can occur for all CONFIG_CHARGING options */ + /* For LiIon, the constant current phase */ +#endif + CHARGING_STATE_LAST, + /* Keep in sync with chargin_state enum! */ + CHARGING_STATE_FIRST = -2, + CHARGING_NUM_STATES = CHARGING_STATE_LAST + 2, } charge_state_type; /* tells what the charger is doing */ extern charge_state_type charge_state; -#endif /* CONFIG_CHARGING >= CHARGING_MONITOR */ #ifdef CONFIG_CHARGING /* @@ -48,10 +61,10 @@ * one time through the power loop when the charger has been plugged in. */ typedef enum { - NO_CHARGER, - CHARGER_UNPLUGGED, /* transient state */ - CHARGER_PLUGGED, /* transient state */ - CHARGER + NO_CHARGER, /* No charger is present */ + CHARGER_UNPLUGGED, /* Transitional state during CHARGER=>NO_CHARGER */ + CHARGER_PLUGGED, /* Transitional state during NO_CHARGER=>CHARGER */ + CHARGER /* Charger is present */ } charger_input_state_type; /* tells the state of the charge input */ @@ -154,6 +167,11 @@ # define MAX_CHG_V 10250 /* anything over 10.25v gives CURRENT_MAX_CHG */ #endif /* not ONDIO */ +#if CONFIG_CHARGING == CHARGING_TARGET +/* Include target-specific definitions */ +#include "powermgmt-target.h" +#endif + extern unsigned short power_history[POWER_HISTORY_LEN]; extern const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT]; extern const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT]; @@ -165,6 +183,12 @@ /* Start up power management thread */ void powermgmt_init(void); +/* Do target portion of init (for CHARGING_TARGET) - called on power thread */ +void powermgmt_init_target(void); + +/* Handle frequent tasks and call charging_algorithm_small_step */ +void power_thread_sleep(int ticks); + #endif /* SIMULATOR */ /* Returns battery statust */ @@ -173,16 +197,26 @@ unsigned int battery_adc_voltage(void); /* voltage from ADC in millivolts */ unsigned int battery_voltage(void); /* filtered batt. voltage in millivolts */ +/* Set the filtered battery voltage (to adjust it before beginning a charge + cycle for instance where old, loaded readings will likely be invalid). */ +void set_filtered_battery_voltage(int millivolts); + /* read unfiltered battery info */ void battery_read_info(int *voltage, int *level); /* Tells if the battery level is safe for disk writes */ bool battery_level_safe(void); +#ifdef TARGET_POWERMGMT_FILTER_CHARGE_STATE +int powermgmt_filter_charge_state(void); +#endif + void set_poweroff_timeout(int timeout); +int get_battery_capacity(void); /* get local battery capacity value */ void set_battery_capacity(int capacity); /* set local battery capacity value */ void set_battery_type(int type); /* set local battery type */ + void set_sleep_timer(int seconds); int get_sleep_timer(void); void set_car_adapter_mode(bool setting); @@ -190,7 +224,11 @@ void cancel_shutdown(void); void shutdown_hw(void); void sys_poweroff(void); +/* Returns true if the system should force shutdown for some reason - + * eg. low battery */ +bool query_force_shutdown(void); #ifdef HAVE_ACCESSORY_SUPPLY void accessory_supply_set(bool); #endif -#endif + +#endif /* _POWERMGMT_H_ */ Index: firmware/export/config.h =================================================================== --- firmware/export/config.h (revision 19141) +++ firmware/export/config.h (working copy) @@ -117,6 +117,8 @@ #define CHARGING_SIMPLE 1 /* Simple, hardware controlled charging */ #define CHARGING_MONITOR 2 /* Hardware controlled charging with monitoring */ #define CHARGING_CONTROL 3 /* Software controlled charging */ +#define CHARGING_TARGET 4 /* Anything the target implements that is not + a generic implementation */ /* CONFIG_LCD */ #define LCD_SSD1815 1 /* as used by Archos Recorders and Ondios */ Index: firmware/export/mc13783.h =================================================================== --- firmware/export/mc13783.h (revision 19141) +++ firmware/export/mc13783.h (working copy) @@ -107,9 +107,7 @@ #define MC13783_LOBATLI (0x1 << 13) #define MC13783_LOBATHI (0x1 << 14) #define MC13783_UDPI (0x1 << 15) -#define MC13783_USB4V4I (0x1 << 16) -#define MC13783_USB2V0I (0x1 << 17) -#define MC13783_USB0V8I (0x1 << 18) +#define MC13783_USBI (0x1 << 16) #define MC13783_IDFLOATI (0x1 << 19) #define MC13783_SE1I (0x1 << 21) #define MC13783_CKDETI (0x1 << 22) @@ -131,9 +129,7 @@ #define MC13783_LOBATLM (0x1 << 13) #define MC13783_LOBATHM (0x1 << 14) #define MC13783_UDPM (0x1 << 15) -#define MC13783_USB4V4M (0x1 << 16) -#define MC13783_USB2V0M (0x1 << 17) -#define MC13783_USB0V8M (0x1 << 18) +#define MC13783_USBM (0x1 << 16) #define MC13783_IDFLOATM (0x1 << 19) #define MC13783_SE1M (0x1 << 21) #define MC13783_CKDETM (0x1 << 22) @@ -1040,6 +1036,8 @@ #define MC13783_VCHRG_3_800V (0x6 << 0) #define MC13783_VCHRG_4_500V (0x7 << 0) #define MC13783_ICHRG (0xf << 3) /* Min Nom Max */ + #define MC13783_ICHRGw(x) (((x) << 3) & MC13783_ICHRG) + #define MC13783_ICHRGr(x) (((x) & MC13783_ICHRG) >> 3) #define MC13783_ICHRG_0MA (0x0 << 3) /* 0 0 0 */ #define MC13783_ICHRG_70MA (0x1 << 3) /* 55 70 85 */ #define MC13783_ICHRG_177MA (0x2 << 3) /* 161 177 195 */ @@ -1057,6 +1055,8 @@ #define MC13783_ICHRG_1596MA (0xe << 3) /* 1450 1596 1755 */ #define MC13783_ICHRG_FULLY_ON (0xf << 3) /* Disallow HW FET turn on */ #define MC13783_ICHRGTR (0x7 << 7) /* Min Nom Max */ + #define MC13783_ICHRGTRw(x) (((x) << 7) & MC13783_ICHRGTR) + #define MC13783_ICHRGTRr(x) (((x) & MC13783_ICHRGTR) >> 7) #define MC13783_ICHRGTR_0MA (0x0 << 7) /* 0 0 0 */ #define MC13783_ICHRGTR_9MA (0x1 << 7) /* 6 9 12 */ #define MC13783_ICHRGTR_20MA (0x2 << 7) /* 14 20 26 */ @@ -1290,8 +1290,8 @@ /* Statically-registered event enable/disable */ enum mc13783_event_sets { - MC13783_EVENT_SET0 = 0, - MC13783_EVENT_SET1 = 1, + MC13783_EVENT_SET0 = 0, /* *STATUS0/MASK0/SENSE0 */ + MC13783_EVENT_SET1 = 1, /* *STATUS1/MASK1/SENSE1 */ }; struct mc13783_event Index: firmware/export/usb.h =================================================================== --- firmware/export/usb.h (revision 19141) +++ firmware/export/usb.h (working copy) @@ -54,9 +54,6 @@ #elif CONFIG_KEYPAD == GIGABEAT_PAD #define USBPOWER_BUTTON BUTTON_MENU #define USBPOWER_BTN_IGNORE BUTTON_POWER -#elif CONFIG_KEYPAD == GIGABEAT_S_PAD -#define USBPOWER_BUTTON BUTTON_MENU -#define USBPOWER_BTN_IGNORE BUTTON_BACK #elif (CONFIG_KEYPAD == IRIVER_H10_PAD) || \ (CONFIG_KEYPAD == MROBE100_PAD) #define USBPOWER_BUTTON BUTTON_RIGHT Index: firmware/export/config-gigabeat-s.h =================================================================== --- firmware/export/config-gigabeat-s.h (revision 19141) +++ firmware/export/config-gigabeat-s.h (working copy) @@ -2,7 +2,6 @@ * This config file is for toshiba Gigabeat S */ -#define NO_LOW_BATTERY_SHUTDOWN #define TARGET_TREE /* this target is using the target tree system */ #define TOSHIBA_GIGABEAT_S 1 @@ -115,8 +114,10 @@ #define BATTERY_CAPACITY_INC 25 /* capacity increment */ #define BATTERY_TYPES_COUNT 1 /* only one type */ -/* Hardware controlled charging with monitoring */ -#define CONFIG_CHARGING CHARGING_MONITOR +/* TODO: have a proper status displayed in the bootloader and have it + * work! */ +/* Charing implemented in a target-specific algorithm */ +#define CONFIG_CHARGING CHARGING_TARGET /* define this if the hardware can be powered off while charging */ #define HAVE_POWEROFF_WHILE_CHARGING @@ -129,7 +130,9 @@ #define CPU_FREQ 264000000 /* Set by retailOS loader */ /* define this if the unit can be powered or charged via USB */ -//#define HAVE_USB_POWER /* Disable for now */ +#define HAVE_USB_POWER +#define USBPOWER_BUTTON BUTTON_MENU +#define USBPOWER_BTN_IGNORE BUTTON_POWER /* USB On-the-go */ #define CONFIG_USBOTG USBOTG_ARC Index: firmware/export/imx31l.h =================================================================== --- firmware/export/imx31l.h (revision 19141) +++ firmware/export/imx31l.h (working copy) @@ -1026,6 +1026,30 @@ #define RTC_DAYR (*(REG32_PTR_T)(RTC_BASE_ADDR+0x20)) #define RTC_DAYALARM (*(REG32_PTR_T)(RTC_BASE_ADDR+0x24)) +/* Watchdog */ +#define WDOG_WCR (*(REG16_PTR_T)(WDOG_BASE_ADDR+0x00)) +#define WDOG_WSR (*(REG16_PTR_T)(WDOG_BASE_ADDR+0x02)) +#define WDOG_WRSR (*(REG16_PTR_T)(WDOG_BASE_ADDR+0x04)) + +#define WDOG_WCR_WT (0xff << 8) +#define WDOG_WCR_WTw(x) (((x) << 8) & WDOG_WCR_WT) +#define WDOG_WCR_WTr(x) (((x) & WDOG_WCR_WT) >> 8) + +#define WDOG_WCR_WOE (0x1 << 6) +#define WDOG_WCR_WDA (0x1 << 5) +#define WDOG_WCR_SRS (0x1 << 4) +#define WDOG_WCR_WRE (0x1 << 3) +#define WDOG_WCR_WDE (0x1 << 2) +#define WDOG_WCR_WDBG (0x1 << 1) +#define WDOG_WCR_WDZST (0x1 << 0) + +#define WDOG_WRSR_JRST (0x1 << 5) +#define WDOG_WRSR_PWR (0x1 << 4) +#define WDOG_WRSR_EXT (0x1 << 3) +#define WDOG_WRSR_CMON (0x1 << 2) +#define WDOG_WRSR_TOUT (0x1 << 1) +#define WDOG_WRSR_SFTW (0x1 << 0) + /* Keypad */ #define KPP_KPCR (*(REG16_PTR_T)(KPP_BASE_ADDR+0x0)) #define KPP_KPSR (*(REG16_PTR_T)(KPP_BASE_ADDR+0x2)) Index: firmware/target/arm/imx31/gigabeat-s/mc13783-target.h =================================================================== --- firmware/target/arm/imx31/gigabeat-s/mc13783-target.h (revision 19141) +++ firmware/target/arm/imx31/gigabeat-s/mc13783-target.h (working copy) @@ -31,8 +31,10 @@ #ifdef HAVE_HEADPHONE_DETECTION MC13783_ONOFD2_EVENT, /* Headphone jack */ #endif - MC13783_CHGDET_EVENT, /* Charger detection */ - MC13783_USB4V4_EVENT, /* USB insertion */ + MC13783_SE1_EVENT, /* Main charger detection */ + MC13783_USB_EVENT, /* USB insertion */ + MC13783_CHGOV_EVENT, /* Charger voltage above threshold */ + MC13783_CCCV_EVENT, /* Constant voltage/current mode changed */ }; #endif /* MC13783_TARGET_H */ Index: firmware/target/arm/imx31/gigabeat-s/power-imx31.h =================================================================== --- firmware/target/arm/imx31/gigabeat-s/power-imx31.h (revision 19141) +++ firmware/target/arm/imx31/gigabeat-s/power-imx31.h (working copy) @@ -21,6 +21,19 @@ #ifndef POWER_IMX31_H #define POWER_IMX31_H -void charger_detect_event(void); +void charger_main_detect_event(void); +void charger_usb_detect_event(void); +bool battery_switch_on(void); +bool charger_plugged(void); +unsigned int charger_sources_available(void); +enum chargecon +{ + CHARGECON_NONE = 0x0, /* No charger connected */ + CHARGECON_MAIN = 0x1, /* Main charger connected */ + CHARGECON_USB = 0x2, /* USB charger connected */ +}; + +unsigned int charger_sources_available(void); + #endif /* POWER_IMX31_H */ Index: firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h =================================================================== --- firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h (revision 0) +++ firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h (revision 0) @@ -0,0 +1,100 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Michael Sevakis + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef POWERMGMT_TARGET_H +#define POWERMGMT_TARGET_H + +/* Can't just run this code willy-nilly. Do not allow charger engagement + * without carefully verifying compatibility. + * + * Things to check: + * 1) Charge path configuration for the PMIC. + * 2) Correct thermistor reading + * 3) Accurate voltage readings + * 4) Accurate current sense for the charge path as the sense resistor may + * deviate from the 0.1 ohms assumed by the charge path regulator. + */ +#ifdef TOSHIBA_GIGABEAT_S +/* + * Gigabeat S qualifications: + * 1) Setup for dual-supply mode with separate inputs and providing USB + * charging capability through external components. + * 2) Curve obtained experimentally - extreme deviation from "optimized" + * characteristics. + * 3) Verified at battery terminals - no deviation from datasheet formula. + * 4) 0.316 ohms <=?? - verified by comparitive current readings on device + * with ammeter readings and measurement of on-board components. + */ +#ifndef BOOTLOADER +#define IMX31_ALLOW_CHARGING +#endif + +#else +#warning This iMX31 target requires validation of charging algorithm - charging disabled +#endif + +#define BATT_VOVER 4243 /* If we get here somehow - stop! > 1% over */ +#define BATT_VTRICKLE_CHARGE 2900 /* Must charge slowly */ +#define BATT_VAUTO_RECHARGE 4100 /* When to begin another cycle */ +#define BATT_TOO_LOW 2401 /* No battery? Short? Can't read below 2400mV */ +#define CHARGER_TOTAL_TIMER 720 /* minutes */ +#define CHARGER_CV_TIMER 180 /* minutes */ + +/* .316 ohms is closest standard value as measured in 1% tolerance - adjust relative + * to .100 ohm which is what the PMIC is "tuned" for. */ +#define ILEVEL_ADJUST_IN(I) (100*(I) / 316) +#define ILEVEL_ADJUST_OUT(I) (316*(I) / 100) + +/* Relative draw to battery capacity - adjusted for sense resistor */ +#define BATTERY_IFAST MC13783_ICHRG_1152MA +#define BATTERY_IFAST_USB MC13783_ICHRG_1152MA +#define BATTERY_ITRICKLE MC13783_ICHRG_266MA +#define BATTERY_ICHARGE_COMPLETE(C) (10*(C) / 116) +#define BATTERY_ICHARGE_COMPLETE_USB(C) (10*(C) / 46) + + /* Max USB draw - must be greater than BATTERY_ICHARGE_COMPLETE (or some dI/dT + * detection could be used). The USB max is a guess based on 500mA - full current + * usage. */ +#define BATT_AVE_SAMPLES 32 +#define ICHARGER_AVE_SAMPLES 32 + +/* Provide filtered charge current */ +int battery_charge_current(void); + +void cccv_event(void); +void charger_over_voltage_event(void); + +#ifndef SIMULATOR +/* Indicate various functions that require implementation at the target level. + * This is because the battery could be low or the battery switch is off but + * with the main charger attached which implies safe power for anything. The + * true battery reading is always reported for voltage readings and not the + * value at the application supply. */ +#define TARGET_QUERY_FORCE_SHUTDOWN + +/* For this the application supply is read out if the charger is attached or + * the battery read if not (completely hardware selected at BP). */ +#define TARGET_BATTERY_LEVEL_SAFE + +/* The state should be adjusted to CHARGING or DISCHARGING */ +#define TARGET_POWERMGMT_CHARGE_STATE +#endif /* SIMULATOR */ + +#endif /* POWERMGMT_TARGET_H */ Property changes on: firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:eol-style + native Index: firmware/target/arm/imx31/gigabeat-s/system-target.h =================================================================== --- firmware/target/arm/imx31/gigabeat-s/system-target.h (revision 19141) +++ firmware/target/arm/imx31/gigabeat-s/system-target.h (working copy) @@ -39,6 +39,10 @@ } #endif +/* Service the watchdog timer - serviced from the power thread every minute */ +void watchdog_service(void); + +/* Prepare for transition to firmware */ void system_prepare_fw_start(void); void tick_stop(void); void kernel_device_init(void); Index: firmware/target/arm/imx31/gigabeat-s/adc-target.h =================================================================== --- firmware/target/arm/imx31/gigabeat-s/adc-target.h (revision 19141) +++ firmware/target/arm/imx31/gigabeat-s/adc-target.h (working copy) @@ -47,7 +47,12 @@ #define ADC_READ_ERROR 0xFFFF void adc_done(void); +/* Enable conversion of specified channel (if switchoff is possible) */ +bool adc_enable_channel(int channel, bool enable); + +/* Implemented in powermgmt-imx31.c */ int battery_adc_charge_current(void); -unsigned int battery_adc_temp(void); +int battery_adc_temp(void); +unsigned int application_supply_adc_voltage(void); #endif Index: firmware/target/arm/imx31/gigabeat-s/system-imx31.c =================================================================== --- firmware/target/arm/imx31/gigabeat-s/system-imx31.c (revision 19141) +++ firmware/target/arm/imx31/gigabeat-s/system-imx31.c (working copy) @@ -32,6 +32,39 @@ #include "clkctl-imx31.h" #include "mc13783.h" +/* Initialize the watchdog timer */ +static void watchdog_init(void) +{ +#ifndef BOOTLOADER /* TODO: Enable if/when bootloader can charge */ + uint16_t wcr = WDOG_WCR_WTw(2*20) | /* 20 seconds */ + WDOG_WCR_WOE | /* WDOG output enabled */ + WDOG_WCR_WDA | /* WDOG assertion - no effect */ + WDOG_WCR_SRS | /* System reset - no effect */ + WDOG_WCR_WRE; /* Generate a WDOG signal */ + + imx31_clkctl_module_clock_gating(CG_WDOG, CGM_ON_RUN_WAIT); + + WDOG_WCR = wcr; + + WDOG_WSR = 0x5555; + + WDOG_WCR = wcr | WDOG_WCR_WDE; /* Enable timer - hardware does + not allow a disable now */ + WDOG_WSR = 0xaaaa; +#endif +} + +/* Service the watchdog timer - serviced from the power thread which + * is important so control of the charger isn't lost with the charge + * regulator enabled. A service failure will inactivate the PMIC. */ +void watchdog_service(void) +{ +#ifndef BOOTLOADER + WDOG_WSR = 0x5555; + WDOG_WSR = 0xaaaa; +#endif +} + int system_memory_guard(int newmode) { (void)newmode; @@ -65,7 +98,6 @@ CG_MEMSTICK2, CG_CSI, CG_PWM, - CG_WDOG, CG_SIM, CG_ECT, CG_USBOTG, @@ -101,6 +133,8 @@ for (i = 0; i < ARRAYLEN(disable_clocks); i++) imx31_clkctl_module_clock_gating(disable_clocks[i], CGM_OFF); + watchdog_init(); + avic_init(); gpio_init(); } Index: firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c =================================================================== --- firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c (revision 19141) +++ firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c (working copy) @@ -28,6 +28,7 @@ #include "button-target.h" #include "usb-target.h" #include "power-imx31.h" +#include "powermgmt-target.h" /* Gigabeat S definitions for static MC13783 event registration */ @@ -53,18 +54,30 @@ .callback = headphone_detect_event, }, #endif - [MC13783_CHGDET_EVENT] = /* Charger detection */ + [MC13783_SE1_EVENT] = /* Main charger detection */ { .set = MC13783_EVENT_SET0, - .mask = MC13783_CHGDETM, - .callback = charger_detect_event, + .mask = MC13783_SE1M, + .callback = charger_main_detect_event, }, - [MC13783_USB4V4_EVENT] = /* USB insertion */ + [MC13783_USB_EVENT] = /* USB insertion/USB charger detection */ { .set = MC13783_EVENT_SET0, - .mask = MC13783_USB4V4M, + .mask = MC13783_USBM, .callback = usb_connect_event, }, + [MC13783_CHGOV_EVENT] = /* Charger voltage too high */ + { + .set = MC13783_EVENT_SET0, + .mask = MC13783_CHGOVM, + .callback = charger_over_voltage_event, + }, + [MC13783_CCCV_EVENT] = /* Constant voltage/current mode changed */ + { + .set = MC13783_EVENT_SET0, + .mask = MC13783_CCCVM, + .callback = cccv_event, + }, }; const struct mc13783_event_list mc13783_event_list = Index: firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c =================================================================== --- firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c (revision 19141) +++ firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c (working copy) @@ -7,8 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese - * Revisions copyright (C) 2005 by Gerald Van Baren + * Copyright (c) 2008 by Michael Sevakis * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -20,11 +19,15 @@ * ****************************************************************************/ -/* FIXME: This is just the Gigabeat F/X file with a different name... */ - #include "config.h" +#include "system.h" +#include "general.h" +#include "thread.h" +#include "mc13783.h" #include "adc.h" #include "powermgmt.h" +#include "power-imx31.h" +#include "usb_core.h" /** * Fixed-point natural log @@ -56,7 +59,7 @@ return y; } - +/* TODO: Battery tests to get the right values! */ const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = { 3450 @@ -88,6 +91,12 @@ return ((adc_read(ADC_BATTERY) * 2303) >> 10) + 2400; } +/* Returns the application supply voltage from ADC [millvolts] */ +unsigned int application_supply_adc_voltage(void) +{ + return ((adc_read(ADC_APPLICATION_SUPPLY) * 2303) >> 10) + 2400; +} + /* Returns battery charge current from ADC [milliamps] */ int battery_adc_charge_current(void) { @@ -95,11 +104,17 @@ * Negative reading = battery to charger terminal * ADC reading -512-511 = -2875mA-2875mA */ unsigned int value = adc_read(ADC_CHARGER_CURRENT); - return (((int)value << 22) >> 22) * 2881 >> 9; + int I; + + if (value == ADC_READ_ERROR) + return INT_MIN; + + I = (((int)value << 22) >> 22) * 2881 >> 9; + return ILEVEL_ADJUST_IN(I); } /* Returns battery temperature from ADC [deg-C] */ -unsigned int battery_adc_temp(void) +int battery_adc_temp(void) { unsigned int value = adc_read(ADC_BATTERY_TEMP); /* E[volts] = value * 2.3V / 1023 @@ -124,3 +139,609 @@ long long t3 = ln*ln*ln / 13418057; return ((32754211579494400ll / (t0 + t1 + t3)) - 27315) / 100; } + +/** Charger control **/ +static int charger_total_timer = 0; /* Total allowed charging time */ +/* No CV timer since this thing is too sloppy to do anything meaningful with + * it - the need should be evaluated per device and be 120 minutes */ +static int icharger_ave = 0; /* Filtered charging current */ +static unsigned int chargers_available = CHARGECON_NONE; +static bool cccv_mode = false; /* true = constant voltage regulation */ +static bool shutdown_charger = false; + +/* One minute between battery open-circuit voltage checks */ +#define TRICKLE_OCV_COUNTER_RELOAD 120 +static int trickle_ocv_counter = 0; + +/* Temperature monitoring */ +enum +{ + TEMP_STATE_NORMAL = 0, + TEMP_STATE_WAIT = 1, + TEMP_LOW_LIMIT = 0, + TEMP_HIGH_LIMIT = 1, +}; + +static int temp_state = TEMP_STATE_NORMAL; +static bool charger_ov = false; + +/* PMIC event for regulator CC/CV mode changes */ +void cccv_event(void) +{ + cccv_mode = + (mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_CCCVS) != 0; +} + +/* This is called from the mc13783 interrupt thread */ +void charger_over_voltage_event(void) +{ + charger_ov = + (mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_CHGOVS) != 0; +} + +/* Set power thread priority for charging mode or not */ +static inline void charging_set_thread_priority(bool charging) +{ +#ifdef HAVE_PRIORITY_SCHEDULING + thread_set_priority(NULL, + charging ? PRIORITY_REALTIME : PRIORITY_SYSTEM); +#endif +} + +/* Update filtered charger current - exponential moving average */ +static bool charger_current_update_ave(void) +{ + int value = battery_adc_charge_current(); + + if (value == ADC_READ_ERROR) + return false; + + icharger_ave += value - (icharger_ave / ICHARGER_AVE_SAMPLES); + return true; +} + +/* Get smoothed readings - used for OCV and initial Ichrg averages */ +static int stat_battery_reading(int type) +{ + int high = INT_MIN, low = INT_MAX; + int value = 0; + int i; + + for (i = 0; i < 7; i++) + { + int reading = ADC_READ_ERROR; + + sleep(2); /* Get unique readings */ + + switch (type) + { + case ADC_BATTERY: + reading = battery_adc_voltage(); + break; + + case ADC_CHARGER_CURRENT: + reading = battery_adc_charge_current(); + break; + } + + if (reading == ADC_READ_ERROR) + return INT_MIN; + + if (reading > high) + high = reading; + + if (reading < low) + low = reading; + + value += reading; + } + + /* Discard extremes */ + return (value - high - low) / 5; +} + +/* Sets the charge current limit rounded down to nearest avaiable value - must + * be >= 70 mA or else charging is turned off and trickle charging must be used. */ +static bool adjust_charger_current(void) +{ + uint32_t i; + unsigned int iset = MC13783_ICHRG_0MA; /* Write OFF by default */ + bool success = true; + + switch (charge_state) + { + case TOPOFF: + case CHARGING: + iset = (chargers_available == CHARGECON_USB) ? + BATTERY_IFAST_USB : BATTERY_IFAST; + break; + + case TRICKLE: + iset = BATTERY_ITRICKLE; + break; + + default: + /* DISCHARGING/ERROR/DISABLED */ + break; + } + + i = mc13783_write_masked(MC13783_CHARGER, iset, MC13783_ICHRG); + + if (i == (uint32_t)-1) + success = false; + + icharger_ave = 0; + + if (iset == MC13783_ICHRG_0MA) + { + /* Normal charger stop */ + /* Disable charge current conversion */ + adc_enable_channel(ADC_CHARGER_CURRENT, false); + } + else + { + int icharger; + + /* Enable charge current conversion */ + adc_enable_channel(ADC_CHARGER_CURRENT, true); + + /* Charge path regulator turn on takes ~100ms max. */ + sleep(HZ/10); + + icharger = stat_battery_reading(ADC_CHARGER_CURRENT); + + if (icharger == INT_MIN) + { + success = false; + } + else + { + icharger_ave = icharger * ICHARGER_AVE_SAMPLES; + } + } + + return success; +} + +/* Turn off the charger, probe open-circuit voltage and restart it */ +static int probe_ocv(void) +{ + int millivolts; + + if (mc13783_write_masked(MC13783_CHARGER, + MC13783_ICHRG_0MA, + MC13783_ICHRG) == (uint32_t)-1) + { + return INT_MIN; + } + + millivolts = stat_battery_reading(ADC_BATTERY); + + if (millivolts != INT_MIN && !adjust_charger_current()) + millivolts = INT_MIN; + + return millivolts; +} + +static void stop_charger(bool error) +{ + charger_total_timer = 0; + + if (charge_state > DISCHARGING) + { + /* Not error state already */ + charge_state = error ? CHARGE_STATE_ERROR : DISCHARGING; + } + + adjust_charger_current(); + + charging_set_thread_priority(false); + + /* Disable charge current conversion */ + mc13783_clear(MC13783_ADC0, MC13783_CHRGICON); +} + +#ifndef NO_LOW_BATTERY_SHUTDOWN +/* Return greater of supply or battery voltage */ +static unsigned int input_millivolts(void) +{ + unsigned int app_millivolts = application_supply_adc_voltage(); + unsigned int bat_millivolts = battery_voltage(); + + return MAX(app_millivolts, bat_millivolts); +} +#endif + +static bool charging_ok(void) +{ + bool ok; + + ok = charge_state >= DISCHARGING; /* Not an error condition? */ + + if (ok) + { + /* Is the battery even connected? */ + ok = battery_switch_on(); + } + + if (ok && chargers_available == CHARGECON_USB) + { + /* USB only - sufficient allowed current? */ + unsigned int imax = usb_allowed_current(); + ok = imax >= 500; + } + + if (ok) + { + /* No tolerance for any over/under temp - wait for it to + * come back into safe range. */ + static const signed char temp_ranges[2][2] = + { + { 0, 45 }, /* Temperature range before beginning charging */ + { 5, 40 }, /* Temperature range after out-of-range detected */ + }; + + int temp = battery_adc_temp(); + const signed char *range = temp_ranges[temp_state]; + + ok = temp >= range[TEMP_LOW_LIMIT] && + temp <= range[TEMP_HIGH_LIMIT]; + + switch (temp_state) + { + case TEMP_STATE_NORMAL: + if (!ok) + temp_state = TEMP_STATE_WAIT; + break; + case TEMP_STATE_WAIT: + if (ok) + temp_state = TEMP_STATE_NORMAL; + break; + } + } + + if (ok) + { + /* Double-check voltage ourselves */ + ok = battery_voltage() < BATT_VOVER; + } + + if (ok) + { + /* Charger input voltage ok? */ + ok = !charger_ov; + } + + if (!ok && charge_state > DISCHARGING) + { + stop_charger(false); /* No error - just wait for favorable + conditions */ + } + + return ok; +} + +void powermgmt_init_target(void) +{ + const uint32_t regval_w = + MC13783_VCHRG_4_200V | MC13783_ICHRGw(0) | + MC13783_ICHRGTRw(0) | MC13783_OVCTRL_6_90V | + MC13783_CHRGRAWPDEN; + + mc13783_write(MC13783_CHARGER, regval_w); + + if (mc13783_read(MC13783_CHARGER) != regval_w) + { + /* Register has the wrong value - set error condition and disable + * since something is wrong. */ + stop_charger(false); + charge_state = CHARGE_STATE_DISABLED; + } + +#ifdef IMX31_ALLOW_CHARGING + /* Enable battery thermistor - also read by debug screen - uses 20uA */ + adc_enable_channel(ADC_BATTERY_TEMP, true); + + mc13783_write(MC13783_INTERRUPT_STATUS0, MC13783_CCCVI | MC13783_CHGOVI); + cccv_event(); + mc13783_enable_event(MC13783_CCCV_EVENT); + + charger_over_voltage_event(); + mc13783_enable_event(MC13783_CHGOV_EVENT); +#else + charge_state = CHARGE_STATE_DISABLED; +#endif /* IMX31_ALLOW_CHARGING */ +} + +/* Returns CHARGING or DISCHARGING since that's all we do */ +int powermgmt_filter_charge_state(void) +{ + switch(charge_state) + { + case TRICKLE: + case TOPOFF: + case CHARGING: + return CHARGING; + default: + return DISCHARGING; + } +} + +/* Returns true if the unit is charging the batteries. */ +bool charging_state(void) +{ + switch (charge_state) + { + case TRICKLE: + case TOPOFF: + case CHARGING: + return true; + default: + return false; + } +} + +/* Filtered battery charge current */ +int battery_charge_current(void) +{ + return icharger_ave / ICHARGER_AVE_SAMPLES; +} + +bool query_force_shutdown(void) +{ +#ifndef NO_LOW_BATTERY_SHUTDOWN + return input_millivolts() < battery_level_shutoff[0]; +#else + return false; +#endif +} + +bool battery_level_safe(void) +{ +#ifndef NO_LOW_BATTERY_SHUTDOWN + return input_millivolts() > battery_level_dangerous[0]; +#else + return true; +#endif +} + +/* Main charging algorithm - called from powermgmt.c */ +void charging_algorithm_small_step(void) +{ + /* Switch by input state */ + switch (charger_input_state) + { + case CHARGER: + { + if (!charging_state()) + break; + + /* Did the available charging sources change? If so, reevaluate + * current draw. */ + unsigned int chrgsrc = charger_sources_available(); + + if (chrgsrc != chargers_available) + { + chargers_available = chrgsrc; + + if (!adjust_charger_current()) + { + stop_charger(true); + break; + } + } + + /* Update charger current average. */ + if (!charger_current_update_ave()) + stop_charger(true); + + break; + } /* CHARGER: */ + + case CHARGER_UNPLUGGED: + { + /* Charger pulled - turn off current sources (though hardware + * will have done that anyway). Reverts state to DISCHARGING. */ + stop_charger(false); + + chargers_available = CHARGECON_NONE; + temp_state = TEMP_STATE_NORMAL; + + /* Clear error condition if one. */ + if (charge_state != CHARGE_STATE_DISABLED) + charge_state = DISCHARGING; + break; + } /* CHARGER_UNPLUGGED: */ + + default: + break; + } /* switch */ + + /* Switch by charge state */ + switch (charge_state) + { + case DISCHARGING: + { + int millivolts; + + switch (charger_input_state) + { + case CHARGER: + /* Battery voltage may have dropped and a charge cycle should + * start again */ + if (battery_voltage() > BATT_VAUTO_RECHARGE) + break; + + /* Attempt another cycle */ + + /* Fall through */ + case CHARGER_PLUGGED: + /* See if we can/should try charging */ + if (!charging_ok()) + break; + + /* Thread should be responsive and it sleeps alot */ + charging_set_thread_priority(true); + + /* Check no-load battery voltage - with a charger plugged + * it should have no load anyways. */ + millivolts = stat_battery_reading(ADC_BATTERY); + + /* Determine the appropriate current level. */ + if (millivolts < BATT_TOO_LOW) + { + /* Problem - no battery? */ + charging_set_thread_priority(false); + + if (charging_ok()) + charge_state = CHARGE_STATE_ERROR; + break; + } + else if (millivolts < BATT_VTRICKLE_CHARGE) + { + /* Battery is very low - trickle. */ + charge_state = TRICKLE; + trickle_ocv_counter = TRICKLE_OCV_COUNTER_RELOAD; + } + else + { + /* Ok for fast charge */ + charge_state = CHARGING; + } + + /* Turn charge regulator ON. */ + chargers_available = charger_sources_available(); + if (!adjust_charger_current()) + { + stop_charger(true); + break; + } + + /* Sync the filtered battery voltage. */ + set_filtered_battery_voltage(millivolts); + + charger_total_timer = CHARGER_TOTAL_TIMER; + break; + /* CHARGER: CHARGER_PLUGGED: */ + + default: + break; + } /* switch */ + + break; + } /* DISCHARGING: */ + + case TRICKLE: /* Low current initial charge */ + { + if (!charging_ok()) + break; + + if (--trickle_ocv_counter <= 0) + { + /* Pause charger and get OCV */ + int millivolts = probe_ocv(); + + trickle_ocv_counter = TRICKLE_OCV_COUNTER_RELOAD; + + if (millivolts == INT_MIN) + { + /* Error reading voltage */ + stop_charger(true); + } + else if (millivolts > BATT_VTRICKLE_CHARGE) + { + /* Switch to normal charge mode. */ + charge_state = CHARGING; + + if (!adjust_charger_current()) + stop_charger(true); + } + } + break; + } /* TRICKLE: */ + + case CHARGING: /* Constant-current stage */ + case TOPOFF: /* Constant-voltage stage */ + { + int icharger, cbatt; + + /* Monitor and stop if current drops below "charge complete" + * threshold */ + if (!charging_ok()) + break; + + if (cccv_mode) + { + /* Switching to constant-voltage phase */ + charge_state = TOPOFF; + } + else + { + /* Switched back to CC? Shouldn't happen but... */ + charge_state = CHARGING; + } + + icharger = battery_charge_current(); + cbatt = get_battery_capacity(); + cbatt = chargers_available == CHARGECON_USB ? + BATTERY_ICHARGE_COMPLETE_USB(cbatt) : + BATTERY_ICHARGE_COMPLETE(cbatt); + + if (icharger < cbatt) + { + /* Should be full now or something's wrong if the battery is + * still not at full voltage. No error since trying to detect + * one here is a race condition. */ + stop_charger(false); + } + + break; + } /* CHARGING: TOPOFF: */ + + default: + break; + } /* switch */ + + if (shutdown_charger) + { + /* Disable starts while shutting down */ + stop_charger(false); + charge_state = CHARGE_STATE_DISABLED; + shutdown_charger = false; + } +} + +void charging_algorithm_big_step(void) +{ + /* Loop for one minute. */ + long sleep_end = current_tick + HZ*60; + + do + { + long ticks = sleep_end - current_tick; + + /* Service the watchdog timer. */ + watchdog_service(); + + /* Sleep for ten seconds or whatever remains. */ + power_thread_sleep(MIN(ticks, HZ * 10)); + } + while (TIME_BEFORE(current_tick, sleep_end)); + + /* Check if charger timer expired and stop it if so. */ + if (charger_total_timer > 0 && --charger_total_timer == 0) + { + stop_charger(true); /* Time ran out - error */ + } +} + +/* Disable the charger and prepare for poweroff - called off-thread so we + * signal the charging thread to prepare to quit. */ +void charging_algorithm_close(void) +{ + shutdown_charger = true; + + /* Power management thread will set it false again */ + while (shutdown_charger) + sleep(HZ/10); +} Index: firmware/target/arm/imx31/gigabeat-s/adc-imx31.c =================================================================== --- firmware/target/arm/imx31/gigabeat-s/adc-imx31.c (revision 19141) +++ firmware/target/arm/imx31/gigabeat-s/adc-imx31.c (working copy) @@ -84,6 +84,30 @@ return (channel & 4) ? MC13783_ADD2r(data) : MC13783_ADD1r(data); } +bool adc_enable_channel(int channel, bool enable) +{ + unsigned reg = MC13783_ADC0; + uint32_t bit, mask; + + switch (channel) + { + case ADC_CHARGER_CURRENT: + bit = enable ? MC13783_CHRGICON : 0; + mask = MC13783_CHRGICON; + break; + + case ADC_BATTERY_TEMP: + bit = enable ? MC13783_RTHEN : 0; + mask = MC13783_RTHEN; + break; + + default: + return false; + } + + return mc13783_write_masked(reg, bit, mask) != (uint32_t)-1; +} + /* Called by mc13783 interrupt thread when conversion is complete */ void adc_done(void) { @@ -98,9 +122,8 @@ /* Init so first reads get data */ last_adc_read[0] = last_adc_read[1] = current_tick-1; - /* Enable increment-by-read, thermistor, charge current */ - mc13783_write(MC13783_ADC0, MC13783_ADINC2 | MC13783_ADINC1 | - MC13783_RTHEN | MC13783_CHRGICON); + /* Enable increment-by-read */ + mc13783_write(MC13783_ADC0, MC13783_ADINC2 | MC13783_ADINC1); /* Enable ADC, set multi-channel mode */ mc13783_write(MC13783_ADC1, MC13783_ADEN); Index: firmware/target/arm/imx31/gigabeat-s/usb-imx31.c =================================================================== --- firmware/target/arm/imx31/gigabeat-s/usb-imx31.c (revision 19141) +++ firmware/target/arm/imx31/gigabeat-s/usb-imx31.c (working copy) @@ -28,6 +28,7 @@ #include "usb_drv.h" #include "usb-target.h" #include "clkctl-imx31.h" +#include "power-imx31.h" #include "mc13783.h" static int usb_status = USB_EXTRACTED; @@ -55,6 +56,8 @@ { uint32_t status = mc13783_read(MC13783_INTERRUPT_SENSE0); usb_status = (status & MC13783_USB4V4S) ? USB_INSERTED : USB_EXTRACTED; + /* Notify power that USB charging is potentially available */ + charger_usb_detect_event(); } int usb_detect(void) @@ -81,7 +84,7 @@ usb_connect_event(); /* Enable PMIC event */ - mc13783_enable_event(MC13783_USB4V4_EVENT); + mc13783_enable_event(MC13783_USB_EVENT); } void usb_enable(bool on) Index: firmware/target/arm/imx31/gigabeat-s/power-imx31.c =================================================================== --- firmware/target/arm/imx31/gigabeat-s/power-imx31.c (revision 19141) +++ firmware/target/arm/imx31/gigabeat-s/power-imx31.c (working copy) @@ -20,6 +20,7 @@ ****************************************************************************/ #include "config.h" #include "system.h" +#include "usb.h" #include "power.h" #include "power-imx31.h" #include "backlight.h" @@ -29,26 +30,50 @@ #ifndef SIMULATOR -static bool charger_detect = false; +static unsigned int charger_sources = CHARGECON_NONE; -/* This is called from the mc13783 interrupt thread */ -void charger_detect_event(void) +bool battery_switch_on(void) { - charger_detect = - mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_CHGDETS; + return (GPIO3_DR & (1 << 20)) != 0; } +/* Read immediate charger status from PMIC */ +bool charger_plugged(void) +{ + return mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_CHGDETS; +} + +/* Return combo of USB and/or AC adaptor for charging */ bool charger_inserted(void) { - return charger_detect; + return charger_sources != CHARGECON_NONE; } -/* Returns true if the unit is charging the batteries. */ -bool charging_state(void) +/* This is called from the mc13783 interrupt thread */ +void charger_main_detect_event(void) { - return false; + if (mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_SE1S) + charger_sources |= CHARGECON_MAIN; + else + charger_sources &= ~CHARGECON_MAIN; } +/* This is called from the mc13783 interrupt thread */ +void charger_usb_detect_event(void) +{ + if (usb_detect() == USB_INSERTED) + charger_sources |= CHARGECON_USB; + else + charger_sources &= ~CHARGECON_USB; +} + +unsigned int charger_sources_available(void) +{ + return charger_sources; +} + +/* charging_state is implemented in powermgmt-imx31.c */ + void ide_power_enable(bool on) { if (!on) @@ -84,10 +109,10 @@ void power_init(void) { /* Poll initial state */ - charger_detect_event(); + charger_main_detect_event(); /* Enable detect event */ - mc13783_enable_event(MC13783_CHGDET_EVENT); + mc13783_enable_event(MC13783_SE1_EVENT); } #else /* SIMULATOR */ @@ -97,11 +122,6 @@ return false; } -void charger_enable(bool on) -{ - (void)on; -} - void power_off(void) { } @@ -112,4 +132,3 @@ } #endif /* SIMULATOR */ - Index: firmware/powermgmt.c =================================================================== --- firmware/powermgmt.c (revision 19141) +++ firmware/powermgmt.c (working copy) @@ -76,7 +76,7 @@ static int shutdown_timeout = 0; #if CONFIG_CHARGING >= CHARGING_MONITOR -charge_state_type charge_state; /* charging mode */ +charge_state_type charge_state = DISCHARGING; /* charging mode */ #endif static void send_battery_level_event(void); @@ -204,8 +204,6 @@ #else /* not SIMULATOR ******************************************************/ -static void power_thread_sleep(int ticks); - /* * Average battery voltage and charger voltage, filtered via a digital * exponential filter (aka. exponential moving average, scaled): @@ -241,6 +239,19 @@ static void battery_status_update(void); static int runcurrent(void); +#ifndef TARGET_POWERMGMT_FILTER_CHARGE_STATE +static inline int powermgmt_filter_charge_state(void) +{ +#if CONFIG_CHARGING >= CHARGING_MONITOR + /* No adjustment of state */ + return charge_state; +#else + /* Always discharging */ + return DISCHARGING; +#endif +} +#endif /* TARGET_POWERMGMT_FILTER_CHARGE_STATE */ + void battery_read_info(int *voltage, int *level) { int millivolts = battery_adc_voltage(); @@ -252,10 +263,12 @@ *level = voltage_to_battery_level(millivolts); } +#ifndef BOOTLOADER void reset_poweroff_timer(void) { last_event_tick = current_tick; } +#endif #if BATTERY_TYPES_COUNT > 1 void set_battery_type(int type) @@ -269,11 +282,12 @@ void set_battery_capacity(int capacity) { + if (capacity > BATTERY_CAPACITY_MAX) + capacity = BATTERY_CAPACITY_MAX; + else if (capacity < BATTERY_CAPACITY_MIN) + capacity = BATTERY_CAPACITY_MIN; + battery_capacity = capacity; - if (battery_capacity > BATTERY_CAPACITY_MAX) - battery_capacity = BATTERY_CAPACITY_MAX; - if (battery_capacity < BATTERY_CAPACITY_MIN) - battery_capacity = BATTERY_CAPACITY_MIN; battery_status_update(); /* recalculate the battery status */ } @@ -294,11 +308,13 @@ return battery_millivolts; } +#ifndef TARGET_BATTERY_LEVEL_SAFE /* Tells if the battery level is safe for disk writes */ bool battery_level_safe(void) { return battery_millivolts > battery_level_dangerous[battery_type]; } +#endif void set_poweroff_timeout(int timeout) { @@ -349,39 +365,38 @@ * when battery capacity / type settings are changed */ static int voltage_to_battery_level(int battery_millivolts) { + const int state = powermgmt_filter_charge_state(); int level; -#if CONFIG_CHARGING >= CHARGING_MONITOR - if (charge_state == DISCHARGING) { + if (state == DISCHARGING) { level = voltage_to_percent(battery_millivolts, percent_to_volt_discharge[battery_type]); } - else if (charge_state == CHARGING) { +#if CONFIG_CHARGING >= CHARGING_MONITOR + else if (state == CHARGING) { /* battery level is defined to be < 100% until charging is finished */ level = MIN(voltage_to_percent(battery_millivolts, percent_to_volt_charge), 99); } - else { /* in topoff/trickle charge, battery is by definition 100% full */ + else { + /* in topoff/trickle charge, battery is by definition 100% full */ level = 100; } -#else - /* always use the discharge table */ - level = voltage_to_percent(battery_millivolts, - percent_to_volt_discharge[battery_type]); -#endif /* CONFIG_CHARGING ... */ +#endif return level; } static void battery_status_update(void) { + const int state = powermgmt_filter_charge_state(); int level = voltage_to_battery_level(battery_millivolts); /* calculate estimated remaining running time */ /* discharging: remaining running time */ /* charging: remaining charging time */ #if CONFIG_CHARGING >= CHARGING_MONITOR - if (charge_state == CHARGING) { + if (state == CHARGING) { powermgmt_est_runningtime_min = (100 - level) * battery_capacity * 60 / 100 / (CURRENT_MAX_CHG - runcurrent()); } @@ -402,6 +417,7 @@ battery_percent = level; send_battery_level_event(); + (void)state; } /* @@ -431,15 +447,10 @@ } #endif -#ifndef NO_LOW_BATTERY_SHUTDOWN - /* switch off unit if battery level is too low for reliable operation */ - if(battery_millivolts < battery_level_shutoff[battery_type]) { - if(!shutdown_timeout) { - backlight_on(); - sys_poweroff(); - } + if( !shutdown_timeout && query_force_shutdown()) { + backlight_on(); + sys_poweroff(); } -#endif if(timeout && #if CONFIG_TUNER && !defined(BOOTLOADER) @@ -546,6 +557,18 @@ } #endif +#ifndef TARGET_QUERY_FORCE_SHUTDOWN +bool query_force_shutdown(void) +{ +#ifndef NO_LOW_BATTERY_SHUTDOWN + /* switch off unit if battery level is too low for reliable operation */ + return battery_millivolts < battery_level_shutoff[battery_type]; +#else + return false; +#endif +} +#endif /* TARGET_QUERY_FORCE_SHUTDOWN */ + /* * This power thread maintains a history of battery voltage * and implements a charging algorithm. @@ -896,6 +919,23 @@ } #endif } +#elif CONFIG_CHARGING == CHARGING_TARGET +extern void charging_algorithm_big_step(void); +extern void charging_algorithm_small_step(void); +extern void charging_algorithm_close(void); + +int get_battery_capacity(void) +{ + return battery_capacity; +} + +void set_filtered_battery_voltage(int millivolts) +{ + avgbat = millivolts * BATT_AVE_SAMPLES; + battery_millivolts = millivolts; + battery_status_update(); +} + #else #define BATT_AVE_SAMPLES 128 /* slw filter constant for all others */ @@ -944,12 +984,12 @@ * While we are waiting for the time to expire, we average the battery * voltages. */ -static void power_thread_sleep(int ticks) +void power_thread_sleep(int ticks) { - int small_ticks; + long tick_return = current_tick + ticks; - while (ticks > 0) { - + do + { #if CONFIG_CHARGING /* * Detect charger plugged/unplugged transitions. On a plugged or @@ -967,7 +1007,8 @@ case NO_CHARGER: case CHARGER_UNPLUGGED: charger_input_state = CHARGER_PLUGGED; - return; + tick_return = current_tick; + goto do_small_step; /* Algorithm should see transition */ case CHARGER_PLUGGED: queue_broadcast(SYS_CHARGER_CONNECTED, 0); last_sent_battery_level = 0; @@ -988,19 +1029,23 @@ case CHARGER_PLUGGED: case CHARGER: charger_input_state = CHARGER_UNPLUGGED; - return; + tick_return = current_tick; + goto do_small_step; /* Algorithm should see transition */ } } #endif /* CONFIG_CHARGING */ - small_ticks = MIN(HZ/2, ticks); - sleep(small_ticks); - ticks -= small_ticks; + ticks = tick_return - current_tick; + if (ticks > 0) { + ticks = MIN(HZ/2, ticks); + sleep(ticks); + } + /* If the power off timeout expires, the main thread has failed to shut down the system, and we need to force a power off */ if(shutdown_timeout) { - shutdown_timeout -= small_ticks; + shutdown_timeout -= MAX(ticks, 1); if(shutdown_timeout <= 0) power_off(); } @@ -1012,9 +1057,13 @@ /* * Do a digital exponential filter. We don't sample the battery if * the disk is spinning unless we are in USB mode (the disk will most - * likely always be spinning in USB mode). + * likely always be spinning in USB mode) or charging. */ - if (!storage_disk_is_active() || usb_inserted()) { + if (!storage_disk_is_active() || usb_inserted() +#if CONFIG_CHARGING >= CHARGING_MONITOR + || charge_state == CHARGER +#endif + ) { avgbat += battery_adc_voltage() - (avgbat / BATT_AVE_SAMPLES); /* * battery_millivolts is the millivolt-scaled filtered battery value. @@ -1035,17 +1084,20 @@ /* update battery status every time an update is available */ battery_status_update(); -#ifndef NO_LOW_BATTERY_SHUTDOWN - if (!shutdown_timeout && - (battery_millivolts < battery_level_shutoff[battery_type])) + if (!shutdown_timeout && query_force_shutdown()) { sys_poweroff(); - else -#endif + } + else { avgbat += battery_millivolts - (avgbat / BATT_AVE_SAMPLES); + } } +#if CONFIG_CHARGING + do_small_step: +#endif charging_algorithm_small_step(); } + while (TIME_BEFORE(current_tick, tick_return)); } static void power_thread(void) @@ -1083,6 +1135,10 @@ battery_percent += (battery_percent < 100); } +#if CONFIG_CHARGING == CHARGING_TARGET + powermgmt_init_target(); +#endif + while (1) { /* rotate the power history */ @@ -1109,6 +1165,7 @@ #endif /* SIMULATOR */ +#ifndef BOOTLOADER void sys_poweroff(void) { logf("sys_poweroff()"); @@ -1128,6 +1185,7 @@ queue_broadcast(SYS_POWEROFF, 0); } +#endif void cancel_shutdown(void) {