sprd_battery.c 是充電驅動,這個是充電功能的核心內容,電量顯示策略、溫度檢測策略、充電保護機制等功能在這裏實現,功能實現與硬件細節剝離,調用通用接口實現邏輯控制;node
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; }
sprdbat_parse_dt函數以下:ios
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:電池內阻和溫度的表格
這個函數是在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更新電池電量:app
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); } }
此函數是爲了喚醒電量計算的功能,在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); } }
其本質意義就是從新調用電池計算的功能定時器;ui
此函數是在sprdbat_vbat_detect_irq
中斷檢測到調用的:this
當vbat檢測管腳爲低電量時,則進入該中斷debug
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); }
在usb插入的回調函數sprdbat_usb_plug_event
中會使用,在probe若是初始狀態也是會使用:rest
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; }
這個函數會在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"); }
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"); } }
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(); } } } }
充電器過壓採用輪詢方式輪詢用戶配置電壓參數修改充電狀態,快充電壓若是降低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); } } }
具體參考《SL8541E充電介紹.pdf》
暫且不知道做用
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"); }
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, //強制關閉充電 };