KT24-1110_65E-HA-651B/cpu/br25/charge.c
2024-11-10 18:44:17 +08:00

546 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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, */
/* }; */