2007-09-21 15:51:53 +00:00
|
|
|
|
/***************************************************************************
|
|
|
|
|
* __________ __ ___.
|
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
|
* $Id$
|
|
|
|
|
*
|
2008-12-21 18:10:36 +00:00
|
|
|
|
* Copyright (c) 2008 by Michael Sevakis
|
2007-09-21 15:51:53 +00:00
|
|
|
|
*
|
2008-06-28 18:10:04 +00:00
|
|
|
|
* 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.
|
2007-09-21 15:51:53 +00:00
|
|
|
|
*
|
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
|
* KIND, either express or implied.
|
|
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
2014-03-03 19:26:08 +00:00
|
|
|
|
|
2008-12-21 18:10:36 +00:00
|
|
|
|
#include <stdlib.h>
|
2007-09-21 15:51:53 +00:00
|
|
|
|
#include "config.h"
|
2008-12-21 18:10:36 +00:00
|
|
|
|
#include "system.h"
|
2014-03-03 19:26:08 +00:00
|
|
|
|
#include "kernel.h"
|
2008-12-21 18:10:36 +00:00
|
|
|
|
#include "thread.h"
|
|
|
|
|
#include "mc13783.h"
|
2007-09-21 15:51:53 +00:00
|
|
|
|
#include "adc.h"
|
|
|
|
|
#include "powermgmt.h"
|
2008-12-21 18:10:36 +00:00
|
|
|
|
#include "power.h"
|
2010-04-09 01:21:53 +00:00
|
|
|
|
#include "power-gigabeat-s.h"
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
|
|
|
|
const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
|
|
|
|
|
{
|
2010-04-09 10:31:40 +00:00
|
|
|
|
3659
|
2008-12-21 18:10:36 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
|
|
|
|
|
{
|
2010-06-29 07:29:42 +00:00
|
|
|
|
3630
|
2008-12-21 18:10:36 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
|
|
|
|
|
const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
|
|
|
|
|
{
|
2010-04-09 10:31:40 +00:00
|
|
|
|
/* Toshiba Gigabeat S Li Ion 700mAH figured from discharge curve */
|
|
|
|
|
{ 3659, 3719, 3745, 3761, 3785, 3813, 3856, 3926, 3984, 4040, 4121 },
|
2008-12-21 18:10:36 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
|
|
|
|
|
const unsigned short percent_to_volt_charge[11] =
|
|
|
|
|
{
|
2010-04-09 10:31:40 +00:00
|
|
|
|
/* Toshiba Gigabeat S Li Ion 700mAH figured from charge curve */
|
2010-04-13 04:57:35 +00:00
|
|
|
|
4028, 4063, 4087, 4111, 4135, 4156, 4173, 4185, 4194, 4202, 4208
|
2008-12-21 18:10:36 +00:00
|
|
|
|
};
|
2007-09-21 15:51:53 +00:00
|
|
|
|
|
|
|
|
|
/* Returns battery voltage from ADC [millivolts] */
|
2012-01-03 23:44:38 +00:00
|
|
|
|
int _battery_voltage(void)
|
2007-09-21 15:51:53 +00:00
|
|
|
|
{
|
2008-04-14 06:52:38 +00:00
|
|
|
|
/* ADC reading 0-1023 = 2400mV-4700mV */
|
|
|
|
|
return ((adc_read(ADC_BATTERY) * 2303) >> 10) + 2400;
|
2007-09-21 15:51:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-12-21 18:10:36 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-25 01:38:08 +00:00
|
|
|
|
/* Returns battery charge current from ADC [milliamps] */
|
|
|
|
|
int battery_adc_charge_current(void)
|
|
|
|
|
{
|
|
|
|
|
/* Positive reading = charger to battery
|
|
|
|
|
* Negative reading = battery to charger terminal
|
|
|
|
|
* ADC reading -512-511 = -2875mA-2875mA */
|
|
|
|
|
unsigned int value = adc_read(ADC_CHARGER_CURRENT);
|
2008-12-21 18:10:36 +00:00
|
|
|
|
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. */
|
|
|
|
|
unsigned 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;
|
|
|
|
|
ichrgsn = abs(ichrgsn);
|
|
|
|
|
|
|
|
|
|
return (chrgraw - ichrgsn - batt)*ILEVEL_ADJUST_IN(ichrgsn) / 10000;
|
2008-05-25 01:38:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns battery temperature from ADC [deg-C] */
|
2008-12-21 18:10:36 +00:00
|
|
|
|
int battery_adc_temp(void)
|
2008-05-25 01:38:08 +00:00
|
|
|
|
{
|
|
|
|
|
/* E[volts] = value * 2.3V / 1023
|
|
|
|
|
* R[ohms] = E/I = E[volts] / 0.00002[A] (Thermistor bias current source)
|
|
|
|
|
*
|
2008-12-24 23:36:33 +00:00
|
|
|
|
* Steinhart-Hart thermistor equation:
|
2008-05-25 02:43:51 +00:00
|
|
|
|
* [A + B*ln(R) + D*ln^3(R)] = 1 / T[<EFBFBD>K]
|
2008-05-25 01:38:08 +00:00
|
|
|
|
*
|
|
|
|
|
* Coeffients that fit experimental data (one thermistor so far, one run):
|
|
|
|
|
* A = 0.0013002631685462800
|
|
|
|
|
* B = 0.0002000841932612330
|
|
|
|
|
* D = 0.0000000640446750919
|
|
|
|
|
*/
|
2008-12-24 23:36:33 +00:00
|
|
|
|
static const unsigned short ntc_table[] =
|
|
|
|
|
{
|
|
|
|
|
#if 0 /* These have degree deltas > 1 (except the final two) so leave them
|
|
|
|
|
* out. 70 deg C upper limit is quite sufficient. */
|
|
|
|
|
0, /* INF */ 1, /* 171 */ 2, /* 145 */ 3, /* 130 */
|
|
|
|
|
4, /* 121 */ 5, /* 114 */ 6, /* 108 */ 7, /* 104 */
|
|
|
|
|
8, /* 100 */ 9, /* 96 */ 10, /* 93 */ 11, /* 91 */
|
|
|
|
|
12, /* 88 */ 13, /* 86 */ 14, /* 84 */ 15, /* 82 */
|
|
|
|
|
16, /* 81 */ 17, /* 79 */ 18, /* 78 */ 19, /* 76 */
|
|
|
|
|
20, /* 75 */ 21, /* 74 */ 22, /* 72 */ 23, /* 71 */
|
|
|
|
|
#endif
|
|
|
|
|
24, /* 70 */ 25, /* 69 */ 26, /* 68 */ 27, /* 67 */
|
|
|
|
|
28, /* 66 */ 30, /* 65 */ 31, /* 64 */ 32, /* 63 */
|
|
|
|
|
33, /* 62 */ 35, /* 61 */ 36, /* 60 */ 38, /* 59 */
|
|
|
|
|
39, /* 58 */ 41, /* 57 */ 43, /* 56 */ 45, /* 55 */
|
|
|
|
|
47, /* 54 */ 49, /* 53 */ 51, /* 52 */ 53, /* 51 */
|
|
|
|
|
56, /* 50 */ 58, /* 49 */ 61, /* 48 */ 63, /* 47 */
|
|
|
|
|
66, /* 46 */ 69, /* 45 */ 73, /* 44 */ 76, /* 43 */
|
|
|
|
|
80, /* 42 */ 83, /* 41 */ 87, /* 40 */ 92, /* 39 */
|
|
|
|
|
96, /* 38 */ 101, /* 37 */ 106, /* 36 */ 111, /* 35 */
|
|
|
|
|
116, /* 34 */ 122, /* 33 */ 128, /* 32 */ 135, /* 31 */
|
|
|
|
|
142, /* 30 */ 149, /* 29 */ 156, /* 28 */ 164, /* 27 */
|
|
|
|
|
173, /* 26 */ 182, /* 25 */ 192, /* 24 */ 202, /* 23 */
|
|
|
|
|
212, /* 22 */ 224, /* 21 */ 236, /* 20 */ 249, /* 19 */
|
|
|
|
|
262, /* 18 */ 277, /* 17 */ 292, /* 16 */ 308, /* 15 */
|
|
|
|
|
325, /* 14 */ 344, /* 13 */ 363, /* 12 */ 384, /* 11 */
|
|
|
|
|
406, /* 10 */ 429, /* 9 */ 454, /* 8 */ 480, /* 7 */
|
|
|
|
|
509, /* 6 */ 539, /* 5 */ 571, /* 4 */ 605, /* 3 */
|
|
|
|
|
641, /* 2 */ 680, /* 1 */ 722, /* 0 */ 766, /* -1 */
|
|
|
|
|
813, /* -2 */ 864, /* -3 */ 918, /* -4 */ 976, /* -5 */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
unsigned int value = adc_read(ADC_BATTERY_TEMP);
|
|
|
|
|
|
2008-12-21 18:10:36 +00:00
|
|
|
|
if (value > 0)
|
|
|
|
|
{
|
2008-12-24 23:36:33 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
for (i = 1; i < ARRAYLEN(ntc_table); i++)
|
|
|
|
|
{
|
|
|
|
|
if (ntc_table[i] > value)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 71 - i;
|
2008-12-21 18:10:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 */
|
2008-12-24 16:58:41 +00:00
|
|
|
|
static unsigned int last_inputs = POWER_INPUT_NONE; /* Detect input changes */
|
2008-12-21 18:10:36 +00:00
|
|
|
|
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 */
|
|
|
|
|
static int chgcurr_timer = 0; /* Countdown to CHGCURR error */
|
|
|
|
|
#define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */
|
|
|
|
|
#define WATCHDOG_TIMEOUT (10*2) /* If not serviced, poweroff in 10s */
|
2008-12-24 16:58:41 +00:00
|
|
|
|
#define CHGCURR_TIMEOUT (4*2) /* 4s debounce */
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
2008-12-24 16:58:41 +00:00
|
|
|
|
return (last_inputs &
|
2008-12-21 18:10:36 +00:00
|
|
|
|
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. */
|
2012-01-04 00:13:06 +00:00
|
|
|
|
static int auto_recharge_voltage(void)
|
2008-12-21 18:10:36 +00:00
|
|
|
|
{
|
|
|
|
|
if (main_charger_connected())
|
|
|
|
|
return BATT_VAUTO_RECHARGE;
|
|
|
|
|
else
|
|
|
|
|
return BATT_USB_VAUTO_RECHARGE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return greater of supply (BP) or filtered battery voltage. */
|
2008-12-24 16:58:41 +00:00
|
|
|
|
unsigned int input_millivolts(void)
|
2008-12-21 18:10:36 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned int app_millivolts = application_supply_adc_voltage();
|
|
|
|
|
unsigned int bat_millivolts = battery_voltage();
|
|
|
|
|
|
|
|
|
|
return MAX(app_millivolts, bat_millivolts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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:
|
2012-01-03 23:44:38 +00:00
|
|
|
|
reading = _battery_voltage();
|
2008-12-21 18:10:36 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2008-12-24 16:58:41 +00:00
|
|
|
|
reset_battery_filter(millivolts);
|
2008-12-21 18:10:36 +00:00
|
|
|
|
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 uint8_t charger_bits[][2] =
|
|
|
|
|
{
|
|
|
|
|
[DISCHARGING] =
|
|
|
|
|
{
|
|
|
|
|
/* These are actually zeros but reflect this setting */
|
|
|
|
|
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 | BATTERY_VCHARGING,
|
|
|
|
|
BATTERY_ITRICKLE_USB | BATTERY_VCHARGING
|
|
|
|
|
},
|
|
|
|
|
[TOPOFF] =
|
|
|
|
|
{
|
|
|
|
|
BATTERY_IFAST | BATTERY_VCHARGING,
|
|
|
|
|
BATTERY_IFAST_USB | BATTERY_VCHARGING
|
|
|
|
|
},
|
|
|
|
|
[CHARGING] =
|
|
|
|
|
{
|
|
|
|
|
BATTERY_IFAST | BATTERY_VCHARGING,
|
|
|
|
|
BATTERY_IFAST_USB | BATTERY_VCHARGING
|
|
|
|
|
},
|
|
|
|
|
/* 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_IFLOAT_USB | BATTERY_VFLOAT_USB,
|
|
|
|
|
BATTERY_IMAINTAIN_USB | BATTERY_VMAINTAIN_USB
|
|
|
|
|
},
|
|
|
|
|
#if 0
|
|
|
|
|
/* Slower settings to so that the linear regulator doesn't dissipate
|
|
|
|
|
* an excessive amount of power when coming out of precharge state. */
|
|
|
|
|
[CHARGING+2] =
|
|
|
|
|
{
|
|
|
|
|
BATTERY_ISLOW | BATTERY_VCHARGING,
|
|
|
|
|
BATTERY_ISLOW_USB | BATTEYR_VCHARGING
|
|
|
|
|
},
|
|
|
|
|
#endif
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool success = false;
|
|
|
|
|
int usb_select;
|
|
|
|
|
uint32_t i;
|
|
|
|
|
|
2008-12-24 16:58:41 +00:00
|
|
|
|
usb_select = ((last_inputs & POWER_INPUT) == POWER_INPUT_USB)
|
2008-12-21 18:10:36 +00:00
|
|
|
|
? 1 : 0;
|
|
|
|
|
|
|
|
|
|
if (charge_state == DISCHARGING && usb_select == 1)
|
|
|
|
|
{
|
|
|
|
|
/* USB-only, DISCHARGING, = maintaining battery */
|
2008-12-24 16:58:41 +00:00
|
|
|
|
int select = (last_inputs & POWER_INPUT_CHARGER) ? 0 : 1;
|
2008-12-21 18:10:36 +00:00
|
|
|
|
charger_setting = charger_bits[CHARGING+1][select];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Take very good care not to write garbage. */
|
|
|
|
|
int state = charge_state;
|
|
|
|
|
|
|
|
|
|
if (state < DISCHARGING || state > CHARGING)
|
|
|
|
|
state = DISCHARGING;
|
|
|
|
|
|
|
|
|
|
charger_setting = charger_bits[state][usb_select];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (charger_setting != 0)
|
|
|
|
|
{
|
2010-04-07 19:59:26 +00:00
|
|
|
|
if ((charger_setting & MC13783_VCHRG) > BATTERY_VCHARGING ||
|
|
|
|
|
(charger_setting & MC13783_ICHRG) > BATTERY_IFAST)
|
|
|
|
|
{
|
|
|
|
|
/* Table is corrupted somehow. We shouldn't run at all.
|
|
|
|
|
*
|
|
|
|
|
* Explanation: On two occasions, even though this driver monitors
|
|
|
|
|
* the regulator register settings on each step and
|
|
|
|
|
* ensures that only valid table indexes are used,
|
|
|
|
|
* the current and voltage seem to be misregulated,
|
|
|
|
|
* resulting in excessively high battery voltage that
|
|
|
|
|
* will trip the battery protection. After careful
|
|
|
|
|
* review it seems that two possibilities exist:
|
|
|
|
|
* This code or data got trashed at some point or
|
|
|
|
|
* there really is a hardware bug of some sort. So
|
|
|
|
|
* far the cause is unknown. Voltage is also
|
|
|
|
|
* monitored in the CHARGING case for that reason.
|
|
|
|
|
* The solution for data or code corruption is to
|
|
|
|
|
* just panic and refuse to run the device. The
|
|
|
|
|
* solution for overvoltage due to hardware bug is to
|
|
|
|
|
* disable the charging. The action taken will reveal
|
|
|
|
|
* the true cause, thus _who_ is responsible.
|
|
|
|
|
* "Burning lithium is baaaad", so sayeth The Council
|
|
|
|
|
* of Seven Ascended Masters. */
|
|
|
|
|
charge_state = CHARGE_STATE_DISABLED;
|
|
|
|
|
service_wdt = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Turn on 5K pulldown. */
|
|
|
|
|
i = mc13783_set(MC13783_CHARGER, MC13783_CHRGRAWPDEN);
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
2010-04-07 19:59:26 +00:00
|
|
|
|
if (i != MC13783_DATA_ERROR)
|
|
|
|
|
{
|
|
|
|
|
/* Turn regulator logically ON. Hardware may still override.
|
|
|
|
|
*/
|
|
|
|
|
i = mc13783_write_masked(MC13783_CHARGER, charger_setting,
|
|
|
|
|
MC13783_ICHRG | MC13783_VCHRG);
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
2010-04-07 19:59:26 +00:00
|
|
|
|
if (i != MC13783_DATA_ERROR)
|
|
|
|
|
{
|
|
|
|
|
int icharger;
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
2010-04-07 19:59:26 +00:00
|
|
|
|
/* Enable charge current conversion */
|
|
|
|
|
adc_enable_channel(ADC_CHARGER_CURRENT, true);
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
2010-04-07 19:59:26 +00:00
|
|
|
|
/* Charge path regulator turn on takes ~100ms max. */
|
|
|
|
|
sleep(HZ/10);
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
2010-04-07 19:59:26 +00:00
|
|
|
|
icharger = stat_battery_reading(ADC_CHARGER_CURRENT);
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
2010-04-07 19:59:26 +00:00
|
|
|
|
if (icharger != INT_MIN)
|
|
|
|
|
{
|
|
|
|
|
icharger_ave = icharger * ICHARGER_AVE_SAMPLES;
|
|
|
|
|
|
|
|
|
|
if (update_filtered_battery_voltage())
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-21 18:10:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-04-07 19:59:26 +00:00
|
|
|
|
/* Force regulator OFF. */
|
|
|
|
|
charge_state = CHARGE_STATE_ERROR;
|
|
|
|
|
}
|
2008-12-21 18:10:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Turn regulator OFF. */
|
|
|
|
|
icharger_ave = 0;
|
2010-04-07 19:59:26 +00:00
|
|
|
|
i = mc13783_write_masked(MC13783_CHARGER,
|
|
|
|
|
MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V,
|
2008-12-21 18:10:36 +00:00
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Stop the charger - if USB only then the regulator will not really be
|
|
|
|
|
* turned off. ERROR or DISABLED will turn it off however. */
|
|
|
|
|
static void stop_charger(void)
|
|
|
|
|
{
|
|
|
|
|
charger_total_timer = 0;
|
|
|
|
|
|
|
|
|
|
if (charge_state > DISCHARGING)
|
|
|
|
|
charge_state = DISCHARGING;
|
|
|
|
|
|
|
|
|
|
adjust_charger_current();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return OK if it is acceptable to start the regulator. */
|
|
|
|
|
static bool charging_ok(void)
|
|
|
|
|
{
|
|
|
|
|
bool ok = charge_state >= DISCHARGING; /* Not an error condition? */
|
|
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
|
{
|
|
|
|
|
/* Is the battery even connected? */
|
2008-12-24 16:58:41 +00:00
|
|
|
|
ok = (last_inputs & POWER_INPUT_BATTERY) != 0;
|
2008-12-21 18:10:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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] =
|
|
|
|
|
{
|
2010-04-07 06:26:18 +00:00
|
|
|
|
/* Temperature range before beginning charging */
|
|
|
|
|
{ BATTERY_CHARGE_MIN,
|
|
|
|
|
BATTERY_CHARGE_MAX },
|
|
|
|
|
/* Temperature range after out-of-range detected -
|
|
|
|
|
charging will self-resume */
|
|
|
|
|
{ BATTERY_CHARGE_RESTART_MIN,
|
|
|
|
|
BATTERY_CHARGE_RESTART_MAX },
|
2008-12-21 18:10:36 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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? */
|
|
|
|
|
|
|
|
|
|
/* Overvoltage at CHRGRAW? */
|
|
|
|
|
ok = (int_sense0 & MC13783_CHGOVS) == 0;
|
|
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
|
{
|
|
|
|
|
/* CHGCURR sensed? */
|
|
|
|
|
ok = (int_sense0 & MC13783_CHGCURRS) != 0;
|
|
|
|
|
|
|
|
|
|
if (!ok)
|
|
|
|
|
{
|
|
|
|
|
/* Debounce transient states */
|
|
|
|
|
if (chgcurr_timer > 0)
|
|
|
|
|
{
|
|
|
|
|
chgcurr_timer--;
|
|
|
|
|
ok = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
chgcurr_timer = CHGCURR_TIMEOUT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Charger may need to be reinserted */
|
|
|
|
|
if (!ok)
|
|
|
|
|
charge_state = CHARGE_STATE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (charger_setting != 0)
|
|
|
|
|
{
|
2010-04-07 19:59:26 +00:00
|
|
|
|
if (ok)
|
|
|
|
|
{
|
|
|
|
|
/* Protect against any conceivable overcharge/voltage condition
|
|
|
|
|
* before hardware protection must intervene. Disable charger
|
|
|
|
|
* until reboot. */
|
|
|
|
|
ok = battery_voltage() < BATT_TOO_HIGH;
|
|
|
|
|
if (!ok)
|
|
|
|
|
charge_state = CHARGE_STATE_DISABLED;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-21 18:10:36 +00:00
|
|
|
|
if (ok)
|
|
|
|
|
{
|
|
|
|
|
/* Watch to not overheat FET (nothing should go over about 1012.7mW).
|
|
|
|
|
* Trying a higher voltage AC adapter can work (up to 6.90V) but
|
|
|
|
|
* we'll just reject that. Reducing current for adapters that bring
|
|
|
|
|
* CHRGRAW to > 4.900V is another possible action. */
|
|
|
|
|
ok = cccv_regulator_dissipation() < 1150;
|
|
|
|
|
if (!ok)
|
|
|
|
|
charge_state = CHARGE_STATE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ok)
|
|
|
|
|
{
|
|
|
|
|
int state = charge_state;
|
|
|
|
|
|
|
|
|
|
if (state > DISCHARGING)
|
|
|
|
|
state = DISCHARGING;
|
|
|
|
|
|
|
|
|
|
/* Force off for all states including maintaining the battery level
|
|
|
|
|
* on USB. */
|
|
|
|
|
charge_state = CHARGE_STATE_ERROR;
|
|
|
|
|
stop_charger();
|
|
|
|
|
charge_state = state;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-11 14:39:35 +00:00
|
|
|
|
void INIT_ATTR powermgmt_init_target(void)
|
2008-12-21 18:10:36 +00:00
|
|
|
|
{
|
2010-05-19 23:39:59 +00:00
|
|
|
|
last_inputs = power_thread_inputs;
|
|
|
|
|
|
2008-12-21 18:10:36 +00:00
|
|
|
|
#ifdef IMX31_ALLOW_CHARGING
|
|
|
|
|
const uint32_t regval_w =
|
|
|
|
|
MC13783_VCHRG_4_050V | MC13783_ICHRG_0MA |
|
|
|
|
|
MC13783_ICHRGTR_0MA | MC13783_OVCTRL_6_90V;
|
|
|
|
|
|
2011-10-30 14:32:42 +00:00
|
|
|
|
/* Must maintain watchdog timer and charger thus must always be able to
|
|
|
|
|
run even if playback is starved. */
|
|
|
|
|
thread_set_priority(thread_self(), PRIORITY_REALTIME);
|
|
|
|
|
|
2008-12-21 18:10:36 +00:00
|
|
|
|
/* Use watchdog to shut system down if we lose control of the charging
|
|
|
|
|
* hardware. */
|
|
|
|
|
watchdog_init(WATCHDOG_TIMEOUT);
|
|
|
|
|
|
|
|
|
|
mc13783_write(MC13783_CHARGER, regval_w);
|
|
|
|
|
|
|
|
|
|
if (mc13783_read(MC13783_CHARGER) == regval_w)
|
|
|
|
|
{
|
|
|
|
|
/* Divide CHRGRAW input by 10 */
|
|
|
|
|
mc13783_clear(MC13783_ADC0, MC13783_CHRGRAWDIV);
|
|
|
|
|
/* Turn off BATTDETB. It's worthless on MESx0V since the battery
|
|
|
|
|
* isn't removable (nor the thermistor). */
|
|
|
|
|
mc13783_clear(MC13783_POWER_CONTROL0, MC13783_BATTDETEN);
|
|
|
|
|
}
|
|
|
|
|
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 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void charger_plugged(void)
|
|
|
|
|
{
|
|
|
|
|
adc_enable_channel(ADC_BATTERY_TEMP, true);
|
|
|
|
|
autorecharge_counter = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Might need to reevaluate these bits in charger_none. */
|
2008-12-24 16:58:41 +00:00
|
|
|
|
last_inputs &= ~(POWER_INPUT | POWER_INPUT_CHARGER);
|
2008-12-21 18:10:36 +00:00
|
|
|
|
temp_state = TEMP_STATE_NORMAL;
|
|
|
|
|
autorecharge_counter = 0;
|
|
|
|
|
chgcurr_timer = 0;
|
|
|
|
|
|
|
|
|
|
adc_enable_channel(ADC_BATTERY_TEMP, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void charger_none(void)
|
|
|
|
|
{
|
2008-12-24 16:58:41 +00:00
|
|
|
|
unsigned int pwr = power_thread_inputs;
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
2008-12-24 16:58:41 +00:00
|
|
|
|
if (last_inputs != pwr)
|
2008-12-21 18:10:36 +00:00
|
|
|
|
{
|
2008-12-24 16:58:41 +00:00
|
|
|
|
last_inputs = pwr;
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
|
|
|
|
if (charge_state == CHARGE_STATE_DISABLED)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ((pwr & (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();
|
2008-12-24 16:58:41 +00:00
|
|
|
|
last_inputs = pwr; /* Restore status */
|
2008-12-21 18:10:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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())
|
|
|
|
|
{
|
|
|
|
|
/* Failed to read current */
|
|
|
|
|
charge_state = CHARGE_STATE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
charging_ok();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void charger_control(void)
|
|
|
|
|
{
|
2008-12-24 16:58:41 +00:00
|
|
|
|
unsigned int pwr = power_thread_inputs;
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
2008-12-24 16:58:41 +00:00
|
|
|
|
if (last_inputs != pwr)
|
2008-12-21 18:10:36 +00:00
|
|
|
|
{
|
2008-12-24 16:58:41 +00:00
|
|
|
|
unsigned int changed = last_inputs ^ pwr;
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
2008-12-24 16:58:41 +00:00
|
|
|
|
last_inputs = pwr;
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
|
|
|
|
if (charger_setting != 0)
|
|
|
|
|
charger_setting = CHARGER_ADJUST;
|
|
|
|
|
|
|
|
|
|
if (charge_state == DISCHARGING)
|
|
|
|
|
{
|
|
|
|
|
if (main_charger_connected())
|
|
|
|
|
{
|
|
|
|
|
/* If main is connected, ignore USB plugs. */
|
|
|
|
|
if (changed & POWER_INPUT_MAIN_CHARGER)
|
|
|
|
|
{
|
|
|
|
|
/* Main charger plugged - try charge */
|
|
|
|
|
autorecharge_counter = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (pwr & POWER_INPUT_USB_CHARGER
|
|
|
|
|
& POWER_INPUT_CHARGER)
|
|
|
|
|
{
|
2009-01-10 02:37:44 +00:00
|
|
|
|
/* USB power only */
|
2008-12-21 18:10:36 +00:00
|
|
|
|
if (changed & POWER_INPUT_USB_CHARGER)
|
|
|
|
|
{
|
|
|
|
|
/* USB charger plugged - try charge */
|
|
|
|
|
autorecharge_counter = -1;
|
|
|
|
|
}
|
2009-01-10 02:37:44 +00:00
|
|
|
|
else if (changed & POWER_INPUT_MAIN_CHARGER)
|
|
|
|
|
{
|
|
|
|
|
/* Main charger pulled - go to battery maintenence. */
|
|
|
|
|
charger_setting = CHARGER_ADJUST;
|
|
|
|
|
}
|
2008-12-21 18:10:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (charger_setting != 0 && !charger_current_filter_step())
|
|
|
|
|
{
|
|
|
|
|
/* Failed to read current */
|
|
|
|
|
charge_state = CHARGE_STATE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0);
|
|
|
|
|
|
|
|
|
|
if (!charging_ok())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (charge_state)
|
|
|
|
|
{
|
|
|
|
|
case DISCHARGING:
|
|
|
|
|
{
|
|
|
|
|
/* Battery voltage may have dropped and a charge cycle should
|
|
|
|
|
* start again. Debounced. */
|
2008-12-24 16:58:41 +00:00
|
|
|
|
if (autorecharge_counter < 0 &&
|
2012-01-03 23:44:38 +00:00
|
|
|
|
_battery_voltage() < BATT_FULL_VOLTAGE)
|
2008-12-21 18:10:36 +00:00
|
|
|
|
{
|
2008-12-24 16:58:41 +00:00
|
|
|
|
/* Try starting a cycle now if battery isn't already topped
|
|
|
|
|
* off to allow user to ensure the battery is full. */
|
2008-12-21 18:10:36 +00:00
|
|
|
|
}
|
|
|
|
|
else if (battery_voltage() > auto_recharge_voltage())
|
|
|
|
|
{
|
|
|
|
|
/* Still above threshold - reset counter */
|
|
|
|
|
autorecharge_counter = AUTORECHARGE_COUNTDOWN;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (autorecharge_counter > 0)
|
|
|
|
|
{
|
|
|
|
|
/* Coundown to restart */
|
|
|
|
|
autorecharge_counter--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-24 16:58:41 +00:00
|
|
|
|
autorecharge_counter = 0;
|
|
|
|
|
|
2008-12-21 18:10:36 +00:00
|
|
|
|
if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE)
|
|
|
|
|
{
|
|
|
|
|
/* Battery is deeply discharged - precharge at lower current. */
|
|
|
|
|
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: /* Very low - precharge */
|
|
|
|
|
{
|
|
|
|
|
if (battery_voltage() <= BATT_VTRICKLE_CHARGE)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* 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 necessity. */
|
|
|
|
|
charge_state = (int_sense0 & MC13783_CCCVS) ? TOPOFF : CHARGING;
|
|
|
|
|
|
|
|
|
|
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 on. */
|
|
|
|
|
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 */
|
2008-12-24 16:58:41 +00:00
|
|
|
|
void charging_algorithm_step(void)
|
2008-12-21 18:10:36 +00:00
|
|
|
|
{
|
2008-12-24 16:58:41 +00:00
|
|
|
|
#ifdef IMX31_ALLOW_CHARGING
|
2008-12-21 18:10:36 +00:00
|
|
|
|
if (service_wdt)
|
|
|
|
|
watchdog_service();
|
2008-12-24 16:58:41 +00:00
|
|
|
|
#endif
|
2008-12-21 18:10:36 +00:00
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
|
|
|
|
if (charge_state != CHARGE_STATE_DISABLED)
|
|
|
|
|
{
|
|
|
|
|
/* 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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|