diff options
author | Christian Vogel <vogelchr@vogel.cx> | 2012-02-19 21:21:49 +0100 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2012-04-28 09:38:44 +0200 |
commit | d53b55016a651d1e587e1c76012dadda162a8095 (patch) | |
tree | ad8db5edd460b8d2de6e1343a7fc81e3b5d7c46f /src | |
parent | e3f9698366fdec7ca5868f8790fa1e277ce236ad (diff) |
Battery charging for C123.
Diffstat (limited to 'src')
-rw-r--r-- | src/target/firmware/battery/compal_e88.c | 296 | ||||
-rw-r--r-- | src/target/firmware/battery/dummy.c | 9 | ||||
-rwxr-xr-x | src/target/firmware/include/battery/battery.h | 37 | ||||
-rw-r--r-- | src/target/firmware/include/battery/compal_e88.h | 15 |
4 files changed, 357 insertions, 0 deletions
diff --git a/src/target/firmware/battery/compal_e88.c b/src/target/firmware/battery/compal_e88.c new file mode 100644 index 00000000..12cdff9d --- /dev/null +++ b/src/target/firmware/battery/compal_e88.c @@ -0,0 +1,296 @@ +/* Battery management for compal_e88 */ + +/* (C) 2010 by Christian Vogel <vogelchr@vogel.cx> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* + * ___C123 (compal e88) very simplified diagram of charging circuitry___ + * + * ICTL + * | + * v + * Charger -> OVP -------> P-Mosfet --->[0.15 Ohm]---> Battery + * (NCP345, | | | + * 6.85V) v v v + * VCHG VCCS VBAT & + * VBATS + * + * Inputs to IOTA: + * VCHG: senses voltage on the charger input + * VCSS/VBATS: to difference amplifier, to measure charge current + * VBAT: senses voltage on the battery terminal + * Outputs from IOTA: + * ICTL: Control signal for the switching mosfet + * + */ + +#include <battery/battery.h> +#include <battery/compal_e88.h> + +#include <stdint.h> +#include <abb/twl3025.h> +#include <comm/timer.h> +#include <stdio.h> + +/* ADC calibration, scale is LSB/uV or LSB/uA, physical unit is mV or mA */ +#define ADC_TO_PHYSICAL(adc,scale) (((adc)*(scale)+500)/1000) +#define PHYSICAL_TO_ADC(phy,scale) (((phy)*1000+(scale)/2)/(scale)) + +/* conversion factors for internal IOTA battery charging/sensing circuitry */ +#define VREF_LSB_uV 1709 /* VREF = 1.75V --> 1.709 mV/LSB */ +#define VBAT_LSB_uV 6836 /* VBAT = 7.0 V FS --> 6.836 mV/LSB */ +#define VCHG_LSB_uV 8545 /* VCHG = 8.75V FS --> 8.545 mV/LSB */ +#define ICHG_LSB_uA 854 /* ICHG = 875mA FS --> 0.854 mA/LSB */ + +/* charger is considered plugged in/removed when over/under... */ +#define VCHG_thr_on PHYSICAL_TO_ADC(4500,VCHG_LSB_uV) +#define VCHG_thr_off PHYSICAL_TO_ADC(3200,VCHG_LSB_uV) + +/* battery is considered full/empty at these thresholds... */ +#define VBAT_full PHYSICAL_TO_ADC(4100,VBAT_LSB_uV) +#define VBAT_empty PHYSICAL_TO_ADC(3200,VBAT_LSB_uV) + +/* we declare overvoltage at this point... */ +#define VBAT_fail PHYSICAL_TO_ADC(4250,VBAT_LSB_uV) + +/* charging current in ADC LSBs */ +#define ICHG_set PHYSICAL_TO_ADC(200,ICHG_LSB_uA) +#define VCHG_set VBAT_full + +/* global battery info */ +struct osmocom_battery_info osmocom_battery_info; + +/* internal bookkeeping */ +static uint16_t compal_e88_madc[8]; /* remembering last ADC values */ + +enum battery_compal_e88_status { + ADC_CONVERSION = 1 << 0 +}; +static uint32_t battery_compal_e88_status; + +static const int BATTERY_TIMER_DELAY=5000; /* 5000ms for control loop */ +static const int ADC_TIMER_DELAY=100; /* 100ms for ADC conversion */ + +/* thermistor sense current, turn it up to eleven! */ +#define TH_SENS (THSENS0|THSENS1|THSENS2|THEN) +#define BATTERY_ALL_SENSE (TH_SENS|MESBAT|TYPEN) + +/* + * charger modes state machine + * + * +------------------+-------------------+ + * | | | lost AC power + * | ^ ^ + * V on AC power | @VBAT_full | + * +-----+ +------------+ -----> +------------+ + * | OFF | -----> | CONST_CURR | | CONST_VOLT | + * +-----+ +------------+ +------------+ + * ^ ^ | | + * | /failure v v failure + * +---------+ / gone | | condition + * | FAILURE | <----------+-------------------+ + * +---------+ + * + * Failure modes currently detected: + * + high battery voltage + * + high battery temperature + */ +enum charger_state { + CHARG_OFF, + CHARG_CONST_CURR, + CHARG_CONST_VOLT, + CHARG_FAIL +}; +static enum charger_state charger_state; + +static void +charger_goto_state(enum charger_state newstate){ + charger_state=newstate; +} + +static void +battery_charger_control(){ + /* with AC power disconnected, always go to off state */ + if(!osmocom_battery_info.flags & BATTERY_CHG_CONNECTED){ + charger_goto_state(CHARG_OFF); + return; + } +#if 0 + /* if failure condition is detected, always goto failure state */ + if(){ + charger_goto_state(CHARG_FAIL); + } + + switch(charger_state){ + case CHARG_OFF: + case CHARG_CONST_CURR: + case CHARG_CONST_VOLT: + case CHARG_FAIL: + default: +#endif +} + +/* + * Charging voltage connection - state machine: + * + * VCHG > VCHG_thr_on + * +-----------------+ ------------------> +---------------+ + * | ! CHG_CONNECTED | | CHG_CONNECTED | + * +-----------------+ <------------------ +---------------+ + * VCHG < VCHG_thr_off + * + */ +static void +check_charg_volt_presence(){ + /* check for presence of charging voltage */ + if(!(osmocom_battery_info.flags & BATTERY_CHG_CONNECTED)){ + if(compal_e88_madc[MADC_VCHG] > VCHG_thr_on){ + printf("CHARGER: external voltage connected!\n"); + osmocom_battery_info.flags |= BATTERY_CHG_CONNECTED; + + /* always keep ADC, voltage dividers and bias voltages on */ + twl3025_unit_enable(TWL3025_UNIT_MAD,1); + twl3025_reg_write(BCICTL1,BATTERY_ALL_SENSE); + } + } else { + if(compal_e88_madc[MADC_VCHG] < VCHG_thr_off){ + /* we'll only run ADC on demand */ + twl3025_unit_enable(TWL3025_UNIT_MAD,0); + twl3025_reg_write(BCICTL1,0); + + osmocom_battery_info.flags &= ~ BATTERY_CHG_CONNECTED; + printf("CHARGER: external voltage disconnected!\n"); + } + } +} + +/* ---- update voltages visible to the user ---- */ +static void +battery_update_measurements(){ + int adc,i; + + osmocom_battery_info.charger_volt_mV= + ADC_TO_PHYSICAL(compal_e88_madc[MADC_VCHG],VCHG_LSB_uV); + osmocom_battery_info.bat_volt_mV= + ADC_TO_PHYSICAL(compal_e88_madc[MADC_VBAT],VBAT_LSB_uV); + osmocom_battery_info.bat_chg_curr_mA= + ADC_TO_PHYSICAL(compal_e88_madc[MADC_ICHG],ICHG_LSB_uA); + + adc = compal_e88_madc[MADC_VBAT]; + if(adc <= VBAT_empty){ + osmocom_battery_info.battery_percent = 0; + } else if (adc >= VBAT_full){ + osmocom_battery_info.battery_percent = 100; + } else { + osmocom_battery_info.battery_percent = + (50+100*(adc-VBAT_empty))/(VBAT_full-VBAT_empty); + } + + /* DEBUG */ + printf("BAT-ADC: "); + for(i=0;i<MADC_NUM_CHANNELS;i++) + printf("%3d ",compal_e88_madc[i]); + printf("%c\n\n",32); + printf("\tCharger at %u mV.\n",osmocom_battery_info.charger_volt_mV); + printf("\tBattery at %u mV.\n",osmocom_battery_info.bat_volt_mV); + printf("\tCharging at %u mA.\n",osmocom_battery_info.bat_chg_curr_mA); + printf("\tBattery capacity is %u%%.\n",osmocom_battery_info.battery_percent); + printf("\tBattery range is %d..%d mV.\n", + ADC_TO_PHYSICAL(VBAT_empty,VBAT_LSB_uV), + ADC_TO_PHYSICAL(VBAT_full,VBAT_LSB_uV)); + printf("\tBattery full at %d LSB .. full at %d LSB\n",VBAT_empty,VBAT_full); + printf("\tCharging at %d LSB (%d mA).\n",ICHG_set, + ADC_TO_PHYSICAL(ICHG_set,ICHG_LSB_uA)); + i = twl3025_reg_read(BCICTL2); + printf("\tBattery charger thresholds in ADC LSBs: on %d and off %d\n", + VCHG_thr_on,VCHG_thr_off); + printf("\tBCICTL2=0x%03x\n",i); + printf("\tosmocom-battery-info.flags=0x%08x\n",osmocom_battery_info.flags); +} + +/* battery_adc_read() starts a conversion on all ADC channels + if battery_compal_e88_status & ADC_CONVERSION is not set and + tries to read back values (and reset ADC_CONVERSION) if it currently + is set. If it returns zero, conversion data is available, if it + returns non-zero a conversion has been triggered and data should + be available "soon". */ + +static int +battery_adc_read(){ + int i; + + if(battery_compal_e88_status & ADC_CONVERSION){ + i = twl3025_reg_read(MADCSTAT); + if(i & ADCBUSY) + return 1; + for(i=0;i<MADC_NUM_CHANNELS;i++) + compal_e88_madc[i]=twl3025_reg_read(VBATREG+i); + /* if charger is connected, we keep the ADC and BIAS on + continuously, if running on battery, we try to save power */ + if(!osmocom_battery_info.flags & BATTERY_CHG_CONNECTED){ + twl3025_reg_write(BCICTL1,0x00); /* turn off bias */ + twl3025_unit_enable(TWL3025_UNIT_MAD,0); /* & ADC */ + } + battery_compal_e88_status &= ~ ADC_CONVERSION; + return 0; + } else { + /* if running on battery, turn on ADC & BIAS on demand */ + if(!osmocom_battery_info.flags & BATTERY_CHG_CONNECTED){ + twl3025_unit_enable(TWL3025_UNIT_MAD,1); + twl3025_reg_write(BCICTL1,BATTERY_ALL_SENSE); + } + twl3025_reg_write(MADCTRL,0xff); /* convert all channels */ + twl3025_reg_write(VBATREG,0); /* trigger conversion */ + + battery_compal_e88_status |= ADC_CONVERSION; + return 1; + } +} + +static void +battery_compal_e88_timer_cb(void *p){ + struct osmo_timer_list *tmr = (struct osmo_timer_list*)p; + int i; + + if(battery_adc_read()){ /* read back ADCs after a brief delay */ + osmo_timer_schedule(tmr,ADC_TIMER_DELAY); + return; + } + + battery_update_measurements(); + + check_charg_volt_presence(); + battery_charger_control(); + + osmo_timer_schedule(tmr,BATTERY_TIMER_DELAY); +} + +/* timer that fires the charging loop regularly */ +static struct osmo_timer_list battery_compal88_timer = { + .cb = &battery_compal_e88_timer_cb, + .data = &battery_compal88_timer +}; + +void +battery_compal_e88_init(){ + printf("%s: starting up\n",__FUNCTION__); + osmo_timer_schedule(&battery_compal88_timer,BATTERY_TIMER_DELAY); +} + diff --git a/src/target/firmware/battery/dummy.c b/src/target/firmware/battery/dummy.c new file mode 100644 index 00000000..356cc910 --- /dev/null +++ b/src/target/firmware/battery/dummy.c @@ -0,0 +1,9 @@ +#include <battery/battery.h> + +/* Battery Management: Dummy file when no charging logic exists. */ +struct osmocom_battery_info osmocom_battery_info; + +void battery_dummy_init(){ + osmocom_battery_info.flags = BATTERY_FAILURE; /* not implemented */ +} + diff --git a/src/target/firmware/include/battery/battery.h b/src/target/firmware/include/battery/battery.h new file mode 100755 index 00000000..d80f0742 --- /dev/null +++ b/src/target/firmware/include/battery/battery.h @@ -0,0 +1,37 @@ +#ifndef _BATTERY_BATTERY_H +#define _BATTERY_BATTERY_H + +/* User-visible state of the battery charger. + * + * If CHG_CONNECTED, power is externally supplied to the mobile. + * + * If CHG_ENABLED, the charger will try to provide charge + * to the battery if needed, but this state might be switchable? + * + * BATTERY_CHARGING: Battery is not full, so a significant charging + * current (not trickle charge) is supplied. + * + * BATTERY_FAILURE: Overtemperature, overvoltage, ... if this bit + * is set, charging should be inhibited. + */ + + +enum osmocom_battery_flags { + BATTERY_CHG_CONNECTED = 1 << 0, /* AC adapter is connected */ + BATTERY_CHG_ENABLED = 1 << 1, /* if needed charger could charge */ + BATTERY_CHARGING = 1 << 2, /* charger is actively charging */ + BATTERY_FAILURE = 1 << 3, /* problem exists preventing charge */ +}; + +struct osmocom_battery_info { + enum osmocom_battery_flags flags; + int charger_volt_mV; /* charger connection voltage */ + int bat_volt_mV; /* battery terminal voltage */ + int bat_chg_curr_mA; /* battery charging current */ + int battery_percent; /* 0(empty) .. 100(full) */ +}; + +extern struct osmocom_battery_info +osmocom_battery_info; + +#endif diff --git a/src/target/firmware/include/battery/compal_e88.h b/src/target/firmware/include/battery/compal_e88.h new file mode 100644 index 00000000..c6c96f39 --- /dev/null +++ b/src/target/firmware/include/battery/compal_e88.h @@ -0,0 +1,15 @@ +#ifndef _BATTERY_COMPAL_E88_H +#define _BATTERY_COMPAL_E88_H + +#include <stdint.h> +#include <abb/twl3025.h> + +/* initialize the charger control loop on C123 */ + +extern void +battery_compal_e88_init(); + +extern uint16_t +compal_e88_madc[MADC_NUM_CHANNELS]; + +#endif |