This commit is contained in:
huxi
2025-12-03 11:12:34 +08:00
parent c23ae4f24c
commit bc195654bf
8163 changed files with 3799544 additions and 92 deletions
+713
View File
@@ -0,0 +1,713 @@
#include "timer.h"
#include "asm/charge.h"
#include "gpadc.h"
#include "uart.h"
#include "device/device.h"
#include "asm/power_interface.h"
#include "system/event.h"
#include "asm/efuse.h"
#include "gpio.h"
#include "clock.h"
#include "app_config.h"
#include "syscfg_id.h"
#define LOG_TAG_CONST CHARGE
#define LOG_TAG "[CHARGE]"
#define LOG_INFO_ENABLE
#define LOG_DUMP_ENABLE
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#include "debug.h"
typedef struct _CHARGE_VAR {
const struct charge_platform_data *data;
u8 charge_flag;
u8 charge_poweron_en;
u8 cc_flag;
u8 cc_counter;
volatile u8 charge_online_flag;
volatile u8 charge_event_flag;
volatile u8 init_ok;
volatile u8 detect_stop;
volatile u16 full_voltage;
volatile u16 ldo5v_timer; //检测LDOIN状态变化的usr timer
volatile u16 charge_timer; //检测充电是否充满的usr timer
volatile u16 cc_timer; //涓流切恒流的sys timer
volatile u16 progi_timer; //获取恒流的progi的电压sys_timer
} CHARGE_VAR;
#define __this (&charge_var)
static CHARGE_VAR charge_var;
// PMU结构简述:(NVDC架构)
// VPWR为充电输入引脚,VBAT为电池接口
// 充电模块的输入为VIN
// 正常情况下,(VPWR>VBAT时,VIN=VPWR) (VPWR<VBAT时,VIN=VBAT)
// >>/<<为电流可能走向
// VPWR----->>-----VIN--->><<-----CHARGE-->><<----VBAT
// |
// |
// IOVDD
/*
* 充电线性环路1说明,开启后对跟随充更友好,将会限制 VIN>VBAT+100mV(@SLT=0)
* 例如:
* VPWR外部供电5V限流50mA,充电模块设置100mA充电,充电开启后VIN电压将被拉低
* 但是由于线性环路开启,VIN恒定处于VBAT+100mV的电压,此时充电电流为50mA
*/
/*
* 充电线性环路2说明,开启后将不支持跟随充功能,充电模块输入端VIN电压将限制在不低于4V.
* 例如:
* 1、VPWR外部供电5V限流50mA,充电模块设置100mA充电,充电开启后,VIN将被拉低,
* 当VIN被拉低至4V时,线性环路2将起作用,主动限制充电电流,VIN电压会恒定处于4V;
*
* 2、VPWR外部供电5V限流50mA,充电模块设置20mA充电,系统推屏需要从IOVDD耗电40mA,充电推屏开启后,VIN将被拉低
* 当VIN被拉低至4V时,线性环路2将起作用,将优先满足系统供电需求,主动限制充电电流,VIN电压会恒定处于4V;
*
* 3、VPWR外部供电5V限流50mA,电池为3.7V,充电模块设置20mA充电,系统推屏需要从IOVDD耗电80mA;
* 充电推屏开启后,VIN将被拉低,系统会把VIN的电压扯到低于4V,充电电流会降低至0mA,VIN会继续下降,
* 一旦下降到低于VBAT电压,电池就会给系统供电,这种模式称为补电模式,在改模式下,VPWR和VBAT同时为系统供电
*/
#define CHARGE_VILOOP1_ENABLE 1//默认开启
#define CHARGE_VILOOP2_ENABLE TCFG_CHARGE_NVDC_EN//默认关闭
//判满时,VBAT的最小电压值
#define CHARGE_FULL_VBAT_MIN_VOLTAGE (__this->full_voltage - 100)
//滤波8s进行判满
#define CHARGE_FULL_FILTER_TIMES 8
//涓流切恒流滤波/s
#define CHARGE_TC2CC_FILTER_TIMES 4
#define BIT_LDO5V_IN BIT(0)
#define BIT_LDO5V_OFF BIT(1)
#define BIT_LDO5V_KEEP BIT(2)
#define GET_PINR_EN() ((P33_CON_GET(P3_PINR_CON1) & BIT(0)) ? 1: 0)
#define GET_PINR_LEVEL() ((P33_CON_GET(P3_PINR_CON1) & BIT(2)) ? 1: 0)
#define GET_PINR_PIN() ((P33_CON_GET(P3_PINR_CON1) & BIT(3)) ? 1: 0)
static u16 constant_current_progi_volt;
static u8 bat_type;
static void charge_full_detect(void *priv);
extern void charge_event_to_user(u8 event);
u8 check_pinr_shutdown_enable(void)
{
log_info("pinr lvl %d, ldo5v det %d", GET_PINR_LEVEL(), LDO5V_DET_GET());
if (GET_PINR_EN() && (GET_PINR_LEVEL() == LDO5V_DET_GET()) && !GET_PINR_PIN()) {
return 0;
}
return 1;
}
u8 get_charge_poweron_en(void)
{
return __this->charge_poweron_en;
}
void set_charge_poweron_en(u32 onOff)
{
__this->charge_poweron_en = onOff;
}
void charge_check_and_set_pinr(u8 level)
{
u8 reg;
reg = P33_CON_GET(P3_PINR_CON1);
//开启LDO5V_DET长按复位
if ((reg & BIT(0)) && ((reg & BIT(3)) == 0)) {
if (level == 0) {
P33_CON_SET(P3_PINR_CON1, 2, 1, 0);
} else {
P33_CON_SET(P3_PINR_CON1, 2, 1, 1);
}
}
}
extern void udelay(u32 usec);
static u8 check_charge_state(void)
{
u16 i;
__this->charge_online_flag = 0;
for (i = 0; i < 20; i++) {
if (LVCMP_DET_GET() || LDO5V_DET_GET()) {
__this->charge_online_flag = 1;
break;
}
udelay(1000);
}
return __this->charge_online_flag;
}
void set_charge_online_flag(u8 flag)
{
__this->charge_online_flag = flag;
}
u8 get_charge_online_flag(void)
{
return __this->charge_online_flag;
}
void set_charge_event_flag(u8 flag)
{
__this->charge_event_flag = flag;
}
u8 get_ldo5v_online_hw(void)
{
return LDO5V_DET_GET();
}
u8 get_lvcmp_det(void)
{
return LVCMP_DET_GET();
}
u8 get_ldo5v_pulldown_en(void)
{
if (!__this->data) {
return 0;
}
if (get_ldo5v_online_hw()) {
if (__this->data->ldo5v_pulldown_keep == 0) {
return 0;
}
}
return __this->data->ldo5v_pulldown_en;
}
u8 get_ldo5v_pulldown_res(void)
{
if (__this->data) {
return __this->data->ldo5v_pulldown_lvl;
}
return CHARGE_PULLDOWN_200K;
}
static const u16 volt_difference[16] = {
224, 261, 288, 320,
360, 383, 437, 496,
543, 587, 603, 643,
702, 755, 819, 898,
};
//更新progi口的电压用来判断满电电流
static void constant_current_progi_volt_config(void *priv)
{
u16 cur_vbat, cur_vpwr;
static u32 progi_volt = 0;
static u8 get_progi_volt_cnt = 0;
static u8 unit_cnt = 0;
if (syscfg_read(VM_CHARGE_PROGI_VOLT, &constant_current_progi_volt, 2) != 2) {
constant_current_progi_volt = 1200;
} else {
if (__this->progi_timer) {
sys_timer_del(__this->progi_timer);
log_info("constant_current_progi_volt 1:%d\n", constant_current_progi_volt);
__this->progi_timer = 0;
}
return;
}
cur_vbat = adc_get_voltage(AD_CH_PMU_VBAT) * AD_CH_PMU_VBAT_DIV;
cur_vpwr = adc_get_voltage(ADC_CH_PMU_VPWR_4) * 4;
//判断VPWR-VBAT的电压大于恒流的压差 && vbat的电压小于4.2,4.4,4.5V类型电池3.8V,4.0v,4.1v获取恒流progi口的电压。
if ((cur_vpwr - cur_vbat > (volt_difference[__this->data->charge_mA] + 50))) {
if (((cur_vbat < 3800) && (bat_type == BAT_4P2)) || ((cur_vbat < 4000) && (bat_type == BAT_4P4)) || ((cur_vbat < 4100) && (bat_type == BAT_4P5))) {
//更新最大的电压值
progi_volt += adc_get_voltage(AD_CH_PMU_PROGI);
get_progi_volt_cnt++;
}
}
unit_cnt++;
if (unit_cnt < 6) {
return;
}
//更新constant_current_progi_volt
if (get_progi_volt_cnt > 3) {
progi_volt = progi_volt / get_progi_volt_cnt;
if ((progi_volt >= 950) && (progi_volt <= 1450)) {
constant_current_progi_volt = progi_volt;
syscfg_write(VM_CHARGE_PROGI_VOLT, &constant_current_progi_volt, 2);
}
}
get_progi_volt_cnt = 0;
unit_cnt = 0;
progi_volt = 0;
if (__this->progi_timer) {
sys_timer_del(__this->progi_timer);
__this->progi_timer = 0;
log_info("constant_current_progi_volt 2:%d\n", constant_current_progi_volt);
}
}
static void charge_cc_check(void *priv)
{
u8 first_entry = (u8)priv;
if ((adc_get_voltage_blocking(AD_CH_PMU_VBAT) * AD_CH_PMU_VBAT_DIV / 10) > CHARGE_CCVOL_V) {
if (__this->cc_flag == 0) {
__this->cc_counter++;
}
if (first_entry || ((__this->cc_flag == 0) && (__this->cc_counter > CHARGE_TC2CC_FILTER_TIMES))) {
__this->cc_flag = 1;
set_charge_mA(__this->data->charge_mA);
if (__this->charge_timer == 0) {
__this->charge_timer = sys_timer_add(NULL, charge_full_detect, 1000);
}
if (__this->progi_timer == 0) {
__this->progi_timer = sys_timer_add(NULL, constant_current_progi_volt_config, 100);
}
}
} else {
__this->cc_counter = 0;
if (first_entry || (__this->cc_flag == 1)) {
__this->cc_flag = 0;
set_charge_mA(__this->data->charge_trickle_mA);
if (__this->charge_timer) {
sys_timer_del(__this->charge_timer);
__this->charge_timer = 0;
}
if (__this->progi_timer) {
sys_timer_del(__this->progi_timer);
__this->progi_timer = 0;
}
}
}
//TC2CC or CC2TC check
if (first_entry && (__this->cc_timer == 0)) {
__this->cc_counter = 0;
__this->cc_timer = usr_timer_add(0, charge_cc_check, 1000, 1);
}
}
void charge_start(void)
{
log_info("%s\n", __func__);
if (__this->charge_timer) {
sys_timer_del(__this->charge_timer);
__this->charge_timer = 0;
}
charge_cc_check((void *)1);
PMU_NVDC_EN(CHARGE_VILOOP2_ENABLE);
CHG_VILOOP_EN(CHARGE_VILOOP1_ENABLE);
CHG_VILOOP2_EN(CHARGE_VILOOP2_ENABLE);
CHARGE_EN(1);
CHGGO_EN(1);
charge_event_to_user(CHARGE_EVENT_CHARGE_START);
}
void charge_close(void)
{
log_info("%s\n", __func__);
CHARGE_EN(0);
CHGGO_EN(0);
CHG_VILOOP_EN(0);
CHG_VILOOP2_EN(0);
PMU_NVDC_EN(1);
charge_event_to_user(CHARGE_EVENT_CHARGE_CLOSE);
if (__this->charge_timer) {
sys_timer_del(__this->charge_timer);
__this->charge_timer = 0;
}
if (__this->cc_timer) {
usr_timer_del(__this->cc_timer);
__this->cc_timer = 0;
}
if (__this->progi_timer) {
sys_timer_del(__this->progi_timer);
__this->progi_timer = 0;
}
}
static const u16 constant_current[16] = {
40, 50, 60, 70,
80, 100, 120, 140,
160, 180, 200, 220,
240, 260, 280, 300,
};
static void charge_full_detect(void *priv)
{
static u16 charge_full_cnt = 0;
u16 vbat_vol, cur_charge_current;
//没有初始化成功或者5v不在线
if ((!__this->init_ok) || (!LVCMP_DET_GET())) {
charge_full_cnt = 0;
log_debug("no init or vpwr not 5v online : %d %d\n", __this->init_ok, LVCMP_DET_GET());
return;
}
vbat_vol = adc_get_voltage(AD_CH_PMU_VBAT) * AD_CH_PMU_VBAT_DIV;
if (vbat_vol < CHARGE_FULL_VBAT_MIN_VOLTAGE) {
charge_full_cnt = 0;
log_debug("vbat voltage not enough: %d < %d\n", vbat_vol, CHARGE_FULL_VBAT_MIN_VOLTAGE);
return;
}
//获取充电电流
cur_charge_current = (adc_get_voltage(AD_CH_PMU_PROGI) * constant_current[__this->data->charge_mA]) / constant_current_progi_volt;
log_debug("cur_charge_current:%d\n", cur_charge_current);
//获取的电流小于满电电流进行判满操作
if (cur_charge_current < __this->data->charge_full_mA) {
charge_full_cnt++;
if (charge_full_cnt > CHARGE_FULL_FILTER_TIMES) {
charge_full_cnt = 0;
charge_event_to_user(CHARGE_EVENT_CHARGE_FULL);
if (__this->charge_timer) {
sys_timer_del(__this->charge_timer);
__this->charge_timer = 0;
}
}
} else {
charge_full_cnt = 0;
}
}
static void ldo5v_detect(void *priv)
{
static u16 ldo5v_on_cnt = 0;
static u16 ldo5v_keep_cnt = 0;
static u16 ldo5v_off_cnt = 0;
if (__this->detect_stop) {
return;
}
if (LVCMP_DET_GET()) { //ldoin > vbat
log_char('X');
if (ldo5v_on_cnt < __this->data->ldo5v_on_filter) {
ldo5v_on_cnt++;
if (ldo5v_off_cnt >= (__this->data->ldo5v_off_filter + 4)) {
ldo5v_off_cnt = __this->data->ldo5v_off_filter + 4;
}
if (__this->data->ldo5v_keep_filter <= 16) {
ldo5v_keep_cnt = 0;
} else if (ldo5v_keep_cnt >= (__this->data->ldo5v_keep_filter - 16)) {
ldo5v_keep_cnt = __this->data->ldo5v_keep_filter - 16;
}
} else {
log_debug("ldo5V_IN\n");
set_charge_online_flag(1);
ldo5v_off_cnt = 0;
ldo5v_keep_cnt = 0;
//消息线程未准备好接收消息,继续扫描
if (__this->charge_event_flag == 0) {
return;
}
ldo5v_on_cnt = 0;
usr_timer_del(__this->ldo5v_timer);
__this->ldo5v_timer = 0;
if ((__this->charge_flag & BIT_LDO5V_IN) == 0) {
__this->charge_flag = BIT_LDO5V_IN;
charge_event_to_user(CHARGE_EVENT_LDO5V_IN);
}
}
} else if (LDO5V_DET_GET() == 0) { //ldoin<拔出电压(0.6
log_char('Q');
if (ldo5v_off_cnt < (__this->data->ldo5v_off_filter + 20)) {
ldo5v_off_cnt++;
if (__this->data->ldo5v_on_filter <= 16) {
ldo5v_on_cnt = 0;
} else if (ldo5v_on_cnt >= (__this->data->ldo5v_on_filter - 16)) {
ldo5v_on_cnt = __this->data->ldo5v_on_filter - 16;
}
if (__this->data->ldo5v_keep_filter <= 16) {
ldo5v_keep_cnt = 0;
} else if (ldo5v_keep_cnt >= (__this->data->ldo5v_keep_filter - 16)) {
ldo5v_keep_cnt = __this->data->ldo5v_keep_filter - 16;
}
} else {
log_debug("ldo5V_OFF\n");
set_charge_online_flag(0);
ldo5v_on_cnt = 0;
ldo5v_keep_cnt = 0;
//消息线程未准备好接收消息,继续扫描
if (__this->charge_event_flag == 0) {
return;
}
ldo5v_off_cnt = 0;
usr_timer_del(__this->ldo5v_timer);
__this->ldo5v_timer = 0;
if ((__this->charge_flag & BIT_LDO5V_OFF) == 0) {
__this->charge_flag = BIT_LDO5V_OFF;
charge_event_to_user(CHARGE_EVENT_LDO5V_OFF);
}
}
} else { //拔出电压(0.6左右)< ldoin < vbat
log_char('E');
if (ldo5v_keep_cnt < __this->data->ldo5v_keep_filter) {
ldo5v_keep_cnt++;
if (ldo5v_off_cnt >= (__this->data->ldo5v_off_filter + 4)) {
ldo5v_off_cnt = __this->data->ldo5v_off_filter + 4;
}
if (__this->data->ldo5v_on_filter <= 16) {
ldo5v_on_cnt = 0;
} else if (ldo5v_on_cnt >= (__this->data->ldo5v_on_filter - 16)) {
ldo5v_on_cnt = __this->data->ldo5v_on_filter - 16;
}
} else {
log_debug("ldo5V_ERR\n");
set_charge_online_flag(1);
ldo5v_off_cnt = 0;
ldo5v_on_cnt = 0;
//消息线程未准备好接收消息,继续扫描
if (__this->charge_event_flag == 0) {
return;
}
ldo5v_keep_cnt = 0;
usr_timer_del(__this->ldo5v_timer);
__this->ldo5v_timer = 0;
if ((__this->charge_flag & BIT_LDO5V_KEEP) == 0) {
__this->charge_flag = BIT_LDO5V_KEEP;
if (__this->data->ldo5v_off_filter) {
charge_event_to_user(CHARGE_EVENT_LDO5V_KEEP);
}
}
}
}
}
void ldoin_wakeup_isr(void)
{
if (!__this->init_ok) {
return;
}
if (__this->ldo5v_timer == 0) {
__this->ldo5v_timer = usr_timer_add(0, ldo5v_detect, 2, 1);
}
}
void charge_set_ldo5v_detect_stop(u8 stop)
{
__this->detect_stop = stop;
}
u8 get_charge_mA_config(void)
{
return __this->data->charge_mA;
}
void set_charge_mA(u8 charge_mA)
{
static u8 charge_mA_old = 0xff;
if (charge_mA_old != charge_mA) {
charge_mA_old = charge_mA;
if (charge_mA & BIT(4)) {
CHG_TRICKLE_EN(1);
} else {
CHG_TRICKLE_EN(0);
}
CHARGE_mA_SEL(charge_mA & 0x0f);
}
}
u16 get_charge_full_value(void)
{
ASSERT(__this->init_ok, "charge not init ok!\n");
ASSERT(__this->data->charge_full_V < CHARGE_FULL_V_MAX);
return __this->full_voltage;
}
static void charge_config(void)
{
u8 charge_trim_val;
u8 offset = 0;
u8 charge_full_v_val = 0;
u8 charge_curr_trim;
//判断是用4.2V 4.4V 4.5V的电池
if (__this->data->charge_full_V < CHARGE_FULL_V_4240_4P4V) {
CHG_HV_MODE(0);
PMU_MFIXI_SET_1(0);
bat_type = BAT_4P2;
charge_trim_val = efuse_get_vbat_trim_4p2();//4.20V对应的trim出来的实际档位
log_info("vbat 4p2v charge_trim_val = %d\n", charge_trim_val);
if (__this->data->charge_full_V >= CHARGE_FULL_V_4200_4P2V) {
offset = __this->data->charge_full_V - CHARGE_FULL_V_4200_4P2V;
charge_full_v_val = charge_trim_val + offset;
if (charge_full_v_val > 0xf) {
charge_full_v_val = 0xf;
}
__this->full_voltage = 4200 + (charge_full_v_val - charge_trim_val) * 20;
} else {
offset = CHARGE_FULL_V_4200_4P2V - __this->data->charge_full_V;
if (charge_trim_val >= offset) {
charge_full_v_val = charge_trim_val - offset;
} else {
charge_full_v_val = 0;
}
__this->full_voltage = 4200 - (charge_trim_val - charge_full_v_val) * 20;
}
} else if (__this->data->charge_full_V < CHARGE_FULL_V_4340_4P5V) {
CHG_HV_MODE(1);
PMU_MFIXI_SET_1(0);
bat_type = BAT_4P4;
charge_trim_val = efuse_get_vbat_trim_4p4();//4.35V对应的trim出来的实际档位
log_info("4p4v charge_trim_val = %d\n", charge_trim_val);
if (__this->data->charge_full_V >= CHARGE_FULL_V_4400_4P4V) {
offset = __this->data->charge_full_V - CHARGE_FULL_V_4400_4P4V;
charge_full_v_val = charge_trim_val + offset;
if (charge_full_v_val > 0xf) {
charge_full_v_val = 0xf;
}
__this->full_voltage = 4400 + (charge_full_v_val - charge_trim_val) * 20;
} else {
offset = CHARGE_FULL_V_4400_4P4V - __this->data->charge_full_V;
if (charge_trim_val >= offset) {
charge_full_v_val = charge_trim_val - offset;
} else {
charge_full_v_val = 0;
}
__this->full_voltage = 4400 - (charge_trim_val - charge_full_v_val) * 20;
}
} else {
CHG_HV_MODE(1);
PMU_MFIXI_SET_1(1);
bat_type = BAT_4P5;
charge_trim_val = efuse_get_vbat_trim_4p5();//4.35V对应的trim出来的实际档位
log_info("4p5v charge_trim_val = %d\n", charge_trim_val);
if (__this->data->charge_full_V >= CHARGE_FULL_V_4500_4P5V) {
offset = __this->data->charge_full_V - CHARGE_FULL_V_4500_4P5V;
charge_full_v_val = charge_trim_val + offset;
if (charge_full_v_val > 0xf) {
charge_full_v_val = 0xf;
}
__this->full_voltage = 4500 + (charge_full_v_val - charge_trim_val) * 20;
} else {
offset = CHARGE_FULL_V_4500_4P5V - __this->data->charge_full_V;
if (charge_trim_val >= offset) {
charge_full_v_val = charge_trim_val - offset;
} else {
charge_full_v_val = 0;
}
__this->full_voltage = 4500 - (charge_trim_val - charge_full_v_val) * 20;
}
}
log_info("charge_full_v_val = %d\n", charge_full_v_val);
log_info("charge_full_voltage = %d mV\n", __this->full_voltage);
//电流校准
charge_curr_trim = efuse_get_charge_cur_trim();
log_info("charge curr set value = %d\n", charge_curr_trim);
CHGI_TRIM_SEL(charge_curr_trim);
CHARGE_FULL_V_SEL(charge_full_v_val);
set_charge_mA(__this->data->charge_trickle_mA);
}
int charge_init(const struct charge_platform_data *data)
{
log_info("%s\n", __func__);
__this->data = data;
ASSERT(__this->data);
__this->init_ok = 0;
__this->charge_online_flag = 0;
__this->charge_poweron_en = data->charge_poweron_en;
CHARGE_EN(0);
CHGGO_EN(0);
CHG_CCLOOP_EN(1);
CHG_VILOOP_EN(0);
CHG_VILOOP2_EN(0);
PMU_NVDC_EN(1);
L5V_IO_MODE(0);
//消除vbat到vpwr的漏电再判断ldo5v状态
u8 temp = 10;
if (is_reset_source(P33_VDDIO_POR_RST)) {
gpio_set_mode(IO_PORT_SPILT(IO_PORTP_00), PORT_INPUT_PULLDOWN_10K);
while (temp) {
udelay(1000);
if (LDO5V_DET_GET() == 0) {
temp = 0;
} else {
temp--;
}
}
gpio_set_mode(IO_PORT_SPILT(IO_PORTP_00), PORT_INPUT_FLOATING);
}
/*LDO5V的100K下拉电阻使能*/
L5V_RES_DET_S_SEL(__this->data->ldo5v_pulldown_lvl);
L5V_LOAD_EN(__this->data->ldo5v_pulldown_en);
charge_config();
adc_add_sample_ch(AD_CH_PMU_VPWR_4);
adc_add_sample_ch(AD_CH_PMU_PROGI);
if (check_charge_state()) {
if (__this->ldo5v_timer == 0) {
__this->ldo5v_timer = usr_timer_add(0, ldo5v_detect, 2, 1);
}
} else {
__this->charge_flag = BIT_LDO5V_OFF;
}
__this->init_ok = 1;
return 0;
}
void charge_module_stop(void)
{
if (!__this->init_ok) {
return;
}
charge_close();
p33_io_wakeup_enable(IO_LDOIN_DET, 0);
p33_io_wakeup_enable(IO_VBTCH_DET, 0);
if (__this->ldo5v_timer) {
usr_timer_del(__this->ldo5v_timer);
__this->ldo5v_timer = 0;
}
}
void charge_module_restart(void)
{
if (!__this->init_ok) {
return;
}
if (!__this->ldo5v_timer) {
__this->ldo5v_timer = usr_timer_add(NULL, ldo5v_detect, 2, 1);
}
p33_io_wakeup_enable(IO_LDOIN_DET, 1);
p33_io_wakeup_enable(IO_VBTCH_DET, 1);
}
//系统进入低功耗时会调用
void charge_enter_lowpower(enum LOW_POWER_LEVEL lp_mode)
{
if (lp_mode == LOW_POWER_MODE_SOFF) {
CHARGE_EN(0);
CHGGO_EN(0);
PMU_NVDC_EN(0);
CHG_CCLOOP_EN(0);
CHG_VILOOP_EN(0);
CHG_VILOOP2_EN(0);
L5V_LOAD_EN(get_ldo5v_pulldown_en());
L5V_RES_DET_S_SEL(get_ldo5v_pulldown_res());
}
}
#if defined TCFG_CHANEG_DONT_ENTER_LOW_POWER &&TCFG_CHANEG_DONT_ENTER_LOW_POWER
static u8 charge_idle_query(void)
{
return !__this->charge_online_flag;
}
REGISTER_LP_TARGET(charge_lp_target) = {
.name = "charge",
.is_idle = charge_idle_query,
};
#endif//TCFG_CHANEG_DONT_ENTER_LOW_POWER
+95
View File
@@ -0,0 +1,95 @@
#include "app_config.h"
#include "cpu/includes.h"
#include "asm/charge.h"
#include "system/init.h"
#include "asm/power_interface.h"
#include "gpio.h"
#if TCFG_CHARGE_ENABLE
static void vpwr_wakeup_callback(P33_IO_WKUP_EDGE edge)
{
ldoin_wakeup_isr();
}
static const struct _p33_io_wakeup_config vbat_port = {
.edge = BOTH_EDGE, //唤醒方式选择,可选:上升沿\下降沿\双边沿
.filter = PORT_FLT_16ms,
.gpio = IO_VBTCH_DET, //唤醒口选择
.callback = vpwr_wakeup_callback,
};
static const struct _p33_io_wakeup_config ldoin_port = {
.edge = BOTH_EDGE, //唤醒方式选择,可选:上升沿\下降沿\双边沿
.filter = PORT_FLT_16ms,
.gpio = IO_LDOIN_DET, //唤醒口选择
.callback = vpwr_wakeup_callback,
};
void charge_wakeup_init()
{
p33_io_wakeup_port_init(&vbat_port);
p33_io_wakeup_enable(IO_VBTCH_DET, 1);
p33_io_wakeup_port_init(&ldoin_port);
p33_io_wakeup_enable(IO_LDOIN_DET, 1);
}
static const struct charge_platform_data charge_data = {
.charge_en = TCFG_CHARGE_ENABLE, //内置充电使能
.charge_poweron_en = TCFG_CHARGE_POWERON_ENABLE, //是否支持充电开机
.charge_full_V = TCFG_CHARGE_FULL_V, //充电截止电压
.charge_full_mA = TCFG_CHARGE_FULL_MA, //充电截止电流
.charge_mA = TCFG_CHARGE_MA, //充电电流
.charge_trickle_mA = TCFG_CHARGE_TRICKLE_MA, //涓流电流
/* ldo5v拔出过滤值,过滤时间 = (filter*2 + 20)ms,
* ldoin < 0.6V且时间大于过滤时间才认为拔出
* 对于充满直接从5V掉到0V的充电仓,该值必须设置成0
* 对于充满由5V先掉到0V之后再升压到xV的 充电仓,需要根据实际情况设置该值大小
* */
.ldo5v_off_filter = TCFG_LDOIN_OFF_FILTER_TIME / 2,
.ldo5v_on_filter = TCFG_LDOIN_ON_FILTER_TIME / 2,
.ldo5v_keep_filter = TCFG_LDOIN_KEEP_FILTER_TIME / 2,
.ldo5v_pulldown_lvl = TCFG_LDOIN_PULLDOWN_LEV,
.ldo5v_pulldown_keep = TCFG_LDOIN_PULLDOWN_KEEP,
/*
* 1. 对于自动升压充电舱,若充电舱需要更大的负载才能检测到插入时,请将该变量置1
并且根据需求配置下拉电阻档位
* 2. 对于按键升压,并且是通过上拉电阻去提供维持电压的舱,请将该变量设置1,
并且根据舱的上拉配置下拉需要的电阻挡位
* 3. 对于常5V的舱,可将改变量设为0,省功耗
*/
.ldo5v_pulldown_en = TCFG_LDOIN_PULLDOWN_EN,
};
int board_charge_init()
{
charge_wakeup_init();
charge_init(&charge_data);
if (get_charge_online_flag()) {
power_set_mode(PWR_LDO15);
} else {
power_set_mode(TCFG_LOWPOWER_POWER_SEL);
}
return 0;
}
platform_initcall(board_charge_init);
#else
//没开启充电时,关闭漏电寄存器(约2uA)
int board_charge_init()
{
CHG_VILOOP_EN(0);
CHG_VILOOP2_EN(0);
return 0;
}
platform_initcall(board_charge_init);
#endif
+168
View File
@@ -0,0 +1,168 @@
#include "board_config.h"
#include "includes.h"
#include "app_config.h"
#include "chargebox.h"
#include "clock.h"
#include "uart.h"
#include "asm/gpio_hw.h"
#if (TCFG_CHARGE_BOX_ENABLE)
#define BUF_LEN (64)
struct chargebox_handle {
const struct chargebox_platform_data *data;
s32 udev;
volatile u8 lr_flag;
volatile u8 rx_index;
};
static u8 chg_uart_buf[BUF_LEN] __attribute__((aligned(4)));
static u8 chg_uart_dma_buf[BUF_LEN] __attribute__((aligned(4)));
#define __this (&hdl)
static struct chargebox_handle hdl;
extern void chargebox_data_deal(u8 cmd, u8 l_r, u8 *data, u8 length);
void __attribute__((weak)) chargebox_data_deal(u8 cmd, u8 l_r, u8 *data, u8 len)
{
}
static void chargebox_uart_isr_cb(uart_dev uart_num, enum uart_event event)
{
if (event == UART_EVENT_TX_DONE) {
chargebox_data_deal(CMD_COMPLETE, __this->lr_flag, NULL, 0);
} else if (event == UART_EVENT_RX_DATA) {
uart_recv_bytes(__this->udev, &chg_uart_buf[__this->rx_index], 1);
chargebox_data_deal(CMD_RECVBYTE, __this->lr_flag, NULL, 0);
if (chg_uart_buf[0] == 0x55) {
__this->rx_index++;
} else {
return;
}
//耳机发来数据长度为head 2byte + (data len +1) + 校验码 1byte
if ((__this->rx_index > 3) && (__this->rx_index == (chg_uart_buf[2] + 4))) {
chargebox_data_deal(CMD_RECVDATA, __this->lr_flag, chg_uart_buf, __this->rx_index);
__this->rx_index = 0;
}
}
}
u8 chargebox_write(u8 l_r, u8 *data, u8 len)
{
if ((len == 0) || (len > BUF_LEN) || (__this->udev < 0)) {
return 0;
}
__this->lr_flag = l_r;
if (((u32)data) % 4) {//4byte对齐
ASSERT(0, "%s: unaligned accesses!", __func__);
}
memcpy(chg_uart_buf, data, len);
uart_send_bytes(__this->udev, chg_uart_buf, (u32)len);
return len;
}
void chargebox_open(u8 l_r, u8 mode)
{
u32 uart_timeout;
u32 io_port;
s32 ret;
struct uart_config config = {0};
struct uart_dma_config dma = {0};
if (mode == MODE_RECVDATA) {
__this->rx_index = 0;
}
if (__this->udev >= 0) {
return;
}
if (l_r == EAR_L) {
io_port = __this->data->L_port;
} else {
io_port = __this->data->R_port;
}
__this->lr_flag = l_r;
config.baud_rate = __this->data->baudrate;
config.tx_pin = io_port;
config.rx_pin = io_port;
config.parity = UART_PARITY_DISABLE;
config.tx_wait_mutex = 0;
__this->udev = uart_init(-1, &config);
if (__this->udev < 0) {
goto __err_exit;
}
//确保2byte的时间
uart_timeout = 25 * 1000000 / __this->data->baudrate;//us
dma.rx_timeout_thresh = uart_timeout;
dma.frame_size = 1;
dma.event_mask = UART_EVENT_RX_DATA | UART_EVENT_RX_TIMEOUT | UART_EVENT_TX_DONE;
dma.irq_priority = 2;
dma.irq_callback = chargebox_uart_isr_cb;
dma.rx_cbuffer = chg_uart_dma_buf;
dma.rx_cbuffer_size = BUF_LEN;
ret = uart_dma_init(__this->udev, &dma);
if (ret < 0) {
goto __err_exit;
}
return;
__err_exit:
ASSERT(0, "chargebox open err!\n");
if (__this->udev >= 0) {
uart_deinit(__this->udev);
}
__this->udev = -1;
}
void chargebox_close(u8 l_r)
{
if (__this->udev < 0) {
return;
}
uart_deinit(__this->udev);
__this->udev = -1;
if (l_r == EAR_L) {
gpio_hw_set_pull_up(IO_PORT_SPILT(__this->data->L_port), GPIO_PULLUP_DISABLE);
gpio_hw_set_pull_down(IO_PORT_SPILT(__this->data->L_port), GPIO_PULLDOWN_DISABLE);
gpio_hw_set_die(IO_PORT_SPILT(__this->data->L_port), 1);
gpio_hw_set_dieh(IO_PORT_SPILT(__this->data->L_port), 1);
gpio_set_mode(IO_PORT_SPILT(__this->data->L_port), PORT_OUTPUT_HIGH);
} else {
gpio_hw_set_pull_up(IO_PORT_SPILT(__this->data->R_port), GPIO_PULLUP_DISABLE);
gpio_hw_set_pull_down(IO_PORT_SPILT(__this->data->R_port), GPIO_PULLDOWN_DISABLE);
gpio_hw_set_die(IO_PORT_SPILT(__this->data->R_port), 1);
gpio_hw_set_dieh(IO_PORT_SPILT(__this->data->R_port), 1);
gpio_set_mode(IO_PORT_SPILT(__this->data->R_port), PORT_OUTPUT_HIGH);
}
}
void chargebox_set_baud(u32 baudrate)
{
u32 uart_timeout;
if (__this->udev < 0) {
return;
}
//确保2byte的时间
uart_timeout = 25 * 1000000 / baudrate;
uart_set_baudrate(__this->udev, baudrate);
uart_set_rx_timeout_thresh(__this->udev, uart_timeout);
//重新初始化DMA,防止高波特率时候出现分包行为
uart_dma_rx_reset(__this->udev);
}
void chargebox_init(const struct chargebox_platform_data *data)
{
__this->data = (struct chargebox_platform_data *)data;
ASSERT(data);
__this->udev = -1;
}
#endif
+204
View File
@@ -0,0 +1,204 @@
#include "generic/typedef.h"
#include "gpio.h"
#include "asm/power_interface.h"
#include "asm/hwi.h"
#include "asm/gpio_hw.h"
#include "asm/charge.h"
#include "asm/chargestore.h"
#include "update.h"
#include "app_config.h"
#include "clock.h"
#include "uart.h"
#include "asm/power/p33/charge_hw.h"
#if (TCFG_CHARGESTORE_ENABLE || TCFG_TEST_BOX_ENABLE || TCFG_ANC_BOX_ENABLE)
struct chargestore_handle {
const struct chargestore_platform_data *data;
s32 udev;
};
#define DMA_BUF_LEN 544
#define __this (&hdl)
static struct chargestore_handle hdl;
u8 uart_rx_buf[DMA_BUF_LEN] __attribute__((aligned(4)));
u8 uart_dma_buf[DMA_BUF_LEN] __attribute__((aligned(4)));
enum {
UPGRADE_NULL = 0,
UPGRADE_USB_HARD_KEY,
UPGRADE_USB_SOFTKEY,
UPGRADE_UART_SOFT_KEY,
UPGRADE_UART_ONE_WIRE_HARD_KEY,
};
extern void nvram_set_boot_state(u32 state);
void chargestore_set_update_ram(void)
{
//需要补充设置ram
int tmp;
__asm__ volatile("%0 =icfg" : "=r"(tmp));
tmp &= ~(3 << 8);
__asm__ volatile("icfg = %0" :: "r"(tmp));//GIE1
local_irq_disable();
nvram_set_boot_state(UPGRADE_UART_SOFT_KEY);
}
void __attribute__((weak)) chargestore_data_deal(u8 cmd, u8 *data, u32 len)
{
}
void __attribute__((weak)) chargestore_uart_data_deal(u8 *data, u8 len)
{
}
static u8 chargestore_get_f95_det_res(u32 equ_res)
{
u8 det_res = (equ_res + 50) / 100;
if (det_res > 0) {
det_res -= 1;
}
if (det_res > 0x0f) {
det_res = 0x0f;
}
return det_res;
}
u8 chargestore_get_det_level(u8 chip_type)
{
u32 res = 1600;
switch (chip_type) {
case TYPE_F95:
if (IS_L5V_LOAD_EN()) {
res = (GET_L5V_RES_DET_S_SEL() + 1) * 50;
}
return chargestore_get_f95_det_res(res);
case TYPE_NORMAL:
default:
return 0x0f;
}
}
static void chargestore_uart_isr_cb(uart_dev uart_num, enum uart_event event)
{
s32 rx_len;
if (event == UART_EVENT_RX_DATA) {
} else if (event == UART_EVENT_TX_DONE) {
if (__this->data->io_port == IO_PORT_LDOIN) {
gpio_set_mode(IO_PORT_SPILT(__this->data->io_port), PORT_INPUT_FLOATING);
}
chargestore_data_deal(CMD_COMPLETE, NULL, 0);
} else if (event == UART_EVENT_RX_TIMEOUT) {
rx_len = uart_recv_bytes(__this->udev, uart_rx_buf, DMA_BUF_LEN);
if (rx_len > 0) {
chargestore_data_deal(CMD_RECVDATA, uart_rx_buf, rx_len);
chargestore_uart_data_deal(uart_rx_buf, rx_len);
}
}
}
void chargestore_write(u8 *data, u8 len)
{
if (__this->udev < 0) {
return;
}
if (((u32)data) % 4) {//4byte对齐
ASSERT(0, "%s: unaligned accesses!", __func__);
}
uart_send_bytes(__this->udev, (const void *)data, (u32)len);
}
void chargestore_open(u8 mode)
{
s32 ret;
u32 uart_timeout;
struct uart_config config = {0};
struct uart_dma_config dma = {0};
#if (TCFG_CHARGE_ENABLE && (TCFG_CHARGESTORE_PORT == IO_PORT_LDOIN))
if (mode == MODE_RECVDATA) {
charge_set_ldo5v_detect_stop(0);
} else {
charge_set_ldo5v_detect_stop(1);
}
#endif
if (__this->udev >= 0) {
return;
}
config.baud_rate = __this->data->baudrate;
config.tx_pin = __this->data->io_port;
config.rx_pin = __this->data->io_port;
config.parity = UART_PARITY_DISABLE;
config.tx_wait_mutex = 0;
__this->udev = uart_init(-1, &config);
if (__this->udev < 0) {
goto __err_exit;
}
//如果为VPWR引脚,则关闭驱动开启的上拉
if (__this->data->io_port == IO_PORT_LDOIN) {
gpio_set_mode(IO_PORT_SPILT(__this->data->io_port), PORT_INPUT_FLOATING);
}
//确保2byte的时间
uart_timeout = 25 * 1000000 / __this->data->baudrate;//us
dma.rx_timeout_thresh = uart_timeout;
dma.frame_size = DMA_BUF_LEN;
dma.event_mask = UART_EVENT_RX_DATA | UART_EVENT_RX_TIMEOUT | UART_EVENT_TX_DONE;
dma.irq_priority = 2;
dma.irq_callback = chargestore_uart_isr_cb;
dma.rx_cbuffer = uart_dma_buf;
dma.rx_cbuffer_size = DMA_BUF_LEN;
ret = uart_dma_init(__this->udev, &dma);
if (ret < 0) {
goto __err_exit;
}
return;
__err_exit:
ASSERT(0, "chargebox open err!\n");
if (__this->udev >= 0) {
uart_deinit(__this->udev);
}
__this->udev = -1;
}
void chargestore_close(void)
{
if (__this->udev < 0) {
return;
}
uart_deinit(__this->udev);
__this->udev = -1;
#if (TCFG_CHARGE_ENABLE && (TCFG_CHARGESTORE_PORT == IO_PORT_LDOIN))
charge_set_ldo5v_detect_stop(0);
#endif
}
void chargestore_set_baudrate(u32 baudrate)
{
u32 uart_timeout;
if (__this->udev < 0) {
return;
}
//确保2byte的时间
uart_timeout = 25 * 1000000 / baudrate;
uart_set_baudrate(__this->udev, baudrate);
uart_set_rx_timeout_thresh(__this->udev, uart_timeout);
//重新初始化DMA,防止高波特率时候出现分包行为
uart_dma_rx_reset(__this->udev);
}
void chargestore_init(const struct chargestore_platform_data *data)
{
__this->data = (struct chargestore_platform_data *)data;
ASSERT(data);
__this->udev = -1;
}
#endif
+50
View File
@@ -0,0 +1,50 @@
#include "app_config.h"
#include "asm/chargestore.h"
#include "asm/charge.h"
#include "gpio_config.h"
#include "system/init.h"
#include "asm/power_interface.h"
#include "gpio.h"
#if TCFG_CHARGESTORE_ENABLE || TCFG_TEST_BOX_ENABLE || TCFG_ANC_BOX_ENABLE
static void chargestore_wakeup_callback(P33_IO_WKUP_EDGE edge)
{
extern void chargestore_ldo5v_fall_deal(void);
chargestore_ldo5v_fall_deal();
}
static const struct _p33_io_wakeup_config port1 = {
.pullup_down_mode = PORT_KEEP_STATE, //配置I/O 保持之前IO状态
.edge = FALLING_EDGE, //唤醒方式选择,可选:上升沿\下降沿
.filter = PORT_FLT_1ms,
.gpio = TCFG_CHARGESTORE_PORT, //唤醒口选择
.callback = chargestore_wakeup_callback,
};
static const struct chargestore_platform_data chargestore_data = {
.io_port = TCFG_CHARGESTORE_PORT,
.baudrate = 9600,
.init = chargestore_init,
.open = chargestore_open,
.close = chargestore_close,
.write = chargestore_write,
};
int board_chargestore_config()
{
p33_io_wakeup_port_init(&port1);
p33_io_wakeup_enable(TCFG_CHARGESTORE_PORT, 1);
chargestore_api_init(&chargestore_data);
return 0;
}
static void board_chargestore_not_config()
{
p33_io_wakeup_enable(TCFG_CHARGESTORE_PORT, 0);
}
platform_initcall(board_chargestore_config);
platform_uninitcall(board_chargestore_not_config);
#endif