【MTK驱动模块_5】battery2--代码架构和流程

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);
		}

充电状态转化图
在这里插入图片描述

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值