sprd_battery.c 是充電驅動,這個是充電功能的核心內容,電量顯示策略、溫度檢測策略、充電保護機制等功能在這裡實現,功能實現與硬體細節剝離,調用通用介面實現邏輯控制; 1 sprdbat_probe函數: 1.1 解析設備樹: sprdbat_parse_dt函數如下: 2. 各個工作隊列 ...
sprd_battery.c 是充電驅動,這個是充電功能的核心內容,電量顯示策略、溫度檢測策略、充電保護機制等功能在這裡實現,功能實現與硬體細節剝離,調用通用介面實現邏輯控制;
1 sprdbat_probe函數:
static int sprdbat_probe(struct platform_device *pdev)
{
int ret = -ENODEV;
enum usb_charger_state usb_online_state = USB_CHARGER_DEFAULT;
struct power_supply *ret_ptr = NULL;
struct sprdbat_drivier_data *data = NULL;
struct device_node *np = pdev->dev.of_node;
struct power_supply_desc *battery_desc = NULL,
*ac_desc = NULL, *usb_desc = NULL;
struct power_supply_config battery_cfg = {}, ac_cfg = {}, usb_cfg = {};
if (!np) {
dev_err(&pdev->dev, "device node not found\n");
return -EINVAL;
}
if (sprd_ext_ic_op == NULL) {
dev_err(&pdev->dev, "sprd_ext_ic_op not found\n");
return -EINVAL;
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (data == NULL) {
ret = -ENOMEM;
goto err_data_alloc_failed;
}
data->pdata = sprdbat_parse_dt(pdev);
if (IS_ERR_OR_NULL(data->pdata))
return -ENOMEM;
data->dev = &pdev->dev;
platform_set_drvdata(pdev, data);
sprdbat_data = data;
//通過ADC獲取溫度
data->pdata->channel_temp = iio_channel_get(data->dev, "adc_temp");
if (IS_ERR(data->pdata->channel_temp)) {
ret = PTR_ERR(data->pdata->channel_temp);
goto err_iio_get_temp;
}
//通過ADC獲取vbat電壓
data->pdata->channel_vbat = iio_channel_get(data->dev, "adc_vbat");
if (IS_ERR(data->pdata->channel_vbat)) {
ret = PTR_ERR(data->pdata->channel_vbat);
goto err_iio_get_vbat;
}
//通過ADC獲取充電電壓
data->pdata->channel_vchg = iio_channel_get(data->dev, "adc_vchg");
if (IS_ERR(data->pdata->channel_vchg)) {
ret = PTR_ERR(data->pdata->channel_vchg);
goto err_iio_get_vchg;
}
print_pdata(sprdbat_data->pdata);
battery_desc = devm_kzalloc(&pdev->dev,
sizeof(struct power_supply_desc), GFP_KERNEL);
if (battery_desc == NULL) {
ret = -ENOMEM;
goto err_desc_alloc_failed;
}
//註冊battery 的power_supply介面,這個為上層提供電量、充電狀態等的介面
battery_desc->properties = sprdbat_battery_props;
battery_desc->num_properties = ARRAY_SIZE(sprdbat_battery_props);
battery_desc->get_property = sprdbat_battery_get_property;
battery_desc->set_property = sprdbat_battery_set_property;
battery_desc->property_is_writeable =
sprdbat_battery_property_is_writeable;
battery_desc->name = "battery";
battery_desc->type = POWER_SUPPLY_TYPE_BATTERY;
battery_desc->no_thermal = true;
battery_cfg.drv_data = sprdbat_data;
ac_desc = devm_kzalloc(&pdev->dev,
sizeof(struct power_supply_desc), GFP_KERNEL);
if (ac_desc == NULL) {
ret = -ENOMEM;
goto err_desc_alloc_failed;
}
ac_desc->properties = sprdbat_ac_props;
ac_desc->num_properties = ARRAY_SIZE(sprdbat_ac_props);
ac_desc->get_property = sprdbat_ac_get_property;
ac_desc->set_property = sprdbat_ac_set_property;
ac_desc->property_is_writeable =
sprdbat_ac_property_is_writeable;
//註冊AC的充電power_supply介面
ac_desc->name = "ac";
ac_desc->type = POWER_SUPPLY_TYPE_MAINS;
ac_desc->no_thermal = true;
ac_cfg.drv_data = sprdbat_data;
//usb介面的介面
usb_desc = devm_kzalloc(&pdev->dev,
sizeof(struct power_supply_desc), GFP_KERNEL);
if (usb_desc == NULL) {
ret = -ENOMEM;
goto err_desc_alloc_failed;
}
usb_desc->properties = sprdbat_usb_props;
usb_desc->num_properties = ARRAY_SIZE(sprdbat_usb_props);
usb_desc->get_property = sprdbat_usb_get_property;
usb_desc->name = "usb";
usb_desc->type = POWER_SUPPLY_TYPE_USB;
usb_desc->no_thermal = true;
usb_cfg.drv_data = sprdbat_data;
data->start_charge = sprdbat_start_charge;
data->stop_charge = sprdbat_stop_charge;
ret_ptr = power_supply_register(&pdev->dev, battery_desc, &battery_cfg);
if (IS_ERR(ret_ptr)) {
goto err_battery_failed;
} else {
data->battery = ret_ptr;
data->battery->supplied_to = battery_supply_list;
data->battery->num_supplicants =
ARRAY_SIZE(battery_supply_list);
}
ret_ptr = power_supply_register(&pdev->dev, ac_desc, &ac_cfg);
if (IS_ERR(ret_ptr)) {
goto err_ac_failed;
} else {
data->ac = ret_ptr;
data->ac->supplied_to = supply_list;
data->ac->num_supplicants = ARRAY_SIZE(supply_list);
}
ret_ptr = power_supply_register(&pdev->dev, usb_desc, &usb_cfg);
if (IS_ERR(ret_ptr)) {
goto err_usb_failed;
} else {
data->usb = ret_ptr;
data->usb->supplied_to = supply_list;
data->usb->num_supplicants = ARRAY_SIZE(supply_list);
}
/*
* TODO: switch polling to interrupt again need open this code.
* data->chg_nb.notifier_call = sprdbat_chg_event_call;
* ret = power_supply_reg_notifier(&data->chg_nb);
*
* if (ret)
* dev_err(data->dev, "failed to reg notifier: %d\n", ret);
*/
//註冊文件節點
ret = sysfs_create_group(&data->battery->dev.kobj,
&sprd_bat_group);
if (ret) {
dev_err(&pdev->dev,
"failed to create sprd_bat sysfs device attributes\n");
goto err_sysfs_create_gr;
}
//vbat檢測腳
data->gpio_vbat_detect = data->pdata->gpio_vbat_detect;
if (data->gpio_vbat_detect > 0) {
devm_gpio_request(&pdev->dev,
data->gpio_vbat_detect, "vbat_detect");
gpio_direction_input(data->gpio_vbat_detect);
data->irq_vbat_detect = gpio_to_irq(data->gpio_vbat_detect);
irq_set_status_flags(data->irq_vbat_detect, IRQ_NOAUTOEN);
ret =
devm_request_threaded_irq(&pdev->dev,
data->irq_vbat_detect, NULL,
sprdbat_vbat_detect_irq,
IRQ_TYPE_LEVEL_LOW | IRQF_NO_SUSPEND,
"sprdbat_vbat_detect", data);
if (ret)
dev_err(&pdev->dev, "failed to use vbat gpio: %d\n",
ret);
}
//otg 使能管腳
data->gpio_otg_en = data->pdata->gpio_otg_en;
if (data->gpio_otg_en > 0) {
devm_gpio_request(&pdev->dev,
data->gpio_otg_en, "otg_en");
ret = gpio_direction_output(data->gpio_otg_en, 0);
if (ret)
dev_err(&pdev->dev, "failed to use otg_en gpio: %d\n",
ret);
}
data->bat_info.bat_present = 1;
mutex_init(&data->lock);
//充電睡眠喚醒鎖
wake_lock_init(&(data->charger_wake_lock), WAKE_LOCK_SUSPEND,
"charger_wake_lock");
//初始化電池工作隊列
INIT_DELAYED_WORK(&data->battery_work, sprdbat_battery_works);
//初始化電池睡眠隊列
INIT_DELAYED_WORK(&data->battery_sleep_work,
sprdbat_battery_sleep_works);
//初始化電池檢測中斷的隊列
INIT_WORK(&data->vbat_detect_irq_work, sprdbat_vbat_detect_irq_works);
//電池拔插隊列
INIT_WORK(&data->plug_work, sprdbat_plug_works);
//電池充電隊列
INIT_DELAYED_WORK(&sprdbat_data->sprdbat_charge_work,
sprdbat_charge_works);
data->monitor_wqueue = create_freezable_workqueue("sprdbat_monitor");
if (data->monitor_wqueue == NULL)
goto err_create_wq;
//充電初始化
sprdchg_init(data->pdata);
//庫侖計的初始化
sprdfgu_init(data->pdata);
if (sprdchg_timer_op->timer_request)
sprdchg_timer_op->timer_request(sprdbat_timer_handler,
data->pdata);
else
SPRDBAT_DEBUG("warning !!charge timer ops = null\n");
//充電led的控制
#ifdef CONFIG_LEDS_TRIGGERS
data->charging_led.name = "sprdbat_charging_led";
data->charging_led.default_trigger = "battery-charging";
data->charging_led.brightness_set = sprdchg_led_brightness_set;
ret = led_classdev_register(&pdev->dev, &data->charging_led);
if (ret)
goto err_led_reg;
#endif
sprd_ext_ic_op->ic_init(sprdbat_data);
sprdbat_info_init(data);
SPRDBAT_DEBUG("register_usb_notifier\n");
//註冊usb插拔的回調函數
sprdbat_data->usb_charger =
usb_charger_find_by_name("usb-charger.0");
if (IS_ERR(sprdbat_data->usb_charger)) {
ret = -EPROBE_DEFER;
dev_err(&pdev->dev,
"Failed to find USB gadget: %d\n", ret);
goto err_usb_find_name;
}
//這個函數實質上回調了工作隊列plug_work,也就是調用了sprdbat_plug_works
sprdbat_data->chg_usb_nb.notifier_call = sprdbat_usb_plug_event;
ret = usb_charger_register_notify(sprdbat_data->usb_charger,
&sprdbat_data->chg_usb_nb);
if (ret != 0) {
dev_err(&pdev->dev,
"Failed to register notifier: %d\n", ret);
goto err_usb_reg_notify;
}
sprdbat_data->usb_charger->get_charger_type =
sprdchg_charger_is_adapter_for_usb;
//獲取usb的狀態
usb_online_state = usb_charger_get_state(sprdbat_data->usb_charger);
if (usb_online_state == USB_CHARGER_PRESENT)
queue_work(sprdbat_data->monitor_wqueue,
&sprdbat_data->plug_work);
if (data->gpio_vbat_detect > 0)
enable_irq(sprdbat_data->irq_vbat_detect);
queue_delayed_work(system_power_efficient_wq,
&data->battery_work, 15 * HZ);
SPRDBAT_DEBUG("sprdbat_probe----------end\n");
return 0;
err_usb_reg_notify:
err_usb_find_name:
#ifdef CONFIG_LEDS_TRIGGERS
led_classdev_unregister(&data->charging_led);
err_led_reg:
#endif
destroy_workqueue(data->monitor_wqueue);
err_create_wq:
sysfs_remove_group(&data->battery->dev.kobj,
&sprd_bat_group);
err_sysfs_create_gr:
power_supply_unregister(data->usb);
err_usb_failed:
power_supply_unregister(data->ac);
err_ac_failed:
power_supply_unregister(data->battery);
err_battery_failed:
iio_channel_release(data->pdata->channel_vchg);
err_desc_alloc_failed:
sprdbat_data = NULL;
err_iio_get_vchg:
iio_channel_release(data->pdata->channel_vbat);
err_iio_get_vbat:
iio_channel_release(data->pdata->channel_temp);
err_iio_get_temp:
err_data_alloc_failed:
sprdbat_data = NULL;
return ret;
}
1.1 解析設備樹:
sprdbat_parse_dt函數如下:
battery-adapt-fun:電池相容函數索引0 即為電池 ID識別
battery-adapt-support:電池相容開關
charger-det-gpios:充電檢測管腳
otg-en-gpios:otg使能管腳
chg-end-vol-check:檢查充電是否滿的電壓(單位為mv)
chg-bat-safety-vol:充電時蓄電池的安全電壓
rechg-vol:註意這裡可以去掉復充電壓
adp-cdp-cur:cdp充電電流(單位為ma)
adp-dcp-cur:dcp充電電流(單位為ma)
adp-sdp-cur :sdp充電電流(單位為ma)
adp-unknown-cur:未知充電電流
adp-fchg-cur:快充充電電流
adp-cdp-cur-limit:用於POWER_SUPPLY_PROP_CURRENT_MAX節點,為CDP類型的最大限制
adp-dcp-cur-limit:為DCP類型的最大限制
adp-unknown-cur-limit:為unknown類型的最大限制
adp-fchg-cur-limit:為快充類型的最大限制
ovp-stop:過電壓保護:停止充電電壓(mv)
ovp-restart:過電壓保護:過壓恢覆電壓
fchg-ovp-stop:快充過電壓保護:停止充電電壓(mv)
fchg-ovp-restart:快充過電壓保護:重啟充電電壓(mv)
chg-timeout :充電超時,最終在函數sprdbat_is_chg_timeout調用到
chg-rechg-timeout:復充超時
trickle-timeout:涓流超時
chg-end-cur:充電端電流
chg-polling-time:充電狀態檢查周期(單位:s)
chg-polling-time-fast:
cap-one-per-time:每個百分比變化最短時間
cap-valid-range-poweron:應該是保存的電池容量,不確定,設備樹里暫時沒有用到過
temp-support:溫度檢測開關
temp-comp-res:讀取溫度補償電阻器
only-vol-mode:獲取電池容量模式,僅電壓模式
fgu-mode:庫侖計模式
chg-full-condition:滿電判斷條件電流電壓或者外置 IC
alm_soc:暫且沒看到用的地方
soft-vbat-uvlo :低電壓關機
rint:電池內阻
cnom:電池容量
rsense-real:fgu 對地電阻真實阻抗
rsense-spec:fgu 對地電阻真實理論值
relax-current:進入 relax 模式下的電流值
fgu-cal-ajust:fgu校準偏移
temp-tab-val:ntc電阻表電壓
charge-vol-tab:對應charge-vol-tab-cap,電壓對應電量,只有電壓測量方式
charge-vol-tab-cap:電量表
ocv-tab-vol:開路電壓測量對應ocv-tab-cap
ocv-tab-cap:電量表
discharge-vol-tab:未充電的開路電壓表,只有電壓測量方式
discharge-vol-tab-cap:電量表
JEITA 功能:可以根據溫度,提供動態修正充電電流及恆壓電壓的功能
jeita-temp-tab 設置的對應溫度點
為調整點對充電電流及恆壓電壓做調整;jeita-temp-recovery-tab 設置的溫度點為溫區恢復點;
溫度變化到更高或者更低區間後如果恢復回來有 3℃的緩衝區
每個區間對應的電流值和電壓值在 jeita-cur-tab 和 jeita-cccv-tab
cnom-temp-tab:電池容量和溫度的表格,
rint-temp-tab:電池內阻和溫度的表格
2. 各個工作隊列的作用:
2.1 sprdbat_battery_works函數
這個函數是在probe函數中最後調用到的,也就是一開始probe的時候就會調用到的
static void sprdbat_battery_works(struct work_struct *work)
{
SPRDBAT_DEBUG("sprdbat_battery_works\n");
mutex_lock(&sprdbat_data->lock);
//由設備樹可知,only_vol_mode是不存在的,所以讀取vbat的電壓和開路電壓
if (!sprdbat_data->pdata->only_vol_mode) {
sprdbat_data->bat_info.vbat_vol = sprdbat_read_vbat_vol();
sprdbat_data->bat_info.vbat_ocv = sprdfgu_read_vbat_ocv();
}
//更新電池信息里的溫度
sprdbat_data->bat_info.last_temp =
sprdbat_data->bat_info.cur_temp;
if (jeita_debug_enable)
sprdbat_data->bat_info.cur_temp = jeita_debug;
else
sprdbat_data->bat_info.cur_temp = sprdbat_read_temp();
//讀取現在庫侖計讀數
sprdbat_data->bat_info.bat_current = sprdfgu_read_batcurrent();
//讀取充電電壓
sprdbat_data->bat_info.vchg_vol = sprdchg_read_vchg_vol();
//讀取現在充電平均電壓
sprdbat_data->bat_info.avg_chg_vol =
sprdbat_get_avgval_from_buff(sprdbat_data->bat_info.vchg_vol,
chg_vol_buff, VOL_BUFF_CNT, 0);
//讀取現在庫侖計平均讀數
sprdbat_data->bat_info.bat_current_avg =
sprdbat_get_avgval_from_buff(sprdbat_data->bat_info.bat_current,
current_buff, CUR_BUFF_CNT, 1);
if (sprdbat_data->pdata->only_vol_mode) {
if (sprdbat_data->bat_info.module_state ==
POWER_SUPPLY_STATUS_DISCHARGING ||
sprdbat_data->bat_info.module_state ==
POWER_SUPPLY_STATUS_UNKNOWN) {
sprdbat_data->bat_info.vbat_vol =
sprdbat_read_vbat_vol();
sprdbat_data->bat_info.vbat_ocv =
sprdfgu_read_vbat_ocv();
sprdbat_update_capacty();
}
} else {
//更新電池電量
sprdbat_update_capacty();
}
mutex_unlock(&sprdbat_data->lock);
sprdbat_print_battery_log();
//不斷輪詢
queue_delayed_work(system_power_efficient_wq,
&sprdbat_data->battery_work,
15 * HZ);
}
其中函數sprdbat_update_capacty更新電池電量:
static void sprdbat_update_capacty(void)
{
uint32_t fgu_capacity;
int flush_time = 0;
int period_time = 0;
struct timespec64 cur_time;
int chging_flag;
if (sprdbat_data->bat_info.capacity == ~0U)
return;
if (sprdbat_data->pdata->only_vol_mode) {
if (sprdbat_data->bat_info.module_state ==
POWER_SUPPLY_STATUS_CHARGING)
chging_flag = 1;
else
chging_flag = 0;
fgu_capacity = sprdfgu_only_vol_read_capacity(chging_flag);
} else {
fgu_capacity = sprdfgu_read_capacity();
}
cur_time = ktime_to_timespec64(ktime_get_boottime());
if (POWER_SUPPLY_STATUS_CHARGING ==
sprdbat_data->bat_info.module_state) {
if (sprdbat_data->bat_info.capacity >= 99) {
trickle_time = cur_time.tv_sec -
trickle_s_time;
} else {
trickle_s_time = cur_time.tv_sec;
trickle_time = 0;
}
} else {
//直接進入到這裡
//涓流時間trickle_s_time=現在時間
trickle_s_time = cur_time.tv_sec;
//trickle_time = 涓流超時時間+周期變化時間+1
trickle_time = sprdbat_data->pdata->trickle_timeout +
sprdbat_data->pdata->cap_one_per_time + 1;
}
SPRDBAT_DEBUG("trickle_s_time: = %lld,trickle_time: = %d\n",
trickle_s_time, trickle_time);
//刷新時間 = 現在時間 - 電量變化時間
//sprdbat_update_capacity_time這個時間會在fgu_capacity != sprdbat_data->bat_info.capacity產生變化
flush_time =
(int)(cur_time.tv_sec -
sprdbat_data->sprdbat_update_capacity_time);
//周期時間為每次調用sprdbat_update_capacty的cur_time-上一次調用該函數的時間
period_time =
(int)(cur_time.tv_sec -
sprdbat_data->sprdbat_last_query_time);
sprdbat_data->sprdbat_last_query_time = cur_time.tv_sec;
SPRDBAT_DEBUG("fgu_cap: = %d,flush: = %d,period:=%d\n",
fgu_capacity, flush_time, period_time);
//根據不同狀態來確定充電時間:
switch (sprdbat_data->bat_info.module_state) {
case POWER_SUPPLY_STATUS_CHARGING:
//如果是充電狀態,但是計算出來的電量跟上一次的小,那就屬於不正常的情況
if (fgu_capacity < sprdbat_data->bat_info.capacity) {
//電流大於0,避免下降
if (sprdfgu_read_batcurrent() >= 0) {
pr_info("avoid vol jumping\n");
fgu_capacity = sprdbat_data->bat_info.capacity;
} else {
//假設周期時間小於最大更新時間,則正常減1
if (period_time <
sprdbat_data->pdata->cap_one_per_time) {
fgu_capacity =
sprdbat_data->bat_info.capacity - 1;
SPRDBAT_DEBUG
("cap decrease fgu_cap:=%d\n",
fgu_capacity);
}
//精度化一下電量
if ((sprdbat_data->bat_info.capacity -
fgu_capacity) >=
(flush_time /
sprdbat_data->pdata->cap_one_per_time)) {
fgu_capacity =
sprdbat_data->bat_info.capacity -
flush_time /
sprdbat_data->pdata->
cap_one_per_time;
}
}
} else if (fgu_capacity > sprdbat_data->bat_info.capacity) {
//假設周期時間小於最大更新時間,則正常加1
if (period_time < sprdbat_data->
pdata->cap_one_per_time) {
fgu_capacity =
sprdbat_data->bat_info.capacity + 1;
SPRDBAT_DEBUG
("avoid jumping! fgu_cap: = %d\n",
fgu_capacity);
}
//精度化一下電量
if ((fgu_capacity - sprdbat_data->bat_info.capacity) >=
(flush_time /
sprdbat_data->pdata->cap_one_per_time)) {
fgu_capacity =
sprdbat_data->bat_info.capacity +
flush_time /
sprdbat_data->pdata->cap_one_per_time;
}
}
//我認為是還未更新到正常電量,但是adc算出的已經是100,但這時候顯示的是99;
if ((sprdbat_data->bat_info.capacity != 100)
&& (fgu_capacity >= 100)) {
fgu_capacity = 99;
}
//涓流充電流程
if ((sprdbat_data->bat_info.capacity >= 99) &&
(trickle_time >= sprdbat_data->pdata->trickle_timeout) &&
(sprdbat_data->pdata->trickle_timeout > 0)) {
SPRDBAT_DEBUG("cap is full, but charge continue\n");
sprdbat_change_module_state
(SPRDBAT_CHARGING_TO_FULL_E);
}
//低電量關機
if (sprdbat_data->bat_info.vbat_vol <=
(sprdbat_data->pdata->soft_vbat_uvlo -
SPRDBAT_SHUTDOWN_OFSSET)) {
fgu_capacity = 0;
SPRDBAT_DEBUG("soft uvlo, shutdown by kernel.. vol:%d",
sprdbat_data->bat_info.vbat_vol);
orderly_poweroff(true);
}
break;
case POWER_SUPPLY_STATUS_NOT_CHARGING:
case POWER_SUPPLY_STATUS_DISCHARGING:
//未充電狀態不應該是計算出來的電量大於原來的電量,所以繼續保持原來的電量
if (fgu_capacity >= sprdbat_data->bat_info.capacity) {
fgu_capacity = sprdbat_data->bat_info.capacity;
} else {
//否則則慢慢下降
if (period_time < sprdbat_data->
pdata->cap_one_per_time) {
fgu_capacity =
sprdbat_data->bat_info.capacity - 1;
SPRDBAT_DEBUG
("avoid jumping! fgu_capacity: = %d\n",
fgu_capacity);
}
//同樣也是精度化
if ((sprdbat_data->bat_info.capacity - fgu_capacity) >=
(flush_time /
sprdbat_data->pdata->cap_one_per_time)) {
fgu_capacity =
sprdbat_data->bat_info.capacity -
flush_time /
sprdbat_data->pdata->cap_one_per_time;
}
}
break;
case POWER_SUPPLY_STATUS_FULL:
//展訊平臺是根據電量來複充的
sprdbat_data->sprdbat_update_capacity_time = cur_time.tv_sec;
//假設計算出來的電壓小於復充電壓-150且不在充電的情況
if ((sprdbat_data->bat_info.vbat_ocv <
(sprdbat_data->pdata->rechg_vol - 150))
&& sprdfgu_read_batcurrent() < 0) {
SPRDBAT_DEBUG("vbat_ocv < rechg_vol -150\n");
//從滿電狀態轉換為充電狀態
sprdbat_change_module_state(SPRDBAT_FULL_TO_CHARGING_E);
}
//電量保持100%
if (fgu_capacity != 100)
fgu_capacity = 100;
if (sprdbat_data->bat_info.vbat_vol <=
(sprdbat_data->pdata->soft_vbat_uvlo -
SPRDBAT_SHUTDOWN_OFSSET)) {
fgu_capacity = 0;
SPRDBAT_DEBUG
("soft uvlo, shutdown by kernel status full\n");
SPRDBAT_DEBUG("vol:%d",
sprdbat_data->bat_info.vbat_vol);
orderly_poweroff(true);
}
break;
default:
break;
}
//低電壓關機
if (sprdbat_data->bat_info.vbat_vol <=
sprdbat_data->pdata->soft_vbat_uvlo) {
fgu_capacity = 0;
SPRDBAT_DEBUG("soft uvlo, vbat very low,level..0.. vol:%d",
sprdbat_data->bat_info.vbat_vol);
}
//更新時間狀態,並且用power_supply_changed給上層切換狀態
if (fgu_capacity != sprdbat_data->bat_info.capacity) {
sprdbat_data->bat_info.capacity = fgu_capacity;
sprdbat_data->sprdbat_update_capacity_time = cur_time.tv_sec;
sprdfgu_record_cap(sprdbat_data->bat_info.capacity);
power_supply_changed(sprdbat_data->battery);
} else {
if (sprdbat_data->bat_info.cur_temp !=
sprdbat_data->bat_info.last_temp)
power_supply_changed(sprdbat_data->battery);
}
}
2.2 sprdbat_battery_sleep_works函數
此函數是為了喚醒電量計算的功能,在sprdbat_resume
函數調用:
static void sprdbat_battery_sleep_works(struct work_struct *work)
{
SPRDBAT_DEBUG("sprdbat_battery_sleep_works\n");
if (!queue_delayed_work(system_power_efficient_wq,
&sprdbat_data->battery_work, 0)) {
cancel_delayed_work_sync(&sprdbat_data->battery_work);
queue_delayed_work(system_power_efficient_wq,
&sprdbat_data->battery_work, 0);
}
}
其本質意義就是重新調用電池計算的功能定時器;
2.3 sprdbat_vbat_detect_irq_works函數:
此函數是在sprdbat_vbat_detect_irq
中斷檢測到調用的:
當vbat檢測管腳為低電量時,則進入該中斷
devm_request_threaded_irq(&pdev->dev,
data->irq_vbat_detect, NULL,
sprdbat_vbat_detect_irq,
IRQ_TYPE_LEVEL_LOW | IRQF_NO_SUSPEND,
"sprdbat_vbat_detect", data);
static __used irqreturn_t sprdbat_vbat_detect_irq(int irq, void *dev_id)
{
disable_irq_nosync(sprdbat_data->irq_vbat_detect);
SPRDBAT_DEBUG("battery detect handle!!!!\n");
queue_work(sprdbat_data->monitor_wqueue,
&sprdbat_data->vbat_detect_irq_work);
return IRQ_HANDLED;
}
sprdbat_vbat_detect_irq_works函數:
static void sprdbat_vbat_detect_irq_works(struct work_struct *work)
{
int value;
value = gpio_get_value(sprdbat_data->gpio_vbat_detect);
SPRDBAT_DEBUG("bat_detect value:0x%x\n", value);
mutex_lock(&sprdbat_data->lock);
//假設高電平進入此中斷,那屬於不正常的情況
if (value) {
if (!sprdbat_data->bat_info.bat_present) {
sprdbat_data->bat_info.bat_present = 1;
//電池拔出後重新插入
sprdbat_change_module_state
(SPRDBAT_CHG_UNSPEC_RESTART_E);
//假設是不是在非充電狀態
if (POWER_SUPPLY_STATUS_DISCHARGING !=
sprdbat_data->bat_info.module_state)
sprdbat_data->start_charge();
SPRDBAT_DEBUG("vbat_detect-start_charge!!!!\n");
}
irq_set_irq_type(sprdbat_data->irq_vbat_detect,
IRQ_TYPE_LEVEL_LOW);
} else {
//電池拔出
sprdbat_data->bat_info.bat_present = 0;
sprdbat_change_module_state(SPRDBAT_CHG_UNSPEC_E);
//停止充電
sprdbat_data->stop_charge();
SPRDBAT_DEBUG("vbat_detect-stop_charge!!!!\n");
irq_set_irq_type(sprdbat_data->irq_vbat_detect,
IRQ_TYPE_LEVEL_HIGH);
}
enable_irq(sprdbat_data->irq_vbat_detect);
mutex_unlock(&sprdbat_data->lock);
}
2.4 sprdbat_plug_works函數
在usb插入的回調函數sprdbat_usb_plug_event
中會使用,在probe如果初始狀態也是會使用:
static void sprdbat_plug_works(struct work_struct *work)
{
if (usb_charger_get_state(sprdbat_data->usb_charger)
== USB_CHARGER_PRESENT)
plugin_callback();
else
plugout_callback();
}
static int plugin_callback(void)
{
SPRDBAT_DEBUG("charger plug in interrupt happen\n");
mutex_lock(&sprdbat_data->lock);
sprdbat_data->sprdbat_vbat_ovp_cnt = 0;
//排除異常情況
if (sprdbat_data->bat_info.module_state
!= POWER_SUPPLY_STATUS_DISCHARGING) {
mutex_unlock(&sprdbat_data->lock);
return 0;
}
sprdbat_data->bat_info.adp_type = sprdchg_charger_is_adapter();
if ((sprdbat_data->bat_info.adp_type == SDP_TYPE) ||
(sprdbat_data->bat_info.adp_type == CDP_TYPE)) {
sprdbat_data->bat_info.usb_online = 1;
power_supply_changed(sprdbat_data->usb);
} else {
sprdbat_data->bat_info.ac_online = 1;
power_supply_changed(sprdbat_data->ac);
}
sprdbat_data->bat_info.chgr_temp
= sprdbat_get_avg_chgr_temp(NORMAL_TEMP, true);
//充電器插入
sprdbat_change_module_state(SPRDBAT_ADP_PLUGIN_E);
sprdbat_adp_plug_nodify(1);
//快充檢測
sprdbat_fchg_detect();
sprdbat_charge_prepare();
//開始充電,回調內部充電裡面的函數
sprdbat_data->start_charge();
if (sprdchg_timer_op->timer_enable) {
u32 polling_time = sprdbat_data->pdata->chg_polling_time;
if (sprdbat_data->pdata->only_vol_mode)
sprdchg_timer_op->timer_enable(polling_time, ONE_TIME);
else
sprdchg_timer_op->timer_enable(polling_time,
PERIOD_TIME);
}
mutex_unlock(&sprdbat_data->lock);
SPRDBAT_DEBUG("plugin_callback:adp_type:%d\n",
sprdbat_data->bat_info.adp_type);
SPRDBAT_DEBUG("plugin_callback: end...\n");
return 0;
}
static int plugout_callback(void)
{
uint32_t adp_type = sprdbat_data->bat_info.adp_type;
SPRDBAT_DEBUG("charger plug out interrupt happen\n");
mutex_lock(&sprdbat_data->lock);
if (sprdbat_data->bat_info.module_state
== POWER_SUPPLY_STATUS_DISCHARGING) {
mutex_unlock(&sprdbat_data->lock);
return 0;
}
disable_irq_nosync(sprdbat_data->irq_vchg_ovi);
if (sprdchg_timer_op->timer_disable)
sprdchg_timer_op->timer_disable();
sprdbat_change_module_state(SPRDBAT_ADP_PLUGOUT_E);
sprdbat_data->stop_charge();
if ((sprd_fchg_op != NULL) && sprd_fchg_op->fchg_deinit)
sprd_fchg_op->fchg_deinit();
sprdbat_adp_plug_nodify(0);
sprdbat_data->bat_info.module_state = POWER_SUPPLY_STATUS_DISCHARGING;
sprdbat_data->bat_info.adp_type = SDP_TYPE;
sprdbat_data->bat_info.ac_online = 0;
sprdbat_data->bat_info.usb_online = 0;
sprdbat_data->fchg_det = 0;
mutex_unlock(&sprdbat_data->lock);
if (sprd_ext_ic_op->set_input_cur_limit) {
unsigned int limit = sprdbat_data->pdata->adp_sdp_cur_limit;
sprd_ext_ic_op->set_input_cur_limit(limit);
sprdbat_data->bat_info.input_cur_limit = limit;
}
if ((adp_type == SDP_TYPE) || (adp_type == CDP_TYPE))
power_supply_changed(sprdbat_data->usb);
else
power_supply_changed(sprdbat_data->ac);
return 0;
}
2.5 sprdbat_charge_works函數
這個函數會在sprdbat_change_module_state
中使用,還有sprdbat_timer_handler
函數中使用定時器中斷;
static void sprdbat_charge_works(struct work_struct *work)
{
SPRDBAT_DEBUG("sprdbat_charge_works----------start\n");
mutex_lock(&sprdbat_data->lock);
//只有電壓模式暫時不考慮
if (!sprdbat_data->pdata->only_vol_mode) {
sprdbat_data->bat_info.vbat_vol = sprdbat_read_vbat_vol();
sprdbat_data->bat_info.vbat_ocv = sprdfgu_read_vbat_ocv();
}
//讀取電流
sprdbat_data->bat_info.bat_current = sprdfgu_read_batcurrent();
//假設外部充電IC存在,則喂狗
if (sprd_ext_ic_op->timer_callback_ext)
sprd_ext_ic_op->timer_callback_ext();
//沒有充電則是返回
if (sprdbat_data->bat_info.module_state ==
POWER_SUPPLY_STATUS_DISCHARGING) {
SPRDBAT_DEBUG("not charing return\n");
mutex_unlock(&sprdbat_data->lock);
return;
}
if (sprdbat_data->pdata->only_vol_mode &&
!sprdchg_timer_op->timer_enable) {
mutex_unlock(&sprdbat_data->lock);
return;
}
//只有電壓模式暫時不考慮
if (sprdbat_data->pdata->only_vol_mode) {
unsigned int poll_time_fast =
sprdbat_data->pdata->chg_polling_time_fast;
unsigned int poll_time =
sprdbat_data->pdata->chg_polling_time;
if (sprdbat_data->bat_info.chg_stop_flags ==
SPRDBAT_CHG_END_NONE_BIT) {
if (sprdbat_data->bat_info.chging_on) {
sprd_ext_ic_op->charge_stop_ext
(SPRDBAT_CHG_END_NONE_BIT);
sprdbat_data->bat_info.chging_on = 0;
sprdchg_timer_op->timer_disable();
sprdchg_timer_op->timer_enable(poll_time_fast,
ONE_TIME);
mutex_unlock(&sprdbat_data->lock);
return;
}
sprdbat_data->bat_info.vbat_vol =
sprdbat_read_vbat_vol();
sprdbat_data->bat_info.vbat_ocv =
sprdfgu_read_vbat_ocv();
sprdbat_update_capacty();
sprdbat_data->bat_info.chging_on = 1;
sprd_ext_ic_op->charge_start_ext();
msleep(20);
sprdchg_timer_op->timer_disable();
sprdchg_timer_op->timer_enable(poll_time, ONE_TIME);
} else {
//讀取vbat_vol
sprdbat_data->bat_info.vbat_vol =
sprdbat_read_vbat_vol();
//讀取vbat開路電壓
sprdbat_data->bat_info.vbat_ocv =
sprdfgu_read_vbat_ocv();
//更新電量
sprdbat_update_capacty();
sprdchg_timer_op->timer_disable();
sprdchg_timer_op->timer_enable(poll_time, ONE_TIME);
}
}
if (sprdbat_data->bat_info.chg_stop_flags & SPRDBAT_CHG_END_FULL_BIT)
//充滿標誌位,如果判斷開路電壓降低到rechg-vol
sprdbat_chg_rechg_monitor();
sprdbat_chg_status_monitor();
sprdbat_chg_timeout_monitor();
sprdbat_chg_ovp_monitor();
sprdbat_temp_monitor();
sprdbat_chgr_temp_monitor();
sprdbat_fault_monitor();
mutex_unlock(&sprdbat_data->lock);
sprdbat_chg_print_log();
SPRDBAT_DEBUG("sprdbat_charge_works----------end\n");
}
2.5.1 充滿電監控函數sprdbat_chg_status_monitor
static void sprdbat_chg_status_monitor(void)
{
int chg_status = POWER_SUPPLY_STATUS_CHARGING;
SPRDBAT_DEBUG
(" %s,ocv=%d, cur=%d,chg_end_vol_l=%d,chg_end_cur=%d\n",
__func__, sprdbat_data->bat_info.vbat_ocv,
sprdbat_data->bat_info.bat_current,
sprdbat_data->pdata->chg_end_vol_l,
sprdbat_data->pdata->chg_end_cur);
//這個暫且不理
if (sprdbat_data->pdata->only_vol_mode) {
if (sprdbat_data->bat_info.vbat_vol >
sprdbat_data->pdata->chg_end_vol_l) {
sprdbat_data->chg_full_trigger_cnt++;
if (sprdbat_data->chg_full_trigger_cnt >= 2) {
sprdbat_data->chg_full_trigger_cnt = 0;
if (sprdbat_data->bat_info.capacity >= 99 &&
trickle_time >=
sprdbat_data->pdata->cap_one_per_time) {
sprdbat_change_module_state
(SPRDBAT_CHG_FULL_E);
sprdbat_data->stop_charge();
} else {
sprdfgu_force_set_soc(1000);
}
}
} else {
sprdbat_data->chg_full_trigger_cnt = 0;
}
return;
}
//chg_full_condition 的設備樹由chg-full-condition決定,並且是0,決定充電結束條件
if (sprdbat_data->pdata->chg_full_condition == FROM_EXT_IC) {
chg_status = sprd_ext_ic_op->get_charging_status();
if (chg_status == POWER_SUPPLY_STATUS_FULL) {
SPRDBAT_DEBUG("chg full\n");
/* capacity is high enough, set the status to full */
if (sprdbat_data->bat_info.capacity >= 99 &&
trickle_time >=
sprdbat_data->pdata->cap_one_per_time)
sprdbat_change_module_state(SPRDBAT_CHG_FULL_E);
else
sprdfgu_force_set_soc(1000);
} else {
SPRDBAT_DEBUG("chging or fault\n");
}
} else if (sprdbat_data->pdata->chg_full_condition == VOL_AND_CUR) {
//兩個條件同時成立兩次,一是vbat的電壓大於截止充電電壓條件,二是vbat的充電電流小於充電電流
if ((sprdbat_data->bat_info.vbat_vol >
sprdbat_data->pdata->chg_end_vol_l)
&& (sprdbat_data->bat_info.bat_current <
sprdbat_data->pdata->chg_end_cur)) {
sprdbat_data->chg_full_trigger_cnt++;
if (sprdbat_data->chg_full_trigger_cnt >= 2) {
SPRDBAT_DEBUG("charge full stop charge\n");
sprdbat_data->chg_full_trigger_cnt = 0;
/* cap is high enough, set the status to full */
if (sprdbat_data->bat_info.capacity >= 99 &&
trickle_time >=
sprdbat_data->pdata->cap_one_per_time) {
sprdbat_change_module_state
(SPRDBAT_CHG_FULL_E);
sprdbat_data->stop_charge();
} else {
sprdfgu_force_set_soc(1000);
}
}
} else {
sprdbat_data->chg_full_trigger_cnt = 0;
}
} else if (sprdbat_data->pdata->chg_full_condition == VOL_AND_STATUS) {
if ((sprdbat_data->bat_info.vbat_vol >
sprdbat_data->pdata->chg_end_vol_l
|| sprd_ext_ic_op->get_charging_status())
&& (sprdbat_data->bat_info.bat_current <
sprdbat_data->pdata->chg_end_cur)) {
sprdbat_data->chg_full_trigger_cnt++;
if (sprdbat_data->chg_full_trigger_cnt >= 2) {
SPRDBAT_DEBUG("charge full stop charge\n");
sprdbat_data->chg_full_trigger_cnt = 0;
/* cap is high enough, set the status to full */
if (sprdbat_data->bat_info.capacity >= 99 &&
trickle_time >=
sprdbat_data->pdata->cap_one_per_time) {
sprdbat_change_module_state
(SPRDBAT_CHG_FULL_E);
sprdbat_data->stop_charge();
} else {
sprdfgu_force_set_soc(1000);
}
}
} else {
sprdbat_data->chg_full_trigger_cnt = 0;
}
} else {
SPRDBAT_DEBUG("bad chg_full_condition\n");
}
}
2.5.2 充電超時監控函數sprdbat_chg_timeout_monitor
充電超時後如果滿足電量充足則狀態設置為滿電狀態,如果電池電壓過低則需要重新啟動充電
static void sprdbat_chg_timeout_monitor(void)
{
SPRDBAT_DEBUG("sprdbat_chg_timeout_monitor enter\n");
if (sprdbat_data->bat_info.chg_stop_flags &
SPRDBAT_CHG_END_TIMEOUT_BIT) {
SPRDBAT_DEBUG("sprdbat_chg_timeout_monitor recharge\n");
sprdbat_change_module_state(SPRDBAT_CHG_TIMEOUT_RESTART_E);
sprdbat_data->start_charge();
}
if (sprdbat_data->bat_info.chg_stop_flags == SPRDBAT_CHG_END_NONE_BIT) {
if (sprdbat_is_chg_timeout()) {
SPRDBAT_DEBUG
("sprdbat_chg_timeout_monitor chg timeout\n");
if (sprdbat_data->bat_info.vbat_ocv >
sprdbat_data->pdata->rechg_vol) {
sprdbat_change_module_state(SPRDBAT_CHG_FULL_E);
sprdbat_data->stop_charge();
} else {
sprdbat_data->bat_info.chg_this_timeout =
sprdbat_data->pdata->chg_rechg_timeout;
sprdbat_change_module_state
(SPRDBAT_CHG_TIMEOUT_E);
sprdbat_data->stop_charge();
}
}
}
}
2.5.3 充電器過壓保護監控函數
充電器過壓採用輪詢方式輪詢用戶配置電壓參數修改充電狀態,快充電壓如果下降2000mv 則退出快充,重新設定充電電流。
ovp-stop = <6500>; //充電過壓保護
ovp-restart = <5800>; //過壓恢覆電壓
fchg-ovp-stop = <11000>; //快充過壓電壓
fchg-ovp-restart = <10000>; //快充過壓恢覆電壓
static void sprdbat_chg_ovp_monitor(void)
{
int ovp_restart, ovp_stop;
if (sprdbat_data->fchg_det) {
ovp_restart = sprdbat_data->pdata->fchg_ovp_restart;
ovp_stop = sprdbat_data->pdata->fchg_ovp_stop;
} else {
ovp_restart = sprdbat_data->pdata->ovp_restart;
ovp_stop = sprdbat_data->pdata->ovp_stop;
}
SPRDBAT_DEBUG("%s chg_vol = %d,ovp_stop =%d,ovp_restart=%d\n",
__func__, sprdbat_data->bat_info.avg_chg_vol,
ovp_stop, ovp_restart);
if (sprdbat_data->bat_info.chg_stop_flags & SPRDBAT_CHG_END_OVP_BIT) {
if (sprdbat_data->bat_info.avg_chg_vol <= ovp_restart) {
SPRDBAT_DEBUG("charge vol low restart chg\n");
sprdbat_change_module_state(SPRDBAT_OVI_RESTART_E);
sprdbat_data->start_charge();
} else {
SPRDBAT_DEBUG("sprdbat_chg_ovp_monitor ovp return ");
}
} else if (sprdbat_data->bat_info.avg_chg_vol >= ovp_stop) {
SPRDBAT_DEBUG("charge vol is too high\n");
sprdbat_change_module_state(SPRDBAT_OVI_STOP_E);
sprdbat_data->stop_charge();
}
if (sprdbat_data->fchg_det) {
/*if vbus vol <(vbus - 2000mv),exit*/
uint32_t fchg_l = sprdbat_data->pdata->fchg_vol - 2000;
if (sprdbat_data->bat_info.avg_chg_vol <= fchg_l) {
SPRDBAT_DEBUG("fchg_l_low:%d\n", fchg_l);
sprdbat_data->fchg_det = 0;
if ((sprd_fchg_op != NULL)
&& (sprd_fchg_op->fchg_deinit))
sprd_fchg_op->fchg_deinit();
sprdbat_charge_prepare();
sprdbat_data->start_charge();
power_supply_changed(sprdbat_data->battery);
}
}
}
2.5.4 充電器溫度監控函數
具體參考《SL8541E充電介紹.pdf》
2.5.5 sprdbat_chgr_temp_monitor函數
暫且不知道作用
2.5.6 sprdbat_fault_monitor監控錯誤函數
static void sprdbat_fault_monitor(void)
{
int chg_fault, status, vbat_ovp, terminal_voltage;
SPRDBAT_DEBUG("sprdbat_fault_monitor enter\n");
status = sprdbat_data->cur_temp_status;
terminal_voltage = sprdbat_data->pdata->jeita_tab[status].z;
vbat_ovp = terminal_voltage + VBAT_OVP_THRESHOLD;
chg_fault = sprd_ext_ic_op->get_charging_fault();
if (chg_fault == SPRDBAT_CHG_END_NONE_BIT)
sprdbat_fault_recovery_monitor();
if (chg_fault & SPRDBAT_CHG_END_OTP_COLD_BIT)
SPRDBAT_DEBUG(" power cold\n");
if (chg_fault & SPRDBAT_CHG_END_OTP_OVERHEAT_BIT)
SPRDBAT_DEBUG("power hot\n");
if (chg_fault & SPRDBAT_CHG_END_TIMEOUT_BIT) {
SPRDBAT_DEBUG(" safe time expire\n");
sprdbat_change_module_state(SPRDBAT_CHG_TIMEOUT_E);
}
if (chg_fault & SPRDBAT_CHG_END_BAT_OVP_BIT) {
if (sprdbat_data->sprdbat_vbat_ovp_cnt > VBAT_OVP_CNT_THRESHOLD &&
sprdbat_data->bat_info.vbat_vol > vbat_ovp) {
SPRDBAT_DEBUG("fault: vbat ovp\n");
sprdbat_change_module_state(SPRDBAT_VBAT_OVP_E);
} else {
SPRDBAT_DEBUG("warning: vbat ovp\n");
sprdbat_fchg_detect();
sprdbat_charge_prepare();
sprdbat_data->start_charge();
sprdbat_data->sprdbat_vbat_ovp_cnt++;
}
} else {
sprdbat_data->sprdbat_vbat_ovp_cnt = 0;
}
if (chg_fault == SPRDBAT_CHG_END_UNSPEC)
SPRDBAT_DEBUG(" unspec fault\n");
}
3. 其他狀態位
3.1 充電狀態:
enum sprdbat_event {
SPRDBAT_ADP_PLUGIN_E, //充電器插入
SPRDBAT_ADP_PLUGOUT_E, //充電器拔出
SPRDBAT_OVI_STOP_E, //充電器電壓過高
SPRDBAT_OVI_RESTART_E, //充電器過壓後恢復
SPRDBAT_OTP_COLD_STOP_E, //電池溫度過低
SPRDBAT_OTP_OVERHEAT_STOP_E, //電池溫度過高
SPRDBAT_OTP_COLD_RESTART_E, //電池溫度從低溫恢復
SPRDBAT_OTP_OVERHEAT_RESTART_E, //電池溫度從高溫恢復
SPRDBAT_CHG_FULL_E, //充滿電
SPRDBAT_RECHARGE_E, //滿電後復充
SPRDBAT_CHG_TIMEOUT_E, //充電超時
SPRDBAT_CHG_TIMEOUT_RESTART_E, //超時後重新啟動充電
SPRDBAT_VBAT_OVP_E, //電池過壓
SPRDBAT_VBAT_OVP_RESTART_E, //電池過壓恢復
SPRDBAT_CHG_UNSPEC_E, //電池拔出
SPRDBAT_CHG_UNSPEC_RESTART_E, //電池拔出後重新插入
SPRDBAT_FULL_TO_CHARGING_E, //滿電強制啟動充電
SPRDBAT_CHARGING_TO_FULL_E, //充電強制顯示 100
SPRDBAT_CHG_FORCE_STOP_E, //強制啟動充電
SPRDBAT_CHG_FORCE_START_E, //強制關閉充電
};