2025-02-18 15:40:42 +08:00

582 lines
16 KiB
C
Raw Permalink 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 "asm/adc_api.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"
#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 LINDT_IN//IO编号
typedef struct _CHARGE_VAR {
struct charge_platform_data *data;
volatile u8 charge_online_flag;
volatile u8 charge_event_flag;
volatile u8 init_ok;
volatile u8 detect_stop;
volatile int ldo5v_timer;
volatile int charge_timer;
volatile int power_sw_timer;
volatile int cc_timer; //涓流切恒流的sys timer
} CHARGE_VAR;
#define __this (&charge_var)
static CHARGE_VAR charge_var;
extern void charge_set_callback(void (*wakup_callback)(void), void (*sub_callback)(void));
static u8 charge_flag;
#define BIT_LDO5V_IN BIT(0)
#define BIT_LDO5V_OFF BIT(1)
#define BIT_LDO5V_ERR BIT(2)
void charge_wakeup_isr(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_SEL0);
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;
JL_TIMER0->PRD = clk_get("timer") / 1000000L * usec; //1us
JL_TIMER0->CON = BIT(0) | BIT(2) | BIT(6); //sys clk
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;
__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;
}
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;
}
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);
}
void charge_power_switch_timer(void *priv)
{
u32 vbat_voltage;
vbat_voltage = adc_get_voltage(AD_CH_VBAT) * 4 / 10;
if (vbat_voltage > 300) {
power_set_charge_mode(0);
power_set_mode(PWR_DCDC15);
power_set_charge_mode(1);
usr_timer_del(__this->power_sw_timer);
__this->power_sw_timer = 0;
adc_set_sample_freq(AD_CH_VBAT, 5000);
}
}
void power_enter_charge_mode(void)
{
if (TCFG_LOWPOWER_POWER_SEL == PWR_DCDC15) {
u8 chip_id = get_chip_version() & 0x0f;
u32 vbat_voltage;
if ((chip_id >= 0x0C) || (chip_id == 0x01) || (chip_id == 0x02)) {
vbat_voltage = adc_get_voltage(AD_CH_VBAT) * 4 / 10;
if (vbat_voltage > 300) {
power_set_charge_mode(0);
power_set_mode(PWR_DCDC15);
power_set_charge_mode(1);
} else {
power_set_charge_mode(0);
power_set_mode(PWR_LDO15);
power_set_charge_mode(1);
if (__this->power_sw_timer == 0) {
//低压充电不进低功耗
adc_set_sample_freq(AD_CH_VBAT, 20);
__this->power_sw_timer = usr_timer_add(NULL, charge_power_switch_timer, 1000, 1);
}
}
} else if ((chip_id == 0x03) || (chip_id == 0x04) || (chip_id == 0x06)) {
power_set_charge_mode(1);
power_set_mode(PWR_DCDC15_FOR_CHARGE);
}
}
}
void power_exit_charge_mode(void)
{
if (TCFG_LOWPOWER_POWER_SEL == PWR_DCDC15) {
power_set_charge_mode(0);
power_set_mode(TCFG_LOWPOWER_POWER_SEL);
if (__this->power_sw_timer) {
usr_timer_del(__this->power_sw_timer);
__this->power_sw_timer = 0;
}
}
}
/*检测是否满足进入恒流充电条件*/
static void charge_cc_check(void *priv)
{
log_info("%s\n", __func__);
if ((adc_get_voltage(AD_CH_VBAT) * 4 / 10) > CHARGE_CCVOL_V) {
/*满足进入恒流充电条件 设置恒流充电电流大小*/
set_charge_mA(__this->data->charge_mA);
usr_timer_del(__this->cc_timer);
__this->cc_timer = 0;
/*触发一次满电唤醒服务函数 将满电使能打开*/
charge_wakeup_isr();
}
}
void charge_start(void)
{
u8 check_full = 0;
log_info("%s\n", __func__);
/*充电开始先将满电检测的定时器关闭*/
if (__this->charge_timer) {
usr_timer_del(__this->charge_timer);
__this->charge_timer = 0;
}
/*充电开始先将满电唤醒关闭,待过涓流阶段再打开*/
power_wakeup_disable_with_port(IO_CHGFL_DET);
/*进入恒流充电之后(VBAT > 3V),才开启充满检测*/
if ((adc_get_voltage(AD_CH_VBAT) * 4 / 10) > CHARGE_CCVOL_V) {
/*设置恒流充电电流大小*/
set_charge_mA(__this->data->charge_mA);
/*恒流充电开启满电唤醒使能*/
power_wakeup_enable_with_port(IO_CHGFL_DET);
check_full = 1;
} else {
/*设置涓流电流大小*/
set_charge_mA(__this->data->charge_trickle_mA);
if (!__this->cc_timer) {
/*每1分钟检测一次是否具备进入恒流充电的条件*/
__this->cc_timer = usr_timer_add(NULL, charge_cc_check, 1000, 1);
}
}
power_enter_charge_mode();
CHGBG_EN(1);
CHARGE_EN(1);
charge_event_to_user(CHARGE_EVENT_CHARGE_START);
/*开启充电时,充满标记为1时,主动检测一次是否充满*/
if (check_full && CHARGE_FULL_FLAG_GET()) {
charge_wakeup_isr();
}
}
void charge_close(void)
{
log_info("%s\n", __func__);
if (charge_flag != BIT_LDO5V_IN) {
CHGBG_EN(0);
CHARGE_EN(0);
}
power_exit_charge_mode();
power_wakeup_disable_with_port(IO_CHGFL_DET);
charge_event_to_user(CHARGE_EVENT_CHARGE_CLOSE);
if (__this->charge_timer) {
usr_timer_del(__this->charge_timer);
__this->charge_timer = 0;
}
if (__this->cc_timer) {
usr_timer_del(__this->cc_timer);
__this->cc_timer = 0;
}
}
static void charge_full_detect(void *priv)
{
static u16 charge_full_cnt = 0;
u16 vbat_vol, vpwr_vol;
u8 chip_id = get_chip_version() & 0x0f;
if (CHARGE_FULL_FLAG_GET() && LVCMP_DET_GET()) {
/* putchar('F'); */
//AB版可能出现误判满
if ((chip_id >= 0x0C) || (chip_id == 0x01)) {
vbat_vol = adc_get_voltage(AD_CH_VBAT) * 4 / 10;
vpwr_vol = adc_get_voltage(AD_CH_LDO5V) * 4 / 10;
if ((vpwr_vol < 450) || (vbat_vol < 410)) {
/* putchar('P'); */
charge_full_cnt = 0;
return;
}
}
if (charge_full_cnt < 5) {
charge_full_cnt++;
} else {
charge_full_cnt = 0;
usr_timer_del(__this->charge_timer);
__this->charge_timer = 0;
charge_event_to_user(CHARGE_EVENT_CHARGE_FULL);
}
} else {
/* putchar('K'); */
charge_full_cnt = 0;
usr_timer_del(__this->charge_timer);
__this->charge_timer = 0;
/*电池未满电,开启满电唤醒使能*/
power_wakeup_enable_with_port(IO_CHGFL_DET);
}
}
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 (__this->detect_stop) {
return;
}
if (LVCMP_DET_GET()) { //ldoin > vbat
/* putchar('X'); */
if (ldo5v_in_normal_cnt < __this->data->ldo5v_on_filter) {
ldo5v_in_normal_cnt++;
} else {
/* printf("ldo5V_IN\n"); */
set_charge_online_flag(1);
ldo5v_off_cnt = 0;
ldo5v_in_err_cnt = 0;
//消息线程未准备好接收消息,继续扫描
if (__this->charge_event_flag == 0) {
return;
}
ldo5v_in_normal_cnt = 0;
usr_timer_del(__this->ldo5v_timer);
__this->ldo5v_timer = 0;
if ((charge_flag & BIT_LDO5V_IN) == 0) {
charge_flag = BIT_LDO5V_IN;
charge_event_to_user(CHARGE_EVENT_LDO5V_IN);
power_wakeup_set_edge(IO_VBTCH_DET, FALLING_EDGE);//检测ldoin比vbat电压低的情况(充电仓给电池充满后会关断,此时电压会掉下来)
}
}
} else if (LDO5V_DET_GET() == 0) { //ldoin<拔出电压0.6
/* putchar('Q'); */
if (ldo5v_off_cnt < (__this->data->ldo5v_off_filter + 20)) {
ldo5v_off_cnt++;
} else {
/* printf("ldo5V_OFF\n"); */
set_charge_online_flag(0);
ldo5v_in_normal_cnt = 0;
ldo5v_in_err_cnt = 0;
//消息线程未准备好接收消息,继续扫描
if (__this->charge_event_flag == 0) {
return;
}
ldo5v_off_cnt = 0;
usr_timer_del(__this->ldo5v_timer);
__this->ldo5v_timer = 0;
if ((charge_flag & BIT_LDO5V_OFF) == 0) {
charge_flag = BIT_LDO5V_OFF;
power_wakeup_set_edge(IO_VBTCH_DET, RISING_EDGE);//拔出后重新检测插入
charge_event_to_user(CHARGE_EVENT_LDO5V_OFF);
}
}
} else { //拔出电压0.6左右)< ldoin < vbat
/* putchar('E'); */
if (ldo5v_in_err_cnt < __this->data->ldo5v_keep_filter) {
ldo5v_in_err_cnt++;
} else {
/* printf("ldo5V_ERR\n"); */
set_charge_online_flag(1);
ldo5v_off_cnt = 0;
ldo5v_in_normal_cnt = 0;
//消息线程未准备好接收消息,继续扫描
if (__this->charge_event_flag == 0) {
return;
}
ldo5v_in_err_cnt = 0;
usr_timer_del(__this->ldo5v_timer);
__this->ldo5v_timer = 0;
if ((charge_flag & BIT_LDO5V_ERR) == 0) {
charge_flag = BIT_LDO5V_ERR;
power_wakeup_set_edge(IO_VBTCH_DET, RISING_EDGE);
if (__this->data->ldo5v_off_filter) {
charge_event_to_user(CHARGE_EVENT_LDO5V_KEEP);
}
}
}
}
}
void sub_wakeup_isr(void)
{
/* printf(" %s \n", __func__); */
if (__this->ldo5v_timer == 0) {
__this->ldo5v_timer = usr_timer_add(0, ldo5v_detect, 2, 1);
}
}
/*满电唤醒服务函数*/
void charge_wakeup_isr(void)
{
/* printf(" %s \n", __func__); */
power_wakeup_disable_with_port(IO_CHGFL_DET);
if (__this->charge_timer == 0) {
__this->charge_timer = usr_timer_add(0, charge_full_detect, 2, 1);
}
}
#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
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;
CHARGE_mA_SEL(charge_mA);
}
}
const u16 full_table[CHARGE_FULL_V_MAX] = {
3962, 4002, 4044, 4086, 4130, 4175, 4222, 4270,
4308, 4349, 4391, 4434, 4472, 4517, 4564, 4611
};
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_4222;
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_4222) {
offset = __this->data->charge_full_V - CHARGE_FULL_V_4222;
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_4222 - __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); */
CHARGE_mA_SEL(__this->data->charge_trickle_mA);
}
static 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;
/*先关闭充电使能,后面检测到充电插入再开启*/
power_wakeup_disable_with_port(IO_CHGFL_DET);
CHGBG_EN(0);
CHARGE_EN(0);
/*LDO5V的100K下拉电阻使能*/
L5V_RES_DET_S_SEL(__this->data->ldo5v_pulldown_lvl);
L5V_LOAD_EN(__this->data->ldo5v_pulldown_en);
charge_config();
if (check_charge_state()) {
if (__this->ldo5v_timer == 0) {
__this->ldo5v_timer = usr_timer_add(0, ldo5v_detect, 2, 1);
}
} else {
charge_flag = BIT_LDO5V_OFF;
}
charge_set_callback(charge_wakeup_isr, sub_wakeup_isr);
/* usr_timer_add(0, test_func, 10); */
//AB版可能出现误判满
u8 chip_id = get_chip_version() & 0x0f;
if ((chip_id >= 0x0C) || (chip_id == 0x01)) {
adc_add_sample_ch(AD_CH_LDO5V);
}
__this->init_ok = 1;
return 0;
}
void charge_module_stop(void)
{
if (!__this->init_ok) {
return;
}
charge_set_callback(NULL, NULL);
charge_close();
power_wakeup_disable_with_port(IO_LDOIN_DET);
power_wakeup_disable_with_port(IO_VBTCH_DET);
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);
}
charge_set_callback(charge_wakeup_isr, sub_wakeup_isr);
power_wakeup_enable_with_port(IO_LDOIN_DET);
power_wakeup_enable_with_port(IO_VBTCH_DET);
}
const struct device_operations charge_dev_ops = {
.init = charge_init,
};