/* 概念:
ZCV:开路电压
OCV: 开路电压
VC:闭路电压
CAR:库伦计
DOD: 放电深度,100-DOD 即电容容量
Cmax/Qmax: 电池容量
相关文件关系:
Battery_common.c (s:\i841\mediatek\kernel\drivers\power) // 充电逻辑文件
Charging_hw_pmic.c (s:\i841\mediatek\platform\mt6582\kernel\drivers\power) // 具体的充电芯片,相关电池参数检测
Linear_charging.c (s:\i841\mediatek\kernel\drivers\power) // 充电状态控制,内部 PMIC
Switch_charging.c (s:\i841\mediatek\kernel\drivers\power) // 充电状态控制,外部 Charge IC
硬件原理图:
NTC 检测温度电路原理如下:
Vu Ru:上拉电阻值
--- Rd: 下拉电阻值
| Rntc: NTC 温度电阻 阻值
||| Ru Vu: 上拉电压值
| Gnd: 地
---------- -----Vntc Vntc: NTC 电压
| |
Rntc ||| ||| Rd
| |
---------- Rntc = (Ru*Rd*Vntc) / (Vru * Rd - Vntc * Ru)
|
-----
---
-
Gnd
【充电初始化流程】:
module_init(battery_init)
battery_init(void)
// 注册平台设备
platform_device_register(&battery_device)
// 注册平台驱动
platform_driver_register(&battery_driver)
// 二者匹配调用 probe 函数
battery_probe()
/////////////////////////////////////////////////////////
// 注册字符设备
alloc_chrdev_region(&adc_cali_devno, 0, 1, ADC_CALI_DEVNAME)
cdev_add(adc_cali_cdev, adc_cali_devno, 1)
/////////////////////////////////////////////////////////////
// 创建相关 sys 系统节点
class_create(THIS_MODULE, ADC_CALI_DEVNAME)
device_create(adc_cali_class, NULL, adc_cali_devno, NULL, ADC_CALI_DEVNAME)
////////////////////////////////////////////////////////////
// 与特定硬件的入口操作函数关联
get_charging_control()
// 用来绑定内部 PMIC 或者外部充电芯片的操作函数入口
battery_charging_control = chr_control_interface;
相关充电芯片驱动实现的函数有:
charging_hw_init
charging_dump_register
charging_enable
charging_set_cv_voltage
charging_get_current
charging_set_current
charging_set_input_current
charging_get_charging_status
charging_reset_watch_dog_timer
charging_set_hv_threshold
charging_get_hv_status
charging_get_battery_status
charging_get_charger_det_status
charging_get_charger_type
charging_get_is_pcm_timer_trigger
charging_set_platform_reset
charging_get_platfrom_boot_mode
charging_set_power_off
//////////////////////////////////////////////////////////////
// 获得启动模式
battery_charging_control(CHARGING_CMD_GET_PLATFORM_BOOT_MODE, &g_platform_boot_mode)
// 对应 pmic 的 charging_get_platfrom_boot_mode()
charging_get_platfrom_boot_mode()
get_boot_mode()
/////////////////////////////////////////////////////////////////////////////
// Integrate with Android Battery Service
// 一般移动设备的供电可来自外部即 AC 与 usb,内部电池供电 battery,
// 所以需通过 power_supply_register() 函数在 /sys/class/power_supply 下分别
// 注册 ac usb battery,注册完成可发现在设备目录 /sys/class/power_supply 下分别
// 出现 ac usb battery 三个文件夹
power_supply_register(&(dev->dev), &ac_main.psy)
power_supply_register(&(dev->dev), &usb_main.psy)
power_supply_register(&(dev->dev), &wireless_main.psy)
power_supply_register(&(dev->dev), &battery_main.psy)
/////////////////////////////////////////////////////////////////////////////
// 初始化电池状态结构体
BMT_status.bat_exist = KAL_TRUE; // phone must have battery
BMT_status.charger_exist = KAL_FALSE; // for default, no charger
BMT_status.bat_vol = 0;
BMT_status.ICharging = 0;
BMT_status.temperature = 0;
BMT_status.charger_vol = 0;
BMT_status.total_charging_time = 0;
BMT_status.PRE_charging_time = 0;
BMT_status.CC_charging_time = 0;
BMT_status.TOPOFF_charging_time = 0;
BMT_status.POSTFULL_charging_time = 0;
BMT_status.SOC = 0;
BMT_status.UI_SOC = 0;
BMT_status.bat_charging_state = CHR_PRE;
BMT_status.bat_in_recharging_state = KAL_FALSE;
BMT_status.bat_full= KAL_FALSE;
BMT_status.nPercent_ZCV = 0;
BMT_status.nPrecent_UI_SOC_check_point= battery_meter_get_battery_nPercent_UI_SOC();
///////////////////////////////////////////////////////////////////////////
//创建一个定时器,用于定时唤醒下面的内核线程更新电量
battery_kthread_hrtimer_init();
ktime = ktime_set(1, 0); // 3s, 10* 1000 ms
// 高分辨率kernel定时器初始化
hrtimer_init(&battery_kthread_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
battery_kthread_timer.function = battery_kthread_hrtimer_func;
// 定时器处理函数
battery_kthread_hrtimer_func()
bat_thread_wakeup();
// 唤醒下面的 bat_thread_kthread() 线程
wake_up(&bat_thread_wq);
// 重置定时器
hrtimer_start(&battery_kthread_timer, ktime, HRTIMER_MODE_REL)
//////////////////////////////////////////////////////////////////////////////
// 【核心充电线程】
kthread_run(bat_thread_kthread, NULL, "bat_thread_kthread");
// 设置定时器超时时间
ktime_t ktime = ktime_set(3, 0); // 10s, 10* 1000 ms
while (1) {
// 这个是核心算法
BAT_thread();
///////////////////////////////////////////////////////////////////////////////////////////
// 0. 第一次执行时运行,获得开机显示电量,初始化电池算法 oam 参数
// 开机时就会运行,只会运行一次,对电池算法 oam 方案进行初始化, 并获得开机显示电量百分比
if(battery_meter_initilized == KAL_FALSE)
{
// 进行的一系列的电池参数与温度对应关系的表格的初始化,并根据电池当前电压,hw ocv 取一个较合适值,
// 取合适值对应容量,再与 RTC 保存容量比较,选择一个合适量,为开机电池容量,最后初始化 oam 算法参数
battery_meter_initial()
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// 1. 判断是插入的是否充电器还是电脑 USB,看能不能进行充电
// 如果连接的 USB 线为 USB 充电线,或者电脑 USB 线,则打开 USB,
// 这里会通过 BC1.1 来判断是电脑 USB 还是 USB 充电器,来决定充电电流
// 否则连接的不是充电线或者 USB 作为一个从设备使用,要断开 USB?
mt_battery_charger_detect_check();
///////////////////////////////////////////////////////////////////////////////////////////////////////
// 2. 通过具体的充电芯片来获得电池信息,充电信息, 获得电池电量百分比
// 通过 oam 算法,获得电量百分比
mt_battery_GetBatteryData();
////////////////////////////////////////////////////////////////////////////////////////////////////////
// 3. 电池温度保护
// 电池温度检查,如果温度超过 60 度,关机重启
mt_battery_thermal_check();
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// 4. 电池状态检查
// 对电池状态进行检查,如果有问题,则会调用 printk() 进行打印
mt_battery_notify_check();
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 5. 调用具本的硬件相关函数进行充电,充电时会进行 CC/CV 之类的状态机切换就是在这里进行的
// 如果存在充电线,则调用具体充电芯片相关的函数进行充电
if( BMT_status.charger_exist == KAL_TRUE )
{
// 检查电池状态,设置到 BMT_status.bat_charging_state 中
mt_battery_CheckBatteryStatus();
// 充电策略,这里有两个文件: switch_charging.c 和 linear_charging.c
// 他们的关系是,如果定义了任一外部充电 IC,则选择 switch_charging.c 的函数,否则就是 linear_charging.c 的函数
// 这里就是调用具体的芯片的充电相关函数进行充电
mt_battery_charging_algorithm();
void mt_battery_charging_algorithm()
{
switch(BMT_status.bat_charging_state)
{
case CHR_PRE :
BAT_PreChargeModeAction();
break;
case CHR_CC :
BAT_ConstantCurrentModeAction();
/////////////////////////////////////////////////////////////
// MTK 充电是充 9s 停 1s
// Charging 9s and discharging 1s : start
battery_charging_control(CHARGING_CMD_ENABLE,&charging_enable);
break;
case CHR_TOP_OFF :
BAT_TopOffModeAction();
break;
case CHR_BATFULL:
BAT_BatteryFullAction();
break;
case CHR_HOLD:
BAT_BatteryHoldAction();
break;
case CHR_ERROR:
BAT_BatteryStatusFailAction();
break;
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 6. 更新电池显示状态
// 更新设置节点的内容:
// /sys/class/power_supply/下的文件夹
// wireless_main
// battery_main
// ac_main
// usb_main
mt_battery_update_status();
// 睡眠等待唤醒
wait_event(bat_thread_wq, (bat_thread_timeout == KAL_TRUE));
// 每 10s 启动一次
hrtimer_start(&battery_kthread_timer, ktime, HRTIMER_MODE_REL);
ktime = ktime_set(BAT_TASK_PERIOD, 0); // 10s, 10* 1000 ms
}
/////////////////////////////////////////////////////////////////////////
// 电池过充保护相关检测与初始化,他 2s 检测一次
charger_hv_detect_sw_workaround_init();
charger_hv_detect_thread = kthread_run(charger_hv_detect_sw_thread_handler, 0, "mtk charger_hv_detect_sw_workaround");
// 这个函数是周期来检查电池电压是否超出限制,即过充保护,超出了就不能充电了
charger_hv_detect_sw_thread_handler()
do{
ktime = ktime_set(0, BAT_MS_TO_NS(2000));
if(chargin_hw_init_done)
// 高压检测,应该是电池超过这个电压时,就不可以充电了
battery_charging_control(CHARGING_CMD_SET_HV_THRESHOLD,&hv_voltage);
// 对应 PMIC charging_set_hv_threshold()
wait_event_interruptible(charger_hv_detect_waiter, (charger_hv_detect_flag == KAL_TRUE));
// 如果检测到充电器,则检测下电池是否存在 检测电池是否存在,通过读取 PMIC 的 CHR_CON7
if ((upmu_is_chr_det() == KAL_TRUE))
{
// 检测电池是否存在
check_battery_exist();
}
hrtimer_start(&charger_hv_detect_timer, ktime, HRTIMER_MODE_REL);
}while (!kthread_should_stop());
//////////////////////////////////////////////////////////////
// battery notofy UI
platform_device_register(&MT_batteryNotify_device)
platform_driver_register(&mt_batteryNotify_driver)
mt_batteryNotify_probe()
ret_device_file = device_create_file(&(dev->dev), &dev_attr_BatteryNotify);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_BN_TestMode);
battery_dir = proc_mkdir("mtk_battery_cmd", NULL);
电池测量模块初始化:
module_init(battery_meter_init);
battery_meter_init(void)
platform_device_register(&battery_meter_device);
platform_driver_register(&battery_meter_driver);
// 调用对应的 probe 函数
battery_meter_probe()
// 测量模块接口设置【核心在于 bm_ctrl_cmd 函数】
battery_meter_ctrl = bm_ctrl_cmd;
*/
module_init(battery_init);
static int __init battery_init(void)
{
int ret;
ret = platform_device_register(&battery_device);
struct platform_device battery_device = {
.name = "battery",
.id = -1,
};
if (ret) {
battery_xlog_printk(BAT_LOG_CRTI, "****[battery_driver] Unable to device register(%d)\n", ret);
return ret;
}
ret = platform_driver_register(&battery_driver);
static struct platform_driver battery_driver = {
.probe = battery_probe,
.remove = battery_remove,
.shutdown = battery_shutdown,
.driver = {
.name = "battery",
.pm = &battery_pm_ops
},
};
if (ret) {
battery_xlog_printk(BAT_LOG_CRTI, "****[battery_driver] Unable to register driver (%d)\n", ret);
return ret;
}
// battery notofy UI
ret = platform_device_register(&MT_batteryNotify_device);
struct platform_device MT_batteryNotify_device = {
.name = "mt-battery",
.id = -1,
};
if (ret) {
battery_xlog_printk(BAT_LOG_CRTI, "****[mt_batteryNotify] Unable to device register(%d)\n", ret);
return ret;
}
ret = platform_driver_register(&mt_batteryNotify_driver);
static struct platform_driver mt_batteryNotify_driver = {
.probe = mt_batteryNotify_probe,
.driver = {
.name = "mt-battery",
},
};
if (ret) {
battery_xlog_printk(BAT_LOG_CRTI, "****[mt_batteryNotify] Unable to register driver (%d)\n", ret);
return ret;
}
battery_xlog_printk(BAT_LOG_CRTI, "****[battery_driver] Initialization : DONE !!\n");
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 第一个调用的 probe,充电相关函数初始化
/* 相关文件关系:
Battery_common.c (s:\i841\mediatek\kernel\drivers\power) // 充电逻辑文件
Charging_hw_pmic.c (s:\i841\mediatek\platform\mt6582\kernel\drivers\power) // 具体的充电芯片,相关电池参数检测
Linear_charging.c (s:\i841\mediatek\kernel\drivers\power) // 充电状态控制,内部 PMIC
Switch_charging.c (s:\i841\mediatek\kernel\drivers\power) // 充电状态控制,外部 Charge IC
*/
static int battery_probe//(struct platform_device *dev)
{
struct class_device *class_dev = NULL;
int ret=0;
battery_xlog_printk(BAT_LOG_CRTI, "******** battery driver probe!! ********\n" );
/* Integrate with NVRAM */
ret = alloc_chrdev_region(&adc_cali_devno, 0, 1, ADC_CALI_DEVNAME); // #define ADC_CALI_DEVNAME "MT_pmic_adc_cali"
if (ret)
battery_xlog_printk(BAT_LOG_CRTI, "Error: Can't Get Major number for adc_cali \n");
adc_cali_cdev = cdev_alloc();
adc_cali_cdev->owner = THIS_MODULE;
adc_cali_cdev->ops = &adc_cali_fops;
static struct file_operations adc_cali_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = adc_cali_ioctl,
.open = adc_cali_open,
.release = adc_cali_release,
};
ret = cdev_add(adc_cali_cdev, adc_cali_devno, 1);
if(ret)
battery_xlog_printk(BAT_LOG_CRTI, "adc_cali Error: cdev_add\n");
adc_cali_major = MAJOR(adc_cali_devno);
adc_cali_class = class_create(THIS_MODULE, ADC_CALI_DEVNAME);
class_dev = (struct class_device *)device_create(adc_cali_class,
NULL,
adc_cali_devno,
NULL,
ADC_CALI_DEVNAME);
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] adc_cali prepare : done !!\n ");
/* 与特定硬件的操作函数关联 */
get_charging_control();
static void get_charging_control(void)
{
/* 这是一个函数指针,指向了硬件驱动对应的接口函数,这里指向的是 pmic 为例,后面调用 battery_charging_control(xxx)
即是调用 chr_control_interface(xxx)
typedef kal_int32 (*CHARGING_CONTROL)(CHARGING_CTRL_CMD cmd, void *data);*/
battery_charging_control = chr_control_interface;
// This function is called to set the charger hw
kal_int32 chr_control_interface//(CHARGING_CTRL_CMD cmd, void *data)
{
kal_int32 status;
if(cmd < CHARGING_CMD_NUMBER)
status = charging_func[cmd](data);
/* 这是在特定充电芯片中实现的,如 pmic 的 */
// static kal_uint32 (*charging_func[CHARGING_CMD_NUMBER])(void *data)=
// {
// charging_hw_init
// ,charging_dump_register
// ,charging_enable
// ,charging_set_cv_voltage
// ,charging_get_current
// ,charging_set_current
// ,charging_set_input_current // not support, empty function
// ,charging_get_charging_status // not support, empty function
// ,charging_reset_watch_dog_timer
// ,charging_set_hv_threshold
// ,charging_get_hv_status
// ,charging_get_battery_status
// ,charging_get_charger_det_status
// ,charging_get_charger_type
// ,charging_get_is_pcm_timer_trigger
// ,charging_set_platform_reset
// ,charging_get_platfrom_boot_mode
// ,charging_set_power_off
// };
else
return STATUS_UNSUPPORTED;
return status;
}
}
/* 获得启动模式 */
battery_charging_control(CHARGING_CMD_GET_PLATFORM_BOOT_MODE, &g_platform_boot_mode);
// 对应 pmic 的 charging_get_platfrom_boot_mode()
static kal_uint32 charging_get_platfrom_boot_mode(void *data)
{
kal_uint32 status = STATUS_OK;
*(kal_uint32*)(data) = get_boot_mode();
BOOTMODE get_boot_mode(void)
{
return g_boot_mode;
}
battery_xlog_printk(BAT_LOG_CRTI, "get_boot_mode=%d\n", get_boot_mode());
return status;
}
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] g_platform_boot_mode = %d\n ", g_platform_boot_mode);
wake_lock_init(&battery_suspend_lock, WAKE_LOCK_SUSPEND, "battery suspend wakelock");
/* Integrate with Android Battery Service
一般移动设备的供电可来自外部即 AC 与 usb,内部电池供电 battery,所以需通过 power_supply_register 函数在
/sys/class/power_supply 下分别注册 ac usb battery,注册完成可发现在设备目录 /sys/class/power_supply 下分别
出现 ac usb battery 三个文件夹 */
ret = power_supply_register(&(dev->dev), &ac_main.psy);
/* 相关的核心结构体
struct power_supply {
struct listnode list;
char name[256];
char type[32];
bool online;
bool valid;
char cap_path[PATH_MAX];
};*/
if (ret)
{
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register AC Fail !!\n");
return ret;
}
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register AC Success !!\n");
ret = power_supply_register(&(dev->dev), &usb_main.psy);
if (ret)
{
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register USB Fail !!\n");
return ret;
}
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register USB Success !!\n");
ret = power_supply_register(&(dev->dev), &wireless_main.psy);
if (ret)
{
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register WIRELESS Fail !!\n");
return ret;
}
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register WIRELESS Success !!\n");
ret = power_supply_register(&(dev->dev), &battery_main.psy);
if (ret)
{
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register Battery Fail !!\n");
return ret;
}
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register Battery Success !!\n");
#if !defined(CONFIG_POWER_EXT)
/* For EM */
{
int ret_device_file=0;
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Charger_Voltage);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_0_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_1_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_2_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_3_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_4_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_5_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_6_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_7_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_8_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_9_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_10_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_11_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_12_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_13_Slope);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_0_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_1_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_2_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_3_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_4_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_5_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_6_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_7_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_8_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_9_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_10_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_11_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_12_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_13_Offset);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_Is_Calibration);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_Power_On_Voltage);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_Power_Off_Voltage);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_Charger_TopOff_Value);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_Battery_CurrentConsumption);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_SW_CoulombCounter);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_Charging_CallState);
}
//battery_meter_initial(); //move to mt_battery_GetBatteryData() to decrease booting time
/* Initialization BMT Struct
初始化电池状态结构体 */
BMT_status.bat_exist = KAL_TRUE; /* phone must have battery */
BMT_status.charger_exist = KAL_FALSE; /* for default, no charger */
BMT_status.bat_vol = 0;
BMT_status.ICharging = 0;
BMT_status.temperature = 0;
BMT_status.charger_vol = 0;
BMT_status.total_charging_time = 0;
BMT_status.PRE_charging_time = 0;
BMT_status.CC_charging_time = 0;
BMT_status.TOPOFF_charging_time = 0;
BMT_status.POSTFULL_charging_time = 0;
BMT_status.SOC = 0;
BMT_status.UI_SOC = 0;
BMT_status.bat_charging_state = CHR_PRE;
BMT_status.bat_in_recharging_state = KAL_FALSE;
BMT_status.bat_full= KAL_FALSE;
BMT_status.nPercent_ZCV = 0;
BMT_status.nPrecent_UI_SOC_check_point= battery_meter_get_battery_nPercent_UI_SOC();
//battery kernel thread for 10s check and charger in/out event
/* Replace GPT timer by hrtime
创建一个定时器,用于定时唤醒下面的内核线程更新电量 */
battery_kthread_hrtimer_init();
void battery_kthread_hrtimer_init(void)
{
ktime_t ktime;
ktime = ktime_set(1, 0); // 3s, 10* 1000 ms
// 高分辨率kernel定时器初始化
hrtimer_init(&battery_kthread_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
battery_kthread_timer.function = battery_kthread_hrtimer_func;
enum hrtimer_restart battery_kthread_hrtimer_func(struct hrtimer *timer)
{
bat_thread_wakeup();
void bat_thread_wakeup(void)
{
battery_xlog_printk(BAT_LOG_CRTI, "******** battery : bat_thread_wakeup ********\n" );
bat_thread_timeout = KAL_TRUE;
bat_meter_timeout = KAL_TRUE;
// 唤醒下面的 bat_thread_kthread() 线程
wake_up(&bat_thread_wq);
}
return HRTIMER_NORESTART;
}
// 重置定时器
hrtimer_start(&battery_kthread_timer, ktime, HRTIMER_MODE_REL);
battery_xlog_printk(BAT_LOG_CRTI, "battery_kthread_hrtimer_init : done\n" );
}
kthread_run(bat_thread_kthread, NULL, "bat_thread_kthread");
///////////////////////////////////////////////////////////////////////////////////////////
//// Internal API
int bat_thread_kthread(void *x)
{
// 设置定时器超时时间
ktime_t ktime = ktime_set(3, 0); // 10s, 10* 1000 ms
/* Run on a process content */
while (1) {
mutex_lock(&bat_mutex);
if((chargin_hw_init_done == KAL_TRUE) && (battery_suspended == KAL_FALSE))
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
/* 这个是核心算法 */
BAT_thread();
void BAT_thread(void)
{
static kal_bool battery_meter_initilized = KAL_FALSE;
_g_bat_sleep_total_time = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
// 0. 第一次执行时运行,获得开机显示电量,初始化电池算法 oam 参数
/* 开机时就会运行,只会运行一次,对电池算法 oam 方案进行初始化, 并获得开机显示电量百分比 */
if(battery_meter_initilized == KAL_FALSE)
{
/* 进行的一系列的电池参数与温度对应关系的表格的初始化,并根据电池当前电压,hw ocv 取一个较合适值,
取合适值对应容量,再与 rtc 容量比较,选择一个合适量,为开机电池容量,最后初始化 oam 算法参数 */
battery_meter_initial(); //move from battery_probe() to decrease booting time
kal_int32 battery_meter_initial(void)
{
#if defined(CONFIG_POWER_EXT)
return 0;
#else
/* 多电池支持 */
#ifdef MTK_MULTI_BAT_PROFILE_SUPPORT
fgauge_get_profile_id();
#endif
/* 针对 AUXADC SW_FG HW_FG 三种不同的电池算法方案,分别初始化,82平台使用的 SW_FG */
#if defined(SOC_BY_AUXADC)
g_auxadc_solution = 1;
table_init();
bm_print(BM_LOG_CRTI, "[battery_meter_initial] SOC_BY_AUXADC done\n");
#endif
#if defined(SOC_BY_HW_FG)
fgauge_initialization();
fgauge_algo_run_init();
bm_print(BM_LOG_CRTI, "[battery_meter_initial] SOC_BY_HW_FG done\n");
#endif
#if defined(SOC_BY_SW_FG)
g_auxadc_solution = 1;
/* 温度-电池容量,温度-电池内阻,温度-NTC阻值表格等初始化,并获得了当前电池温度及所用掉的容量值 */
table_init();
void table_init(void)
{
BATTERY_PROFILE_STRUC_P profile_p;
R_PROFILE_STRUC_P profile_p_r_table;
/* 获得当前电池温度*/
int temperature = force_get_tbat();
/* 通过获得当前 NTC 电压,查表并进行线性插值法,得到当前的温度值 */
int force_get_tbat(void)
{
#if defined(CONFIG_POWER_EXT) || defined(FIXED_TBAT_25)
bm_print(BM_LOG_CRTI, "[force_get_tbat] fixed TBAT=25 t\n");
return 25;
#else
int bat_temperature_volt=0;
int bat_temperature_val=0;
int fg_r_value=0;
kal_int32 fg_current_temp=0;
kal_bool fg_current_state=KAL_FALSE;
int bat_temperature_volt_temp=0;
int ret=0;
/* Get V_BAT_Temperature */
bat_temperature_volt = 2;
/* 对应 PMIC 的硬件接口函数,这个对应的函数是在下面 battery_meter_init 模块中设置的
获得电池 NTC 对应的电压 */
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_TEMP, &bat_temperature_volt);
static kal_int32 read_adc_v_bat_temp(void *data)
{
#if defined(CONFIG_POWER_EXT)
*(kal_int32*)(data) = 0;
#else
#if defined(MTK_PCB_TBAT_FEATURE)
int ret = 0, data[4], i, ret_value = 0, ret_temp = 0;
int Channel=1;
if( IMM_IsAdcInitReady() == 0 )
{
bm_print(BM_LOG_CRTI, "[get_tbat_volt] AUXADC is not ready");
return 0;
}
i = times;
while (i--)
{
ret_value = IMM_GetOneChannelValue(Channel, data, &ret_temp);
ret += ret_temp;
bm_print(BM_LOG_FULL, "[get_tbat_volt] ret_temp=%d\n",ret_temp);
}
ret = ret*1500/4096 ;
ret = ret/times;
bm_print(BM_LOG_CRTI, "[get_tbat_volt] Battery output mV = %d\n",ret);
*(kal_int32*)(data) = ret;
#else
bm_print(BM_LOG_FULL, "[read_adc_v_charger] return PMIC_IMM_GetOneChannelValue(4,times,1);\n");
/* 读 PMIC 对应的 ADC 的值,并将其转化为对应的电压值,这里即读取 BATON 引脚电压 */
*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(VBATTEMP_CHANNEL_NUMBER,*(kal_int32*)(data),1);
//==============================================================================
// PMIC-AUXADC
//==============================================================================
int PMIC_IMM_GetOneChannelValue(int dwChannel, int deCount, int trimd)
{
kal_int32 ret_data;
kal_int32 count=0;
kal_int32 u4Sample_times = 0;
kal_int32 u4channel=0;
kal_int32 adc_result_temp=0;
kal_int32 r_val_temp=0;
kal_int32 adc_result=0;
kal_int32 ret=0;
kal_int32 adc_reg_val=0;
/*
0 : BATON2 **
1 : CH6
2 : THR SENSE2 **
3 : THR SENSE1
4 : VCDT
5 : BATON1 // 其实对应的就是硬件 PMIC ADC3
6 : ISENSE
7 : BATSNS
8 : ACCDET
9-16 : audio
*/
//do not suppport BATON2 and THR SENSE2 for sw workaround
if (dwChannel==0 || dwChannel==2)
return 0;
wake_lock(&pmicAuxadc_irq_lock);
/* 采样通道号数,读取对应的 PMIC 寄存器的 ADC 的值 */
do
{
mutex_lock(&pmic_adc_mutex);
/* 硬件上,ADC 通过一个 mux 数据选择器对各路模拟信号进行切换,这里检查哪里通知有数据了 */
PMIC_IMM_PollingAuxadcChannel();
void PMIC_IMM_PollingAuxadcChannel(void)
{
kal_uint32 ret=0;
//xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[PMIC_IMM_PollingAuxadcChannel] before:%d ",upmu_get_rg_adc_deci_gdly());
if (upmu_get_rg_adc_deci_gdly()==1)
{
while(upmu_get_rg_adc_deci_gdly()==1)
{
unsigned long flags;
spin_lock_irqsave(&pmic_adc_lock, flags);
if (pmic_is_auxadc_busy()==0)
{
//upmu_set_rg_adc_deci_gdly(0);
ret=pmic_config_interface_nolock(AUXADC_CON19,0,PMIC_RG_ADC_DECI_GDLY_MASK,PMIC_RG_ADC_DECI_GDLY_SHIFT);
}
spin_unlock_irqrestore(&pmic_adc_lock, flags);
}
}
//xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[PMIC_IMM_PollingAuxadcChannel] after:%d ",upmu_get_rg_adc_deci_gdly());
}
/* adc */
if (dwChannel<9)
{
upmu_set_rg_vbuf_en(1);
//set 0
ret=pmic_read_interface(AUXADC_CON22,&adc_reg_val,PMIC_RG_AP_RQST_LIST_MASK,PMIC_RG_AP_RQST_LIST_SHIFT);
adc_reg_val = adc_reg_val & (~(1<=9 && dwChannel<=16)
{
ret=pmic_read_interface(AUXADC_CON23,&adc_reg_val,PMIC_RG_AP_RQST_LIST_RSV_MASK,PMIC_RG_AP_RQST_LIST_RSV_SHIFT);
adc_reg_val = adc_reg_val & (~(1<<(dwChannel-9)));
ret=pmic_config_interface(AUXADC_CON23,adc_reg_val,PMIC_RG_AP_RQST_LIST_RSV_MASK,PMIC_RG_AP_RQST_LIST_RSV_SHIFT);
//set 1
ret=pmic_read_interface(AUXADC_CON23,&adc_reg_val,PMIC_RG_AP_RQST_LIST_RSV_MASK,PMIC_RG_AP_RQST_LIST_RSV_SHIFT);
adc_reg_val = adc_reg_val | (1<<(dwChannel-9));
ret=pmic_config_interface(AUXADC_CON23,adc_reg_val,PMIC_RG_AP_RQST_LIST_RSV_MASK,PMIC_RG_AP_RQST_LIST_RSV_SHIFT);
}
mutex_unlock(&pmic_adc_mutex);
//Duo to HW limitation
if(dwChannel!=8)
msleep(1);
count=0;
ret_data=0;
/* 根据不同通道选择,来读取对应的 PMIC 寄存器的值 */
switch(dwChannel){
case 0:
while( upmu_get_rg_adc_rdy_baton2() != 1 )
{
msleep(1);
if( (count++) > count_time_out)
{
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);
break;
}
}
ret_data = upmu_get_rg_adc_out_baton2();
break;
case 1:
while( upmu_get_rg_adc_rdy_ch6() != 1 )
{
msleep(1);
if( (count++) > count_time_out)
{
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);
break;
}
}
ret_data = upmu_get_rg_adc_out_ch6();
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[upmu_get_rg_adc_out_ch6] 0x%x\n", ret_data);
break;
case 2:
while( upmu_get_rg_adc_rdy_thr_sense2() != 1 )
{
msleep(1);
if( (count++) > count_time_out)
{
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);
break;
}
}
ret_data = upmu_get_rg_adc_out_thr_sense2();
break;
case 3:
while( upmu_get_rg_adc_rdy_thr_sense1() != 1 )
{
msleep(1);
if( (count++) > count_time_out)
{
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);
break;
}
}
ret_data = upmu_get_rg_adc_out_thr_sense1();
break;
case 4:
while( upmu_get_rg_adc_rdy_vcdt() != 1 )
{
msleep(1);
if( (count++) > count_time_out)
{
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);
break;
}
}
ret_data = upmu_get_rg_adc_out_vcdt();
break;
/* 读取 PMIC 的 VBATON 引用的电压值*/
case 5:
while( upmu_get_rg_adc_rdy_baton1() != 1 )
{
msleep(1);
if( (count++) > count_time_out)
{
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);
break;
}
}
ret_data = upmu_get_rg_adc_out_baton1();
kal_uint32 upmu_get_rg_adc_rdy_baton1(void)
{
kal_uint32 ret=0;
kal_uint32 val=0;
pmic_lock();
ret=pmic_read_interface( (kal_uint32)(AUXADC_ADC3),
(&val),
(kal_uint32)(PMIC_RG_ADC_RDY_BATON1_MASK),
(kal_uint32)(PMIC_RG_ADC_RDY_BATON1_SHIFT)
);
pmic_unlock();
return val;
}
break;
case 6:
while( upmu_get_rg_adc_rdy_isense() != 1 )
{
msleep(1);
if( (count++) > count_time_out)
{
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);
break;
}
}
ret_data = upmu_get_rg_adc_out_isense();
break;
case 7:
while( upmu_get_rg_adc_rdy_batsns() != 1 )
{
msleep(1);
if( (count++) > count_time_out)
{
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);
break;
}
}
ret_data = upmu_get_rg_adc_out_batsns();
break;
case 8:
while( upmu_get_rg_adc_rdy_ch5() != 1 );
ret_data = upmu_get_rg_adc_out_ch5();
break;
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
while( upmu_get_rg_adc_rdy_int() != 1 )
{
msleep(1);
if( (count++) > count_time_out)
{
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);
break;
}
}
ret_data = upmu_get_rg_adc_out_int();
break;
default:
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[AUXADC] Invalid channel value(%d,%d)\n", dwChannel, trimd);
wake_unlock(&pmicAuxadc_irq_lock);
return -1;
break;
}
u4channel += ret_data;
u4Sample_times++;
if (Enable_BATDRV_LOG == 2)
{
//debug
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[AUXADC] u4channel[%d]=%d.\n",
dwChannel, ret_data);
}
}while (u4Sample_times < deCount);
/* Value averaging
adc 采样值取平均值 */
adc_result_temp = u4channel / deCount;
/* ADC 分辨率: 即 ADC 数据每加 1 所对应的电压变化,计算公式为 【测量电压范围/(2^AD位数-1)】*/
switch(dwChannel){
case 0:
r_val_temp = 1;
adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
break;
case 1:
r_val_temp = 1;
adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
break;
case 2:
r_val_temp = 1;
adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
break;
case 3:
r_val_temp = 1;
adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
break;
case 4:
r_val_temp = 1;
adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
break;
/* ADC 分辨率: 即 ADC 数据每加 1 所对应的电压变化,计算公式为
0-正数表示: 【测量电压范围/(2^AD位数)】
负数-正数表示: 【测量电压范围/(2^AD位数-1)】
获得 BATON 电压值
#define VOLTAGE_FULL_RANGE 1800 // 测量的电压范围
#define ADC_PRECISE 32768 // 15 bits 2^15 = 32768
*/
case 5:
r_val_temp = 1;
/* 这里得到将数字化数值转换为对应的模拟电压值 */
adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
break;
case 6:
r_val_temp = 4; // 电阻分压比,因为测量 adc 量程不够,要采用电阻分压
adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
break;
case 7:
r_val_temp = 4; // 电阻分压比,因为测量 adc 量程不够,要采用电阻分压
adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
break;
case 8:
r_val_temp = 1;
adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
break;
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
adc_result = adc_result_temp;
break;
default:
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[AUXADC] Invalid channel value(%d,%d)\n", dwChannel, trimd);
wake_unlock(&pmicAuxadc_irq_lock);
return -1;
break;
}
if (Enable_BATDRV_LOG == 2)
{
//debug
xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[AUXADC] adc_result_temp=%d, adc_result=%d, r_val_temp=%d.\n",
adc_result_temp, adc_result, r_val_temp);
}
wake_unlock(&pmicAuxadc_irq_lock);
return adc_result;
}
#endif
#endif
return STATUS_OK;
}
/* 如果获得的 PMIC 的 BATON 引脚电压值不为 0 */
if(bat_temperature_volt != 0)
{
#if defined(SOC_BY_HW_FG)
/* 获得配置的放电电阻值的大小 */
fg_r_value = get_r_fg_value();
int get_r_fg_value(void)
{
return (R_FG_VALUE+CUST_R_FG_OFFSET);
}
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_HW_FG_CURRENT, &fg_current_temp);
static kal_int32 fgauge_read_current(void *data)
{
return STATUS_OK;
}
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_HW_FG_CURRENT_SIGN, &fg_current_state);
static kal_int32 fgauge_read_current_sign(void *data)
{
return STATUS_OK;
}
fg_current_temp = fg_current_temp/10;
if(fg_current_state==KAL_TRUE)
{
bat_temperature_volt_temp = bat_temperature_volt;
bat_temperature_volt = bat_temperature_volt - ((fg_current_temp*fg_r_value)/1000);
}
else
{
bat_temperature_volt_temp = bat_temperature_volt;
// 这里对 NTC 电压进行补偿: NTC 实际温度对应电压 = 测量电压 + 负载电压*负载电阻 // 【这里的 1000 是将小数整数化】
bat_temperature_volt = bat_temperature_volt + ((fg_current_temp*fg_r_value)/1000);
}
#endif
///////////////////////////////////////////////////////////////////////////////////////
/* 将 NTC 的电压通过查表获得对应温度值 */
bat_temperature_val = BattVoltToTemp(bat_temperature_volt);
int BattVoltToTemp(int dwVolt)
{
kal_int64 TRes_temp;
kal_int64 TRes;
int sBaTTMP = -100;
// TRes_temp = ((kal_int64)RBAT_PULL_UP_R*(kal_int64)dwVolt) / (RBAT_PULL_UP_VOLT-dwVolt);
//TRes = (TRes_temp * (kal_int64)RBAT_PULL_DOWN_R)/((kal_int64)RBAT_PULL_DOWN_R - TRes_temp);
/* 电路原理如下:
Vu Ru:上拉电阻值
--- Rd: 下拉电阻值
| Rntc: NTC 温度电阻 阻值
||| Ru Vu: 上拉电压值
| Gnd: 地
---------- -----Vntc Vntc: NTC 电压
| |
Rntc ||| ||| Rd
| |
---------- Rntc = (Ru*Rd*Vntc) / (Vru * Rd - Vntc * Ru)
|
-----
---
-
Gnd
*/
/* TRes_temp = 上拉电阻值 * NTC电压 */
TRes_temp = (RBAT_PULL_UP_R*(kal_int64)dwVolt);
/* 上拉电阻分压 = 上拉电压 - NTC分压 = RBAT_PULL_UP_VOLT-dwVolt
do_div(): 做除法
(上拉电阻值 * NTC电压)/上拉电阻分压 = (上拉电阻值/上拉电压分压)*NTC电压 */
do_div(TRes_temp, (RBAT_PULL_UP_VOLT-dwVolt));
/* (上拉电阻值/上拉电压分压)*NTC电压*下拉电阻值 */
TRes = (TRes_temp * RBAT_PULL_DOWN_R);
/* (上拉电阻值/上拉电压分压)*NTC电压*下拉电阻值 / (下拉电阻值 - (上拉电阻值/上拉电压分压)*NTC电压)*/
do_div(TRes, abs(RBAT_PULL_DOWN_R - TRes_temp));
/* convert register to temperature
将得到的 NTC 阻值通过表格换算成温度 */
sBaTTMP = BattThermistorConverTemp((int)TRes);
int BattThermistorConverTemp//(int Res)
{
int i=0;
int RES1=0,RES2=0;
int TBatt_Value=-200,TMP1=0,TMP2=0;
/* 如果大于最低温度阻值,返回 -20 */
if(Res>=Batt_Temperature_Table[0].TemperatureR) /* Cust_battery_meter_table.h (s:\i841\mediatek\custom\mt6582\kernel\battery\battery) */
{
TBatt_Value = -20;
}
/* 如果大于最高温度阻值,返回 60 */
else if(Res<=Batt_Temperature_Table[16].TemperatureR)
{
TBatt_Value = 60;
}
/* 其他温度 */
else
{
RES1=Batt_Temperature_Table[0].TemperatureR;
TMP1=Batt_Temperature_Table[0].BatteryTemp;
/* 遍历 NTC 阻值与温度对应表,获得当前阻值所对应的表项 */
for(i=0;i<=16;i++)
{
if(Res>=Batt_Temperature_Table[i].TemperatureR)
{
RES2=Batt_Temperature_Table[i].TemperatureR;
TMP2=Batt_Temperature_Table[i].BatteryTemp;
break;
}
else
{
RES1=Batt_Temperature_Table[i].TemperatureR;
TMP1=Batt_Temperature_Table[i].BatteryTemp;
}
}
/* Liner Interpolation Method: 线性插值法,方法详见 Battery_Charging Introduction for Customer.pdf
核心就是一条直接函数,已知两个点的 (X0,Y0) (X1,Y1),并知道第三个点的 X,就对应的 Y
计算公式为:
Y = [(X-X0)*Y1 + (X1-X)*Y0] / (X1-X0)
*/
TBatt_Value = (((Res-RES2)*TMP1)+((RES1-Res)*TMP2))/(RES1-RES2);
}
return TBatt_Value;
}
return sBaTTMP;
}
}
bm_print(BM_LOG_CRTI, "[force_get_tbat] %d,%d,%d,%d,%d,%d\n",
bat_temperature_volt_temp, bat_temperature_volt, fg_current_state, fg_current_temp, fg_r_value, bat_temperature_val);
return bat_temperature_val;
#endif
}
// Re-constructure r-table profile according to current temperature
/* 获得温度-电池内阻表 */
profile_p_r_table = fgauge_get_profile_r_table(TEMPERATURE_T);
R_PROFILE_STRUC_P fgauge_get_profile_r_table(kal_uint32 temperature)
{
switch (temperature)
{
case TEMPERATURE_T0:
return &r_profile_t0[0];
break;
case TEMPERATURE_T1:
return &r_profile_t1[0];
break;
case TEMPERATURE_T2:
return &r_profile_t2[0];
break;
case TEMPERATURE_T3:
return &r_profile_t3[0];
break;
case TEMPERATURE_T:
return &r_profile_temperature[0];
break;
default:
return NULL;
break;
}
}
if (profile_p_r_table == NULL)
{
bm_print(BM_LOG_CRTI, "[FGADC] fgauge_get_profile_r_table : create table fail !\r\n");
}
/* 获得当前温度使用的 温度-电池内阻 表格*/
fgauge_construct_r_table_profile(temperature, profile_p_r_table);
void fgauge_construct_r_table_profile(kal_int32 temperature, R_PROFILE_STRUC_P temp_profile_p)
{
R_PROFILE_STRUC_P low_profile_p, high_profile_p;
kal_int32 low_temperature, high_temperature;
int i, saddles;
kal_int32 temp_v_1 = 0, temp_v_2 = 0;
kal_int32 temp_r_1 = 0, temp_r_2 = 0;
/* 这里跟 NTC 类似,获得在 温度上下限时的电池内阻表 */
if (temperature <= TEMPERATURE_T1)
{
low_profile_p = fgauge_get_profile_r_table(TEMPERATURE_T0);
high_profile_p = fgauge_get_profile_r_table(TEMPERATURE_T1);
low_temperature = (-10);
high_temperature = TEMPERATURE_T1;
if(temperature < low_temperature)
{
temperature = low_temperature;
}
}
else if (temperature <= TEMPERATURE_T2)
{
low_profile_p = fgauge_get_profile_r_table(TEMPERATURE_T1);
high_profile_p = fgauge_get_profile_r_table(TEMPERATURE_T2);
low_temperature = TEMPERATURE_T1;
high_temperature = TEMPERATURE_T2;
if(temperature < low_temperature)
{
temperature = low_temperature;
}
}
else
{
low_profile_p = fgauge_get_profile_r_table(TEMPERATURE_T2); // 温度下限温度-电池内阻表
high_profile_p = fgauge_get_profile_r_table(TEMPERATURE_T3); // 温度上限温度-电池内阻表
low_temperature = TEMPERATURE_T2; // 温度下限
high_temperature = TEMPERATURE_T3; // 温度上限
/* 防溢出温度区间 */
if(temperature > high_temperature)
{
temperature = high_temperature;
}
}
/* 获得 温度-电池内阻表 的表格项数 */
saddles = fgauge_get_saddles_r_table();
int fgauge_get_saddles_r_table(void)
{
return sizeof(r_profile_t2) / sizeof(R_PROFILE_STRUC);
}
/* Interpolation for V_BAT
插值得到当前温度对应的电池电压 V_BAT */
for (i = 0; i < saddles; i++)
{
/* 如果表格中温度上限电压 > 温度下限时电压 */
if( ((high_profile_p + i)->voltage) > ((low_profile_p + i)->voltage) )
{
/* 温度上限时电压 */
temp_v_1 = (high_profile_p + i)->voltage;
/* 温度下限时电压 */
temp_v_2 = (low_profile_p + i)->voltage;
/* 插值得到此时温度对应的电压值 */
(temp_profile_p + i)->voltage = temp_v_2 +
(
(
(temperature - low_temperature) *
(temp_v_1 - temp_v_2)
) /
(high_temperature - low_temperature)
);
}
/* 如果表格中温度上限电压 <= 温度下限时电压 */
else
{
temp_v_1 = (low_profile_p + i)->voltage;
temp_v_2 = (high_profile_p + i)->voltage;
(temp_profile_p + i)->voltage = temp_v_2 +
(
(
(high_temperature - temperature) *
(temp_v_1 - temp_v_2)
) /
(high_temperature - low_temperature)
);
}
#if 0
//(temp_profile_p + i)->resistance = (high_profile_p + i)->resistance;
(temp_profile_p + i)->voltage = temp_v_2 +
(
(
(temperature - low_temperature) *
(temp_v_1 - temp_v_2)
) /
(high_temperature - low_temperature)
);
#endif
}
/* Interpolation for R_BAT
插值得到当前温度对应的电池内阻 R_BAT */
for (i = 0; i < saddles; i++)
{
if( ((high_profile_p + i)->resistance) > ((low_profile_p + i)->resistance) )
{
/* 温度上限时电池内阻 */
temp_r_1 = (high_profile_p + i)->resistance;
/* 温度下限时电池内阻 */
temp_r_2 = (low_profile_p + i)->resistance;
/* 插值得到此时温度对应的电池内阻值 */
(temp_profile_p + i)->resistance = temp_r_2 +
(
(
(temperature - low_temperature) *
(temp_r_1 - temp_r_2)
) /
(high_temperature - low_temperature)
);
}
else
{
temp_r_1 = (low_profile_p + i)->resistance;
temp_r_2 = (high_profile_p + i)->resistance;
(temp_profile_p + i)->resistance = temp_r_2 +
(
(
(high_temperature - temperature) *
(temp_r_1 - temp_r_2)
) /
(high_temperature - low_temperature)
);
}
#if 0
//(temp_profile_p + i)->voltage = (high_profile_p + i)->voltage;
(temp_profile_p + i)->resistance = temp_r_2 +
(
(
(temperature - low_temperature) *
(temp_r_1 - temp_r_2)
) /
(high_temperature - low_temperature)
);
#endif
}
// Dumpt new r-table profile
for (i = 0; i < saddles ; i++)
{
bm_print(BM_LOG_CRTI, " at %d = <%d,%d>\r\n",
temperature, (temp_profile_p+i)->resistance, (temp_profile_p+i)->voltage);
}
}
// Re-constructure battery profile according to current temperature
/* 获得当前温度对应的 温度-电池容量表格 */
profile_p = fgauge_get_profile(TEMPERATURE_T);
BATTERY_PROFILE_STRUC_P fgauge_get_profile(kal_uint32 temperature)
{
switch (temperature)
{
case TEMPERATURE_T0:
return &battery_profile_t0[0];
break;
case TEMPERATURE_T1:
return &battery_profile_t1[0];
break;
case TEMPERATURE_T2:
return &battery_profile_t2[0];
break;
case TEMPERATURE_T3:
return &battery_profile_t3[0];
break;
case TEMPERATURE_T:
return &battery_profile_temperature[0];
break;
default:
return NULL;
break;
}
}
if (profile_p == NULL)
{
bm_print(BM_LOG_CRTI, "[FGADC] fgauge_get_profile : create table fail !\r\n");
}
/* 由当前温度,通过 温度-电池容量表格,获得对应的 温度-电池容量 数据,
即能得到当前温度所对应的用掉的电池容量 */
fgauge_construct_battery_profile(temperature, profile_p);
void fgauge_construct_battery_profile(kal_int32 temperature, BATTERY_PROFILE_STRUC_P temp_profile_p)
{
BATTERY_PROFILE_STRUC_P low_profile_p, high_profile_p;
kal_int32 low_temperature, high_temperature;
int i, saddles;
kal_int32 temp_v_1 = 0, temp_v_2 = 0;
/* 获得对就温度所在温度上限/下限所对应的 温度-电池容量表格 */
if (temperature <= TEMPERATURE_T1)
{
low_profile_p = fgauge_get_profile(TEMPERATURE_T0);
high_profile_p = fgauge_get_profile(TEMPERATURE_T1);
low_temperature = (-10);
high_temperature = TEMPERATURE_T1;
if(temperature < low_temperature)
{
temperature = low_temperature;
}
}
else if (temperature <= TEMPERATURE_T2)
{
low_profile_p = fgauge_get_profile(TEMPERATURE_T1);
high_profile_p = fgauge_get_profile(TEMPERATURE_T2);
low_temperature = TEMPERATURE_T1;
high_temperature = TEMPERATURE_T2;
if(temperature < low_temperature)
{
temperature = low_temperature;
}
}
else
{
low_profile_p = fgauge_get_profile(TEMPERATURE_T2); // 温度下限所对应的 温度-电池容量表格
high_profile_p = fgauge_get_profile(TEMPERATURE_T3); // 温度上限所对应的 温度-电池容量表格
low_temperature = TEMPERATURE_T2; // 温度下限
high_temperature = TEMPERATURE_T3; // 温度上限
if(temperature > high_temperature)
{
temperature = high_temperature;
}
}
/* 获得 温度-电池容量 表格项数 */
saddles = fgauge_get_saddles();
/* 遍历表格,插值获得当前温度对应的容量 */
for (i = 0; i < saddles; i++)
{
if( ((high_profile_p + i)->voltage) > ((low_profile_p + i)->voltage) )
{
temp_v_1 = (high_profile_p + i)->voltage;
temp_v_2 = (low_profile_p + i)->voltage;
(temp_profile_p + i)->voltage = temp_v_2 +
(
(
(temperature - low_temperature) *
(temp_v_1 - temp_v_2)
) /
(high_temperature - low_temperature)
);
}
else
{
temp_v_1 = (low_profile_p + i)->voltage;
temp_v_2 = (high_profile_p + i)->voltage;
(temp_profile_p + i)->voltage = temp_v_2 +
(
(
(high_temperature - temperature) *
(temp_v_1 - temp_v_2)
) /
(high_temperature - low_temperature)
);
}
(temp_profile_p + i)->percentage = (high_profile_p + i)->percentage;
#if 0
(temp_profile_p + i)->voltage = temp_v_2 +
(
(
(temperature - low_temperature) *
(temp_v_1 - temp_v_2)
) /
(high_temperature - low_temperature)
);
#endif
}
// Dumpt new battery profile
for (i = 0; i < saddles ; i++)
{
bm_print(BM_LOG_CRTI, " at %d = <%d,%d>\r\n",
temperature, (temp_profile_p+i)->percentage, (temp_profile_p+i)->voltage);
}
}
}
/* 首先获得电池电压,然后获得 hw ocv 电压,两者差值较小时,即 hw ocv 为准,
然后以此计算出开机电池容量值,并且与 rtc 中记录的电量值进行比较,如果误差较小(小于 40%)
则以 rtc 容量值为准,最后初始化 oam 算法的相关参数 */
oam_init();
void oam_init(void)
{
int ret=0;
int vol_bat=0;
kal_int32 vbat_capacity = 0;
vol_bat = 5; //set avg times
/* 读 PMIC 引用 BATSNS 的电压,即电池电压 */
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &vol_bat); // battery_meter_ctrl() 被初化为 bm_ctrl_cmd()
static kal_int32 read_adc_v_bat_sense(void *data)
{
#if defined(CONFIG_POWER_EXT)
*(kal_int32*)(data) = 4201;
#else
/* 读 PMIC 对应的 ADC 的值,并将其转化为对应的电压值,这里即读取 BATSNS 引脚电压 */
*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(VBAT_CHANNEL_NUMBER,*(kal_int32*)(data),1);
#endif
return STATUS_OK;
}
/* 这里应该是获得 hw ocv 电压 */
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_HW_OCV, &gFG_voltage);
static kal_int32 read_hw_ocv(void *data)
{
/* 外部充电器的话直接固定值 */
#if defined(CONFIG_POWER_EXT)
*(kal_int32*)(data) = 3999;
#else
/* 这里应该是读取相应寄存器, 获得一个 hw ocv 电压 */
*(kal_int32*)(data) = get_hw_ocv();
int get_hw_ocv(void)
{
#if defined(CONFIG_POWER_EXT)
return 4001;
#else
kal_int32 adc_result_reg=0;
kal_int32 adc_result=0;
kal_int32 r_val_temp=4;
#if defined(SWCHR_POWER_PATH)
adc_result_reg = upmu_get_rg_adc_out_wakeup_swchr();
adc_result = (adc_result_reg*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
bm_print(BM_LOG_CRTI, "[oam] get_hw_ocv (swchr) : adc_result_reg=%d, adc_result=%d\n",
adc_result_reg, adc_result);
#else
/* 这里应该是读取相应寄存器,获得充电电压值,因为从 PMIC 手册上说,只有在充电电压处于 4.3V <= x <= 7V 时才可以充电*/
adc_result_reg = upmu_get_rg_adc_out_wakeup_pchr();
kal_uint32 upmu_get_rg_adc_out_wakeup_pchr(void)
{
kal_uint32 ret=0;
kal_uint32 val=0;
pmic_lock();
ret=pmic_read_interface( (kal_uint32)(AUXADC_ADC8),
(&val),
(kal_uint32)(PMIC_RG_ADC_OUT_WAKEUP_PCHR_MASK),
(kal_uint32)(PMIC_RG_ADC_OUT_WAKEUP_PCHR_SHIFT)
);
pmic_unlock();
return val;
}
/* 将得到的 adc 数值转换为对应的 模拟电压值 */
adc_result = (adc_result_reg*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;
bm_print(BM_LOG_CRTI, "[oam] get_hw_ocv (pchr) : adc_result_reg=%d, adc_result=%d\n",
adc_result_reg, adc_result);
#endif
adc_result += g_hw_ocv_tune_value;
return adc_result;
#endif
}
#endif
return STATUS_OK;
}
/* 获取 hw ocv 电压对应的的电池剩余容量百分比 */
gFG_capacity_by_v = fgauge_read_capacity_by_v(gFG_voltage);
kal_int32 fgauge_read_capacity_by_v(kal_int32 voltage)
{
int i = 0, saddles = 0;
BATTERY_PROFILE_STRUC_P profile_p;
kal_int32 ret_percent = 0;
/* 获得当前温度对应的 温度-电池容量表格 */
profile_p = fgauge_get_profile(TEMPERATURE_T);
if (profile_p == NULL)
{
bm_print(BM_LOG_CRTI, "[FGADC] fgauge get ZCV profile : fail !\r\n");
return 100;
}
/* 获得 温度-电池容量 表格项数 */
saddles = fgauge_get_saddles();
if (voltage > (profile_p+0)->voltage)
{
return 100; // battery capacity, not dod
}
if (voltage < (profile_p+saddles-1)->voltage)
{
return 0; // battery capacity, not dod
}
/* 遍历表格,插值获得当前电压对应的用掉容量百分比 */
for (i = 0; i < saddles - 1; i++)
{
if ((voltage <= (profile_p+i)->voltage) && (voltage >= (profile_p+i+1)->voltage))
{
ret_percent = (profile_p+i)->percentage +
(
(
( ((profile_p+i)->voltage) - voltage ) *
( ((profile_p+i+1)->percentage) - ((profile_p + i)->percentage) )
) /
( ((profile_p+i)->voltage) - ((profile_p+i+1)->voltage) )
);
break;
}
}
/* 获得当前剩余电量值 */
ret_percent = 100 - ret_percent;
return ret_percent;
}
/* 获得当前电池电压对应的剩余容量百分比 */
vbat_capacity = fgauge_read_capacity_by_v(vol_bat);
kal_int32 fgauge_read_capacity_by_v(kal_int32 voltage)
{
int i = 0, saddles = 0;
BATTERY_PROFILE_STRUC_P profile_p;
kal_int32 ret_percent = 0;
/* 获得当前温度对应的 温度-电池容量表格 */
profile_p = fgauge_get_profile(TEMPERATURE_T);
if (profile_p == NULL)
{
bm_print(BM_LOG_CRTI, "[FGADC] fgauge get ZCV profile : fail !\r\n");
return 100;
}
/* 获得 温度-电池容量 表格项数 */
saddles = fgauge_get_saddles();
if (voltage > (profile_p+0)->voltage)
{
return 100; // battery capacity, not dod
}
if (voltage < (profile_p+saddles-1)->voltage)
{
return 0; // battery capacity, not dod
}
/* 遍历表格,插值获得当前电压对应的用掉容量百分比 */
for (i = 0; i < saddles - 1; i++)
{
if ((voltage <= (profile_p+i)->voltage) && (voltage >= (profile_p+i+1)->voltage))
{
ret_percent = (profile_p+i)->percentage +
(
(
( ((profile_p+i)->voltage) - voltage ) *
( ((profile_p+i+1)->percentage) - ((profile_p + i)->percentage) )
) /
( ((profile_p+i)->voltage) - ((profile_p+i+1)->voltage) )
);
break;
}
}
ret_percent = 100 - ret_percent;
return ret_percent;
}
/* 如果充电器插上了,则比较使用当前电压与 hw ocv 电压计算的两都容量差,如果差值较小,继续使用 hw ocv 电压及
对应容量为准 */
if(bat_is_charger_exist() == KAL_TRUE)
///////////////////////////////////////////////////////////////////////////////////////////
//// Pulse Charging Algorithm
// 判断充电是否插上了
///////////////////////////////////////////////////////////////////////////////////////////
kal_bool bat_is_charger_exist(void)
{
return get_charger_detect_status();
int get_charger_detect_status(void)
{
kal_bool chr_status;
/* 对应的充电芯片的硬件函数 */
battery_charging_control(CHARGING_CMD_GET_CHARGER_DET_STATUS,&chr_status);
static kal_uint32 charging_get_charger_det_status(void *data)
{
kal_uint32 status = STATUS_OK;
#if defined(CHRDET_SW_MODE_EN)
kal_uint32 vchr_val=0;
/* 读 PMIC 对应的 ADC 的值,并将其转化为对应的电压值,这里即读取 VCDT 引脚电压 */
vchr_val = PMIC_IMM_GetOneChannelValue(4,5,1);
vchr_val = (((330+39)*100*vchr_val)/39)/100;
if( vchr_val > 4300 )
{
battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=Y (%d)\n", vchr_val);
*(kal_uint32 *)data = KAL_TRUE;
}
else
{
battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=N (%d)\n", vchr_val);
*(kal_uint32 *)data = KAL_FALSE;
}
#else
/* 读取 CHR_CON0 寄存器 */
*(kal_bool*)(data) = upmu_get_rgs_chrdet();
kal_uint32 upmu_get_rgs_chrdet(void)
{
kal_uint32 ret=0;
kal_uint32 val=0;
pmic_lock();
ret=pmic_read_interface( (kal_uint32)(CHR_CON0),
(&val),
(kal_uint32)(PMIC_RGS_CHRDET_MASK),
(kal_uint32)(PMIC_RGS_CHRDET_SHIFT)
);
pmic_unlock();
return val;
}
#endif
/* 读取 CHR_CON0 寄存器 */
if( upmu_get_rgs_chrdet() == 0 )
g_charger_type = CHARGER_UNKNOWN;
return status;
}
return chr_status;
}
}
// 如果插入了充电器,hw_ocv 大部分情况是准的,插着 charger 的情况,采集的时候可能会不准
{
bm_print(BM_LOG_CRTI, "[oam_init_inf] gFG_capacity_by_v=%d, vbat_capacity=%d, \n",gFG_capacity_by_v,vbat_capacity);
// to avoid plug in cable without battery, then plug in battery to make hw soc = 100%
// if the difference bwtween ZCV and vbat is too large, using vbat instead ZCV
/* 如果两者差值超过 30%,我们认为 hw ocv 存在偏差,会以 Vbat 作为 ocv
如果两者差值在 30%,继续以 hw ocv 作为 ocv 的数值 */
if(((gFG_capacity_by_v == 100) && (vbat_capacity < CUST_POWERON_MAX_VBAT_TOLRANCE)) ||(abs(gFG_capacity_by_v-vbat_capacity)>CUST_POWERON_DELTA_VBAT_TOLRANCE))
{
bm_print(BM_LOG_CRTI, "[oam_init] fg_vbat=(%d), vbat=(%d), set fg_vat as vat\n", gFG_voltage,vol_bat);
gFG_voltage = vol_bat;
gFG_capacity_by_v = vbat_capacity;
}
}
// 获得的比较真实的开机容量
gFG_capacity_by_v_init = gFG_capacity_by_v;
/* 这里与 rtc 中的电量进行比较,确定一个开机显示 UI 电量 */
dod_init();
void dod_init(void)
{
#if defined(SOC_BY_HW_FG)
int ret = 0;
//use get_hw_ocv-----------------------------------------------------------------
/* 获取 PMIC 硬件上的 hw ocv 电压 */
ret=battery_meter_ctrl(BATTERY_METER_CMD_GET_HW_OCV, &gFG_voltage);
// 对应 PMIC 硬件相关操作函数
read_hw_ocv()
get_hw_ocv()
/* 根据 hw ocv 电压,查表获取对应的电池剩余电量百分比*/
gFG_capacity_by_v = fgauge_read_capacity_by_v(gFG_voltage);
bm_print(BM_LOG_CRTI, "[FGADC] get_hw_ocv=%d, HW_SOC=%d, SW_SOC = %d\n",
gFG_voltage, gFG_capacity_by_v, gFG_capacity_by_v_init);
// compare with hw_ocv & sw_ocv, check if less than or equal to 5% tolerance
// 将软件 ocv 与硬件 ocv 比较,误差要少于 5%, 才使用之前计算的 sw hov?
if ((abs(gFG_capacity_by_v_init - gFG_capacity_by_v) > 5) && (bat_is_charger_exist() == KAL_TRUE))
{
gFG_capacity_by_v = gFG_capacity_by_v_init;
}
//-------------------------------------------------------------------------------
#endif
#if defined(CONFIG_POWER_EXT)
g_rtc_fg_soc = gFG_capacity_by_v;
#else
/* 获取 DOD0 的数值,电池电量每 10s 写入 RTC 一次,开机会从 RTC 里面读取 DOD0 的值
保存在 rtc 数值,即上次关机的 UI 电量数值 */
g_rtc_fg_soc = get_rtc_spare_fg_value();
int get_rtc_spare_fg_value(void)
{
//RTC_AL_HOU bit8~14
u16 temp;
unsigned long flags;
spin_lock_irqsave(&rtc_lock, flags);
temp = hal_rtc_get_register_status("FG");
spin_unlock_irqrestore(&rtc_lock, flags);
return temp;
}
#endif
/* g_rtc_fg_soc: 为上次关机前的 UI 电量
gFG_capacity_by_v: 是经过 oam_init() 初始化后的电池真实电压
由于存在换电池的风险, 会去判断两者的差值:
1. 40% 以内,采用 rtc 电量,即继续显示上次 UI 的电量
2. 如果在 40% 以外,采用真实电池电压的电量,因为有可能更换了电池
设计的初衷: 因为电池特性,在确定 dod 会有误差,从而导致开关机电量跳变,
因此采用 rtc 是为了防止电量跳变
*/
if(g_rtc_fg_soc >= gFG_capacity_by_v)
{
if(((g_rtc_fg_soc != 0) && ((g_rtc_fg_soc-gFG_capacity_by_v) < CUST_POWERON_DELTA_CAPACITY_TOLRANCE) &&(( gFG_capacity_by_v > CUST_POWERON_LOW_CAPACITY_TOLRANCE || bat_is_charger_exist() == KAL_TRUE)))
|| ((g_rtc_fg_soc != 0) &&(g_boot_reason == BR_WDT_BY_PASS_PWK || g_boot_reason == BR_WDT || g_boot_reason == BR_TOOL_BY_PASS_PWK || g_boot_reason == BR_2SEC_REBOOT || g_boot_mode == RECOVERY_BOOT)))
{
gFG_capacity_by_v = g_rtc_fg_soc;
}
}
else
{
if(((g_rtc_fg_soc != 0) && ((gFG_capacity_by_v-g_rtc_fg_soc) < CUST_POWERON_DELTA_CAPACITY_TOLRANCE) &&(( gFG_capacity_by_v > CUST_POWERON_LOW_CAPACITY_TOLRANCE || bat_is_charger_exist() == KAL_TRUE)))
|| ((g_rtc_fg_soc != 0) &&(g_boot_reason == BR_WDT_BY_PASS_PWK || g_boot_reason == BR_WDT || g_boot_reason == BR_TOOL_BY_PASS_PWK || g_boot_reason == BR_2SEC_REBOOT || g_boot_mode == RECOVERY_BOOT)))
{
gFG_capacity_by_v = g_rtc_fg_soc;
}
}
bm_print(BM_LOG_CRTI, "[FGADC] g_rtc_fg_soc=%d, gFG_capacity_by_v=%d\n",
g_rtc_fg_soc, gFG_capacity_by_v);
if (gFG_capacity_by_v == 0 && bat_is_charger_exist() == KAL_TRUE) {
gFG_capacity_by_v = 1;
bm_print(BM_LOG_CRTI, "[FGADC] gFG_capacity_by_v=%d\n",
gFG_capacity_by_v);
}
//////////////////////////////////////////////////////////////////
// 在这里最终定义的开机 UI 电量显示
gFG_capacity = gFG_capacity_by_v; // 电池剩余容量百分比
gFG_capacity_by_c_init = gFG_capacity;
gFG_capacity_by_c = gFG_capacity;
gFG_DOD0 = 100 - gFG_capacity; // 电池使用完电量百分比
gFG_DOD1=gFG_DOD0; // 电池使用完电量百分比
gfg_percent_check_point = gFG_capacity; // 容量检测百分比
#if defined(CHANGE_TRACKING_POINT)
gFG_15_vlot = fgauge_read_v_by_capacity( (100-g_tracking_point) );
bm_print(BM_LOG_CRTI, "[FGADC] gFG_15_vlot = %dmV\n", gFG_15_vlot);
#else
//gFG_15_vlot = fgauge_read_v_by_capacity(86); //14%
/* 通过当前使用掉的容量,获取当前电压 */
gFG_15_vlot = fgauge_read_v_by_capacity( (100-g_tracking_point) );
bm_print(BM_LOG_CRTI, "[FGADC] gFG_15_vlot = %dmV\n", gFG_15_vlot);
if( (gFG_15_vlot > 3800) || (gFG_15_vlot < 3600) )
{
bm_print(BM_LOG_CRTI, "[FGADC] gFG_15_vlot(%d) over range, reset to 3700\n", gFG_15_vlot);
gFG_15_vlot = 3700;
}
#endif
}
/* 先通过 battery_meter_get_battery_temperature() 获得电池温度,再通过 fgauge_get_Q_max() 计算电量
这里获得当前温度的电池的最大容量 */
gFG_BATT_CAPACITY_aging = fgauge_get_Q_max(battery_meter_get_battery_temperature());
kal_int32 battery_meter_get_battery_temperature(void)
{
/* 通过获得当前 NTC 电压,查表并进行线性插值法,得到当前的温度值 */
return force_get_tbat();
}
// 通过当前温度获插值得到当前最大容量
kal_int32 fgauge_get_Q_max(kal_int16 temperature)
{
kal_int32 ret_Q_max=0;
kal_int32 low_temperature = 0, high_temperature = 0;
kal_int32 low_Q_max = 0, high_Q_max = 0;
/* 如果温度位于 -20< x <= 0 */
if (temperature <= TEMPERATURE_T1)
{
low_temperature = (-10);
low_Q_max = Q_MAX_NEG_10;
high_temperature = TEMPERATURE_T1;
high_Q_max = Q_MAX_POS_0;
if(temperature < low_temperature)
{
temperature = low_temperature;
}
}
/* 如果温度位于 0< x <= 25 */
else if (temperature <= TEMPERATURE_T2)
{
low_temperature = TEMPERATURE_T1;
low_Q_max = Q_MAX_POS_0;
high_temperature = TEMPERATURE_T2;
high_Q_max = Q_MAX_POS_25;
if(temperature < low_temperature)
{
temperature = low_temperature;
}
}
/* 如果温度位于 -25< x <=50 */
else
{
low_temperature = TEMPERATURE_T2; // 低温下限
low_Q_max = Q_MAX_POS_25; // 低温最大容量
high_temperature = TEMPERATURE_T3; // 高温上限
high_Q_max = Q_MAX_POS_50; // 高温对应最大容量
// 防止范围溢出
if(temperature > high_temperature)
{
temperature = high_temperature;
}
}
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
核心算法:
当前容量 = 低温下限最大容量 + (当前温度 - 低温下限) * [(高温容量 - 低温容量)/(高温上限-低温下限)] */
ret_Q_max = low_Q_max +
(
(
(temperature - low_temperature) *
(high_Q_max - low_Q_max)
) /
(high_temperature - low_temperature)
);
bm_print(BM_LOG_FULL, "[fgauge_get_Q_max] Q_max = %d\r\n", ret_Q_max);
return ret_Q_max;
}
//oam_v_ocv_1 = gFG_voltage;
//oam_v_ocv_2 = gFG_voltage;
/* 通过当前使用电池容量的百分比,反换算出当前的电压 */
oam_v_ocv_init = fgauge_read_v_by_d(gFG_DOD0);
kal_int32 fgauge_read_v_by_d(int d_val)
{
int i = 0, saddles = 0;
BATTERY_PROFILE_STRUC_P profile_p;
kal_int32 ret_volt = 0;
profile_p = fgauge_get_profile(TEMPERATURE_T);
if (profile_p == NULL)
{
bm_print(BM_LOG_CRTI, "[fgauge_read_v_by_capacity] fgauge get ZCV profile : fail !\r\n");
return 3700;
}
saddles = fgauge_get_saddles();
if (d_val < (profile_p+0)->percentage)
{
return 3700;
}
if (d_val > (profile_p+saddles-1)->percentage)
{
return 3700;
}
for (i = 0; i < saddles - 1; i++)
{
if ((d_val >= (profile_p+i)->percentage) && (d_val <= (profile_p+i+1)->percentage))
{
ret_volt = (profile_p+i)->voltage -
(
(
( d_val - ((profile_p+i)->percentage) ) *
( ((profile_p+i)->voltage) - ((profile_p+i+1)->voltage) )
) /
( ((profile_p+i+1)->percentage) - ((profile_p+i)->percentage) )
);
break;
}
}
return ret_volt;
}
oam_v_ocv_2 = oam_v_ocv_1 = oam_v_ocv_init; // 通过当前使用电池容量的百分比,反换算出当前的电压
g_vol_bat_hw_ocv = gFG_voltage; // hw ocv 电压
//vbat = 5; //set avg times
//ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &vbat);
//oam_r_1 = fgauge_read_r_bat_by_v(vbat);
/* 通过当前 hw ocv 电压,获得的当前电池内阻 */
oam_r_1 = fgauge_read_r_bat_by_v(gFG_voltage);
oam_r_2 = oam_r_1;
oam_d0 = gFG_DOD0;
oam_d_5 = oam_d0;
oam_i_ori = gFG_current;
g_d_hw_ocv = oam_d0;
if(oam_init_i == 0)
{
bm_print(BM_LOG_CRTI, "[oam_init] oam_v_ocv_1,oam_v_ocv_2,oam_r_1,oam_r_2,oam_d0,oam_i_ori\n");
oam_init_i=1;
}
bm_print(BM_LOG_CRTI, "[oam_init] %d,%d,%d,%d,%d,%d\n",
oam_v_ocv_1, oam_v_ocv_2, oam_r_1, oam_r_2, oam_d0, oam_i_ori);
bm_print(BM_LOG_CRTI, "[oam_init_inf] hw_OCV, hw_D0, RTC, D0, oam_OCV_init, tbat\n");
bm_print(BM_LOG_CRTI, "[oam_run_inf] oam_OCV1, oam_OCV2, vbat, I1, I2, R1, R2, Car1, Car2,qmax, tbat\n");
bm_print(BM_LOG_CRTI, "[oam_result_inf] D1, D2, D3, D4, D5, UI_SOC\n");
bm_print(BM_LOG_CRTI, "[oam_init_inf] %d, %d, %d, %d, %d, %d\n",
gFG_voltage, (100 - fgauge_read_capacity_by_v(gFG_voltage)), g_rtc_fg_soc, gFG_DOD0 ,oam_v_ocv_init, force_get_tbat());
}
bm_print(BM_LOG_CRTI, "[battery_meter_initial] SOC_BY_SW_FG done\n");
#endif
return 0;
#endif
}
/* 获得要监控的电量,(用这个电量反换出电压来校验?)*/
BMT_status.nPercent_ZCV = battery_meter_get_battery_nPercent_zcv();
kal_int32 battery_meter_get_battery_nPercent_zcv(void)
{
#if defined(CONFIG_POWER_EXT)
return 3700;
#else
return gFG_15_vlot; // 15% zcv, 15% can be customized by 100-g_tracking_point
#endif
}
battery_meter_initilized = KAL_TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// 1. 判断是插入的是否充电器还是电脑 USB,看能不能进行充电
/* 如果连接的 USB 线为 USB 充电线,或者电脑 USB 线,则打开 USB,
这里会通过 BC1.1 来判断是电脑 USB 还是 USB 充电器,来决定充电电流
否则连接的不是充电线或者 USB 作为一个从设备使用,要断开 USB?
*/
mt_battery_charger_detect_check();
static void mt_battery_charger_detect_check(void)
{
/* 根据 USB 是主从状态,以及外部充电电压是否正常来判断是否允许充电,
这里如果是 USB 作为从设备,允许充电 */
if( upmu_is_chr_det() == KAL_TRUE )
///////////////////////////////////////////////////////////////////////////////////////////
//// PMIC PCHR Related APIs
///////////////////////////////////////////////////////////////////////////////////////////
kal_bool upmu_is_chr_det(void)
{
#if defined(CONFIG_POWER_EXT)
//return KAL_TRUE;
return get_charger_detect_status();
// 如果是外部充电芯片,则直接会调用外部芯片的充电相关函数,如 charging_hw_bq24196.c 中的
battery_charging_control(CHARGING_CMD_GET_CHARGER_DET_STATUS,&chr_status);
#else
kal_uint32 tmp32;
/* 这里是 PMIC,调用 PMIC 的充电相关函数 charging_hw_pmic.c,
这里查询 PMIC 的 CHR_CON0 寄存器,检查充电状态 */
tmp32=get_charger_detect_status();
int get_charger_detect_status(void)
{
kal_bool chr_status;
battery_charging_control(CHARGING_CMD_GET_CHARGER_DET_STATUS,&chr_status);
static kal_uint32 charging_get_charger_det_status(void *data)
{
kal_uint32 status = STATUS_OK;
#if defined(CHRDET_SW_MODE_EN)
kal_uint32 vchr_val=0;
// 获得 PMIC 的 VCDT 充电电压值
vchr_val = PMIC_IMM_GetOneChannelValue(4,5,1);
vchr_val = (((330+39)*100*vchr_val)/39)/100;
if( vchr_val > 4300 )
{
battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=Y (%d)\n", vchr_val);
*(kal_uint32 *)data = KAL_TRUE;
}
else
{
battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=N (%d)\n", vchr_val);
*(kal_uint32 *)data = KAL_FALSE;
}
#else
*(kal_bool*)(data) = upmu_get_rgs_chrdet();
#endif
/* 读取 PMIC CHR_CON0 寄存器 */
if( upmu_get_rgs_chrdet() == 0 )
g_charger_type = CHARGER_UNKNOWN;
return status;
}
return chr_status;
}
if(tmp32 == 0)
{
return KAL_FALSE;
}
else
{
/* 如果 USB 作为一个不是以从设备运行,是不允许充电的 */
if( mt_usb_is_device() )
// Usb20.c (s:\i841\mediatek\platform\mt6582\kernel\drivers\usb20)
/* ================================ */
/* connect and disconnect functions */
/* ================================ */
bool mt_usb_is_device(void)
{
DBG(4,"called\n");
/* 如果 mtk_musb 为 0 的话,表明 MTK 的 USB 在作为一个主机使用,不允许充电的 */
if(!mtk_musb){
DBG(0,"mtk_musb is NULL\n");
return false; // don't do charger detection when usb is not ready
} else {
DBG(4,"is_host=%d\n",mtk_musb->is_host);
}
return !mtk_musb->is_host;
}
{
battery_xlog_printk(BAT_LOG_FULL, "[upmu_is_chr_det] Charger exist and USB is not host\n");
return KAL_TRUE;
}
else
{
battery_xlog_printk(BAT_LOG_FULL, "[upmu_is_chr_det] Charger exist but USB is host\n");
return KAL_FALSE;
}
}
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// 如果插入的是 USB 充电线或者 电脑的 USB 线,则打开 USB ,因为充电时 MTK 作为从设备,是允许充电的
{
wake_lock(&battery_suspend_lock);
BMT_status.charger_exist = KAL_TRUE;
// 无线充电支持宏
#if defined(MTK_WIRELESS_CHARGER_SUPPORT)
mt_charger_type_detection();
if((BMT_status.charger_type==STANDARD_HOST) || (BMT_status.charger_type==CHARGING_HOST) )
{
// 这应该是做为从设备连接到电脑上用的
mt_usb_connect();
}
#else
/* 这个结构体是在前面初始化的,所以第一次执行,该值未设置 = 0,
在这里通过 BC1.1 协议检测外部连接的 USB 线类型,是主是从,还是充电/被充
最后根据类型判断要不要启动 USB */
if(BMT_status.charger_type == CHARGER_UNKNOWN)
{
/* 调用 PMIC 硬件相关函数,检测充电类型, 这里应用了 BC1.1 协议来检测 */
mt_charger_type_detection();
CHARGER_TYPE mt_charger_type_detection(void)
{
CHARGER_TYPE CHR_Type_num = CHARGER_UNKNOWN;
mutex_lock(&charger_type_mutex);
#if defined(MTK_WIRELESS_CHARGER_SUPPORT)
battery_charging_control(CHARGING_CMD_GET_CHARGER_TYPE,&CHR_Type_num);
BMT_status.charger_type = CHR_Type_num;
#else
/* 调用 PMIC 硬件相关函数,检测充电类型, 这里应用了 BC1.1 协议来检测 */
if(BMT_status.charger_type == CHARGER_UNKNOWN)
{
/* 调用 PMIC 硬件相关函数,检测充电类型, 这里应用了 BC1.1 协议来检测 */
battery_charging_control(CHARGING_CMD_GET_CHARGER_TYPE,&CHR_Type_num);
static kal_uint32 charging_get_charger_type(void *data)
{
kal_uint32 status = STATUS_OK;
#if defined(CONFIG_POWER_EXT)
*(CHARGER_TYPE*)(data) = STANDARD_HOST;
#else
#if defined(MTK_WIRELESS_CHARGER_SUPPORT)
int wireless_state = 0;
/* 无线充电判断引脚,直接拉低就好 */
wireless_state = mt_get_gpio_in(wireless_charger_gpio_number);
if(wireless_state == WIRELESS_CHARGER_EXIST_STATE)
{
*(CHARGER_TYPE*)(data) = WIRELESS_CHARGER;
battery_xlog_printk(BAT_LOG_CRTI, "WIRELESS_CHARGER!\r\n");
return status;
}
#endif
if(g_charger_type!=CHARGER_UNKNOWN && g_charger_type!=WIRELESS_CHARGER)
{
*(CHARGER_TYPE*)(data) = g_charger_type;
battery_xlog_printk(BAT_LOG_CRTI, "return %d!\r\n", g_charger_type);
return status;
}
charging_type_det_done = KAL_FALSE;
/*//////////////////////////////////////////////////////////////////////////////////////////////
BC1.1 充电协议,主要用来区分是插入的是 USB 还是充电器,如果是 USB 只能提供 500ma 充电,
如果是充电器,则可以大电流充电 */
/********* Step initial ***************/
hw_bc11_init();
static void hw_bc11_init(void)
{
msleep(300);
Charger_Detect_Init();
//RG_BC11_BIAS_EN=1
upmu_set_rg_bc11_bias_en(0x1);
//RG_BC11_VSRC_EN[1:0]=00
upmu_set_rg_bc11_vsrc_en(0x0);
//RG_BC11_VREF_VTH = [1:0]=00
upmu_set_rg_bc11_vref_vth(0x0);
//RG_BC11_CMP_EN[1.0] = 00
upmu_set_rg_bc11_cmp_en(0x0);
//RG_BC11_IPU_EN[1.0] = 00
upmu_set_rg_bc11_ipu_en(0x0);
//RG_BC11_IPD_EN[1.0] = 00
upmu_set_rg_bc11_ipd_en(0x0);
//BC11_RST=1
upmu_set_rg_bc11_rst(0x1);
//BC11_BB_CTRL=1
upmu_set_rg_bc11_bb_ctrl(0x1);
//msleep(10);
mdelay(50);
if(Enable_BATDRV_LOG == BAT_LOG_FULL)
{
battery_xlog_printk(BAT_LOG_FULL, "hw_bc11_init() \r\n");
hw_bc11_dump_register();
}
}
/********* Step DCD ***************/
if(1 == hw_bc11_DCD())
{
/********* Step A1 ***************/
if(1 == hw_bc11_stepA1())
{
*(CHARGER_TYPE*)(data) = APPLE_2_1A_CHARGER;
battery_xlog_printk(BAT_LOG_CRTI, "step A1 : Apple 2.1A CHARGER!\r\n");
}
else
{
*(CHARGER_TYPE*)(data) = NONSTANDARD_CHARGER;
battery_xlog_printk(BAT_LOG_CRTI, "step A1 : Non STANDARD CHARGER!\r\n");
}
}
else
{
/********* Step A2 ***************/
if(1 == hw_bc11_stepA2())
{
/********* Step B2 ***************/
if(1 == hw_bc11_stepB2())
{
*(CHARGER_TYPE*)(data) = STANDARD_CHARGER;
battery_xlog_printk(BAT_LOG_CRTI, "step B2 : STANDARD CHARGER!\r\n");
}
else
{
*(CHARGER_TYPE*)(data) = CHARGING_HOST;
battery_xlog_printk(BAT_LOG_CRTI, "step B2 : Charging Host!\r\n");
}
}
else
{
*(CHARGER_TYPE*)(data) = STANDARD_HOST;
battery_xlog_printk(BAT_LOG_CRTI, "step A2 : Standard USB Host!\r\n");
}
}
/********* Finally setting *******************************/
hw_bc11_done();
static void hw_bc11_done(void)
{
//RG_BC11_VSRC_EN[1:0]=00
upmu_set_rg_bc11_vsrc_en(0x0);
//RG_BC11_VREF_VTH = [1:0]=0
upmu_set_rg_bc11_vref_vth(0x0);
//RG_BC11_CMP_EN[1.0] = 00
upmu_set_rg_bc11_cmp_en(0x0);
//RG_BC11_IPU_EN[1.0] = 00
upmu_set_rg_bc11_ipu_en(0x0);
//RG_BC11_IPD_EN[1.0] = 00
upmu_set_rg_bc11_ipd_en(0x0);
//RG_BC11_BIAS_EN=0
upmu_set_rg_bc11_bias_en(0x0);
Charger_Detect_Release();
void Charger_Detect_Release(void)
{
/* RG_USB20_BC11_SW_EN = 1'b0 */
USBPHY_CLR8(0x1a, 0x80);
udelay(1);
//4 14. turn off internal 48Mhz PLL.
usb_enable_clock(false);
printk("Charger_Detect_Release\n");
}
if(Enable_BATDRV_LOG == BAT_LOG_FULL)
{
battery_xlog_printk(BAT_LOG_FULL, "hw_bc11_done() \r\n");
hw_bc11_dump_register();
}
}
charging_type_det_done = KAL_TRUE;
g_charger_type = *(CHARGER_TYPE*)(data);
#endif
return status;
}
BMT_status.charger_type = CHR_Type_num;
}
#endif
mutex_unlock(&charger_type_mutex);
return BMT_status.charger_type;
}
/* 如果充电线是 标准主模式 或者 主充电模式, 连接上 USB */
if((BMT_status.charger_type==STANDARD_HOST) || (BMT_status.charger_type==CHARGING_HOST) )
{
/* 连接 USB,充电时可以连接 USB 的 */
mt_usb_connect();
void mt_usb_connect(void)
{
printk("[MUSB] USB is ready for connect\n");
DBG(3, "is ready %d is_host %d power %d\n",mtk_musb->is_ready,mtk_musb->is_host , mtk_musb->power);
if (!mtk_musb || !mtk_musb->is_ready || mtk_musb->is_host || mtk_musb->power)
return;
DBG(0,"cable_mode=%d\n",cable_mode);
/* CABLE_MODE_CHRG_ONLY = 0, CABLE_MODE_NORMAL, CABLE_MODE_HOST_ONLY, CABLE_MODE_MAX
上面这些模式,应该是通过 BC1.1 来判断出来的 */
if(cable_mode != CABLE_MODE_NORMAL)
{
DBG(0,"musb_sync_with_bat, USB_CONFIGURED\n");
musb_sync_with_bat(mtk_musb,USB_CONFIGURED);
void musb_sync_with_bat(struct musb *musb,int usb_state)
{
#ifndef FPGA_PLATFORM
DBG(0,"BATTERY_SetUSBState, state=%d\n",usb_state);
// linear_charging.c,线性充电
BATTERY_SetUSBState(usb_state);
void BATTERY_SetUSBState(int usb_state_value)
{
#if defined(CONFIG_POWER_EXT)
battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY_SetUSBState] in FPGA/EVB, no service\r\n");
#else
if ( (usb_state_value < USB_SUSPEND) || ((usb_state_value > USB_CONFIGURED))){
battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] BAT_SetUSBState Fail! Restore to default value\r\n");
usb_state_value = USB_UNCONFIGURED;
} else {
battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] BAT_SetUSBState Success! Set %d\r\n", usb_state_value);
g_usb_state = usb_state_value;
}
#endif
}
wake_up_bat();
void wake_up_bat (void)
{
battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] wake_up_bat. \r\n");
chr_wake_up_bat = KAL_TRUE;
bat_thread_timeout = KAL_TRUE;
wake_up(&bat_thread_wq);
}
#endif
}
mtk_musb->power = true;
return;
}
if (!wake_lock_active(&mtk_musb->usb_lock))
wake_lock(&mtk_musb->usb_lock);
// Program the HDRC to start (enable interrupts, dma, etc.).
// 启动 USB
musb_start(mtk_musb);
/*-------------------------------------------------------------------------*/
/*
* Program the HDRC to start (enable interrupts, dma, etc.).
*/
void musb_start(struct musb *musb)
{
void __iomem *regs = musb->mregs;
int vbusdet_retry = 5;
u8 intrusbe;
DBG(0, "start, is_host=%d is_active=%d\n", musb->is_host, musb->is_active);
if(musb->is_active) {
if(musb->is_host) {
DBG(0, "we are host now, add more interrupt devctl=%x\n", musb_readb(mtk_musb->mregs,MUSB_DEVCTL));
musb->intrtxe = 0xffff;
musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
musb->intrrxe = 0xfffe;
musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
musb_writeb(regs,MUSB_INTRUSBE,0xf7);
return;
}
}
musb_platform_enable(musb);
musb_generic_disable(musb);
intrusbe= musb_readb(regs, MUSB_INTRUSBE);
if (musb->is_host){
musb->intrtxe = 0xffff;
musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
musb->intrrxe = 0xfffe;
musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
intrusbe = 0xf7;
while(!musb_platform_get_vbus_status(musb)) {
mdelay(100);
if(vbusdet_retry--<=1) {
DBG(0, "VBUS detection fail!\n");
break;
}
}
} else if(!musb->is_host){
intrusbe |= MUSB_INTR_RESET; //device mode enable reset interrupt
}
musb_writeb(regs,MUSB_INTRUSBE,intrusbe);
if (musb_speed) {
/* put into basic highspeed mode and start session */
musb_writeb(regs, MUSB_POWER, MUSB_POWER_SOFTCONN
| MUSB_POWER_HSENAB
/* ENSUSPEND wedges tusb */
| MUSB_POWER_ENSUSPEND);
} else {
/* put into basic fullspeed mode and start session */
musb_writeb(regs, MUSB_POWER, MUSB_POWER_SOFTCONN
/* ENSUSPEND wedges tusb */
| MUSB_POWER_ENSUSPEND);
}
musb->is_active = 1;
}
printk("[MUSB] USB connect\n");
}
}
}
#endif
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_thread]Cable in, CHR_Type_num=%d\r\n", BMT_status.charger_type);
}
////////////////////////////////////////////////////////////////////////////////////
// 如果连接入的不是正常的充电线,或者此时 USB 接口以主机状态运行 ,则设置电池状态参数,断开 USB 连接?
else
{
wake_unlock(&battery_suspend_lock);
BMT_status.charger_exist = KAL_FALSE;
BMT_status.charger_type = CHARGER_UNKNOWN;
BMT_status.bat_full = KAL_FALSE;
BMT_status.bat_in_recharging_state = KAL_FALSE;
BMT_status.bat_charging_state = CHR_PRE;
BMT_status.total_charging_time = 0;
BMT_status.PRE_charging_time = 0;
BMT_status.CC_charging_time = 0;
BMT_status.TOPOFF_charging_time = 0;
BMT_status.POSTFULL_charging_time = 0;
battery_xlog_printk(BAT_LOG_CRTI, "[BAT_thread]Cable out \r\n");
// 断开 USB 与电脑的连接
mt_usb_disconnect();
void mt_usb_disconnect(void)
{
printk("[MUSB] USB is ready for disconnect\n");
if (!mtk_musb || !mtk_musb->is_ready || mtk_musb->is_host || !mtk_musb->power)
return;
musb_stop(mtk_musb);
if (wake_lock_active(&mtk_musb->usb_lock))
wake_unlock(&mtk_musb->usb_lock);
DBG(0,"cable_mode=%d\n",cable_mode);
if (cable_mode != CABLE_MODE_NORMAL) {
DBG(0,"musb_sync_with_bat, USB_SUSPEND\n");
musb_sync_with_bat(mtk_musb,USB_SUSPEND);
mtk_musb->power = false;
}
printk("[MUSB] USB disconnect\n");
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// 2. 通过具体的充电芯片来获得电池信息,充电信息, 获得电池电量百分比
/* 通过 oam 算法,获得电量百分比 */
mt_battery_GetBatteryData();
void mt_battery_GetBatteryData(void)
{
kal_uint32 bat_vol, charger_vol, Vsense, ZCV;
kal_int32 ICharging, temperature, temperatureR, temperatureV, SOC;
static kal_int32 bat_sum, icharging_sum, temperature_sum;
static kal_int32 batteryVoltageBuffer[BATTERY_AVERAGE_SIZE];
static kal_int32 batteryCurrentBuffer[BATTERY_AVERAGE_SIZE];
static kal_int32 batteryTempBuffer[BATTERY_AVERAGE_SIZE];
static kal_uint8 batteryIndex = 0;
static kal_int32 previous_SOC = -1;
// 获得 BATSNS 引脚电压
bat_vol = battery_meter_get_battery_voltage();
kal_int32 battery_meter_get_battery_voltage(void)
{
int ret=0;
int val=5;
val = 5; //set avg times
// 获得 PMIC 的 BATSNS 引脚电压
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &val);
static kal_int32 read_adc_v_bat_sense(void *data)
{
#if defined(CONFIG_POWER_EXT)
*(kal_int32*)(data) = 4201;
#else
*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(VBAT_CHANNEL_NUMBER,*(kal_int32*)(data),1);
#endif
return STATUS_OK;
}
g_sw_vbat_temp = val;
return val;
}
/* 获得 PMIC 的 ISENSE 引脚电压*/
Vsense = battery_meter_get_VSense();
kal_int32 battery_meter_get_VSense(void)
{
#if defined(CONFIG_POWER_EXT)
return 0;
#else
int ret=0;
int val=0;
val = 1; //set avg times
/* 获得 PMIC 的 ISENSE 引脚电压*/
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_I_SENSE, &val);
read_adc_v_i_sense(void *data)
*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(ISENSE_CHANNEL_NUMBER,*(kal_int32*)(data),1);
return val;
#endif
}
/* 获得充电电流 */
ICharging = battery_meter_get_charging_current();
kal_int32 battery_meter_get_charging_current(void)
{
kal_int32 ADC_BAT_SENSE_tmp[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
kal_int32 ADC_BAT_SENSE_sum=0;
kal_int32 ADC_BAT_SENSE=0;
kal_int32 ADC_I_SENSE_tmp[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
kal_int32 ADC_I_SENSE_sum=0;
kal_int32 ADC_I_SENSE=0;
int repeat=20;
int i=0;
int j=0;
kal_int32 temp=0;
int ICharging=0;
int ret=0;
int val=1;
for(i=0 ; i ADC_BAT_SENSE)
{
ICharging = (ADC_I_SENSE - ADC_BAT_SENSE + g_I_SENSE_offset)*1000/CUST_R_SENSE;
}
else
{
ICharging = 0;
}
return ICharging;
}
/* 获得充电器电压 */
charger_vol = battery_meter_get_charger_voltage();
kal_int32 battery_meter_get_charger_voltage(void)
{
int ret=0;
int val=0;
val = 5; // set avg times
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_CHARGER, &val);
static kal_int32 read_adc_v_charger(void *data)
{
#if defined(CONFIG_POWER_EXT)
*(kal_int32*)(data) = 5001;
#else
kal_int32 val;
/* 获取 PMIC 的 VCDT 引脚电压 */
val = PMIC_IMM_GetOneChannelValue(VCHARGER_CHANNEL_NUMBER,*(kal_int32*)(data),1);
val = (((R_CHARGER_1+R_CHARGER_2)*100*val)/R_CHARGER_2)/100;
*(kal_int32*)(data) = val;
#endif
return STATUS_OK;
}
//val = (((R_CHARGER_1+R_CHARGER_2)*100*val)/R_CHARGER_2)/100;
return val;
}
/* 通过获得当前 NTC 电压,查表并进行线性插值法,得到当前的温度值 */
temperature = battery_meter_get_battery_temperature();
return force_get_tbat();
/* 这里用来获取电池 NTC 的电压 */
temperatureV = battery_meter_get_tempV();
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_TEMP, &val);
static kal_int32 read_adc_v_bat_temp(void *data)
{
#if defined(CONFIG_POWER_EXT)
*(kal_int32*)(data) = 0;
#else
#if defined(MTK_PCB_TBAT_FEATURE)
int ret = 0, data[4], i, ret_value = 0, ret_temp = 0;
int Channel=1;
if( IMM_IsAdcInitReady() == 0 )
return g_adc_init_flag;
{
bm_print(BM_LOG_CRTI, "[get_tbat_volt] AUXADC is not ready");
return 0;
}
i = times;
while (i--)
{
ret_value = IMM_GetOneChannelValue(Channel, data, &ret_temp);
ret += ret_temp;
bm_print(BM_LOG_FULL, "[get_tbat_volt] ret_temp=%d\n",ret_temp);
}
ret = ret*1500/4096 ;
ret = ret/times;
bm_print(BM_LOG_CRTI, "[get_tbat_volt] Battery output mV = %d\n",ret);
*(kal_int32*)(data) = ret;
#else
bm_print(BM_LOG_FULL, "[read_adc_v_charger] return PMIC_IMM_GetOneChannelValue(4,times,1);\n");
/* 读取 PMIC 的 BATON1 引脚电压 */
*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(VBATTEMP_CHANNEL_NUMBER,*(kal_int32*)(data),1);
#endif
#endif
return STATUS_OK;
}
/* 获得 下拉电阻与 NTC 并并联的电压 */
temperatureR = battery_meter_get_tempR(temperatureV);
/* 上拉电压/下拉电压 = 上拉电阻/ 下拉电阻 */
TRes = (RBAT_PULL_UP_R*dwVolt) / (RBAT_PULL_UP_VOLT-dwVolt);
/* bat_thread_wakeup() 每 10s 唤醒一次,唤醒时设置 bat_meter_timeout = KAL_TRUE
这时候更新电池电量百分比 */
if(bat_meter_timeout == KAL_TRUE || bat_spm_timeout == TRUE)
{
/* oam 算法通过两种方式更新电压,去逼近真实的开路电压,最终查表获取近似真实的电量值百分比,方法 1,查表获得电池百分比,方法 2,库伦积分
以方法2获得的参数补偿方法 1 的值,具体方法见 oam_run()*/
SOC = battery_meter_get_battery_percentage();
kal_int32 battery_meter_get_battery_percentage(void)
{
#if defined(CONFIG_POWER_EXT)
return 50;
#else
if(bat_is_charger_exist() == KAL_FALSE)
// 这里查询 PMIC 的 CHR_CON0 寄存器,检查充电状态
return get_charger_detect_status();
battery_charging_control(CHARGING_CMD_GET_CHARGER_DET_STATUS,&chr_status);
static kal_uint32 charging_get_charger_det_status(void *data)
{
kal_uint32 status = STATUS_OK;
#if defined(CHRDET_SW_MODE_EN)
kal_uint32 vchr_val=0;
vchr_val = PMIC_IMM_GetOneChannelValue(4,5,1);
vchr_val = (((330+39)*100*vchr_val)/39)/100;
if( vchr_val > 4300 )
{
battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=Y (%d)\n", vchr_val);
*(kal_uint32 *)data = KAL_TRUE;
}
else
{
battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=N (%d)\n", vchr_val);
*(kal_uint32 *)data = KAL_FALSE;
}
#else
*(kal_bool*)(data) = upmu_get_rgs_chrdet();
#endif
if( upmu_get_rgs_chrdet() == 0 )
g_charger_type = CHARGER_UNKNOWN;
return status;
}
fg_qmax_update_for_aging_flag = 1;
// AUX ADC算法指只依赖ADC读值,然后查表读取电量的算法
#if defined(SOC_BY_AUXADC)
return auxadc_algo_run();
#endif
//通过开路电压查表得到初始电量D0,后续电量通过电流积分累积,通用性强,依赖初始电量的精确度。
#if defined(SOC_BY_HW_FG)
if(g_auxadc_solution == 1)
{
return auxadc_algo_run();
}
else
{
fgauge_algo_run();
return gFG_capacity_by_c; // hw fg, //return gfg_percent_check_point; // voltage mode
}
#endif
/*//////////////////////////////////////////////////////////////////////////
6582 平台用的计量方法【在 Battery_Charging_Introduction_for_customer_V1.0.pdf】
SW FG算法和HW FG算法。事实上MTK平台项目通常采用的是混合型算法。 */
#if defined(SOC_BY_SW_FG)
oam_run();
void oam_run(void)
{
/* SW FG的核心 在于 通过两种方式更新电压,去逼近真实开路电压 最终查表获取近似真实的电量值。
ocv1 被假定为开路电压
ocv2则是闭路电压,
D0 D1 D2 D3 D4 D5 代表不同的放电深度*/
int vol_bat=0;
//int vol_bat_hw_ocv=0;
//int d_hw_ocv=0;
int charging_current=0;
int ret=0;
//kal_uint32 now_time;
struct timespec now_time;
kal_int32 delta_time = 0;
//now_time = rtc_read_hw_time();
getrawmonotonic(&now_time);
//delta_time = now_time - last_oam_run_time;
delta_time = now_time.tv_sec - last_oam_run_time.tv_sec;
bm_print(BM_LOG_FULL, "[oam_run_time] last time=%d, now time=%d, delta time=%d\n",
last_oam_run_time.tv_sec, now_time.tv_sec, delta_time);
last_oam_run_time = now_time;
// Reconstruct table if temp changed;
fgauge_construct_table_by_temp();
void fgauge_construct_table_by_temp(void)
{
#if defined(CONFIG_POWER_EXT)
#else
kal_uint32 i;
static kal_int32 init_temp = KAL_TRUE;
static kal_int32 curr_temp, last_temp, avg_temp;
static kal_int32 battTempBuffer[TEMP_AVERAGE_SIZE];
static kal_int32 temperature_sum;
static kal_uint8 tempIndex = 0;
/* 通过获得当前 NTC 电压,查表并进行线性插值法,得到当前的温度值 */
curr_temp = battery_meter_get_battery_temperature();
// Temperature window init
if (init_temp == KAL_TRUE)
{
for (i=0; i 100) oam_d_1 = 100;
oam_d_2 = oam_d0 + (oam_car_2*100/10)/gFG_BATT_CAPACITY_aging;
if(oam_d_2 < 0) oam_d_2 = 0;
if(oam_d_2 > 100) oam_d_2 = 100;
/*//////////////////////////////////////////////////////////////////////////////////
// 整个程序的核心在这里, 他使用了两种方法更新电量:
1. 使用补偿过的闭路电压,查表获得电量 【返回给用户的】
2. 使用软件库伦积分,得到电量值 【用来校正的】
两个方法相对独立,但是在此处,方法 1 使用了 方法 2 的电流来进行较正!!!!!!!!!!
//////////////////////////////////////////////////////////////////////////////////////
mtk_imp_tracking() 对闭合电压补偿后,当作开路电压使用
通过对当前有负载的电池电压进行补偿,获得当前开路电压 */
oam_v_ocv_1 = vol_bat + mtk_imp_tracking(vol_bat, oam_i_2, 5);
// ============================================================ // SW FG
// 这个里面返回的是 I*R 的值,即 当前电流 * 当前负载
kal_int32 mtk_imp_tracking(kal_int32 ori_voltage, kal_int32 ori_current, kal_int32 recursion_time)
{
kal_int32 ret_compensate_value = 0;
kal_int32 temp_voltage_1 = ori_voltage; // 闭路电压
kal_int32 temp_voltage_2 = temp_voltage_1; // 开路电压,第一次 = 闭路电压,后来都是补偿过 IR 的
int i = 0;
/* 迭代 5 次,反复执行 闭合补偿得开路电压,开路电压查表得内阻,内阻补偿闭路电压 */
for(i=0 ; i < recursion_time ; i++)
{
/* 将闭路电压当做开路电压查内阻 */
gFG_resistance_bat = fgauge_read_r_bat_by_v(temp_voltage_2);
/* 算出 IR drop */
ret_compensate_value = ( (ori_current) * (gFG_resistance_bat + R_FG_VALUE)) / 1000;
// ret_compensate是int型变量 做除法时取整处理 会引入较大误差, 加上这个值使结果四舍五入
ret_compensate_value = (ret_compensate_value+(10/2)) / 10;
/* 开路电压 = 当前电压 + IR drop */
temp_voltage_2 = temp_voltage_1 + ret_compensate_value;
bm_print(BM_LOG_FULL, "[mtk_imp_tracking] temp_voltage_2=%d,temp_voltage_1=%d,ret_compensate_value=%d,gFG_resistance_bat=%d\n",
temp_voltage_2,temp_voltage_1,ret_compensate_value,gFG_resistance_bat);
}
/* 通过上面获得的开路电压查找最终内阻 */
gFG_resistance_bat = fgauge_read_r_bat_by_v(temp_voltage_2);
/* 算出 IR drop */
ret_compensate_value = ( (ori_current) * (gFG_resistance_bat + R_FG_VALUE + FG_METER_RESISTANCE)) / 1000;
/* 四舍五入 */
ret_compensate_value = (ret_compensate_value+(10/2)) / 10;
gFG_compensate_value = ret_compensate_value; // I*R 补偿负载
bm_print(BM_LOG_FULL, "[mtk_imp_tracking] temp_voltage_2=%d,temp_voltage_1=%d,ret_compensate_value=%d,gFG_resistance_bat=%d\n",
temp_voltage_2,temp_voltage_1,ret_compensate_value,gFG_resistance_bat);
/* 该内阻 R* 电流 I 算出最终的电压补偿值 V
Vbat 是闭路电压
SW ocv 是开路电压
Sw ocv = V + Vbat
*/
return ret_compensate_value;
}
////////////////////////////////////////////////////////////////////////
/* 通过补偿的开路电压,获得电池电量百分比 */
oam_d_3 = fgauge_read_d_by_v(oam_v_ocv_1);
if(oam_d_3 < 0) oam_d_3 = 0;
if(oam_d_3 > 100) oam_d_3 = 100;
/* 通过开路电压,获得电池内阻 */
oam_r_1 = fgauge_read_r_bat_by_v(oam_v_ocv_1);
////////////////////////////////////////////////////////////////////////
/* 通过库伦积分获得的电池容量百分比,来获得电池开路电压*/
oam_v_ocv_2 = fgauge_read_v_by_d(oam_d_2);
/* 通过开路电压,获得电池内阻 */
oam_r_2 = fgauge_read_r_bat_by_v(oam_v_ocv_2);
#if 0
oam_d_4 = (oam_d_2+oam_d_3)/2;
#else
oam_d_4 = oam_d_3;
#endif
// 从上一次运行本函数到现在当前电量变化的值
gFG_columb = oam_car_2/10; //mAh
/* 判断充电状态 */
if( (oam_i_1 < 0) || (oam_i_2 < 0) )
gFG_Is_Charging = KAL_TRUE;
else
gFG_Is_Charging = KAL_FALSE;
#if 0
if(gFG_Is_Charging == KAL_FALSE)
{
d5_count_time = 60;
}
else
{
charging_current = get_charging_setting_current();
charging_current = charging_current / 100;
d5_count_time_rate = (((gFG_BATT_CAPACITY_aging*60*60/100/(charging_current-50))*10)+5)/10;
if(d5_count_time_rate < 1)
d5_count_time_rate = 1;
d5_count_time = d5_count_time_rate;
}
#else
d5_count_time = 60;
#endif
/////////////////////////////////////////////////////////////////////////////////////////////
// D5 与 D3 对比
/* 对获得的 D3 进行优化,有 1min 的限制,不会跳变,电量变化更平滑
1分钟内电量值不会改变,且每分钟电量的变化不会大于1%,这样用户体验会比较好。
防止因为低电压时陡峭的电量曲线,以及比较耗电的应用,或突然的大电流引起的电量跳变。
当然特殊应用下应该取消这个功能,否则会带来电量变化延时等问题*/
if(d5_count >= d5_count_time)
// 60s 执行一次
{
/* 限制电量变化,每 1 分钟,只能变化 1% */
/* 放电状态 */
if(gFG_Is_Charging == KAL_FALSE)
{
if( oam_d_3 > oam_d_5 )
{
oam_d_5 = oam_d_5 + 1;
}
else
{
if(oam_d_4 > oam_d_5)
{
oam_d_5 = oam_d_5 + 1;
}
}
}
/* 充电状态 */
else
{
if( oam_d_5 > oam_d_3 )
{
oam_d_5 = oam_d_5 - 1;
}
else
{
if(oam_d_4 < oam_d_5)
{
oam_d_5 = oam_d_5 - 1;
}
}
}
d5_count = 0;
oam_d_3_pre = oam_d_3;
oam_d_4_pre = oam_d_4;
}
else
// 10s 执行一次本函数
{
d5_count = d5_count + 10;
}
bm_print(BM_LOG_CRTI, "[oam_run] %d,%d,%d,%d,%d,%d,%d,%d\n",
d5_count, d5_count_time, oam_d_3_pre, oam_d_3, oam_d_4_pre, oam_d_4, oam_d_5, charging_current);
if(oam_run_i == 0)
{
bm_print(BM_LOG_FULL, "[oam_run] oam_i_1,oam_i_2,oam_car_1,oam_car_2,oam_d_1,oam_d_2,oam_v_ocv_1,oam_d_3,oam_r_1,oam_v_ocv_2,oam_r_2,vol_bat,g_vol_bat_hw_ocv,g_d_hw_ocv\n");
oam_run_i=1;
}
bm_print(BM_LOG_FULL, "[oam_run] %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
oam_i_1,oam_i_2,oam_car_1,oam_car_2,oam_d_1,oam_d_2,oam_v_ocv_1,oam_d_3,oam_r_1,oam_v_ocv_2,oam_r_2,vol_bat,g_vol_bat_hw_ocv,g_d_hw_ocv);
bm_print(BM_LOG_FULL, "[oam_total] %d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
gFG_capacity_by_c, gFG_capacity_by_v, gfg_percent_check_point,
oam_d_1, oam_d_2, oam_d_3, oam_d_4, oam_d_5, gFG_capacity_by_c_init, g_d_hw_ocv);
bm_print(BM_LOG_CRTI, "[oam_total_s] %d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
gFG_capacity_by_c, // 1
gFG_capacity_by_v, // 2
gfg_percent_check_point, // 3
(100-oam_d_1), // 4
(100-oam_d_2), // 5
(100-oam_d_3), // 6
(100-oam_d_4), // 9
(100-oam_d_5), // 10
gFG_capacity_by_c_init, // 7
(100-g_d_hw_ocv) // 8
);
bm_print(BM_LOG_FULL, "[oam_total_s_err] %d,%d,%d,%d,%d,%d,%d\n",
(gFG_capacity_by_c - gFG_capacity_by_v),
(gFG_capacity_by_c - gfg_percent_check_point),
(gFG_capacity_by_c - (100-oam_d_1)),
(gFG_capacity_by_c - (100-oam_d_2)),
(gFG_capacity_by_c - (100-oam_d_3)),
(gFG_capacity_by_c - (100-oam_d_4)),
(gFG_capacity_by_c - (100-oam_d_5))
);
bm_print(BM_LOG_CRTI, "[oam_init_inf] %d, %d, %d, %d, %d, %d\n",
gFG_voltage, (100 - fgauge_read_capacity_by_v(gFG_voltage)), g_rtc_fg_soc, gFG_DOD0 ,oam_v_ocv_init, force_get_tbat());
bm_print(BM_LOG_CRTI, "[oam_run_inf] %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
oam_v_ocv_1,oam_v_ocv_2,vol_bat,oam_i_1,oam_i_2,oam_r_1,oam_r_2,oam_car_1,oam_car_2,gFG_BATT_CAPACITY_aging,force_get_tbat(), oam_d0);
bm_print(BM_LOG_CRTI, "[oam_result_inf] %d, %d, %d, %d, %d, %d\n",
oam_d_1, oam_d_2, oam_d_3, oam_d_4, oam_d_5, BMT_status.UI_SOC);
}
// 这边的返回值将填充BMT_status.SOC ,这个参数再经过优化得到BMT_status.UI_SOC就是菜单栏看到的电池电量了。
//////////////////////////////////////////////////////////////////////////////////////////////
/* D5 是做过平滑,每 1min 电量变化只能是 1% 的,可能会有延时 */
#if (OAM_D5 == 1)
return (100-oam_d_5);
#else
/* D2 则是原版的,没有做平滑处理,电量变化可能会跳变,但是变化及时 */
return (100-oam_d_2);
#endif
#endif
#endif
}
bat_meter_timeout = KAL_FALSE;
bat_spm_timeout = FALSE;
}
else
{
if (previous_SOC == -1)
/* 通过两种方式更新电压,去逼近真实的开路电压,最终查表获取近似真实的电量值百分比 */
SOC = battery_meter_get_battery_percentage();
else
SOC = previous_SOC;
}
ZCV = battery_meter_get_battery_zcv();
return gFG_voltage; // 返回 hw ocv 电压
/* 更新电池状态 */
BMT_status.ICharging = mt_battery_average_method(&batteryCurrentBuffer[0],ICharging, &icharging_sum, batteryIndex);
BMT_status.bat_vol = mt_battery_average_method(&batteryVoltageBuffer[0],bat_vol, &bat_sum, batteryIndex);
BMT_status.temperature = mt_battery_average_method(&batteryTempBuffer[0],temperature, &temperature_sum, batteryIndex);
BMT_status.Vsense = Vsense;
BMT_status.charger_vol = charger_vol;
BMT_status.temperatureV = temperatureV;
BMT_status.temperatureR = temperatureR;
BMT_status.SOC = SOC;
BMT_status.ZCV = ZCV;
if(BMT_status.charger_exist == KAL_FALSE)
{
if(BMT_status.SOC > previous_SOC && previous_SOC >= 0)
BMT_status.SOC = previous_SOC;
}
previous_SOC = BMT_status.SOC;
batteryIndex++;
if (batteryIndex >= BATTERY_AVERAGE_SIZE)
batteryIndex = 0;
battery_xlog_printk(BAT_LOG_CRTI, "AvgVbat=(%d),bat_vol=(%d),AvgI=(%d),I=(%d),VChr=(%d),AvgT=(%d),T=(%d),pre_SOC=(%d),SOC=(%d),ZCV=(%d)\n",
BMT_status.bat_vol,bat_vol,BMT_status.ICharging,ICharging,BMT_status.charger_vol,BMT_status.temperature,temperature,previous_SOC,BMT_status.SOC,BMT_status.ZCV);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// 3. 电池温度保护
/* 电池温度检查,如果温度超过 60 度,关机重启 */
mt_battery_thermal_check();
static void mt_battery_thermal_check(void)
{
if( (g_battery_thermal_throttling_flag==1) || (g_battery_thermal_throttling_flag==3) )
{
if(battery_cmd_thermal_test_mode == 1){
BMT_status.temperature = battery_cmd_thermal_test_mode_value;
battery_xlog_printk(BAT_LOG_FULL, "[Battery] In thermal_test_mode , Tbat=%d\n", BMT_status.temperature);
}
#if defined(MTK_JEITA_STANDARD_SUPPORT)
//ignore default rule
#else
if(BMT_status.temperature >= 60)
{
#if defined(CONFIG_POWER_EXT)
battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] CONFIG_POWER_EXT, no update battery update power down.\n");
#else
{
if( (g_platform_boot_mode==META_BOOT) || (g_platform_boot_mode==ADVMETA_BOOT) || (g_platform_boot_mode==ATE_FACTORY_BOOT) )
{
battery_xlog_printk(BAT_LOG_FULL, "[BATTERY] boot mode = %d, bypass temperature check\n", g_platform_boot_mode);
}
else
// 正常启动的话,超过温度,系统重启
{
struct battery_data *bat_data = &battery_main;
struct power_supply *bat_psy = &bat_data->psy;
battery_xlog_printk(BAT_LOG_CRTI, "[Battery] Tbat(%d)>=60, system need power down.\n", BMT_status.temperature);
bat_data->BAT_CAPACITY = 0;
power_supply_changed(bat_psy);
if( BMT_status.charger_exist == KAL_TRUE )
{
// can not power down due to charger exist, so need reset system
//battery_charging_control(CHARGING_CMD_SET_PLATFORM_RESET,NULL);
}
//avoid SW no feedback
battery_charging_control(CHARGING_CMD_SET_POWER_OFF,NULL);
static kal_uint32 charging_set_power_off(void *data)
{
kal_uint32 status = STATUS_OK;
battery_xlog_printk(BAT_LOG_CRTI, "charging_set_power_off=%d\n");
/* */
mt_power_off();
void mt_power_off(void)
{
printk("mt_power_off\n");
/* pull PWRBB low */
rtc_bbpu_power_down();
void rtc_bbpu_power_down(void)
{
unsigned long flags;
spin_lock_irqsave(&rtc_lock, flags);
hal_rtc_bbpu_pwdn();
spin_unlock_irqrestore(&rtc_lock, flags);
}
while (1) {
#if defined(CONFIG_POWER_EXT)
//EVB
printk("EVB without charger\n");
#else
//Phone
printk("Phone with charger\n");
if (pmic_chrdet_status() == KAL_TRUE)
arch_reset(0, "power_off_with_charger");
#endif
}
}
return status;
}
//mt_power_off();
}
}
#endif
}
#endif
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// 4. 电池状态检查
/* 对电池状态进行检查,如果有问题,则会调用 printk() 进行打印 */
mt_battery_notify_check();
void mt_battery_notify_check(void)
{
g_BatteryNotifyCode = 0x0000;
if(g_BN_TestMode == 0x0000) /* for normal case */
{
battery_xlog_printk(BAT_LOG_FULL, "[BATTERY] mt_battery_notify_check\n");
mt_battery_notify_VCharger_check();
mt_battery_notify_VBatTemp_check();
mt_battery_notify_ICharging_check();
mt_battery_notify_VBat_check();
mt_battery_notify_TatalChargingTime_check();
}
else /* for UI test */
{
mt_battery_notify_UI_test();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 5. 调用具本的硬件相关函数进行充电,充电时会进行 CC/CV 之类的状态机切换就是在这里进行的
/* 如果存在充电线,则调用具体充电芯片相关的函数进行充电 */
if( BMT_status.charger_exist == KAL_TRUE )
{
/* 检查电池状态,设置到 BMT_status.bat_charging_state 中 */
mt_battery_CheckBatteryStatus();
/* 充电策略,这里有两个文件: switch_charging.c 和 linear_charging.c
他们的关系是,如果定义了任一外部充电 IC,则选择 switch_charging.c 的函数,否则就是 linear_charging.c 的函数
这里就是调用具体的芯片的充电相关函数进行充电 */
mt_battery_charging_algorithm();
void mt_battery_charging_algorithm()
{
switch(BMT_status.bat_charging_state)
{
case CHR_PRE :
BAT_PreChargeModeAction();
break;
case CHR_CC :
BAT_ConstantCurrentModeAction();
break;
case CHR_TOP_OFF :
BAT_TopOffModeAction();
break;
case CHR_BATFULL:
BAT_BatteryFullAction();
break;
case CHR_HOLD:
BAT_BatteryHoldAction();
break;
case CHR_ERROR:
BAT_BatteryStatusFailAction();
break;
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 6. 更新电池显示状态
/* 更新设置节点的内容:
/sys/class/power_supply/下的文件夹
wireless_main
battery_main
ac_main
usb_main
*/
mt_battery_update_status();
static void mt_battery_update_status(void)
{
#if defined(CONFIG_POWER_EXT)
battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] CONFIG_POWER_EXT, no update Android.\n");
#else
{
wireless_update(&wireless_main);
battery_update(&battery_main);
ac_update(&ac_main);
usb_update(&usb_main);
}
#endif
}
}
mutex_unlock(&bat_mutex);
battery_xlog_printk(BAT_LOG_FULL, "wait event \n" );
/* 睡眠等待唤醒 */
wait_event(bat_thread_wq, (bat_thread_timeout == KAL_TRUE));
bat_thread_timeout = KAL_FALSE;
/* 每 10s 启动一次 */
hrtimer_start(&battery_kthread_timer, ktime, HRTIMER_MODE_REL);
ktime = ktime_set(BAT_TASK_PERIOD, 0); // 10s, 10* 1000 ms
/* 如果有充电线插入且 xxx */
if( chr_wake_up_bat == KAL_TRUE && g_smartbook_update != 1) // for charger plug in/ out
{
g_smartbook_update = 0;
/* 重新计算当前电池电量,复位 oam 算法相关参数 */
battery_meter_reset();
kal_int32 battery_meter_reset(void)
{
#if defined(CONFIG_POWER_EXT)
return 0;
#else
/* 获得 ui 显示的百分比 */
kal_uint32 ui_percentage = bat_get_ui_percentage();
kal_uint32 bat_get_ui_percentage(void)
{
// for plugging out charger in recharge phase, using SOC as UI_SOC
if(chr_wake_up_bat == KAL_TRUE)
return BMT_status.SOC;
else
return BMT_status.UI_SOC;
// typedef struct
// {
// kal_bool bat_exist;
// kal_bool bat_full; // 电池是否充满标志
// INT32 bat_charging_state;
// UINT32 bat_vol;
// kal_bool bat_in_recharging_state;
// kal_uint32 Vsense;
// kal_bool charger_exist;
// UINT32 charger_vol;
// INT32 charger_protect_status;
// INT32 ICharging;
// INT32 IBattery;
// INT32 temperature;
// INT32 temperatureR;
// INT32 temperatureV;
// UINT32 total_charging_time;
// UINT32 PRE_charging_time;
// UINT32 CC_charging_time;
// UINT32 TOPOFF_charging_time;
// UINT32 POSTFULL_charging_time;
// UINT32 charger_type;
// INT32 SOC;
// INT32 UI_SOC; // ui 显示电量百分比
// UINT32 nPercent_ZCV;
// UINT32 nPrecent_UI_SOC_check_point;
// UINT32 ZCV;
// } PMU_ChargerStruct;
}
/* ////////////////////////////////////////////////
// 如果电池充满了,则更新电池最大容量 */
if(bat_is_charging_full() == KAL_TRUE) // charge full
// 判断电池是否充满
kal_bool bat_is_charging_full(void)
{
if((BMT_status.bat_full == KAL_TRUE) && (BMT_status.bat_in_recharging_state == KAL_FALSE))
return KAL_TRUE;
else
return KAL_FALSE;
}
{
/* 如果 fg_qmax_update_for_aging_flag == 1
则更新电池电容量,并将 fg_qmax_update_for_aging_flag 置为 0 */
if(fg_qmax_update_for_aging_flag == 1)
{
fg_qmax_update_for_aging();
void fg_qmax_update_for_aging(void)
{
#if defined(CONFIG_POWER_EXT)
#else
kal_bool hw_charging_done = bat_is_charging_full();
/* 如果电池充满了,更新电池最大容量 */
if(hw_charging_done == KAL_TRUE) // charging full, g_HW_Charging_Done == 1
{
/* gFG_DOD0 应该是 100%-ui 显示百分比
即当前使用完的容量百分比 */
if(gFG_DOD0 > 85)
{
// 表示在放电
if(gFG_columb < 0) // gFG_columb: 从上一次运行本函数到现在当前电量变化的值
gFG_columb = gFG_columb - gFG_columb*2; // absolute value
gFG_BATT_CAPACITY_aging = ( ( (gFG_columb*1000)+(5*gFG_DOD0) ) / gFG_DOD0 ) / 10; // gFG_BATT_CAPACITY_aging: 当前温度电池最大容量
// tuning
gFG_BATT_CAPACITY_aging = (gFG_BATT_CAPACITY_aging * 100) / AGING_TUNING_VALUE;
/* 如果当前电池容量值为 0 */
if(gFG_BATT_CAPACITY_aging == 0)
{
/* 先通过 battery_meter_get_battery_temperature() 获得电池温度,再通过 fgauge_get_Q_max() 计算电量
这里获得当前电池的容量 */
gFG_BATT_CAPACITY_aging = fgauge_get_Q_max(battery_meter_get_battery_temperature());
bm_print(BM_LOG_CRTI, "[fg_qmax_update_for_aging] error, restore gFG_BATT_CAPACITY_aging (%d)\n", gFG_BATT_CAPACITY_aging);
}
bm_print(BM_LOG_CRTI, "[fg_qmax_update_for_aging] need update : gFG_columb=%d, gFG_DOD0=%d, new_qmax=%d\r\n",
gFG_columb, gFG_DOD0, gFG_BATT_CAPACITY_aging);
}
else
{
bm_print(BM_LOG_CRTI, "[fg_qmax_update_for_aging] no update : gFG_columb=%d, gFG_DOD0=%d, new_qmax=%d\r\n",
gFG_columb, gFG_DOD0, gFG_BATT_CAPACITY_aging);
}
}
/* 如果电池未充满 */
else
{
bm_print(BM_LOG_CRTI, "[fg_qmax_update_for_aging] hw_charging_done=%d\r\n", hw_charging_done);
}
#endif
}
fg_qmax_update_for_aging_flag=0;
}
}
/* car: 库伦计的缩写
这里是复位库伦计 */
reset_parameter_car();
void reset_parameter_car(void)
{
#if defined(SOC_BY_HW_FG)
int ret = 0;
/* 调用对应充电芯片的操作函数,复位硬件,这里对应 PMIC 的函数为 */
ret = battery_meter_ctrl(BATTERY_METER_CMD_HW_RESET, NULL);
static kal_int32 fgauge_hw_reset//(void *data)
{
return STATUS_OK;
}
gFG_columb = 0;
#endif
#if defined(SOC_BY_SW_FG)
oam_car_1 = 0;
oam_car_2 = 0;
gFG_columb = 0;
#endif
}
/* DOD: DOD: 放电深度,100-DOD 即电容容量 */
reset_parameter_dod_full(ui_percentage);
void reset_parameter_dod_full(kal_uint32 ui_percentage)
{
#if defined(SOC_BY_HW_FG)
bm_print(BM_LOG_CRTI, "[battery_meter_reset]1 DOD0=%d,DOD1=%d,ui=%d\n", gFG_DOD0, gFG_DOD1, ui_percentage);
gFG_DOD0 = 100 - ui_percentage;
gFG_DOD1 = gFG_DOD0;
bm_print(BM_LOG_CRTI, "[battery_meter_reset]2 DOD0=%d,DOD1=%d,ui=%d\n", gFG_DOD0, gFG_DOD1, ui_percentage);
#endif
// 我们用的是这种,软件库伦积分
#if defined(SOC_BY_SW_FG)
bm_print(BM_LOG_CRTI, "[battery_meter_reset]1 oam_d0=%d,oam_d_5=%d,ui=%d\n", oam_d0, oam_d_5, ui_percentage);
oam_d0 = 100 - ui_percentage;
gFG_DOD0 = oam_d0;
gFG_DOD1 = oam_d0;
oam_d_1 = oam_d0;
oam_d_2 = oam_d0;
oam_d_3 = oam_d0;
oam_d_4 = oam_d0;
oam_d_5 = oam_d0; // 相当于平滑处理过的 D3
bm_print(BM_LOG_CRTI, "[battery_meter_reset]2 oam_d0=%d,oam_d_5=%d,ui=%d\n", oam_d0, oam_d_5, ui_percentage);
#endif
}
return 0;
#endif
}
chr_wake_up_bat = KAL_FALSE;
battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] Charger plug in/out, Call battery_meter_reset. (%d)\n", BMT_status.UI_SOC);
}
}
return 0;
}
battery_xlog_printk(BAT_LOG_CRTI, "[battery_probe] bat_thread_kthread Done\n");
// 电池过充保护相关检测与初始化,他 2s 检测一次
charger_hv_detect_sw_workaround_init();
void charger_hv_detect_sw_workaround_init(void)
{
ktime_t ktime;
ktime = ktime_set(0, BAT_MS_TO_NS(2000));
hrtimer_init(&charger_hv_detect_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
charger_hv_detect_timer.function = charger_hv_detect_sw_workaround;
hrtimer_start(&charger_hv_detect_timer, ktime, HRTIMER_MODE_REL);
// 这个函数是周期来检查电池电压是否超出限制,即过充保护,超出了就不能充电了
charger_hv_detect_thread = kthread_run(charger_hv_detect_sw_thread_handler, 0, "mtk charger_hv_detect_sw_workaround");
// 这个函数是周期来检查电池电压是否超出限制,即过充保护,超出了就不能充电了
int charger_hv_detect_sw_thread_handler(void *unused)
{
ktime_t ktime;
kal_uint32 charging_enable;
kal_uint32 hv_voltage = BATTERY_VOLT_07_000000_V;
kal_bool hv_status;
do
{
ktime = ktime_set(0, BAT_MS_TO_NS(2000));
if(chargin_hw_init_done)
/* 高压检测,应该是电池超过这个电压时,就不可以充电了 */
battery_charging_control(CHARGING_CMD_SET_HV_THRESHOLD,&hv_voltage);
static kal_uint32 charging_set_hv_threshold(void *data)
{
kal_uint32 status = STATUS_OK;
kal_uint32 set_hv_voltage;
kal_uint32 array_size;
kal_uint16 register_value;
kal_uint32 voltage = *(kal_uint32*)(data);
array_size = GETARRAYNUM(VCDT_HV_VTH);
set_hv_voltage = bmt_find_closest_level(VCDT_HV_VTH, array_size, voltage);
register_value = charging_parameter_to_value(VCDT_HV_VTH, array_size ,set_hv_voltage);
/* 设置 PMIC 的 CHR_CON1 */
upmu_set_rg_vcdt_hv_vth(register_value);
return status;
}
wait_event_interruptible(charger_hv_detect_waiter, (charger_hv_detect_flag == KAL_TRUE));
/* 如果检测到充电器,则检测下电池是否存在 */
if ((upmu_is_chr_det() == KAL_TRUE))
{
/* 检测电池是否存在 */
check_battery_exist();
void check_battery_exist(void)
{
#if defined(CONFIG_DIS_CHECK_BATTERY)
battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] Disable check battery exist.\n");
#else
kal_uint32 baton_count = 0;
kal_uint32 charging_enable = KAL_FALSE;
kal_uint32 battery_status;
kal_uint32 i;
for(i=0;i<3;i++)
{
/* 检测电池是否存在,通过读取 PMIC 的 CHR_CON7 */
battery_charging_control(CHARGING_CMD_GET_BATTERY_STATUS,&battery_status);
static kal_uint32 charging_get_battery_status(void *data)
{
kal_uint32 status = STATUS_OK;
upmu_set_baton_tdet_en(1);
upmu_set_rg_baton_en(1);
*(kal_bool*)(data) = upmu_get_rgs_baton_undet();
return status;
}
baton_count += battery_status;
}
if( baton_count >= 3)
{
if( (g_platform_boot_mode==META_BOOT) || (g_platform_boot_mode==ADVMETA_BOOT) || (g_platform_boot_mode==ATE_FACTORY_BOOT) )
{
battery_xlog_printk(BAT_LOG_FULL, "[BATTERY] boot mode = %d, bypass battery check\n", g_platform_boot_mode);
}
else
{
battery_xlog_printk(BAT_LOG_FULL, "[BATTERY] Battery is not exist, power off FAN5405 and system (%d)\n", baton_count);
//battery_charging_control(CHARGING_CMD_ENABLE,&charging_enable);
//battery_charging_control(CHARGING_CMD_SET_PLATFORM_RESET,NULL);
}
}
#endif
}
}
charger_hv_detect_flag = KAL_FALSE;
if(chargin_hw_init_done)
/* 查看 PMIC 是否开启了高压保护? */
battery_charging_control(CHARGING_CMD_GET_HV_STATUS,&hv_status);
static kal_uint32 charging_get_hv_status(void *data)
{
kal_uint32 status = STATUS_OK;
*(kal_bool*)(data) = upmu_get_rgs_vcdt_hv_det();
return status;
}
if(hv_status == KAL_TRUE)
{
battery_xlog_printk(BAT_LOG_CRTI, "[charger_hv_detect_sw_thread_handler] charger hv\n");
charging_enable = KAL_FALSE;
if(chargin_hw_init_done)
battery_charging_control(CHARGING_CMD_ENABLE,&charging_enable);
}
else
{
battery_xlog_printk(BAT_LOG_FULL, "[charger_hv_detect_sw_thread_handler] upmu_chr_get_vcdt_hv_det() != 1\n");
}
if(chargin_hw_init_done)
battery_charging_control(CHARGING_CMD_RESET_WATCH_DOG_TIMER,NULL);
hrtimer_start(&charger_hv_detect_timer, ktime, HRTIMER_MODE_REL);
} while (!kthread_should_stop());
return 0;
}
if (IS_ERR(charger_hv_detect_thread))
{
battery_xlog_printk(BAT_LOG_FULL, "[%s]: failed to create charger_hv_detect_sw_workaround thread\n", __FUNCTION__);
}
battery_xlog_printk(BAT_LOG_CRTI, "charger_hv_detect_sw_workaround_init : done\n" );
}
/*LOG System Set*/
init_proc_log();
#endif
g_bat_init_flag = KAL_TRUE;
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 第二个调用的 probe
static int mt_batteryNotify_probe(struct platform_device *dev)
{
int ret_device_file = 0;
//struct proc_dir_entry *entry = NULL;
struct proc_dir_entry *battery_dir = NULL;
battery_xlog_printk(BAT_LOG_CRTI, "******** mt_batteryNotify_probe!! ********\n" );
ret_device_file = device_create_file(&(dev->dev), &dev_attr_BatteryNotify);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_BN_TestMode);
battery_dir = proc_mkdir("mtk_battery_cmd", NULL);
if (!battery_dir)
{
pr_err("[%s]: mkdir /proc/mtk_battery_cmd failed\n", __FUNCTION__);
}
else
{
#if 1
proc_create("battery_cmd", S_IRUGO | S_IWUSR, battery_dir, &battery_cmd_proc_fops);
battery_xlog_printk(BAT_LOG_CRTI, "proc_create battery_cmd_proc_fops\n");
#else
entry = create_proc_entry("battery_cmd", S_IRUGO | S_IWUSR, battery_dir);
if (entry)
{
entry->read_proc = battery_cmd_read;
entry->write_proc = battery_cmd_write;
}
#endif
}
battery_xlog_printk(BAT_LOG_CRTI, "******** mtk_battery_cmd!! ********\n" );
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 电池测量模块初始化
module_init(battery_meter_init);
static int __init battery_meter_init(void)
{
int ret;
ret = platform_device_register(&battery_meter_device);
struct platform_device battery_meter_device = {
.name = "battery_meter",
.id = -1,
};
if (ret) {
bm_print(BM_LOG_CRTI, "[battery_meter_driver] Unable to device register(%d)\n", ret);
return ret;
}
ret = platform_driver_register(&battery_meter_driver);
static struct platform_driver battery_meter_driver = {
.probe = battery_meter_probe,
.remove = battery_meter_remove,
.shutdown = battery_meter_shutdown,
.suspend = battery_meter_suspend,
.resume = battery_meter_resume,
.driver = {
.name = "battery_meter",
},
};
if (ret) {
bm_print(BM_LOG_CRTI, "[battery_meter_driver] Unable to register driver (%d)\n", ret);
return ret;
}
bm_print(BM_LOG_CRTI, "[battery_meter_driver] Initialization : DONE \n");
return 0;
}
/////////////////////////////////////////////////////////////////////
// 调用的 probe
// ============================================================ //
static int battery_meter_probe(struct platform_device *dev)
{
int ret_device_file = 0;
battery_meter_ctrl = bm_ctrl_cmd;
bm_print(BM_LOG_CRTI, "[battery_meter_probe] probe\n");
//select battery meter control method
battery_meter_ctrl = bm_ctrl_cmd;
//LOG System Set
init_proc_log_fg();
//last_oam_run_time = rtc_read_hw_time();
getrawmonotonic(&last_oam_run_time);
//Create File For FG UI DEBUG
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_Current);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_volt);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_current);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_zcv);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_temp);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_r);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_car);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_qmax);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_d0);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_d1);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_percentage);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_percentage_fg);
ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_percentage_voltmode);
return 0;
}