power的代码如下:
不同的内核代码和cpu架构位置会稍微有点差异,项目上暂时接触较多的是MT6580平台,使用的GM1.0电量的算法。
主要跑的各个文件的作用
battery_common.c ----对battery的device和driver进行platform平台总线注册、匹配,充电控制主线程
battery_meter.c ---- SW FG和HW FG 算法
linear_charging.c ---- PMIC充电控制。
battery架构分析
参考博客:MTK Battery系统
MTK文档:Customer_Training_Battery_Charging.pdf download
MTK电池显示的具体过程为:硬件ADC读取Battery的各路信息:包括温度,电压等。然后利用MTK开发的电量算法分析得到的数据。Kernel层将电量信息通过写文件节点的方式更新,并通过UEVENT通知上层。上层Service开启UEVENT LISTENER,监听到UEVENT后,读取battery相关文件节点,获取电量信息。Service更新数据后,通过Broadcast通知所有开启了相关listener的activities。
MTK电池驱动分析
先介绍几个重要的结构体:PMU_ChargerStruct,
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; // Charger是否存在Charger电压
UINT32 charger_vol; // Charger电压
INT32 charger_protect_status; //充电保护状态,过流或者过压保护状态
INT32 ICharging; // 充电电流
INT32 IBattery;
INT32 temperature; // 电池温度
INT32 temperatureR;
INT32 temperatureV;
UINT32 total_charging_time; //总的充电时间
UINT32 PRE_charging_time; // Pre cc充电时间
UINT32 CC_charging_time; //cc充电时间
UINT32 TOPOFF_charging_time; //TOPOFF充电时间
UINT32 POSTFULL_charging_time; //Postfull充电时间
UINT32 charger_type; //充电器类型
INT32 SOC; //底层的电量
INT32 UI_SOC; // 上层的电量
UINT32 nPercent_ZCV;
UINT32 nPrecent_UI_SOC_check_point; //N%同步点对应的开路电压以及UI电量
UINT32 ZCV; //电池当前开路电压
} PMU_ChargerStruct;
/* battery_data initialization *///到时会在线程中重新赋值,更新到相应的节点供上层调用
static struct battery_data battery_main = {
.psy = {
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = battery_props,
.num_properties = ARRAY_SIZE(battery_props),
.get_property = battery_get_property,
},
/* CC: modify to have a full power supply status */
#if defined(CONFIG_POWER_EXT)
.BAT_STATUS = POWER_SUPPLY_STATUS_FULL,
.BAT_HEALTH = POWER_SUPPLY_HEALTH_GOOD,
.BAT_PRESENT = 1,
.BAT_TECHNOLOGY = POWER_SUPPLY_TECHNOLOGY_LION,
.BAT_CAPACITY = 100,
.BAT_batt_vol = 4200,
.BAT_batt_temp = 22,
/* Dual battery */
.status_smb = POWER_SUPPLY_STATUS_DISCHARGING,
.capacity_smb = 50,
.present_smb = 0,
/* ADB CMD discharging */
.adjust_power = -1,
#else
.BAT_STATUS = POWER_SUPPLY_STATUS_DISCHARGING,
.BAT_HEALTH = POWER_SUPPLY_HEALTH_GOOD,
.BAT_PRESENT = 1,
.BAT_TECHNOLOGY = POWER_SUPPLY_TECHNOLOGY_LION,
#if defined(PUMP_EXPRESS_SERIES)
.BAT_CAPACITY = -1,
#else
.BAT_CAPACITY = 50,
#endif
.BAT_batt_vol = 0,
.BAT_batt_temp = 0,
/* Dual battery */
.status_smb = POWER_SUPPLY_STATUS_DISCHARGING,
.capacity_smb = 50,
.present_smb = 0,
/* ADB CMD discharging */
.adjust_power = -1,
#endif
/* ============================================================ */
/* ENUM */ battery_meter_hal.h,此枚举变量是用来进行各种数据读取的命令,
battery_meter_hal.c(/kernel-3.18/drivers/misc/mediatek/power/mt6580/)
static signed int (*bm_func[BATTERY_METER_CMD_NUMBER]) (void *data);
使用bm_func绑定相应的adc读取函数,读取计算出battery驱动所需要的各种数据。
/* ============================================================ */
typedef enum {
BATTERY_METER_CMD_HW_FG_INIT,
BATTERY_METER_CMD_GET_HW_FG_CURRENT, /* fgauge_read_current */
BATTERY_METER_CMD_GET_HW_FG_CURRENT_SIGN, /* */
BATTERY_METER_CMD_GET_HW_FG_CAR, /* fgauge_read_columb */
BATTERY_METER_CMD_HW_RESET, /* FGADC_Reset_SW_Parameter */
BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE,
BATTERY_METER_CMD_GET_ADC_V_I_SENSE,
BATTERY_METER_CMD_GET_ADC_V_BAT_TEMP,
BATTERY_METER_CMD_GET_ADC_V_CHARGER,
BATTERY_METER_CMD_GET_HW_OCV,
BATTERY_METER_CMD_DUMP_REGISTER,
BATTERY_METER_CMD_SET_COLUMB_INTERRUPT,
BATTERY_METER_CMD_GET_BATTERY_PLUG_STATUS,
BATTERY_METER_CMD_GET_HW_FG_CAR_ACT, /* fgauge_read_columb */
BATTERY_METER_CMD_SET_LOW_BAT_INTERRUPT,
BATTERY_METER_CMD_GET_LOW_BAT_INTERRUPT_STATUS,
BATTERY_METER_CMD_GET_REFRESH_HW_OCV,
BATTERY_METER_CMD_SET_META_CALI_CURRENT,
BATTERY_METER_CMD_META_CALI_CAR_TUNE_VALUE,
BATTERY_METER_CMD_GET_IS_HW_OCV_READY,
BATTERY_METER_CMD_NUMBER
} BATTERY_METER_CTRL_CMD;
};
battery驱动代码流程分析
可以从out/target/product/k80hd_bsp_fwv_512m/obj/KERNEL_OBJ/System.map看出模块的加载顺序(因为battery_init采用late_initcall)。
c0d38298 t battery_meter_init
c0d382d4 t battery_init
battery_meter.c会先进行加载:
1、
module_init(battery_meter_init);
/* module_exit(battery_meter_exit); *///被注释掉了,没有卸载函数,battery_meter这个模块不会被卸载掉。
2、battery_meter_init:
ret = platform_device_register(&battery_meter_device);
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",
},
};
3、名字匹配上后跑prob函数 battery_meter_probe:
//关键函数解析
battery_meter_ctrl = bm_ctrl_cmd;//绑定对应的adc读取函数,到时会直接调用battery_meter_ctrl函数进行各个电池数据的读取。
bm_func[BATTERY_METER_CMD_HW_FG_INIT] = fgauge_initialization;
bm_func[BATTERY_METER_CMD_GET_HW_FG_CURRENT] = fgauge_read_current;
bm_func[BATTERY_METER_CMD_GET_HW_FG_CURRENT_SIGN] = fgauge_read_current_sign;
bm_func[BATTERY_METER_CMD_GET_HW_FG_CAR] = fgauge_read_columb;
bm_func[BATTERY_METER_CMD_HW_RESET] = fgauge_hw_reset;
bm_func[BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE] = read_adc_v_bat_sense;
bm_func[BATTERY_METER_CMD_GET_ADC_V_I_SENSE] = read_adc_v_i_sense;
bm_func[BATTERY_METER_CMD_GET_ADC_V_BAT_TEMP] = read_adc_v_bat_temp;
bm_func[BATTERY_METER_CMD_GET_ADC_V_CHARGER] = read_adc_v_charger;
bm_func[BATTERY_METER_CMD_GET_HW_OCV] = read_hw_ocv;
bm_func[BATTERY_METER_CMD_DUMP_REGISTER] = dump_register_fgadc;
bm_func[BATTERY_METER_CMD_SET_COLUMB_INTERRUPT] = fgauge_set_columb_interrupt;
bm_func[BATTERY_METER_CMD_GET_BATTERY_PLUG_STATUS] = read_battery_plug_out_status;
bm_func[BATTERY_METER_CMD_SET_LOW_BAT_INTERRUPT] = fgauge_set_low_battery_interrupt;
bm_func[BATTERY_METER_CMD_GET_LOW_BAT_INTERRUPT_STATUS] = fgauge_get_low_battery_interrupt_status;
bm_func[BATTERY_METER_CMD_GET_REFRESH_HW_OCV] = get_refresh_hw_ocv;
batt_meter_init_cust_data();//初始化用户对电池的配置
/* select battery meter control method */
battery_meter_ctrl = bm_ctrl_cmd;//不知道为什么这里由来一遍???
/* LOG System Set */
init_proc_log_fg();//这里可以使用proc节点控制打印的等级
proc_create("fgadc_log", 0644, NULL, &fgadc_proc_fops);
if (proc_fgadc_data == '1') {
bm_print(BM_LOG_CRTI, "enable FGADC driver log system\n");
Enable_FGADC_LOG = BM_LOG_CRTI;
} else if (proc_fgadc_data == '2') {
bm_print(BM_LOG_CRTI, "enable FGADC driver log system:2\n");
Enable_FGADC_LOG = BM_LOG_FULL;
} else {
bm_print(BM_LOG_CRTI, "Disable FGADC driver log system\n");
Enable_FGADC_LOG = 0;
}
/* Create File For FG UI DEBUG */这里创建一系列节点供上层调用使用。/sys/bus/platform/drivers/battery/...
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);
...
//结束,这么最有意义的函数是battery_meter_ctrl = bm_ctrl_cmd;方便battery_common.c中的调用
battery_common.c:
1、late_initcall(battery_init);
battery_init:
ret = platform_device_register(&battery_device);
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,
},
};
//name匹配到之后执行prob函数
2、
get_charging_control()
{
battery_charging_control = chr_control_interface;
}
/*在get_charging_control函数里面,就是将chr_control_interface函数指向battery_charging_control,在后面会有很多对battery_charging_control函数的调用,而所有的调用都是传递一个参数进来,然后对比charging_func数组里面的函数指针,在对其他函数进行调用。*/
signed int chr_control_interface(CHARGING_CTRL_CMD cmd, void *data)
{
signed int status;
status = charging_func[cmd](data);
return status;
}
//charging_func定义在/kernel-3.18/drivers/misc/mediatek/power/mt6580/charing_hw_pmic.c中
static unsigned int (*const 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,
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_platform_boot_mode, charging_set_power_off,
charging_get_power_source, charging_get_csdac_full_flag,
charging_set_ta_current_pattern, charging_set_error_state};
3、battery_charging_control(CHARGING_CMD_GET_PLATFORM_BOOT_MODE, &g_platform_boot_mode);//根据2中绑定的函数得到启动模式
4、 当probe函数注册完了字符设备后,函数进行的随后的进行的操作是在sys下面建立设备节点,总共建立了四个设备节点,分别为ac_main、usb_main、wireless_main和battery_main,这四个节点分别为使用适配器、USB、无线充电以及使用电池供电。电池电量发生变化的时候,会通过这些节点将数据上报给上层,也就是说上层是通过这些节点来读取底层电池电量变化的数据的。
ret = power_supply_register(&(dev->dev), &ac_main.psy);
ret = power_supply_register(&(dev->dev), &usb_main.psy);
ret = power_supply_register(&(dev->dev), &wireless_main.psy);
ret = power_supply_register(&(dev->dev), &battery_main.psy);
5、当初始化完成后,probe函数会创建一个hrtimer定时器,定时器启动bat_thread_kthread函数,bat_thread_kthread函数中的while(1)里面包含了BAT_thread()函数,BAT_thread()就是充电的核心函数。
/* battery kernel thread for 10s check and charger in/out event */
/* Replace GPT timer by hrtime */
battery_kthread_hrtimer_init();
kthread_run(bat_thread_kthread, NULL, "bat_thread_kthread");
//当时间到了就会唤醒bat_thread_kthread这个线程,bat_thread_kthread里会有while1死循环,里面包含了BAT_thread()函数。
while(1){
if (((chargin_hw_init_done == KAL_TRUE) && (battery_suspended == KAL_FALSE))
|| ((chargin_hw_init_done == KAL_TRUE) && (fg_wake_up_bat == KAL_TRUE)))
BAT_thread();
//时间进行重新赋值
bat_thread_timeout = KAL_FALSE;
hrtimer_start(&battery_kthread_timer, ktime, HRTIMER_MODE_REL);
ktime = ktime_set(BAT_TASK_PERIOD, 0); /* 10s, 10* 1000 ms */
if (chr_wake_up_bat == KAL_TRUE && g_smartbook_update != 1) { /* for charger plug in/ out chr_wake_up_bat会被置位 */
g_smartbook_update = 0;
battery_meter_reset();
chr_wake_up_bat = KAL_FALSE;
}
}
//BAT_thread函数分析
void BAT_thread(void)
{
static kal_bool battery_meter_initilized = KAL_FALSE;
if (battery_meter_initilized == KAL_FALSE) {
battery_meter_initial(); /* move from battery_probe() to decrease booting time */
BMT_status.nPercent_ZCV = battery_meter_get_battery_nPercent_zcv();
battery_meter_initilized = KAL_TRUE;
battery_update(&battery_main);
battery_log(BAT_LOG_CRTI, "[battery_meter_initilized] uisoc=soc=%d.\n", gFG_capacity_by_c);
}
mt_battery_charger_detect_check();
mt_battery_GetBatteryData();
if (BMT_status.charger_exist == KAL_TRUE)
check_battery_exist();
mt_battery_thermal_check();
mt_battery_notify_check();
if ((BMT_status.charger_exist == KAL_TRUE) && (battery_suspended == KAL_FALSE)) {
mt_battery_CheckBatteryStatus();
mt_battery_charging_algorithm();
}
mt_battery_update_status();
mt_kpoc_power_off_check();
}
// 当系统第一次启动的时候battery_meter_initilized为KAL_FALSE,所以BAT_thread会调用battery_meter_initial函数。
battery_meter_initial函数为系统启动时运行的,也电池充电做一些初始化操作。
battery_meter_initilized分析:
signed int battery_meter_initial(void)
{
#if defined(SOC_BY_SW_FG)
g_auxadc_solution = 1;
table_init();
oam_init();
#endif
}
在table_init函数中,重构zcv表格:
void table_init(void)
{
int temperature = force_get_tbat(KAL_FALSE);//由ntc电阻得到电压-->read_adc_v_bat_temp,再由电压得到当前温度 bat_temperature_val = BattVoltToTemp(bat_temperature_volt);查表得到。
/* Re-constructure r-table profile according to current temperature */
profile_p_r_table = fgauge_get_profile_r_table(batt_meter_cust_data.temperature_t);//获取ZCV表的电阻和zcv电压关系
if (profile_p_r_table != NULL)
fgauge_construct_r_table_profile(temperature, profile_p_r_table);//根据当前温度重构zcv表,采用线性插值的方式
/* Re-constructure battery profile according to current temperature */
profile_p = fgauge_get_profile(batt_meter_cust_data.temperature_t);
if (profile_p == NULL)
battery_log(BAT_LOG_CRTI, "[FGADC] fgauge_get_profile : create table fail !\r\n");
if (profile_p != NULL)
fgauge_construct_battery_profile(temperature, profile_p);
}
当table_init函数后系统会对一些变量进行初始化操作,包括在dod_init函数中对oam_v_ocv_1和oam_v_ocv_2进行初始化赋值,读取RTC实时时钟芯片的电量值等等,经过这一系列操作后,就会进入battery系统一个最重要的部分,利用积分的方式来求电池的当前电量。
mt_battery_GetBatteryData();
SOC = battery_meter_get_battery_percentage();//求电池的当前电量
oam_run();//算法函数
void oam_run(void)
{
……
//now_time = rtc_read_hw_time();
getrawmonotonic(&now_time); //获取系统当前时间
delta_time = now_time.tv_sec - last_oam_run_time.tv_sec;
last_oam_run_time = now_time;
// Reconstruct table if temp changed;
fgauge_construct_table_by_temp(); // 当电压表发生改变了的时候,重构电压表
vol_bat = 15; //set avg times
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &vol_bat); //得到闭路电压
oam_i_1 = (((oam_v_ocv_1-vol_bat)*1000)*10) / oam_r_1; //0.1mA
oam_i_2 = (((oam_v_ocv_2-vol_bat)*1000)*10) / oam_r_2; //0.1mA
oam_car_1 = (oam_i_1*delta_time/3600) + oam_car_1; //0.1mAh
oam_car_2 = (oam_i_2*delta_time/3600) + oam_car_2; //0.1mAh
oam_d_1 = oam_d0 + (oam_car_1*100/10)/gFG_BATT_CAPACITY_aging; //gFG_BATT_CAPACITY_aging is Q_MAX
if(oam_d_1 < 0) oam_d_1 = 0;
if(oam_d_1 > 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;
oam_v_ocv_1 = vol_bat + mtk_imp_tracking(vol_bat, oam_i_2, 5);
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);
}
oam_run函数中算法的大致思路为:系统当前的电量通过最终的开路电压oam_v_ocv_1查ZCV表得到当前的电量值,而最终开路电压需要通过闭路电压v_bat和闭路电流oam_i_2 去回溯电池内阻,逐次逼近,而oam_i_2 通过另一种方式即电量积分更新的电压oam_v_ocv_2来得到。
6、
/*LOG System Set */
init_proc_log();
proc_create("batdrv_log", 0644, NULL, &bat_proc_fops);//设置Enable_BATDRV_LOG等级
static ssize_t bat_log_write(struct file *filp, const char __user *buff, size_t len, loff_t *data)
{
char proc_bat_data;
if ((len <= 0) || copy_from_user(&proc_bat_data, buff, 1)) {
battery_log(BAT_LOG_FULL, "bat_log_write error.\n");
return -EFAULT;
}
if (proc_bat_data == '1') {
battery_log(BAT_LOG_CRTI, "enable battery driver log system\n");
Enable_BATDRV_LOG = 1;
} else if (proc_bat_data == '2') {
battery_log(BAT_LOG_CRTI, "enable battery driver log system:2\n");
Enable_BATDRV_LOG = 2;
} else {
battery_log(BAT_LOG_CRTI, "Disable battery driver log system\n");
Enable_BATDRV_LOG = 0;
}
return len;
}
BAT_thread在mt_battery_GetBatteryData()获取相关数据后面还有一些检测函数及充电流程函数:
一些需求也包含在里面
mt_battery_charger_detect_check();
mt_battery_GetBatteryData();//获取电池数据和电量计算法
if (BMT_status.charger_exist == KAL_TRUE)
check_battery_exist();//检测电池的连接状态
mt_battery_thermal_check();//thermal,电池温度检测,当温度大于55度(可以客制化下)时会自动关机。
mt_battery_notify_check();
if ((BMT_status.charger_exist == KAL_TRUE) && (battery_suspended == KAL_FALSE)) {
mt_battery_CheckBatteryStatus();//检查电池的状态情况,给BMT_status赋值,包括温度(客制化高低温)、电压、充电时间的状态。后面的充电流程要用到这些状态值。
mt_battery_charging_algorithm();//充电函数,根据不同状态进行充电,可以看下图的状态转化流程。
}
mt_battery_update_status();//更新电池状态到相应节点
mt_kpoc_power_off_check();//是否关机检测,vbus < 2.5V时关机
if ((upmu_is_chr_det() == KAL_FALSE) && (BMT_status.charger_vol < 2500)) { /* vbus < 2.5V */
battery_log(BAT_LOG_CRTI,
"[bat_thread_kthread] Unplug Charger/USB In Kernel Power Off Charging Mode! Shutdown OS!\r\n");
battery_charging_control(CHARGING_CMD_SET_POWER_OFF, NULL);
}
充电状态转化图