Index: apps/debug_menu.c =================================================================== --- apps/debug_menu.c (revision 19485) +++ apps/debug_menu.c (working copy) @@ -1649,15 +1649,87 @@ charger_inserted() ? "present" : "absent"); lcd_puts(0, 3, buf); #if defined TOSHIBA_GIGABEAT_S + int line = 4; + unsigned int st; + + static const unsigned char * const chrgstate_strings[] = + { + "Disabled", + "Error", + "Discharging", + "Precharge", + "Constant Voltage", + "Constant Current", + "", + }; + + st = power_input_status() & + (POWER_INPUT_CHARGER | POWER_INPUT_BATTERY); + snprintf(buf, 30, "%s%s", + (st & POWER_INPUT_MAIN_CHARGER) ? " Main" : "", + (st & POWER_INPUT_USB_CHARGER) ? " USB" : ""); + lcd_puts(0, line++, buf); + + snprintf(buf, 30, "IUSB Max: %d", usb_allowed_current()); + lcd_puts(0, line++, buf); + + y = ARRAYLEN(chrgstate_strings) - 1; + + switch (charge_state) + { + case CHARGE_STATE_DISABLED: y--; + case CHARGE_STATE_ERROR: y--; + case DISCHARGING: y--; + case TRICKLE: y--; + case TOPOFF: y--; + case CHARGING: y--; + default:; + } + + snprintf(buf, 30, "State: %s", chrgstate_strings[y]); + lcd_puts(0, line++, buf); + + snprintf(buf, 30, "Battery Switch: %s", + (st & POWER_INPUT_BATTERY) ? "On" : "Off"); + lcd_puts(0, line++, buf); + + y = chrgraw_adc_voltage(); + snprintf(buf, 30, "CHRGRAW: %d.%03d V", + y / 1000, y % 1000); + lcd_puts(0, line++, buf); + + y = application_supply_adc_voltage(); + snprintf(buf, 30, "BP : %d.%03d V", + y / 1000, y % 1000); + lcd_puts(0, line++, buf); + 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); - lcd_puts(0, 4, buf); + if (y < 0) x = '-', y = -y; + else x = ' '; + snprintf(buf, 30, "CHRGISN:%c%d mA", x, y); + lcd_puts(0, line++, buf); + y = cccv_regulator_dissipation(); + snprintf(buf, 30, "P CCCV : %d mW", y); + lcd_puts(0, line++, buf); + + y = battery_charge_current(); + if (y < 0) x = '-', y = -y; + else x = ' '; + snprintf(buf, 30, "I Charge:%c%d mA", x, y); + lcd_puts(0, line++, buf); + y = battery_adc_temp(); - snprintf(buf, 30, "T Battery: %dC (%dF)", y, (9*y + 160) / 5); - lcd_puts(0, 5, buf); + + if (y != INT_MIN) { + snprintf(buf, 30, "T Battery: %dC (%dF)", y, + (9*y + 160) / 5); + } else { + /* Conversion disabled */ + snprintf(buf, 30, "T Battery: ?"); + } + + lcd_puts(0, line++, 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 19485) +++ firmware/export/powermgmt.h (working copy) @@ -30,17 +30,26 @@ #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 } 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 +57,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 = 0, /* 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 +163,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 +179,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,12 +193,20 @@ 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); void set_battery_capacity(int capacity); /* set local battery capacity value */ void set_battery_type(int type); /* set local battery type */ @@ -190,7 +218,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 19485) +++ 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/usb.h =================================================================== --- firmware/export/usb.h (revision 19485) +++ 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 19485) +++ 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 @@ -132,8 +131,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 @@ -146,8 +147,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_CHARGING_ENABLE +#define HAVE_USB_POWER +#define USBPOWER_BUTTON BUTTON_MENU +#define USBPOWER_BTN_IGNORE BUTTON_POWER /* define this if the unit has a battery switch or battery can be removed * when running */ Index: firmware/target/arm/imx31/gigabeat-s/mc13783-target.h =================================================================== --- firmware/target/arm/imx31/gigabeat-s/mc13783-target.h (revision 19485) +++ firmware/target/arm/imx31/gigabeat-s/mc13783-target.h (working copy) @@ -31,8 +31,8 @@ #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 */ }; #endif /* MC13783_TARGET_H */ Index: firmware/target/arm/imx31/gigabeat-s/power-imx31.h =================================================================== --- firmware/target/arm/imx31/gigabeat-s/power-imx31.h (revision 19485) +++ firmware/target/arm/imx31/gigabeat-s/power-imx31.h (working copy) @@ -21,6 +21,7 @@ #ifndef POWER_IMX31_H #define POWER_IMX31_H -void charger_detect_event(void); +void charger_main_detect_event(void); +void charger_usb_detect_event(int status); #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,96 @@ +/*************************************************************************** + * __________ __ ___. + * 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_VTRICKLE_CHARGE 2900 /* Must charge slowly */ +#define BATT_VAUTO_RECHARGE 4100 /* When to begin another cycle */ +#define BATT_USB_VAUTO_RECHARGE 4000 /* When to cycle with USB only */ +#define BATT_USB_VSTOP 4140 /* When to stop when USB only */ +#define BATT_TOO_LOW 2400 /* 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_ICHARGE_COMPLETE (505*9/100) /* 9% of nominal max output */ +#define BATTERY_IFAST MC13783_ICHRG_1596MA /* 505mA */ +#define BATTERY_ITRICKLE MC13783_ICHRG_266MA /* 84mA */ +#define BATTERY_IFAST_USB MC13783_ICHRG_1152MA /* 365mA */ +#define BATTERY_ITRICKLE_USB MC13783_ICHRG_532MA /* 168mA */ +#define BATTERY_IMAINTAIN_USB MC13783_ICHRG_266MA /* 84mA */ + +#define BATT_AVE_SAMPLES 32 +#define ICHARGER_AVE_SAMPLES 32 + +/* Provide filtered charge current */ +int battery_charge_current(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_FILTER_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 19485) +++ firmware/target/arm/imx31/gigabeat-s/system-target.h (working copy) @@ -39,6 +39,11 @@ } #endif +/* Service the watchdog timer - serviced from the power thread every minute */ +void watchdog_init(unsigned int half_seconds); +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 19485) +++ firmware/target/arm/imx31/gigabeat-s/adc-target.h (working copy) @@ -47,7 +47,14 @@ #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); +unsigned int chrgraw_adc_voltage(void); +int cccv_regulator_dissipation(void); #endif Index: firmware/target/arm/imx31/gigabeat-s/system-imx31.c =================================================================== --- firmware/target/arm/imx31/gigabeat-s/system-imx31.c (revision 19485) +++ firmware/target/arm/imx31/gigabeat-s/system-imx31.c (working copy) @@ -32,6 +32,31 @@ #include "clkctl-imx31.h" #include "mc13783.h" +/* Initialize the watchdog timer */ +void watchdog_init(unsigned int half_seconds) +{ + uint16_t wcr = WDOG_WCR_WTw(half_seconds) | /* Timeout */ + 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; +} + +/* Service the watchdog timer */ +void watchdog_service(void) +{ + WDOG_WSR = 0x5555; + WDOG_WSR = 0xaaaa; +} + int system_memory_guard(int newmode) { (void)newmode; Index: firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c =================================================================== --- firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c (revision 19485) +++ 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 */ @@ -45,6 +46,18 @@ .mask = MC13783_ONOFD1M, .callback = button_power_event, }, + [MC13783_SE1_EVENT] = /* Main charger detection */ + { + .set = MC13783_EVENT_SET0, + .mask = MC13783_SE1M, + .callback = charger_main_detect_event, + }, + [MC13783_USB_EVENT] = /* USB insertion/USB charger detection */ + { + .set = MC13783_EVENT_SET0, + .mask = MC13783_USBM, + .callback = usb_connect_event, + }, #ifdef HAVE_HEADPHONE_DETECTION [MC13783_ONOFD2_EVENT] = /* Headphone jack */ { @@ -53,18 +66,6 @@ .callback = headphone_detect_event, }, #endif - [MC13783_CHGDET_EVENT] = /* Charger detection */ - { - .set = MC13783_EVENT_SET0, - .mask = MC13783_CHGDETM, - .callback = charger_detect_event, - }, - [MC13783_USB4V4_EVENT] = /* USB insertion */ - { - .set = MC13783_EVENT_SET0, - .mask = MC13783_USBM, - .callback = usb_connect_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 19485) +++ 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,16 @@ * ****************************************************************************/ -/* 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.h" +#include "power-imx31.h" +#include "usb_core.h" /** * Fixed-point natural log @@ -56,7 +60,7 @@ return y; } - +/* TODO: Battery tests to get the right values! */ const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = { 3450 @@ -88,6 +92,17 @@ 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; +} + +unsigned int chrgraw_adc_voltage(void) +{ + return (adc_read(ADC_CHARGER_VOLTAGE) * 23023) >> 10; +} + /* Returns battery charge current from ADC [milliamps] */ int battery_adc_charge_current(void) { @@ -95,11 +110,30 @@ * 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 = ((((int32_t)value << 22) >> 22) * 2881) >> 9; + return ILEVEL_ADJUST_IN(I); } +/* Estimate power dissipation in the charge path regulator in mW. */ +int cccv_regulator_dissipation(void) +{ + /* BATTISNS is shorted to BATT so we don't need to use the + * battery current reading. */ + int chrgraw = (adc_read(ADC_CHARGER_VOLTAGE) * 230225) >> 10; + int batt = ((adc_read(ADC_BATTERY) * 23023) >> 10) + 24000; + int ichrgsn = adc_read(ADC_CHARGER_CURRENT); + ichrgsn = ((((int32_t)ichrgsn << 22) >> 22) * 2881) >> 9; + + return (chrgraw - ichrgsn - batt)*ILEVEL_ADJUST_IN(ichrgsn) / 10000; +} + /* 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 @@ -117,10 +151,691 @@ * Fixed-point output matches the floating-point version for each ADC * value. */ - int R = 2070000 * value; - long long ln = flog(R) + 83196; - long long t0 = 425890304133ll; - long long t1 = 1000000*ln; - long long t3 = ln*ln*ln / 13418057; - return ((32754211579494400ll / (t0 + t1 + t3)) - 27315) / 100; + if (value > 0) + { + int R = 2070000 * value; + long long ln = flog(R) + 83196; + long long t0 = 425890304133ll; + long long t1 = 1000000*ln; + long long t3 = ln*ln*ln / 13418057; + return ((32754211579494400ll / (t0 + t1 + t3)) - 27315) / 100; + } + + return INT_MIN; } + +/** Charger control **/ + +/* All code has a preference for the main charger being connected over + * USB. USB is considered in the algorithm only if it is the sole source. */ +static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */ +static unsigned int power_status = POWER_INPUT_NONE; /* Detect input changes */ +static int charger_total_timer = 0; /* Total allowed charging time */ +static int icharger_ave = 0; /* Filtered charging current */ +static bool charger_close = false; /* Shutdown notification */ +static bool service_wdt = true; /* Service the watchdog timer, if things + go very wrong, cease and shut down. */ +static uint32_t charger_setting = 0; /* Current ICHRG and VCHRG regulator + * setting (register bits) */ +#define CHARGER_ADJUST ((uint32_t)-1)/* Force change in regulator setting */ +static int autorecharge_counter = 0 ; /* Battery < threshold debounce */ +#define AUTORECHARGE_COUNTDOWN (30*2) /* 30s debounce */ + +/* Temperature monitoring */ +static enum +{ + TEMP_STATE_NORMAL = 0, /* Within range */ + TEMP_STATE_WAIT = 1, /* Went out of range, wait to come back */ + TEMP_LOW_LIMIT = 0, /* Min temp */ + TEMP_HIGH_LIMIT = 1, /* Max temp */ +} temp_state = TEMP_STATE_NORMAL; + +/* 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(THREAD_ID_CURRENT, + charging ? PRIORITY_REALTIME : PRIORITY_SYSTEM); +#endif + (void)charging; +} + +/* Update filtered charger current - exponential moving average */ +static bool charger_current_filter_step(void) +{ + int value = battery_adc_charge_current(); + + if (value == ADC_READ_ERROR) + return false; + + icharger_ave += value - (icharger_ave / ICHARGER_AVE_SAMPLES); + return true; +} + +/* Return true if the main charger is connected. */ +static bool main_charger_connected(void) +{ + return (power_status & + POWER_INPUT_MAIN_CHARGER & + POWER_INPUT_CHARGER) != 0; +} + +/* Return the voltage level which should automatically trigger + * another recharge cycle based upon which power source is available. + * Assumes at least one is. */ +static unsigned int auto_recharge_voltage(void) +{ + if (main_charger_connected()) + return BATT_VAUTO_RECHARGE; + else + return BATT_USB_VAUTO_RECHARGE; +} + +#ifndef NO_LOW_BATTERY_SHUTDOWN +/* Return greater of supply (BP) or filtered 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 + +/* Get smoothed readings for initializing filtered data. */ +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; +} + +/* Update filtered battery voltage instead of waiting for filter + * decay. */ +static bool update_filtered_battery_voltage(void) +{ + int millivolts = stat_battery_reading(ADC_BATTERY); + + if (millivolts != INT_MIN) + { + set_filtered_battery_voltage(millivolts); + return true; + } + + return false; +} + +/* Sets the charge current limit based upon state. charge_state should be + * set before calling. */ +static bool adjust_charger_current(void) +{ + static const uint16_t charger_bits[][2] = + { + [DISCHARGING] = + { + MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V, + MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V + }, + /* Main(+USB): Charge slowly from the adapter until voltage is + * sufficient for normal charging. + * + * + * USB: The truth is that things will probably not make it this far. + * Cover the case, just in case the disk isn't used and it is + * manageable. */ + [TRICKLE] = + { + BATTERY_ITRICKLE | MC13783_VCHRG_4_200V, + BATTERY_ITRICKLE_USB | MC13783_VCHRG_4_200V + }, + [TOPOFF] = + { + BATTERY_IFAST | MC13783_VCHRG_4_200V, + BATTERY_IFAST_USB | MC13783_VCHRG_4_200V + }, + [CHARGING] = + { + BATTERY_IFAST | MC13783_VCHRG_4_200V, + BATTERY_IFAST_USB | MC13783_VCHRG_4_200V + }, + /* Must maintain battery when on USB power only - utterly nasty + * but true and something retailos does (it will even end up charging + * the battery but not reporting that it is doing so). + * Float lower than MAX - could end up slightly discharging after + * a full charge but this is safer than maxing it out. */ + [CHARGING+1] = + { + BATTERY_IFAST_USB | MC13783_VCHRG_4_150V, + BATTERY_IMAINTAIN_USB | MC13783_VCHRG_4_150V + }, + }; + + bool success = false; + int usb_select; + uint32_t i; + + usb_select = ((power_status & POWER_INPUT) == POWER_INPUT_USB) ? 1 : 0; + + if (charge_state == DISCHARGING && usb_select == 1) + { + /* USB-only, DISCHARGING, = maintaining battery */ + int select = (power_status & POWER_INPUT_CHARGER) ? 0 : 1; + charger_setting = charger_bits[CHARGING+1][select]; + } + else + { + charger_setting = charger_bits[charge_state][usb_select]; + } + + if (charger_setting != 0) + { + charging_set_thread_priority(true); + + /* Turn regulator logically ON. Hardware may still override. */ + i = mc13783_write_masked(MC13783_CHARGER, + charger_setting | MC13783_CHRGRAWPDEN, + MC13783_ICHRG | MC13783_VCHRG | + MC13783_CHRGRAWPDEN); + + if (i != MC13783_DATA_ERROR) + { + 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) + { + icharger_ave = icharger * ICHARGER_AVE_SAMPLES; + + if (update_filtered_battery_voltage()) + return true; + } + } + + /* Force regulator OFF. */ + charge_state = CHARGE_STATE_ERROR; + } + + /* Turn regulator OFF. */ + icharger_ave = 0; + i = mc13783_write_masked(MC13783_CHARGER, charger_bits[0][0], + MC13783_ICHRG | MC13783_VCHRG | + MC13783_CHRGRAWPDEN); + + if (MC13783_DATA_ERROR == i) + { + /* Failed. Force poweroff by not servicing the watchdog. */ + service_wdt = false; + } + else if (0 == charger_setting) + { + /* Here because OFF was requested state */ + success = true; + } + + charger_setting = 0; + + adc_enable_channel(ADC_CHARGER_CURRENT, false); + update_filtered_battery_voltage(); + charging_set_thread_priority(false); + + return success; +} + +static void stop_charger(void) +{ + charger_total_timer = 0; + + if (charge_state > DISCHARGING) + charge_state = DISCHARGING; + + adjust_charger_current(); +} + +static bool charging_ok(void) +{ + bool ok = charge_state >= DISCHARGING; /* Not an error condition? */ + + if (ok) + { + /* Is the battery even connected? */ + ok = (power_status & POWER_INPUT_BATTERY) != 0; + } + + 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; + + default: + break; + } + } + + if (ok) + { + /* Any events that should stop the regulator? */ + ok = (int_sense0 & MC13783_CHGOVS) == 0; + } + + if (!ok && charger_setting != 0) + { + int state = charge_state; + + if (state > DISCHARGING) + state = DISCHARGING; + + charge_state = CHARGE_STATE_ERROR; /* Force off for all states */ + stop_charger(); + charge_state = state; + } + + return ok; +} + +void powermgmt_init_target(void) +{ +#ifdef IMX31_ALLOW_CHARGING + const uint32_t regval_w = + MC13783_VCHRG_4_050V | MC13783_ICHRG_0MA | + MC13783_ICHRGTR_0MA | MC13783_OVCTRL_6_90V; + + mc13783_write(MC13783_CHARGER, regval_w); + + if (mc13783_read(MC13783_CHARGER) == regval_w) + { + /* Divide CHRGRAW input by 10 */ + mc13783_clear(MC13783_ADC0, MC13783_CHRGRAWDIV); + + /* Use watchdog to shut system down if we lose control of the charging + * hardware. */ + watchdog_init(20*2); /* 20 seconds in half-second steps */ + } + else + { + /* Register has the wrong value - set error condition and disable + * since something is wrong. */ + charge_state = CHARGE_STATE_DISABLED; + stop_charger(); + } +#else + /* Disable charger use */ + charge_state = CHARGE_STATE_DISABLED; +#endif +} + +/* Returns CHARGING or DISCHARGING since that's all we really 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 +} + +static void charger_plugged(void) +{ + adc_enable_channel(ADC_BATTERY_TEMP, true); + autorecharge_counter = 0; +} + +static void charger_unplugged(void) +{ + /* Charger pulled - turn off current sources (though hardware + * will have done that anyway). */ + if (charge_state > CHARGE_STATE_DISABLED) + { + /* Reset state and clear any error. If disabled, the charger + * will not have been started or will have been stopped already. */ + stop_charger(); + charge_state = DISCHARGING; + } + + power_status = POWER_INPUT_NONE; + temp_state = TEMP_STATE_NORMAL; + autorecharge_counter = 0; + + adc_enable_channel(ADC_BATTERY_TEMP, false); + update_filtered_battery_voltage(); +} + +static void charger_none(void) +{ + unsigned int pwr = power_input_status(); + + if (power_status != pwr) + { + if ((power_status ^ pwr) & POWER_INPUT_BATTERY) + update_filtered_battery_voltage(); + + power_status = pwr; + + if (charge_state == CHARGE_STATE_DISABLED) + return; + + if ((power_status & (POWER_INPUT | POWER_INPUT_CHARGER)) + == POWER_INPUT_USB) + { + /* USB connected but not configured. Maintain battery to the + * greatest degree possible. It probably won't be enough but the + * discharge won't be so severe. */ + charger_plugged(); + charger_setting = CHARGER_ADJUST; + } + else + { + charger_unplugged(); + } + } + else if (charger_setting != 0) + { + /* Maintaining - keep filter going and check charge state */ + int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0); + + if (!charger_current_filter_step()) + charge_state = CHARGE_STATE_ERROR; + + charging_ok(); + } +} + +static void charger_control(void) +{ + unsigned int pwr = power_input_status(); + + if (power_status != pwr) + { + if ((power_status ^ pwr) & POWER_INPUT_BATTERY) + update_filtered_battery_voltage(); + + power_status = pwr; + charger_setting = CHARGER_ADJUST; + autorecharge_counter = 0; + } + + if (charger_setting != 0 && !charger_current_filter_step()) + charge_state = CHARGE_STATE_ERROR; + + int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0); + + if (!charging_ok()) + return; + + switch (charge_state) + { + case DISCHARGING: + { + unsigned int millivolts = battery_voltage(); + + /* Battery voltage may have dropped and a charge cycle should + * start again. Debounced. */ + if (millivolts > auto_recharge_voltage()) + { + autorecharge_counter = AUTORECHARGE_COUNTDOWN; + break; + } + + if (autorecharge_counter > 0) + { + autorecharge_counter--; + break; + } + + autorecharge_counter = 0; + + charging_set_thread_priority(true); + + millivolts = stat_battery_reading(ADC_BATTERY); + + if (millivolts <= BATT_TOO_LOW) + { + /* Problem - no battery? If so, something else is wrong. */ + charging_set_thread_priority(false); + + if (power_status & POWER_INPUT_BATTERY) + charge_state = CHARGE_STATE_ERROR; + + break; + } + else if (millivolts < BATT_VTRICKLE_CHARGE) + { + /* Battery is very low - trickle. */ + charge_state = TRICKLE; + } + else + { + /* Ok for fast charge */ + charge_state = CHARGING; + } + + charger_setting = CHARGER_ADJUST; + charger_total_timer = CHARGER_TOTAL_TIMER*60*2; + break; + } /* DISCHARGING: */ + + case TRICKLE: /* Low current initial charge */ + { + if (battery_voltage() > BATT_VTRICKLE_CHARGE) + { + /* Switch to normal charge mode. */ + charge_state = CHARGING; + charger_setting = CHARGER_ADJUST; + } + break; + } /* TRICKLE: */ + + case CHARGING: /* Constant-current stage */ + case TOPOFF: /* Constant-voltage stage */ + { + /* Reg. mode is more informative than an operational nescessity. */ + charge_state = (int_sense0 & MC13783_CCCVS) ? TOPOFF : CHARGING; + + if ((int_sense0 & MC13783_CHGCURRS) == 0) + { + /* Charge current is low */ + } + else if (main_charger_connected()) + { + /* Monitor and stop if current drops below threshold. */ + if (battery_charge_current() > BATTERY_ICHARGE_COMPLETE) + break; + } + else + { + /* Accurate I-level can't be determined since device also + * powers through the I sense. This simply stops the reporting + * of charging but the regulator remains running. */ + if (battery_voltage() <= BATT_USB_VSTOP) + break; + } + + stop_charger(); + break; + } /* CHARGING: TOPOFF: */ + + default: + break; + } /* switch */ + + /* Check if charger timer expired and stop it if so. */ + if (charger_total_timer > 0 && --charger_total_timer == 0) + { + charge_state = CHARGE_STATE_ERROR; + stop_charger(); /* Time ran out - error */ + } +} + +/* Main charging algorithm - called from powermgmt.c */ +void charging_algorithm_small_step(void) +{ + if (service_wdt) + watchdog_service(); + + /* Switch by input state */ + switch (charger_input_state) + { + case NO_CHARGER: + charger_none(); + break; + + case CHARGER_PLUGGED: + charger_plugged(); + break; + + case CHARGER: + charger_control(); + break; + + case CHARGER_UNPLUGGED: + charger_unplugged(); + break; + } /* switch */ + + if (charger_close) + { + /* Disable starts while shutting down */ + charge_state = CHARGE_STATE_DISABLED; + stop_charger(); + charger_close = false; + return; + } + + if (charger_setting != 0) + { + if ((mc13783_read(MC13783_CHARGER) & (MC13783_ICHRG | MC13783_VCHRG)) != + charger_setting) + { + /* The hardware setting doesn't match. It could have turned the + * charger off in a race of plugging/unplugging or the setting + * was changed in one of the calls. */ + adjust_charger_current(); + } + } +} + +void charging_algorithm_big_step(void) +{ + /* Sleep for one minute */ + power_thread_sleep(HZ*60); +} + +/* 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) +{ + charger_close = true; + + /* Power management thread will set it false again */ + while (charger_close) + sleep(HZ/10); +} + +#ifdef BOOTLOADER +void sys_poweroff(void) +{ +} +#endif /* BOOTLOADER */ Index: firmware/target/arm/imx31/gigabeat-s/adc-imx31.c =================================================================== --- firmware/target/arm/imx31/gigabeat-s/adc-imx31.c (revision 19485) +++ 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) +{ + uint32_t bit, mask; + + switch (channel) + { + case ADC_CHARGER_CURRENT: + mask = MC13783_CHRGICON; + break; + + case ADC_BATTERY_TEMP: + mask = MC13783_RTHEN; + break; + + default: + return false; + } + + bit = enable ? mask : 0; + + return mc13783_write_masked(MC13783_ADC0, bit, mask) + != MC13783_DATA_ERROR; +} + /* Called by mc13783 interrupt thread when conversion is complete */ void adc_done(void) { @@ -98,9 +122,9 @@ /* 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, turn off extra conversions. */ + 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 19485) +++ 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; @@ -53,8 +54,11 @@ void usb_connect_event(void) { - uint32_t status = mc13783_read(MC13783_INTERRUPT_SENSE0); - usb_status = (status & MC13783_USB4V4S) ? USB_INSERTED : USB_EXTRACTED; + 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(usb_status); } int usb_detect(void) @@ -81,7 +85,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 19485) +++ firmware/target/arm/imx31/gigabeat-s/power-imx31.c (working copy) @@ -20,6 +20,8 @@ ****************************************************************************/ #include "config.h" #include "system.h" +#include "usb.h" +#include "usb_core.h" #include "power.h" #include "power-imx31.h" #include "backlight.h" @@ -29,35 +31,47 @@ #include "i2c-imx31.h" extern struct i2c_node si4700_i2c_node; +static unsigned int power_status = POWER_INPUT_NONE; -static bool charger_detect = false; - -/* This is called from the mc13783 interrupt thread */ -void charger_detect_event(void) -{ - charger_detect = - mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_CHGDETS; -} - +/* Detect which power sources are present. */ unsigned int power_input_status(void) { - unsigned int status = POWER_INPUT_NONE; + unsigned int status = power_status; - if ((GPIO3_DR & (1 << 20)) != 0) + if (GPIO3_DR & (1 << 20)) status |= POWER_INPUT_BATTERY; - if (charger_detect) - status |= POWER_INPUT_MAIN_CHARGER; + if (usb_allowed_current() < 500) + { + /* ACK that USB is connected but NOT chargeable */ + status &= ~(POWER_INPUT_USB_CHARGER & POWER_INPUT_CHARGER); + } return status; } -/* Returns true if the unit is charging the batteries. */ -bool charging_state(void) +/* Detect changes in presence of the AC adaptor. */ +void charger_main_detect_event(void) { - return false; + if (mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_SE1S) + power_status |= POWER_INPUT_MAIN_CHARGER; + else + power_status &= ~POWER_INPUT_MAIN_CHARGER; } +/* Detect changes in USB bus power. Called from usb connect event handler. */ +void charger_usb_detect_event(int status) +{ + /* USB plugged does not imply charging is possible or even + * powering the device to maintain the battery. */ + if (status == USB_INSERTED) + power_status |= POWER_INPUT_USB_CHARGER; + else + power_status &= ~POWER_INPUT_USB_CHARGER; +} + +/* charging_state is implemented in powermgmt-imx31.c */ + void ide_power_enable(bool on) { if (!on) @@ -83,7 +97,7 @@ } #if CONFIG_TUNER -bool tuner_power(bool status) + bool tuner_power(bool status) { if (status) { @@ -129,9 +143,8 @@ 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); } - Index: firmware/target/arm/imx31/debug-imx31.c =================================================================== --- firmware/target/arm/imx31/debug-imx31.c (revision 19485) +++ firmware/target/arm/imx31/debug-imx31.c (working copy) @@ -139,6 +139,7 @@ MC13783_INTERRUPT_SENSE0, MC13783_INTERRUPT_STATUS1, MC13783_INTERRUPT_SENSE1, + MC13783_CHARGER, MC13783_RTC_TIME, MC13783_RTC_ALARM, MC13783_RTC_DAY, @@ -151,6 +152,7 @@ "Int Sense0", "Int Stat1 ", "Int Sense1", + "Charger ", "RTC Time ", "RTC Alarm ", "RTC Day ", Index: firmware/powermgmt.c =================================================================== --- firmware/powermgmt.c (revision 19485) +++ 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(); @@ -285,6 +296,10 @@ /* Returns battery level in percent */ int battery_level(void) { +#ifdef HAVE_BATTERY_SWITCH + if ((power_input_status() & POWER_INPUT_BATTERY) == 0) + return -1; +#endif return battery_percent; } @@ -294,11 +309,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 +366,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 +418,7 @@ battery_percent = level; send_battery_level_event(); + (void)state; } /* @@ -431,15 +448,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 +558,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 +920,18 @@ } #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); + +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 */ @@ -961,12 +997,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 @@ -979,7 +1015,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; @@ -1000,19 +1037,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(); } @@ -1024,9 +1065,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. @@ -1047,17 +1092,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) @@ -1074,7 +1122,7 @@ #ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */ /* The battery voltage is usually a little lower directly after turning on, because the disk was used heavily. Raise it by 5% */ -#ifdef HAVE_CHARGING +#if CONFIG_CHARGING if(!charger_inserted()) /* only if charger not connected */ #endif avgbat += (percent_to_volt_discharge[battery_type][6] - @@ -1095,6 +1143,10 @@ battery_percent += (battery_percent < 100); } +#if CONFIG_CHARGING == CHARGING_TARGET + powermgmt_init_target(); +#endif + while (1) { /* rotate the power history */ @@ -1121,6 +1173,7 @@ #endif /* SIMULATOR */ +#ifndef BOOTLOADER void sys_poweroff(void) { #ifndef BOOTLOADER @@ -1142,6 +1195,7 @@ queue_broadcast(SYS_POWEROFF, 0); #endif /* BOOTLOADER */ } +#endif void cancel_shutdown(void) {