/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2011 by Amaury Pouly * * 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. * ****************************************************************************/ #include "system.h" #include "powermgmt.h" #include "power-imx233.h" #include "usb.h" #include "string.h" //#define LOGF_ENABLE #include "logf.h" #include "powermgmt-imx233.h" #include "regs/power.h" #if !defined(IMX233_CHARGE_CURRENT) || !defined(IMX233_STOP_CURRENT) \ || !defined(IMX233_CHARGING_TIMEOUT) || !defined(IMX233_TOPOFF_TIMEOUT) #error You must define IMX233_CHARGE_CURRENT, IMX233_STOP_CURRENT, \ IMX233_CHARGING_TIMEOUT and IMX233_TOPOFF_TIMEOUT ! #endif /* charger state is maintained in charge_state (see powermgmt.h) */ static int timeout_charging; /* timeout before charging will be declared broken */ static int timeout_topping_off; /* timeout before stopping charging after topping off */ /* Returns battery voltage from ADC [millivolts] */ int _battery_voltage(void) { /* battery value is in 8mV LSB */ return BF_RD(POWER_BATTMONITOR, BATT_VAL) * 8; } void imx233_powermgmt_init(void) { imx233_power_set_charge_current(IMX233_CHARGE_CURRENT); imx233_power_set_stop_current(IMX233_STOP_CURRENT); #if IMX233_SUBTARGET >= 3700 /* assume that adc_init was called and battery monitoring via LRADC setup */ BF_WR(POWER_BATTMONITOR, EN_BATADJ(1)); /* setup linear regulator offsets to 25 mV below to prevent contention between * linear regulators and DCDC */ BF_WR(POWER_VDDDCTRL, LINREG_OFFSET(2)); BF_WR(POWER_VDDACTRL, LINREG_OFFSET(2)); BF_WR(POWER_VDDIOCTRL, LINREG_OFFSET(2)); /* enable a few bits controlling the DC-DC as recommended by Freescale */ BF_SET(POWER_LOOPCTRL, TOGGLE_DIF); BF_SET(POWER_LOOPCTRL, EN_CM_HYST); BF_CS(POWER_LOOPCTRL, EN_RCSCALE(1)); #else BF_SET(POWER_5VCTRL, LINREG_OFFSET); #endif } #define MAX_4P2_ILIMIT 0x3f #if IMX233_SUBTARGET >= 3780 /* The code below assumes HZ = 100 so that it runs every 10ms */ #if HZ != 100 #warning The ramp_up_4p2_rail() tick task assumes HZ = 100, this may break charging #endif static void ramp_up_4p2_rail(void) { /* only ramp up in the TRICKLE state and if we haven't reached the maximum yet */ if(charge_state == TRICKLE && BF_RD(POWER_5VCTRL, CHARGE_4P2_ILIMIT) < MAX_4P2_ILIMIT) HW_POWER_5VCTRL += BF_POWER_5VCTRL_CHARGE_4P2_ILIMIT(1); } #endif /* IMX233_SUBTARGET >= 3780 */ void powermgmt_init_target(void) { charge_state = DISCHARGING; /* stmp < 3780 does not have a 4.2 rail */ #if IMX233_SUBTARGET >= 3780 tick_add_task(&ramp_up_4p2_rail); #endif } void charging_algorithm_step(void) { #if IMX233_SUBTARGET >= 3700 bool is_5v_present = usb_detect() == USB_INSERTED; /* initial state & 5v -> battery transition */ if(!is_5v_present && charge_state != DISCHARGING) { logf("pwrmgmt: * -> discharging"); logf("pwrmgmt: disable charger and 4p2"); charge_state = DISCHARGING; /* 5V has been lost: disable 4p2 power rail */ BF_SET(POWER_CHARGE, PWD_BATTCHRG); #if IMX233_SUBTARGET >= 3780 BF_WR(POWER_DCDC4P2, ENABLE_DCDC(0)); BF_WR(POWER_DCDC4P2, ENABLE_4P2(0)); BF_WR(POWER_5VCTRL, CHARGE_4P2_ILIMIT(0)); BF_SET(POWER_5VCTRL, PWD_CHARGE_4P2); #endif } /* battery -> 5v transition */ else if(is_5v_present && charge_state == DISCHARGING) { logf("pwrmgmt: discharging -> trickle"); logf("pwrmgmt: begin charging 4p2"); #if IMX233_SUBTARGET >= 3780 /* 5V has been detected: prepare 4.2V power rail for activation * WARNING we can reach this situation when starting after Freescale bootloader * or after RoLo in a state where the DCDC is running. In this case, * we must *NOT* disable it or this will shutdown the device. This procedure * is safe: it will never disable the DCDC and will not reduce the charge * limit on the 4P2 rail. */ BF_WR(POWER_DCDC4P2, ENABLE_4P2(1)); BF_SET(POWER_CHARGE, ENABLE_LOAD); BF_WR(POWER_5VCTRL, CHARGE_4P2_ILIMIT(0)); /* start by drawing 0mA */ BF_CLR(POWER_5VCTRL, PWD_CHARGE_4P2);// FIXME: manual error ? BF_WR(POWER_DCDC4P2, ENABLE_DCDC(1)); /* the tick task will take care of slowly ramping up the current in the rail * every 10ms (since it runs at HZ and HZ=100) */ #endif charge_state = TRICKLE; } /* trickle -> charging transition */ else if(charge_state == TRICKLE) { #if IMX233_SUBTARGET >= 3780 /* If 4.2V current limit has not reached 780mA, don't do anything, the * DPC is still running */ /* If we've reached the maximum, take action */ if(BF_RD(POWER_5VCTRL, CHARGE_4P2_ILIMIT) == MAX_4P2_ILIMIT) #endif { logf("pwrmgmt: enable dcdc and charger"); logf("pwrmgmt: trickle -> charging"); #if IMX233_SUBTARGET >= 3780 /* adjust arbitration between 4.2 and battery */ BF_WR(POWER_DCDC4P2, CMPTRIP(0)); /* 85% */ BF_WR(POWER_DCDC4P2, DROPOUT_CTRL(0xe)); /* select greater, 200 mV drop */ #endif /* switch to DCDC */ BF_CLR(POWER_5VCTRL, DCDC_XFER); BF_SET(POWER_5VCTRL, ENABLE_DCDC); /* enable battery charging */ BF_CLR(POWER_CHARGE, PWD_BATTCHRG); charge_state = CHARGING; timeout_charging = current_tick + IMX233_CHARGING_TIMEOUT; } } /* charging -> error transition */ else if(charge_state == CHARGING && TIME_AFTER(current_tick, timeout_charging)) { /* we have charged for a too long time, declare charger broken */ logf("pwrmgmt: charging timeout exceeded!"); logf("pwrmgmt: charging -> error"); /* stop charging, note that we leave the 4.2 rail active so that the DCDC * keep drawing current from the 4.2 only and leave the battery untouched */ BF_SET(POWER_CHARGE, PWD_BATTCHRG); /* goto error state */ charge_state = CHARGE_STATE_ERROR; } /* charging -> topoff transition */ else if(charge_state == CHARGING && !BF_RD(POWER_STS, CHRGSTS)) { logf("pwrmgmt: topping off"); logf("pwrmgmt: charging -> topoff"); charge_state = TOPOFF; timeout_topping_off = current_tick + IMX233_TOPOFF_TIMEOUT; } /* topoff -> disabled transition */ else if(charge_state == TOPOFF && TIME_AFTER(current_tick, timeout_topping_off)) { logf("pwrmgmt: charging finished"); logf("pwrmgmt: topoff -> disabled"); /* stop charging, note that we leave the 4.2 rail active so that the DCDC * keep drawing current from the 4.2 only and leave the battery untouched */ BF_SET(POWER_CHARGE, PWD_BATTCHRG); charge_state = CHARGE_STATE_DISABLED; } #endif } void charging_algorithm_close(void) { #if IMX233_SUBTARGET >= 3780 tick_remove_task(&ramp_up_4p2_rail); #endif } struct imx233_powermgmt_info_t imx233_powermgmt_get_info(void) { struct imx233_powermgmt_info_t info; memset(&info, 0, sizeof(info)); info.state = charge_state; info.charging_timeout = charge_state == CHARGING ? timeout_charging - current_tick : 0; info.topoff_timeout = charge_state == TOPOFF ? timeout_topping_off - current_tick : 0; return info; }