赞
踩
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); }
充电状态转化图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。