接上一篇: [单片机框架][device层] charger 电源管理
bq25601 器件是高度集成的 3A 开关模式电池充电管理和系统电源路径管理器件,适用于单节锂离子和锂聚合物电池。低阻抗电源路径对开关模式运行效率进行了优化、缩短了电池充电时间并延长了放电阶段的电池使用寿命。具有充电和系统设置的 I2C 串行接口使得此器件成为一个真正的灵活解决方案。
bq25601 是高度集成的3.0A 开关模式电池充电管理和系统电源路径管理器件,适用于单节锂离子和锂聚合物电池。该器件 可针对 各种智能手机、平板电脑和便携式设备实现快速充电,并提供高输入电压支持。其低阻抗电源路径对开关模式运行效率进行了优化、缩短了电池充电时间并延长了放电阶段的电池使用寿命。其输入电压和电流调节可以为电池提供最大的充电功率。该解决方案在系统和电池之间高度集成输入反向阻断场 FET(RBFET,Q1)、高侧开关 FET(HSFET,Q2)、低侧开关 FET(LSFET,Q3)以及电池 FET(BATFET、Q4)。它还集成了自举二极管以进行高侧栅极驱动,从而简化系统设计。具有充电和系统设置的 I2C 串行接口使得此器件成为一个真正的灵活解决方案。
该器件支持多种输入源,包括标准 USB 主机端口、USB 充电端口以及兼容 USB 的高电压适配器。该器件根据内置 USB 接口设置默认输入电流限值。 为了设置默认输入电流限值,该器件从系统检测电路(如 USB PHY 器件)中获取结果。该器件符合 USB 2.0 和 USB 3.0 电源规范,具有输入电流和电压调节功能。 该器件还具有高达 1.2A 的很定电流限制能力,能够为 VBUS 提供 5.15V 的电压,符合 USB On-the-Go (OTG) 运行功率额定值规范。
电源路径管理将系统电压调节至稍高于电池电压的水平,但是不会下降至 3.5V 最小系统电压(可编程)以下。借助于这个特性,即使在电池电量完全耗尽或者电池被拆除时,系统也能保持运行。当达到输入电流限值或电压限值时,电源路径管理自动将充电电流减少为 0。随着系统负载持续增加,电源路径在满足系统电源需求之前将电池放电。该补充模式可防止输入源过载。
此器件在无需软件控制情况下启动并完成一个充电周期。它感应电池电压并通过三个阶段为电池充电:预充电、恒定电流和恒定电压。在充电周期的末尾,当充电电流低于预设限值并且电池电压高于再充电阈值时,充电器自动终止。如果已完全充电的电池降至再充电阈值以下,则充电器自动启动另一个充电周期。
此充电器提供针对电池充电和系统运行的多种安全 功能 ,其中包括电池负温度系数热敏电阻监视、充电安全计时器以及过压和过流保护。当结温超过 110°C(可编程)时,热调节会减小充电电流。STAT 输出报告充电状态和任何故障状况。其他安全 功能 包括针对充电和升压模式的电池温度感应、热调节和热关断以及输入 UVLO 和过压保护。VBUS_GD 位指示电源是否正常。当发生故障时,INT 输出会立即通知主机。
该器件还提供用于 BATFET 使能和复位控制的 QON 引脚,以退出低功耗出厂模式或完全系统复位功能。
特性
高效1.5MHz同步开关模式降压充电器
在2A电流(5V输入)下具有92%的充电效率
针对USB电压输入(5V)进行了优化
用于轻负载运行的可选低功耗脉冲频率调制(PFM)模式
支持USB On-The-Go(OTG)
具有高达1.2A输出的升压转换器
在1A输出下具有92%的升压效率
精确的恒定电流(CC)限制
高达500μF电容负载的软启动
输出短路保护
用于轻负载运行的可选低功耗PFM模式
单个输入,支持USB输入和高电压适配器
支持3.9V至13.5V输入电压范围,绝对最大输入电压额定值为22V
可编程输入电流限制(100mA至3.2A,分辨率为100mA),支持USB2.0,USB3.0标准和高电压适配器(IINDPM)
通过高达5.4V的输入电压限制(VINDPM)进行最大功率跟踪
VINDPM阈值自动跟踪电池电压
自动检测USB SDP,DCP以及非标准适配器
高电池放电效率,电池放电MOSFET为19.5 mΩ
窄VDC(NVDC)电源路径管理无需电池或深度放电的电池即可瞬时启动电池充电模式下的理想二极管运行BATFET控制,支持运输模式,唤醒和完全系统复位灵活的自主和I2C模式,可实现<优>
高精度±5%1.5A充电电流调节
bq25601.c
#include <string.h>
#include "log.h"
#include "errorno.h"
#include "modules.h"
#include "bsp_i2c.h"
#include "bsp_gpio.h"
#include "charger.h"
#define BQ25601_LOG_EN 0
#if BQ25601_LOG_EN
#define BQ25601_LOG_D(str, ...) LOG_D(str, ##__VA_ARGS__)
#define BQ25601_LOG_I(str, ...) LOG_I(str, ##__VA_ARGS__)
#define BQ25601_LOG_N(str, ...) LOG_N(str, ##__VA_ARGS__)
#define BQ25601_LOG_W(str, ...) LOG_W(str, ##__VA_ARGS__)
#define BQ25601_LOG_E(str, ...) LOG_E(str, ##__VA_ARGS__)
#define BQ25601_LOG_C(str, ...) LOG_C(str, ##__VA_ARGS__)
#else
#define BQ25601_LOG_D(str, ...)
#define BQ25601_LOG_I(str, ...)
#define BQ25601_LOG_N(str, ...)
#define BQ25601_LOG_W(str, ...)
#define BQ25601_LOG_E(str, ...)
#define BQ25601_LOG_C(str, ...)
#endif
#define DEV_NAME "bq25601"
#define BQ25601_SLAVE_ADDR 0x6B
#define BQ25601_DEVID 0x02
/* Register 00h */
#define BQ25601_REG_00 0x00
#define REG00_ENHIZ_MASK 0x80
#define REG00_ENHIZ_SHIFT 7
#define REG00_STAT_CTRL_MASK 0x60
#define REG00_STAT_CTRL_SHIFT 5
#define REG00_IINLIM_MASK 0x1F
#define REG00_IINLIM_SHIFT 0
#define REG00_IINLIM_LSB 100
#define REG00_IINLIM_BASE 100
/* Register 01h */
#define BQ25601_REG_01 0x01
#define REG01_PFM_DIS_MASK 0x80
#define REG01_PFM_DIS_SHIFT 7
#define REG01_WDT_RESET_MASK 0x40
#define REG01_WDT_RESET_SHIFT 6
#define REG01_WDT_RESET 1
#define REG01_OTG_CONFIG_MASK 0x20
#define REG01_OTG_CONFIG_SHIFT 5
#define REG01_CHG_CONFIG_MASK 0x10
#define REG01_CHG_CONFIG_SHIFT 4
/* Register 0x02*/
#define BQ25601_REG_02 0x02
#define REG02_BOOST_LIM_MASK 0x80
#define REG02_BOOST_LIM_SHIFT 7
#define REG02_BOOST_LIM_0P5A 0
#define REG02_BOOST_LIM_1P2A 1
#define REG02_Q1_FULLON_MASK 0x40
#define REG02_Q1_FULLON_SHIFT 6
#define REG02_ICHG_MASK 0x3F
#define REG02_ICHG_SHIFT 0
#define REG02_ICHG_BASE 0
#define REG02_ICHG_LSB 60
/* Register 0x03*/
#define BQ25601_REG_03 0x03
#define REG03_IPRECHG_MASK 0xF0
#define REG03_IPRECHG_SHIFT 4
#define REG03_IPRECHG_BASE 60
#define REG03_IPRECHG_LSB 60
#define REG03_ITERM_MASK 0x0F
#define REG03_ITERM_SHIFT 0
#define REG03_ITERM_BASE 60
#define REG03_ITERM_LSB 60
/* Register 0x04*/
#define BQ25601_REG_04 0x04
#define REG04_VREG_MASK 0xF8
#define REG04_VREG_SHIFT 3
#define REG04_VREG_BASE 3856
#define REG04_VREG_LSB 32
/* Register 0x05*/
#define BQ25601_REG_05 0x05
#define REG05_EN_TERM_MASK 0x80
#define REG05_EN_TERM_SHIFT 7
#define REG05_WDT_MASK 0x30
#define REG05_WDT_SHIFT 4
#define REG05_WDT_DISABLE 0
#define REG05_WDT_ENABLE 1
#define REG05_WDT_40S 1
#define REG05_WDT_80S 2
#define REG05_WDT_160S 3
#define REG05_WDT_BASE 0
#define REG05_WDT_LSB 40
/* Register 0x06*/
#define BQ25601_REG_06 0x06
#define REG06_OVP_MASK 0xC0
#define REG06_OVP_SHIFT 6
#define REG06_OVP_5P5V 0
#define REG06_OVP_6P5V 1
#define REG06_OVP_10P5V 2
#define REG06_OVP_14P0V 3
#define REG06_BOOSTV_MASK 0x30
#define REG06_BOOSTV_SHIFT 4
#define REG06_BOOSTV_4P85V 0
#define REG06_BOOSTV_5V 1
#define REG06_BOOSTV_5P15V 2
#define REG06_BOOSTV_5P3V 3
#define REG06_VINDPM_MASK 0x0F
#define REG06_VINDPM_SHIFT 0
#define REG06_VINDPM_BASE 3900
#define REG06_VINDPM_LSB 100
/* Register 0x07*/
#define BQ25601_REG_07 0x07
#define REG07_BATFET_DIS_MASK 0x20
#define REG07_BATFET_DIS_SHIFT 5
#define REG07_BATFET_OFF 1
#define REG07_BATFET_ON 0
#define REG07_BATFET_DLY_MASK 0x08
#define REG07_BATFET_DLY_SHIFT 3
#define REG07_BATFET_DLY_0S 0
#define REG07_BATFET_DLY_10S 1
/* Register 0x08*/
#define BQ25601_REG_08 0x08
#define REG08_VBUS_STAT_MASK 0xE0
#define REG08_VBUS_STAT_SHIFT 5
#define REG08_VBUS_TYPE_NONE 0
#define REG08_VBUS_TYPE_SDP 0x01
#define REG08_VBUS_TYPE_CDP 0x02
#define REG08_VBUS_TYPE_DCP 0x03
#define REG08_VBUS_TYPE_UNKNOWN 0x05
#define REG08_VBUS_TYPE_NON_STD 0x06
#define REG08_VBUS_TYPE_USB 1
#define REG08_VBUS_TYPE_ADAPTER 3
#define REG08_VBUS_TYPE_OTG 7
#define REG08_CHRG_STAT_MASK 0x18
#define REG08_CHRG_STAT_SHIFT 3
#define REG08_CHRG_STAT_IDLE 0
#define REG08_CHRG_STAT_PRECHG 1
#define REG08_CHRG_STAT_FASTCHG 2
#define REG08_CHRG_STAT_CHGDONE 3
#define REG08_PG_STAT_MASK 0x04
#define REG08_PG_STAT_SHIFT 2
#define REG08_POWER_GOOD 1
/* Register 0x09*/
#define BQ25601_REG_09 0x09
#define REG09_FAULT_WDT_MASK 0x80
#define REG09_FAULT_WDT_SHIFT 7
#define REG09_FAULT_WDT 1
/* Register 0x0A */
#define BQ25601_REG_0A 0x0A
#define REG0A_VBUS_GD_MASK 0x80
#define REG0A_VBUS_GD_SHIFT 7
#define REG0A_VBUS_GD 1
#define REG0A_VINDPM_STAT_MASK 0x40
#define REG0A_VINDPM_STAT_SHIFT 6
#define REG0A_VINDPM_ACTIVE 1
#define REG0A_IINDPM_STAT_MASK 0x20
#define REG0A_IINDPM_STAT_SHIFT 5
#define REG0A_IINDPM_ACTIVE 1
#define REG0A_TOPOFF_ACTIVE_MASK 0x08
#define REG0A_TOPOFF_ACTIVE_SHIFT 3
#define REG0A_TOPOFF_ACTIVE 1
#define REG0A_ACOV_STAT_MASK 0x04
#define REG0A_ACOV_STAT_SHIFT 2
#define REG0A_ACOV_ACTIVE 1
#define REG0A_VINDPM_INT_MASK 0x02
#define REG0A_VINDPM_INT_SHIFT 1
#define REG0A_IINDPM_INT_MASK 0x01
#define REG0A_IINDPM_INT_SHIFT 0
#define REG0A_INT_MASK_MASK 0x03
#define REG0A_INT_MASK_SHIFT 0
#define BQ25601_REG_0B 0x0B
#define REG0B_REG_RESET_MASK 0x80
#define REG0B_REG_RESET_SHIFT 7
#define REG0B_REG_RESET 1
#define REG0B_PN_MASK 0x78
#define REG0B_PN_SHIFT 3
#define REG0B_DEV_REV_MASK 0x03
#define REG0B_DEV_REV_SHIFT 0
#define BQ25601_REG_ENABLE 1
#define BQ25601_REG_DISABLE 0
#define CHARGER_MIN_VOLT 3500
#define CHARGER_PRECHG_CURR 180
#define CHARGER_TERM_CURR 60
#define CHARGER_TERM_VOLT 4400
#define CHARGER_INPUT_VOLT_LIMIT 4500
#define CHARGER_INPUT_CURR_LIMIT 1200
#define CHARGER_DEFAULT_CHG_CURR 2040
#define CHARGER_DEFAULT_CHG_VOLT 4250
#define CHIP_ID 2
enum stat_ctrl {
STAT_CTRL_STAT,
STAT_CTRL_ICHG,
STAT_CTRL_INDPM,
STAT_CTRL_DISABLE,
};
enum vboost {
BOOSTV_4850 = 4850,
BOOSTV_5000 = 5000,
BOOSTV_5150 = 5150,
BOOSTV_5300 = 5300,
};
enum iboost {
BOOSTI_500 = 500,
BOOSTI_1200 = 1200,
};
enum vac_ovp {
VAC_OVP_5500 = 5500,
VAC_OVP_6500 = 6500,
VAC_OVP_10500 = 10500,
VAC_OVP_14000 = 14000,
};
enum charger_type {
CHARGER_UNKNOWN = 0,
STANDARD_HOST,
CHARGING_HOST,
STANDARD_CHARGER,
NONSTANDARD_CHARGER
};
struct bq25601_data {
int32_t volt_min;
int32_t iprechg;
int32_t iterm;
int32_t ivolt;
int32_t vlim;
int32_t ilim;
int32_t ichg;
int32_t vreg;
uint8_t batfet_delay;
uint8_t batfet_rst;
uint8_t batfet_dis;
uint8_t otg_config;
uint8_t chg_config;
uint8_t vindpm_int;
uint8_t iindpm_int;
uint8_t iindet_en;
uint8_t hiz_mode;
uint8_t wd_timer;
uint8_t timer_en;
uint8_t term_en;
uint8_t pfm_en;
enum stat_ctrl statctrl;
enum vac_ovp vac_ovp;
enum vboost boostv;
enum iboost boosti;
};
struct bq25601_chip
{
struct bq25601_data *platform_data;
enum charger_type chg_type;
int32_t status;
int32_t revision;
uint8_t part_no;
uint8_t chg_det_enable;
uint8_t charge_enabled;
uint8_t power_good;
uint8_t charge_done;
};
static struct bq25601_chip g_bq25601_chip;
static struct bq25601_data g_pdata;
static charger_event_cb_t bq25601_event_cb = NULL;
static int32_t bq25601_plug_in(void);
static int32_t bq25601_plug_out(void);
static int32_t bq25601_plug_toggle(uint8_t toggle);
static int32_t bq25601_reg_read(uint8_t reg, uint8_t *buf, uint16_t len) {
i2c_transfer_t i2c;
int32_t ret = RETVAL(E_OK);
i2c.bus = BSP_I2C_BUS1;
i2c.slave_addr = BQ25601_SLAVE_ADDR;
ret = bsp_i2c_read(&i2c, reg, buf, 1);
if (ret != RETVAL(E_OK))
BQ25601_LOG_D("[dri][charger][bq25601][reg_read] charger i2c read buf failed !!!\r\n");
return ret;
}
static int32_t bq25601_reg_write(uint8_t reg, uint8_t *buf, uint16_t len) {
int32_t ret;
i2c_transfer_t i2c;
i2c.bus = BSP_I2C_BUS1;
i2c.slave_addr = BQ25601_SLAVE_ADDR;
ret = bsp_i2c_write(&i2c, reg, buf, 1);
if (ret != RETVAL(E_OK)) {
BQ25601_LOG_D("[dri][charger][bq25601][reg_read] charger i2c write buf failed !!!\r\n");
return RETVAL(E_FAIL);
}
return ret;
}
static int32_t bq25601_read_byte(uint8_t reg, uint8_t *data) {
return bq25601_reg_read(reg, data, 1);
}
static int32_t bq25601_update_bits(uint8_t reg, uint8_t mask, uint8_t data) {
int32_t ret;
uint8_t regval;
ret = bq25601_reg_read(reg, ®val, 1);
if (ret != RETVAL(E_OK))
return ret;
regval &= ~mask;
regval |= (data & mask);
ret = bq25601_reg_write(reg, ®val, 1);
ret = bq25601_reg_read(reg, ®val, 1);
return ret;
}
static int32_t bq25601_enable_otg(uint8_t enable) {
uint8_t val = enable << REG01_OTG_CONFIG_SHIFT;
return bq25601_update_bits(BQ25601_REG_01, REG01_OTG_CONFIG_MASK, val);
}
static int32_t bq25601_enable_charger(uint8_t enable) {
int32_t ret;
uint8_t val = enable << REG01_CHG_CONFIG_SHIFT;
ret = bq25601_update_bits(BQ25601_REG_01, REG01_CHG_CONFIG_MASK, val);
return ret;
}
/* Fast Charge Current */
int32_t bq25601_set_charge_current(int32_t curr) {
uint8_t ichg;
if (curr < REG02_ICHG_BASE)
curr = REG02_ICHG_BASE;
ichg = (curr - REG02_ICHG_BASE) / REG02_ICHG_LSB;
ichg <<= REG02_ICHG_SHIFT;
return bq25601_update_bits(BQ25601_REG_02, REG02_ICHG_MASK, ichg);
}
/* Terminal Current */
int32_t bq25601_set_term_current(int32_t curr) {
uint8_t iterm;
if (curr < REG03_ITERM_BASE)
curr = REG03_ITERM_BASE;
iterm = (curr - REG03_ITERM_BASE) / REG03_ITERM_LSB;
iterm <<= REG03_ITERM_SHIFT;
return bq25601_update_bits(BQ25601_REG_03, REG03_ITERM_MASK, iterm);
}
/* Precharge Current */
int32_t bq25601_set_prechg_current(int32_t curr) {
uint8_t iprechg;
if (curr < REG03_IPRECHG_BASE)
curr = REG03_IPRECHG_BASE;
iprechg = (curr - REG03_IPRECHG_BASE) / REG03_IPRECHG_LSB;
iprechg <<= REG03_IPRECHG_SHIFT;
return bq25601_update_bits(BQ25601_REG_03, REG03_IPRECHG_MASK, iprechg);
}
/* Charge Voltage */
int32_t bq25601_set_charge_volt(int32_t volt) {
uint8_t val;
if (volt < REG04_VREG_BASE)
volt = REG04_VREG_BASE;
val = (volt - REG04_VREG_BASE) / REG04_VREG_LSB;
val <<= REG04_VREG_SHIFT;
return bq25601_update_bits(BQ25601_REG_04, REG04_VREG_MASK, val);
}
/* Absolute VINDPM Threshold */
int32_t bq25601_set_input_volt_limit(int32_t volt) {
uint8_t val;
if (volt < REG06_VINDPM_BASE)
volt = REG06_VINDPM_BASE;
val = (volt - REG06_VINDPM_BASE) / REG06_VINDPM_LSB;
val <<= REG06_VINDPM_SHIFT;
return bq25601_update_bits(BQ25601_REG_06, REG06_VINDPM_MASK, val);
}
/* Input Current Limit IINDPM */
int32_t bq25601_set_input_current_limit(int32_t curr) {
uint8_t val;
if (curr < REG00_IINLIM_BASE)
curr = REG00_IINLIM_BASE;
val = (curr - REG00_IINLIM_BASE) / REG00_IINLIM_LSB;
val <<= REG00_IINLIM_SHIFT;
return bq25601_update_bits(BQ25601_REG_00, REG00_IINLIM_MASK, val);
}
int32_t bq25601_set_watchdog_timer(uint8_t timeout) {
uint8_t temp;
temp = (uint8_t) ((timeout - REG05_WDT_BASE) / REG05_WDT_LSB);
temp <<= REG05_WDT_SHIFT;
return bq25601_update_bits(BQ25601_REG_05, REG05_WDT_MASK, temp);
}
int32_t bq25601_reset_watchdog_timer(void) {
uint8_t val = REG01_WDT_RESET << REG01_WDT_RESET_SHIFT;
return bq25601_update_bits(BQ25601_REG_01, REG01_WDT_RESET_MASK, val);
}
int32_t bq25601_reset_chip(void) {
int32_t ret;
uint8_t val = REG0B_REG_RESET << REG0B_REG_RESET_SHIFT;
ret = bq25601_update_bits(BQ25601_REG_0B, REG0B_REG_RESET_MASK, val);
return ret;
}
int32_t bq25601_set_hiz_mode(uint8_t enable) {
uint8_t val = enable << REG00_ENHIZ_SHIFT;
return bq25601_update_bits(BQ25601_REG_00, REG00_ENHIZ_MASK, val);
}
int32_t bq25601_get_hiz_mode(uint8_t *state) {
uint8_t val;
int32_t ret;
ret = bq25601_read_byte(BQ25601_REG_00, &val);
if (ret != RETVAL(E_OK))
return ret;
*state = (val & REG00_ENHIZ_MASK) >> REG00_ENHIZ_SHIFT;
return RETVAL(E_OK);
}
static int32_t bq25601_enable_term(uint8_t enable) {
uint8_t val;
int32_t ret;
if (enable)
val = BQ25601_REG_ENABLE << REG05_EN_TERM_SHIFT;
else
val = BQ25601_REG_DISABLE << REG05_EN_TERM_SHIFT;
ret = bq25601_update_bits(BQ25601_REG_05, REG05_EN_TERM_MASK, val);
return ret;
}
static int32_t bq25601_set_boost_current(int32_t curr) {
uint8_t val;
val = REG02_BOOST_LIM_0P5A << REG02_BOOST_LIM_SHIFT;
if (curr == BOOSTI_1200)
val = REG02_BOOST_LIM_1P2A << REG02_BOOST_LIM_SHIFT;
return bq25601_update_bits(BQ25601_REG_02, REG02_BOOST_LIM_MASK, val);
}
/* Boost Regulation Voltage */
static int32_t bq25601_set_boost_voltage(int32_t volt) {
uint8_t val;
if (volt == BOOSTV_4850)
val = REG06_BOOSTV_4P85V;
else if (volt == BOOSTV_5150)
val = REG06_BOOSTV_5P15V;
else if (volt == BOOSTV_5300)
val = REG06_BOOSTV_5P3V;
else
val = REG06_BOOSTV_5V;
val <<= REG06_BOOSTV_SHIFT;
return bq25601_update_bits(BQ25601_REG_06, REG06_BOOSTV_MASK, val);
}
/* VAC OVP threshold */
static int32_t bq25601_set_acovp_threshold(int32_t volt) {
uint8_t val;
if (volt == VAC_OVP_14000)
val = REG06_OVP_14P0V;
else if (volt == VAC_OVP_10500)
val = REG06_OVP_10P5V;
else if (volt == VAC_OVP_6500)
val = REG06_OVP_6P5V;
else
val = REG06_OVP_5P5V;
val <<= REG06_OVP_SHIFT;
return bq25601_update_bits(BQ25601_REG_06, REG06_OVP_MASK, val);
}
static int32_t bq25601_set_stat_ctrl(int32_t ctrl) {
uint8_t val = ctrl << REG00_STAT_CTRL_SHIFT;
return bq25601_update_bits(BQ25601_REG_00, REG00_STAT_CTRL_MASK, val);
}
static int32_t bq25601_set_int_mask(int32_t mask) {
uint8_t val = mask << REG0A_INT_MASK_SHIFT;
return bq25601_update_bits(BQ25601_REG_0A, REG0A_INT_MASK_MASK, val);
}
static int32_t bq25601_enable_batfet(uint8_t onoff) {
const uint8_t val = onoff << REG07_BATFET_DIS_SHIFT;
return bq25601_update_bits(BQ25601_REG_07, REG07_BATFET_DIS_MASK, val);
}
static int32_t bq25601_set_batfet_delay(uint8_t delay) {
uint8_t val;
val = REG07_BATFET_DLY_0S;
if (delay)
val = REG07_BATFET_DLY_10S;
val <<= REG07_BATFET_DLY_SHIFT;
return bq25601_update_bits(BQ25601_REG_07, REG07_BATFET_DLY_MASK, val);
}
int32_t bq25601_set_otg(uint8_t en) {
int32_t ret;
struct bq25601_chip *bq = &g_bq25601_chip;
ret = bq25601_enable_otg(en);
LOG_D("[dri][charger][bq25601]%s OTG %s\r\n", en ? "enable" : "disable", !ret ? "successfully" : "failed");
if (ret == RETVAL(E_OK))
bq->platform_data->otg_config = en;
return ret;
}
static struct bq25601_data *bq25601_parse_dt(struct bq25601_chip *bq) {
struct bq25601_data *pdata = &g_pdata;
if (!pdata)
return NULL;
bq->chg_det_enable = 1;
bq->charge_enabled = 0;
bq->power_good = 0;
pdata->volt_min = CHARGER_MIN_VOLT;
pdata->iprechg = CHARGER_PRECHG_CURR;
pdata->iterm = CHARGER_TERM_CURR;
pdata->ivolt = CHARGER_TERM_VOLT;
pdata->vlim = CHARGER_INPUT_VOLT_LIMIT;
pdata->ilim = CHARGER_INPUT_CURR_LIMIT;
pdata->ichg = CHARGER_DEFAULT_CHG_CURR;
pdata->vreg = CHARGER_DEFAULT_CHG_VOLT;
pdata->batfet_delay = REG07_BATFET_DLY_10S;
pdata->batfet_rst = BQ25601_REG_ENABLE;
pdata->batfet_dis = REG07_BATFET_ON;
pdata->vindpm_int = BQ25601_REG_DISABLE;
pdata->iindpm_int = BQ25601_REG_DISABLE;
pdata->iindet_en = BQ25601_REG_DISABLE;
pdata->hiz_mode = BQ25601_REG_DISABLE;
pdata->wd_timer = REG05_WDT_DISABLE;
pdata->timer_en = BQ25601_REG_ENABLE;
pdata->term_en = BQ25601_REG_ENABLE;
pdata->chg_config = BQ25601_REG_ENABLE;
pdata->pfm_en = BQ25601_REG_DISABLE;
pdata->otg_config = BQ25601_REG_DISABLE;
pdata->statctrl = STAT_CTRL_STAT;
pdata->vac_ovp = VAC_OVP_6500;
pdata->boostv = BOOSTV_4850;
pdata->boosti = BOOSTI_1200;
return pdata;
}
static int32_t bq25601_get_charger_type(struct bq25601_chip *bq, enum charger_type *type) {
int32_t ret;
uint8_t reg_val = 0;
int32_t vbus_stat = 0;
enum charger_type chg_type = CHARGER_UNKNOWN;
ret = bq25601_read_byte(BQ25601_REG_08, ®_val);
if (ret != RETVAL(E_OK))
return ret;
vbus_stat = (reg_val & REG08_VBUS_STAT_MASK);
vbus_stat >>= REG08_VBUS_STAT_SHIFT;
switch (vbus_stat)
{
case REG08_VBUS_TYPE_NONE:
chg_type = CHARGER_UNKNOWN;
break;
case REG08_VBUS_TYPE_SDP:
chg_type = STANDARD_HOST;
break;
case REG08_VBUS_TYPE_CDP:
chg_type = CHARGING_HOST;
break;
case REG08_VBUS_TYPE_DCP:
chg_type = STANDARD_CHARGER;
break;
case REG08_VBUS_TYPE_UNKNOWN:
chg_type = NONSTANDARD_CHARGER;
break;
case REG08_VBUS_TYPE_NON_STD:
chg_type = NONSTANDARD_CHARGER;
break;
default:
chg_type = NONSTANDARD_CHARGER;
break;
}
*type = chg_type;
return RETVAL(E_OK);
}
static int32_t bq25601_get_vbus_status(uint8_t* status) {
struct bq25601_chip *bq = &g_bq25601_chip;
enum charger_type chg_type = CHARGER_UNKNOWN;
int32_t ret = -1;
ret = bq25601_get_charger_type(bq, &chg_type);
if (ret == RETVAL(E_OK)) {
if (chg_type == CHARGER_UNKNOWN || chg_type == NONSTANDARD_CHARGER)
*status = false;
else
*status = true;
}
else
*status = false;
return ret;
}
static int32_t bq25601_power_good_stat(struct bq25601_chip *bq) {
int32_t ret;
uint8_t reg_val;
uint8_t prev_pg;
ret = bq25601_read_byte(BQ25601_REG_08, ®_val);
if (ret != RETVAL(E_OK))
return ret;
prev_pg = bq->power_good;
bq->power_good = !!(reg_val & REG08_PG_STAT_MASK);
if (!prev_pg && bq->power_good) {
LOG_D("[dri][charger][bq25601]adapter/usb inserted\r\n");
if (bq25601_event_cb)
(*bq25601_event_cb)(POWER_CHARGER_EVT_PLUG_IN);
}
else if (prev_pg && !bq->power_good) {
LOG_D("[dri][charger][bq25601]adapter/usb removed\r\n");
if (bq25601_event_cb)
(*bq25601_event_cb)(POWER_CHARGER_EVT_PLUG_OUT);
}
return ret;
}
int32_t bq25601_is_charging_done(uint8_t *done) {
int32_t ret;
uint8_t val;
ret = bq25601_read_byte(BQ25601_REG_08, &val);
if (ret == RETVAL(E_OK)) {
val = val & REG08_CHRG_STAT_MASK;
val = val >> REG08_CHRG_STAT_SHIFT;
*done = (val == REG08_CHRG_STAT_CHGDONE);
if (*done == 1) {
if (bq25601_event_cb)
(*bq25601_event_cb)(POWER_CHARGER_EVT_CHG_DONE);
}
}
return ret;
}
static void bq25601_check_equipment_status(void) {
struct bq25601_chip *bq = &g_bq25601_chip;
bq25601_power_good_stat(bq);
bq25601_plug_toggle(bq->power_good);
bq25601_get_charger_type(bq, &bq->chg_type);
bq25601_is_charging_done(&bq->charge_done);
}
static void bq25601_irq_handler(void *arg) {
bq25601_check_equipment_status();
}
static int32_t bq25601_register_interrupt(struct bq25601_chip *bq) {
int32_t ret;
ret = bsp_gpio_register_irq_callback(BSP_GPIO_2_USB, bq25601_irq_handler, NULL);
bsp_gpio_init(BSP_GPIO_2_USB, BSP_GPIO_EXTI_ENABLE);
return ret;
}
static int32_t bq25601_init_device(struct bq25601_chip *bq) {
int32_t ret;
bq25601_reset_chip();
bq25601_set_watchdog_timer(bq->platform_data->wd_timer);
ret = bq25601_set_hiz_mode(bq->platform_data->hiz_mode);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set hiz mode, ret = %d\r\n", ret);
ret = bq25601_enable_term(bq->platform_data->term_en);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set term en/dis, ret = %d\r\n", ret);
ret = bq25601_set_stat_ctrl(bq->platform_data->statctrl);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set stat ctrl, ret = %d\r\n", ret);
ret = bq25601_enable_batfet(bq->platform_data->batfet_dis);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set batfet on/off, ret = %d\r\n", ret);
ret = bq25601_set_batfet_delay(bq->platform_data->batfet_delay);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set batfet delay, ret = %d\r\n", ret);
ret = bq25601_set_input_current_limit(bq->platform_data->ilim);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set input current limit, ret = %d\r\n", ret);
ret = bq25601_set_prechg_current(bq->platform_data->iprechg);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set prechg current, ret = %d\r\n", ret);
ret = bq25601_set_term_current(bq->platform_data->iterm);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set termination current, ret = %d\r\n", ret);
ret = bq25601_set_charge_volt(bq->platform_data->ivolt);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set termination voltage, ret = %d\r\n", ret);
ret = bq25601_set_boost_voltage(bq->platform_data->boostv);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set boost voltage, ret = %d\r\n", ret);
ret = bq25601_set_boost_current(bq->platform_data->boosti);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set boost current, ret = %d\r\n", ret);
ret = bq25601_set_acovp_threshold(bq->platform_data->vac_ovp);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set acovp threshold, ret = %d\r\n", ret);
ret = bq25601_set_int_mask(REG0A_IINDPM_INT_MASK | REG0A_VINDPM_INT_MASK);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to set vindpm and iindpm int32_t mask\r\n");
return RETVAL(E_OK);
}
static int32_t bq25601_detect_device(struct bq25601_chip *bq) {
int32_t ret;
uint8_t data;
ret = bq25601_read_byte(BQ25601_REG_0B, &data);
if (ret == RETVAL(E_OK)) {
bq->part_no = (data & REG0B_PN_MASK) >> REG0B_PN_SHIFT;
if (bq->part_no != BQ25601_DEVID) {
BQ25601_LOG_D("[dri][charger][bq25601][init_setting] incorrect devid 0x%02X\r\n", bq->part_no);
return RETVAL(E_FAIL);
}
bq->revision = (data & REG0B_DEV_REV_MASK) >> REG0B_DEV_REV_SHIFT;
}
return ret;
}
static int32_t bq25601_charging(uint8_t enable) {
struct bq25601_chip *bq = &g_bq25601_chip;
int32_t ret = 0;
uint8_t val;
ret = bq25601_enable_charger(enable);
BQ25601_LOG_D("[dri][charger][bq25601]%s charger %s\r\n", enable ? "enable" : "disable",!ret ? "successfully" : "failed");
ret = bq25601_read_byte(BQ25601_REG_01, &val);
if (ret == RETVAL(E_OK))
bq->charge_enabled = !!(val & REG01_CHG_CONFIG_MASK);
return ret;
}
static int32_t bq25601_is_charging_enable(uint8_t *en) {
struct bq25601_chip *bq = &g_bq25601_chip;
*en = bq->charge_enabled;
return RETVAL(E_OK);
}
/* set Fast Charge Current */
static int32_t bq25601_set_ichg(uint32_t curr) {
BQ25601_LOG_D("[dri][charger][bq25601]charge curr = %d\r\n", curr);
return bq25601_set_charge_current(curr);
}
/* get Fast Charge Current */
static int32_t bq25601_get_ichg(uint32_t *curr) {
uint8_t reg_val;
int32_t ichg;
int32_t ret;
ret = bq25601_read_byte(BQ25601_REG_02, ®_val);
if (ret == RETVAL(E_OK)) {
ichg = (reg_val & REG02_ICHG_MASK) >> REG02_ICHG_SHIFT;
ichg = ichg * REG02_ICHG_LSB + REG02_ICHG_BASE;
*curr = ichg;
}
return ret;
}
/* Set Charge Voltage */
static int32_t bq25601_set_vchg(uint32_t volt) {
BQ25601_LOG_D("[dri][charger][bq25601]charge volt = %d\r\n", volt);
return bq25601_set_charge_volt(volt);
}
/* feeding watchdog */
static int32_t bq25601_feeding_watchdog(uint32_t volt) {
uint8_t val = volt << REG01_WDT_RESET_SHIFT;
return bq25601_update_bits(BQ25601_REG_01, REG01_WDT_RESET_MASK, val);
}
/* Get Charge Voltage */
static int32_t bq25601_get_vchg(uint32_t *volt) {
uint8_t reg_val;
int32_t vchg;
int32_t ret;
ret = bq25601_read_byte(BQ25601_REG_04, ®_val);
if (ret == RETVAL(E_OK)) {
vchg = (reg_val & REG04_VREG_MASK) >> REG04_VREG_SHIFT;
vchg = vchg * REG04_VREG_LSB + REG04_VREG_BASE;
*volt = vchg;
}
return ret;
}
static int32_t bq25601_plug_in(void) {
int32_t ret;
struct bq25601_chip *bq = &g_bq25601_chip;
bq25601_power_good_stat(bq);
ret = bq25601_charging(bq->power_good);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to enable charging:%d\r\n", ret);
return ret;
}
static int32_t bq25601_plug_out(void) {
int32_t ret;
struct bq25601_chip *bq = &g_bq25601_chip;
bq25601_power_good_stat(bq);
ret = bq25601_charging(false);
if (ret)
BQ25601_LOG_D("[dri][charger][bq25601]Failed to disable charging:%d\r\n", ret);
return ret;
}
static int32_t bq25601_plug_toggle(uint8_t toggle) {
int32_t ret = 0;
uint8_t enable;
bq25601_is_charging_enable(&enable);
if (toggle) {
if (!enable)
ret = bq25601_plug_in();
}
else {
if (enable)
ret = bq25601_plug_out();
}
return ret;
}
static int32_t bq25601_set_property(charger_property_t *prop) {
switch (prop->charger_prop)
{
case CHARGER_ENABLE_CHARGING:
return bq25601_plug_toggle((uint8_t)prop->val);
case CHARGER_SET_CHARGE_CURRENT:
return bq25601_set_ichg(prop->val);
case CHARGER_SET_TERMINAL_CURRENT:
return bq25601_set_term_current(prop->val);
case CHARGER_SET_CHARGE_VOLTAGE:
return bq25601_set_vchg(prop->val);
case CHARGER_SET_FEEDING_WATCHDOG:
return bq25601_feeding_watchdog(prop->val);
default:
break;
}
return RETVAL(E_OK);
}
static int32_t bq25601_get_property(charger_property_t *prop) {
switch (prop->charger_prop)
{
case CHARGER_GET_CHARGE_STATUS:
return bq25601_is_charging_enable((uint8_t*)&prop->val);
case CHARGER_GET_CHARGE_CURRENT:
return bq25601_get_ichg(&prop->val);
case CHARGER_GET_CHARGE_VOLTAGE:
return bq25601_get_vchg(&prop->val);
case CHARGER_GET_VBUS_STATUS:
return bq25601_get_vbus_status((uint8_t*)&prop->val);
case CHARGER_GET_USB_STATUS:
bq25601_check_equipment_status();
break;
default:
break;
}
return RETVAL(E_OK);
}
static int32_t bq25601_set_callback(charger_event_cb_t cb) {
bq25601_event_cb = cb;
return RETVAL(E_OK);
}
static int32_t bq25601_write_data(charger_data_t *data) {
return RETVAL(E_OK);
}
static int32_t bq25601_read_data(charger_data_t *data) {
return RETVAL(E_OK);
}
static int32_t bq25601_probe(void) {
struct bq25601_chip *bq = &g_bq25601_chip;
int ret = 0;
ret = bq25601_detect_device(bq);
if (ret) {
BQ25601_LOG_D("[dri][charger][bq25601]No bq25601 device found!\r\n");
return RETVAL(E_FAIL);
}
bq->platform_data = bq25601_parse_dt(bq);
if (!bq->platform_data) {
BQ25601_LOG_D("[dri][charger][bq25601]No platform data provided.\r\n");
return RETVAL(E_FAIL);
}
ret = bq25601_init_device(bq);
if (ret) {
BQ25601_LOG_D("[dri][charger][bq25601]Failed to init device\r\n");
return ret;
}
bq25601_register_interrupt(bq);
return RETVAL(E_OK);
}
static int32_t bq25601_remove(void) {
return RETVAL(E_OK);
}
static int32_t bq25601_shutdown(driver_t *drv) {
return RETVAL(E_OK);
}
static int32_t bq25601_suspend(driver_t *drv) {
return RETVAL(E_OK);
}
static int32_t bq25601_resume(driver_t *drv) {
return RETVAL(E_OK);
}
static int32_t bq25601_init(void) {
uint8_t data;
uint8_t ret;
ret = bq25601_read_byte(BQ25601_REG_0B, &data);
if (ret == RETVAL(E_OK)) {
data = (data & REG0B_PN_MASK) >> REG0B_PN_SHIFT;
if (CHIP_ID == data)
return RETVAL(E_OK);
return RETVAL(E_FAIL);
}
else
return RETVAL(E_FAIL);
}
static driver_pm_ops_t bq25601_pm = {
.shutdown = bq25601_shutdown,
.suspend = bq25601_suspend,
.resume = bq25601_resume,
};
static charger_driver_t bq25601_driver = {
.drv = {
.probe = bq25601_probe,
.remove = bq25601_remove,
.pm_ops = &bq25601_pm,
},
.ops = {
.init = bq25601_init,
.write_data = bq25601_write_data,
.read_data = bq25601_read_data,
.set_property = bq25601_set_property,
.get_property = bq25601_get_property,
.set_callback = bq25601_set_callback,
}
};
static void bq25601_register(void) {
int32_t ret;
ret = charger_driver_register(DEV_NAME, &bq25601_driver);
if (ret != RETVAL(E_OK))
BQ25601_LOG_D("[dri][charger][bq25601][register]:bq25601 register fail ret=%d\r\n", ret);
}
DRIVER_INITCALL(DEV_NAME, bq25601_register);
charger.c
#include "log.h"
#include "errorno.h"
#include "charger.h"
int32_t charger_driver_register(const char *name, charger_driver_t *drv) {
int32_t ret;
driver_t *pdrv;
pdrv = &(drv->drv);
pdrv->drv_data = (void*)drv;
pdrv->type = DRIVER_CLASS_CHARGER;
/* register to driver manager */
ret = driver_register(pdrv, name);
return ret;
}
charger_driver_t* charger_driver_find(const char *name) {
charger_driver_t *charger;
driver_t *pdrv;
pdrv = driver_find(name);
if (pdrv == NULL || pdrv->type != DRIVER_CLASS_CHARGER)
return NULL;
charger = (charger_driver_t*)pdrv->drv_data;
return charger;
}
int32_t charger_driver_probe(charger_driver_t *drv) {
int32_t ret;
if (drv == NULL)
return RETVAL(E_NO_DEV);
ret = driver_probe(&drv->drv);
return ret;
}
int32_t charger_driver_init(charger_driver_t *drv) {
if (drv == NULL)
return RETVAL(E_NO_DEV);
if (drv->ops.init)
return drv->ops.init();
return RETVAL(E_NOT_SUPPORT);
}
int32_t charger_driver_read_data(charger_driver_t *drv, charger_data_t *data) {
if (drv == NULL)
return RETVAL(E_NO_DEV);
if (drv->ops.read_data)
return drv->ops.read_data(data);
return RETVAL(E_NOT_SUPPORT);
}
int32_t charger_driver_write_data(charger_driver_t *drv, charger_data_t *data) {
if (drv == NULL)
return RETVAL(E_NO_DEV);
if (drv->ops.write_data)
return drv->ops.write_data(data);
return RETVAL(E_NOT_SUPPORT);
}
int32_t charger_driver_set_property(charger_driver_t *drv, charger_property_t *prop) {
if (drv == NULL)
return RETVAL(E_NO_DEV);
if (drv->ops.set_property)
return drv->ops.set_property(prop);
return RETVAL(E_NOT_SUPPORT);
}
int32_t charger_driver_get_property(charger_driver_t *drv, charger_property_t *prop) {
if (drv == NULL)
return RETVAL(E_NO_DEV);
if (drv->ops.get_property)
return drv->ops.get_property(prop);
return RETVAL(E_NOT_SUPPORT);
}
int32_t charger_driver_set_callback(charger_driver_t *drv, charger_event_cb_t cb) {
if (drv == NULL)
return RETVAL(E_NO_DEV);
if (drv->ops.set_callback)
return drv->ops.set_callback(cb);
return RETVAL(E_NOT_SUPPORT);
}
charger.h
#ifndef __CHARGER_H__
#define __CHARGER_H__
#include "typedefs.h"
#include "driver.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CHARGER_DEV_GET_PROPERTY 0x0400
#define CHARGER_DEV_SET_PROPERTY 0x0500
#define CHARGER_DEV_SET_CALLBACK 0x0600
enum charger_cmd {
CHARGER_GET_INFO = 0,
CHARGER_GET_CHARGE_STATUS,
CHARGER_GET_VBUS_STATUS,
CHARGER_GET_CHARGE_CURRENT,
CHARGER_GET_CHARGE_VOLTAGE,
CHARGER_ENABLE_CHARGING,
CHARGER_ENABLE_OTG,
CHARGER_SET_PRECHARGE_CURRENT,
CHARGER_SET_CHARGE_CURRENT,
CHARGER_SET_TERMINAL_CURRENT,
CHARGER_SET_CHARGE_VOLTAGE,
CHARGER_SET_SAFETY_TIMER,
CHARGER_SET_EVENT_CALLBACK,
CHARGER_GET_USB_STATUS,
CHARGER_SET_FEEDING_WATCHDOG,
};
enum charge_status {
CHARGER_NOT_CHARGING = 0x00,
CHARGER_NOT_PRECHARGE,
CHARGER_NOT_FASTCHARGING,
CHARGER_NOT_CHARGE_TERMINAL,
};
typedef enum
{
POWER_CHARGER_EVT_PLUG_OUT,
POWER_CHARGER_EVT_PLUG_IN,
POWER_CHARGER_EVT_CHG_DONE,
} charger_event_t;
typedef struct charger_property {
enum charger_cmd charger_prop;
uint32_t val;
} charger_property_t;
typedef struct
{
uint32_t voltage; /* charging voltage */
uint32_t current; /* charging current */
int16_t term_current; /* terminal current */
uint8_t status; /* status of charger */
uint8_t full; /* charging done , full capacity */
uint8_t otg; /* otg enable */
uint8_t vbus_stat; /* vbus status */
uint8_t source_stat; /* sourceStatus */
} charger_data_t;
typedef struct charger_driver_s charger_driver_t;
typedef void (*charger_event_cb_t)(charger_event_t event);
typedef struct charger_ops_s
{
int32_t (*init)(void);
int32_t (*write_data)(charger_data_t *data);
int32_t (*read_data)(charger_data_t *data);
int32_t (*set_property)(charger_property_t *prop);
int32_t (*get_property)(charger_property_t *prop);
int32_t (*set_callback)(charger_event_cb_t cb);
} charger_ops_t;
struct charger_driver_s
{
driver_t drv;
charger_ops_t ops;
};
int32_t charger_driver_register(const char *name, charger_driver_t *drv);
charger_driver_t* charger_driver_find(const char *name);
int32_t charger_driver_probe(charger_driver_t *drv);
int32_t charger_driver_init(charger_driver_t *drv);
int32_t charger_driver_write_data(charger_driver_t *drv, charger_data_t *data);
int32_t charger_driver_read_data(charger_driver_t *drv, charger_data_t *data);
int32_t charger_driver_set_property(charger_driver_t *drv, charger_property_t *prop);
int32_t charger_driver_get_property(charger_driver_t *drv, charger_property_t *prop);
int32_t charger_driver_set_callback(charger_driver_t *drv, charger_event_cb_t cb);
#ifdef __cplusplus
}
#endif
#endif // __CHARGER_H__