summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Vogel <vogelchr@vogel.cx>2012-02-19 21:21:49 +0100
committerHarald Welte <laforge@gnumonks.org>2012-04-28 09:38:44 +0200
commitd53b55016a651d1e587e1c76012dadda162a8095 (patch)
treead8db5edd460b8d2de6e1343a7fc81e3b5d7c46f
parente3f9698366fdec7ca5868f8790fa1e277ce236ad (diff)
Battery charging for C123.
-rw-r--r--src/target/firmware/battery/compal_e88.c296
-rw-r--r--src/target/firmware/battery/dummy.c9
-rwxr-xr-xsrc/target/firmware/include/battery/battery.h37
-rw-r--r--src/target/firmware/include/battery/compal_e88.h15
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