#include "asm/clock.h" #include "timer.h" #include "asm/power/p33.h" #include "asm/charge.h" #include "uart.h" #include "device/device.h" #include "asm/power_interface.h" /*#include "power/power_hw.h"*/ #include "system/event.h" #include "asm/efuse.h" #include "gpio.h" #include "app_config.h" #include "asm/adc_api.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" #define IO_PORT_LDOIN 58//IO编号 #define BIT_LDO5V_IN BIT(0) #define BIT_LDO5V_OFF BIT(1) #define BIT_LDO5V_ERR BIT(2) enum { CHARGE_TIMER_10_MS = 0, CHARGE_TIMER_10_S, } CHARGE_CHECK_TIME; typedef struct _CHARGE_VAR { struct charge_platform_data *data; volatile u8 charge_online_flag; volatile u8 init_ok; volatile u8 timer_period; volatile int ldo5v_timer; volatile int charge_timer; volatile int charge_timer_pre; } CHARGE_VAR; #define __this (&charge_var) static CHARGE_VAR charge_var; static u8 charge_flag; extern void charge_set_callback(void (*wakup_callback)(void), void (*sub_callback)(void)); u8 get_charge_poweron_en(void) { return __this->data->charge_poweron_en; } void charge_check_and_set_pinr(u8 level) { u8 pinr_io; if (P33_CON_GET(P3_PINR_CON) & BIT(0)) { pinr_io = P33_CON_GET(P3_PORT_SEL10); if (pinr_io == IO_PORT_LDOIN) { if (level == 0) { P33_CON_SET(P3_PINR_CON, 2, 1, 0); } else { P33_CON_SET(P3_PINR_CON, 2, 1, 1); } } } } static void udelay(u32 usec) { JL_TIMER0->CON = BIT(14); JL_TIMER0->CNT = 0; #if(TCFG_CLOCK_SYS_SRC==SYS_CLOCK_INPUT_PLL_RCL)//省晶振,注意时钟源 JL_TIMER0->PRD = clk_get("lsb") / 1000000L * usec; //1us JL_TIMER0->CON = BIT(0); // clk #else JL_TIMER0->PRD = clk_get("timer") / 1000000L * usec; //1us JL_TIMER0->CON = BIT(0) | BIT(3); //sys clk #endif while ((JL_TIMER0->CON & BIT(15)) == 0); JL_TIMER0->CON = BIT(14); } static u8 check_charge_state(void) { u8 online_cnt = 0; u8 i = 0; //only for br23 fix charge load too small if (__this->data->ldo5v_pulldown_en) { gpio_set_pull_down(IO_PORTB_05, 1); } __this->charge_online_flag = 0; for (i = 0; i < 20; i++) { if (LVCMP_DET_GET() || LDO5V_DET_GET()) { online_cnt++; } udelay(1000); } log_info("online_cnt = %d\n", online_cnt); if (online_cnt > 5) { __this->charge_online_flag = 1; } return __this->charge_online_flag; } //only for br23 fix charge load too small void charge_reset_pb5_pd_status(void) { if (__this->data == NULL) { return; } switch (charge_flag) { case 0: case BIT_LDO5V_OFF: if (__this->data->ldo5v_pulldown_en) { gpio_set_pull_down(IO_PORTB_05, 1); } break; case BIT_LDO5V_IN: case BIT_LDO5V_ERR: if (__this->data->ldo5v_pulldown_en) { gpio_set_pull_down(IO_PORTB_05, 0); } break; } } //only for br23/br25,还是负载弱问题 bool charge_check_wakeup_is_set_ok(void) { #if TCFG_CHARGE_ENABLE //判断插拔检测设置是否正确 if (LDO5V_IS_EN() && LDO5V_EDGE_WKUP_IS_EN()) { //设置为下降沿时(bit1 = 1),当前状态应该为插入(bit5 = 1) //设置为上升沿时(bit1 = 0),当前状态应该为拔出(bit5 = 0) if (LDO5V_EDGE_GET() ^ LDO5V_DET_GET()) { return FALSE; } } #endif return TRUE; } void set_charge_online_flag(u8 flag) { __this->charge_online_flag = flag; } u8 get_charge_online_flag(void) { return __this->charge_online_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) { return __this->data->ldo5v_pulldown_en; } void charge_event_to_user(u8 event) { struct sys_event e; e.type = SYS_DEVICE_EVENT; e.arg = (void *)DEVICE_EVENT_FROM_CHARGE; e.u.dev.event = event; e.u.dev.value = 0; sys_event_notify(&e); } static void set_charge_wkup_source(u8 source) { CHARGE_WKUP_EDGE_SEL(0); //0:上升沿,高电平 1:下降沿,低电平 CHARGE_WKUP_SOURCE_SEL(source); CHARGE_EDGE_DETECT_EN(1); CHARGE_LEVEL_DETECT_EN(1); CHARGE_WKUP_EN(1); CHARGE_WKUP_PND_CLR(); } void charge_start(void) { log_info("%s\n", __func__); if (__this->charge_timer_pre) { sys_hi_timer_del(__this->charge_timer_pre); __this->charge_timer_pre = 0; } if (__this->charge_timer) { sys_timer_del(__this->charge_timer); __this->charge_timer = 0; } set_charge_wkup_source(CHARGE_FULL_33V); CHGBG_EN(1); CHARGE_EN(1); /* PDIO_KEEP(1);//防止进入pdown后充电通路被切断 */ charge_event_to_user(CHARGE_EVENT_CHARGE_START); //开始充电,加速电池电压采集速率 adc_set_sample_freq(AD_CH_VBAT, 5000); } void charge_close(void) { log_info("%s\n", __func__); CHARGE_WKUP_EN(0); CHARGE_EDGE_DETECT_EN(0); CHARGE_LEVEL_DETECT_EN(0); CHGBG_EN(0); CHARGE_EN(0); CHARGE_WKUP_PND_CLR(); /* PDIO_KEEP(0); */ charge_event_to_user(CHARGE_EVENT_CHARGE_CLOSE); if (__this->charge_timer_pre) { sys_hi_timer_del(__this->charge_timer_pre); __this->charge_timer_pre = 0; } if (__this->charge_timer) { sys_timer_del(__this->charge_timer); __this->charge_timer = 0; } //关闭充电,恢复电池电压采集速率 adc_set_sample_freq(AD_CH_VBAT, 30000); } static void charge_full_detect(void *priv) { static u16 charge_full_cnt = 0; u16 vbat_vol = adc_get_voltage(AD_CH_VBAT) * 4 / 10; u16 vpwr_vol = adc_get_voltage(AD_CH_LDO5V) * 4 / 10; if (__this->timer_period == CHARGE_TIMER_10_S) { sys_timer_modify(__this->charge_timer, 10); __this->timer_period = CHARGE_TIMER_10_MS; } //满足 充满标记 && (VPWR > VBAT) && (VPWR > 4.5V) && (VBAT > 4.1V) if (CHARGE_FULL_FLAG_GET() && LVCMP_DET_GET() && (vpwr_vol > 450) && (vbat_vol >= ((get_charge_full_value() - 100) / 10))) { /* putchar('F'); */ if (charge_full_cnt < 5) { charge_full_cnt++; } else { charge_full_cnt = 0; sys_timer_del(__this->charge_timer); __this->charge_timer = 0; charge_event_to_user(CHARGE_EVENT_CHARGE_FULL); } } else { /* putchar('K'); */ charge_full_cnt = 0; sys_timer_modify(__this->charge_timer, 10000); __this->timer_period = CHARGE_TIMER_10_S; } } static void charge_full_detect_pre(void *priv) { u8 full_flag, lvcmp_lvl; static u16 charge_no_full_cnt = 0; sys_hi_timer_del(__this->charge_timer_pre); __this->charge_timer_pre = 0; full_flag = CHARGE_FULL_FLAG_GET(); lvcmp_lvl = LVCMP_DET_GET(); if (full_flag && lvcmp_lvl) { goto __exit_det_pre; } else { if ((!full_flag) && lvcmp_lvl) { //电压正常且连续起了n个充满中断后,进入查询方式 charge_no_full_cnt++; if (charge_no_full_cnt > 50) { goto __exit_det_pre; } } else { charge_no_full_cnt = 0; } CHARGE_EDGE_DETECT_EN(1); CHARGE_LEVEL_DETECT_EN(1); } return; __exit_det_pre: //充满信号检测到之后,切换检测方式,采用查询方法(为了提高充电效率) //若不进入查询方式,此时充满中断会频繁唤醒系统,系统功耗会增大,造成充电效率低 charge_no_full_cnt = 0; __this->charge_timer = sys_timer_add(NULL, charge_full_detect, 10); __this->timer_period = CHARGE_TIMER_10_MS; return; } static void ldo5v_detect(void *priv) { /* log_info("%s\n",__func__); */ static u16 ldo5v_in_normal_cnt = 0; static u16 ldo5v_in_err_cnt = 0; static u16 ldo5v_off_cnt = 0; if (LVCMP_DET_GET()) { //ldoin > vbat /* putchar('X'); */ ldo5v_off_cnt = 0; ldo5v_in_err_cnt = 0; if (ldo5v_in_normal_cnt < 50) { ldo5v_in_normal_cnt++; } else { /* printf("ldo5V_IN\n"); */ set_charge_online_flag(1); ldo5v_in_normal_cnt = 0; sys_hi_timer_del(__this->ldo5v_timer); __this->ldo5v_timer = 0; if ((charge_flag & BIT_LDO5V_IN) == 0) { charge_flag = BIT_LDO5V_IN; //only for br23/br25 fix charge load too small if (__this->data->ldo5v_pulldown_en) { gpio_set_pull_down(IO_PORTB_05, 0); } charge_event_to_user(CHARGE_EVENT_LDO5V_IN); LVCMP_EDGE_SEL(1); //检测ldoin比vbat电压低的情况(充电仓给电池充满后会关断,此时电压会掉下来) LDO5V_EDGE_SEL(1); } } } else if (LDO5V_DET_GET() == 0) { //ldoin<拔出电压(0.6) /* putchar('Q'); */ ldo5v_in_normal_cnt = 0; ldo5v_in_err_cnt = 0; if (ldo5v_off_cnt < (__this->data->ldo5v_off_filter + 10)) { ldo5v_off_cnt++; } else { /* printf("ldo5V_OFF\n"); */ set_charge_online_flag(0); ldo5v_off_cnt = 0; sys_hi_timer_del(__this->ldo5v_timer); __this->ldo5v_timer = 0; if ((charge_flag & BIT_LDO5V_OFF) == 0) { charge_flag = BIT_LDO5V_OFF; LVCMP_EDGE_SEL(0);//拔出后重新检测插入 LDO5V_EDGE_SEL(0); //only for br23/br25 fix charge load too small if (__this->data->ldo5v_pulldown_en) { gpio_set_pull_down(IO_PORTB_05, 1); } charge_event_to_user(CHARGE_EVENT_LDO5V_OFF); } } } else { //拔出电压(0.6左右)< ldoin < vbat ldo5v_in_normal_cnt = 0; /* putchar('E'); */ ldo5v_off_cnt = 0; if (ldo5v_in_err_cnt < 50) { ldo5v_in_err_cnt++; } else { /* printf("ldo5V_ERR\n"); */ set_charge_online_flag(1); ldo5v_in_err_cnt = 0; sys_hi_timer_del(__this->ldo5v_timer); __this->ldo5v_timer = 0; if ((charge_flag & BIT_LDO5V_ERR) == 0) { charge_flag = BIT_LDO5V_ERR; LVCMP_EDGE_SEL(0); LDO5V_EDGE_SEL(1); //only for br23/br25 fix charge load too small if (__this->data->ldo5v_pulldown_en) { gpio_set_pull_down(IO_PORTB_05, 0); } if (__this->data->ldo5v_off_filter) { charge_event_to_user(CHARGE_EVENT_LDO5V_KEEP); } } } } } void sub_wakeup_isr(void) { if (__this->ldo5v_timer == 0) { __this->ldo5v_timer = sys_hi_timer_add(0, ldo5v_detect, 2); } } void charge_wakeup_isr(void) { /* printf("\ncharge_wkup_deal\n"); */ if (__this->charge_timer_pre == 0) { __this->charge_timer_pre = sys_hi_timer_add(0, charge_full_detect_pre, 2); } } #if 0 static void test_func(void *priv) { if (P33_CON_GET(P3_CHG_READ) & BIT(0)) { JL_PORTA->DIR &= ~BIT(3); JL_PORTA->OUT |= BIT(3); } else { JL_PORTA->OUT &= ~BIT(3); } } #endif 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; CHARGE_mA_SEL(charge_mA); } } const u16 full_table[CHARGE_FULL_V_MAX] = { 3869, 3907, 3946, 3985, 4026, 4068, 4122, 4157, 4202, 4249, 4295, 4350, 4398, 4452, 4509, 4567, }; 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 full_table[__this->data->charge_full_V]; } static void charge_config(void) { u8 charge_4202_trim_val = CHARGE_FULL_V_4202; u8 offset = 0; u8 charge_full_v_val = 0; if (get_vbat_trim() == 0xf) { log_info("vbat not trim, use default config!!!!!!"); } else { charge_4202_trim_val = get_vbat_trim(); //4.2V对应的trim出来的实际档位 } log_info("charge_4202_trim_val = %d\n", charge_4202_trim_val); if (__this->data->charge_full_V >= CHARGE_FULL_V_4202) { offset = __this->data->charge_full_V - CHARGE_FULL_V_4202; charge_full_v_val = charge_4202_trim_val + offset; if (charge_full_v_val > 0xf) { charge_full_v_val = 0xf; } } else { offset = CHARGE_FULL_V_4202 - __this->data->charge_full_V; if (charge_4202_trim_val >= offset) { charge_full_v_val = charge_4202_trim_val - offset; } else { charge_full_v_val = 0; } } log_info("charge_full_v_val = %d\n", charge_full_v_val); CHARGE_FULL_V_SEL(charge_full_v_val); CHARGE_FULL_mA_SEL(__this->data->charge_full_mA); /* CHARGE_mA_SEL(__this->data->charge_mA); */ CHARGE_mA_SEL(CHARGE_mA_20); } int charge_init(const struct dev_node *node, void *arg) { log_info("%s\n", __func__); __this->data = (struct charge_platform_data *)arg; ASSERT(__this->data); __this->init_ok = 0; __this->charge_online_flag = 0; /*先关闭充电使能,后面检测到充电插入再开启*/ /* CHGBG_EN(0); */ /* CHARGE_EN(0); */ /*LDO5V的100K下拉电阻使能*/ L5V_LOAD_EN(__this->data->ldo5v_pulldown_en); /*LDO5V,检测上升沿,用于检测ldoin插入*/ LDO5V_EN(1); LDO5V_EDGE_SEL(0); LDO5V_PND_CLR(); LDO5V_EDGE_WKUP_EN(1); /*LVCMP,检测上升沿,用于检测ldoin插入*/ LVCMP_EN(1); LVCMP_EDGE_SEL(0); LVCMP_PND_CLR(); LVCMP_EDGE_WKUP_EN(1); /*使用COMPH,在LDOIN和VBAT比较接近时检测信号会抖动,所以切成使用COMPL,注意softoff需要切成COMPH*/ /*COMPH:LDO5V<->VBAT COMPL:LDO5V<->VDD50*/ LVCMP_CMP_SEL(1); charge_config(); if (check_charge_state()) { if (__this->ldo5v_timer == 0) { __this->ldo5v_timer = sys_hi_timer_add(0, ldo5v_detect, 2); } } else { charge_flag = BIT_LDO5V_OFF; CHARGE_WKUP_PND_CLR(); CHGBG_EN(0); CHARGE_EN(0); } charge_set_callback(charge_wakeup_isr, sub_wakeup_isr); /* sys_hi_timer_add(0,test_func,1); */ adc_add_sample_ch(AD_CH_LDO5V); __this->init_ok = 1; return 0; } /* const struct device_operations charge_dev_ops = { */ /* .init = charge_init, */ /* }; */