android電池(五):電池 充電IC(PM2301)驅動分析篇
關鍵詞:android 電池 電量計 PL2301任務初始化巨集 power_supply 中斷執行緒化
平臺資訊:
核心:linux2.6/linux3.0
系統:android/android4.0
平臺:samsung exynos 4210、exynos 4412 、exynos 5250
作者:xubin341719(歡迎轉載,請註明作者)
歡迎指正錯誤,共同學習、共同進步!!
完整驅動程式碼&規格書下載:MAX17040_PL2301
android 電池(二):android關機充電流程、充電畫面顯示
android電池(四):電池 電量計(MAX17040)驅動分析篇
android電池(五):電池 充電IC(PM2301)驅動分析篇android充電這塊,有的電源管理晶片內部包含充電管理,如s5pv210上常用的AT8937。我們這次用的max77686沒有充電控制這塊,所以我們加入一個充電IC來控制,選用PM2301.
一、PM2301和主控、電池的邏輯
如下圖所示:
1、藍色部分:IIC控制介面,這個說得太多了,好多外圍器件都是通過IIC控制的,這個一定要熟悉、熟悉、熟爛了,然後可以完成比較多的工作。
2、黃色部分:中斷、使能控制腳,CHG_STATUS(IRQ)、 DC_IN_INT(WAKE_UP) 、 PM2301_LP(LPN)、CHARGER_EN(ENN)控制引腳;
IRQ:充電IC的狀態,如果有動作通知主控;
WAKE_UP:如果有DC插入,產生中斷通知主控;
LPN:
ENN:充電IC使能;
3、PM2301 、電池、系統電壓的大致邏輯
標號1:系統電壓有PM2301提供;
標號2:PM2301給電池充電;
標號3:系統電壓有電池提供;
標號:1和標號:3不同時提供電壓給系統,中間有一個MOS管切換;分兩種情況:
(1)、不插充電器時,有電池提供電壓給系統,走通道標號:3給系統供電;
(2)、插入DC後,系統偵測到DC插入,把3的通道關閉,開啟1給系統供電,同時有2給電池充電;
二、PM2301硬體電路
如下所示:
Q5這個MOS管,就是控制系統供電的,沒有充電時,VBATT有VBAT+提供,充電時,VBATT有SENSE_COMM提供。
控制腳對應主控的引腳:
IIC |
IIC ID 為2 |
CHG_STATUS(IRQ) |
EXYNOS4_GPX1(3) |
DC_IN_INT(WAKE_UP) |
EXYNOS4_GPX0(7) |
PM2301_LP(LPN) |
EXYNOS4_GPX1(7) |
CHARGER_EN(ENN) |
EXYNOS4_GPL2(0) |
下圖為PM2301的參考電路解法,同樣看到P1控制VSYSTEM電源部分的切換控制。
下圖為整個電池充電的過程控制:
Trickle mode、Constant current mode (CC mode or fast charge mode)、Constant voltage mode (CV mode) 、End of charge feature
三、PL2301驅動部分
PL2301的硬體、工作原理做簡單的解釋,接下來我們分析驅動程式:
驅動用到知識點:
IIC的註冊;
任務初始化巨集(在上一篇我們簡單提過);
中斷執行緒化;
1、IIC的註冊
這個和上一篇所說的電量計相似;
(1)、pm2301驅動部分
- static const struct i2c_device_id pm2301_id[] = {
- { "pm2301", 0 },
- { }
- };
- MODULE_DEVICE_TABLE(i2c, pm2301_id);
- static struct i2c_driver pm2301_i2c_driver = {
- .driver = {
- .name = "pm2301",
- },
- .probe = pm2301_probe,
- .remove = __devexit_p(pm2301_remove),
- .suspend = pm2301_suspend,
- .resume = pm2301_resume,
- .id_table = pm2301_id,
- };
- static int __init pm2301_init(void)
- {
- printk(KERN_INFO "pm2301_init !!\n");
- return i2c_add_driver(&pm2301_i2c_driver);
- }
- module_init(pm2301_init);
(2)、平臺驅動部分
arch/arm/mach-exynos/mach-smdk4x12.c
- static struct i2c_board_info i2c_devs1[] __initdata = {
- …………
- #ifdef CONFIG_CHARGER_PM2301
- {
- I2C_BOARD_INFO("pm2301", 0x2c),
- .platform_data = &pm2301_platform_data,
- },
- #endif
- …………
- };
下圖就是我們IIC驅動註冊生成的檔案;
/sys/bus/i2c/drivers/pm2301
2、關於:pm2301_platform_data這個結構體
- static struct pm2301_platform_data pm2301_platform_data = {
- .hw_init = pm2301_hw_init,//(1)、硬體介面初始化化;
- .gpio_lpn = GPIO_PM2301_LP,//(2)、結構體初始化;
- .gpio_irq = GPIO_CHARGER_STATUS,
- .gpio_enn = GPIO_CHARGER_ENABLE,
- .gpio_wakeup = GPIO_CHARGER_ONLINE,
- };
arch/arm/mach-exynos/mach-smdk4x12.c
(1)、硬體介面初始化
- static int pm2301_hw_init(void)
- {
- printk("pm2301_hw_init !!\n");
- if (gpio_request(GPIO_CHARGER_ONLINE, "GPIO_CHARGER_ONLINE")) {
- printk(KERN_ERR "%s :GPIO_CHARGER_ONLINE request port error!\n", __func__);
- goto err_gpio_failed;
- } else {
- s3c_gpio_setpull(GPIO_CHARGER_ONLINE, S3C_GPIO_PULL_NONE);
- s3c_gpio_cfgpin(GPIO_CHARGER_ONLINE, S3C_GPIO_SFN(0));
- gpio_direction_input(GPIO_CHARGER_ONLINE);
- gpio_free(GPIO_CHARGER_ONLINE);
- }
- if (gpio_request(GPIO_CHARGER_STATUS, "GPIO_CHARGER_STATUS")) {
- printk(KERN_ERR "%s :GPIO_CHARGER_STATUS request port error!\n", __func__);
- goto err_gpio_failed;
- } else {
- s3c_gpio_setpull(GPIO_CHARGER_STATUS, S3C_GPIO_PULL_NONE);
- s3c_gpio_cfgpin(GPIO_CHARGER_STATUS, S3C_GPIO_SFN(0));
- gpio_direction_input(GPIO_CHARGER_STATUS);
- gpio_free(GPIO_CHARGER_STATUS);
- }
- if (gpio_request(GPIO_CHARGER_ENABLE, "GPIO_CHARGER_ENABLE")) {
- printk(KERN_ERR "%s :GPIO_CHARGER_ENABLE request port error!\n", __func__);
- goto err_gpio_failed;
- } else {
- s3c_gpio_setpull(GPIO_CHARGER_ENABLE, S3C_GPIO_PULL_NONE);
- s3c_gpio_cfgpin(GPIO_CHARGER_ENABLE, S3C_GPIO_SFN(1));
- gpio_direction_output(GPIO_CHARGER_ENABLE, 0);
- gpio_free(GPIO_CHARGER_ENABLE);
- }
- if (gpio_request(GPIO_PM2301_LP, "GPIO_PM2301_LP")) {
- printk(KERN_ERR "%s :GPIO_PM2301_LP request port error!\n", __func__);
- goto err_gpio_failed;
- } else {
- s3c_gpio_setpull(GPIO_PM2301_LP, S3C_GPIO_PULL_NONE);
- s3c_gpio_cfgpin(GPIO_PM2301_LP, S3C_GPIO_SFN(1));
- gpio_direction_output(GPIO_PM2301_LP, 1);
- gpio_free(GPIO_PM2301_LP);
- }
- return 1;
- err_gpio_failed:
- return 0;
- }
(2)、結構體初始化
Include/linux/pm2301_charger.h
- #define GPIO_CHARGER_ONLINE EXYNOS4_GPX0(7)//對應控制腳的主控介面
- #define GPIO_CHARGER_STATUS EXYNOS4_GPX1(3)
- #define GPIO_CHARGER_ENABLE EXYNOS4_GPL2(0)
- #define GPIO_PM2301_LP EXYNOS4_GPX1(7)
- struct pm2301_platform_data {
- int (*hw_init)(void);
- int gpio_enn;
- int gpio_wakeup;
- int gpio_irq;
- int gpio_lpn;
- };
- extern int pm2301_get_online(void);
- extern int pm2301_get_status(void);
3、probe函式分析
如果你是初學者,建議多看程式,你會發現,其實驅動程式的格式大多都是相同的,如這個IIC 器件的, 佇列、定時器之類的東西。
- static int __devinit pm2301_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct pm2301_chip *chip;
- int ret;
- printk(KERN_INFO "PM2301 probe !!\n");
- //(1)、前面這部分是對IIC的初始化;
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
- return -EIO;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
- g_chip = chip;
- chip->client = client;
- chip->pdata = client->dev.platform_data;
- i2c_set_clientdata(client, chip);
- /* Hardware Init for PM2301 */
- if (chip->pdata->hw_init && !(chip->pdata->hw_init())) {
- dev_err(&client->dev, "hardware initial failed.\n");
- goto err_hw_failed;
- }
- mutex_init(&i2c_lock);
- //(2)、初始化兩個佇列
- INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online, pm2301_online_work);
- INIT_DELAYED_WORK_DEFERRABLE(&chip->work_status, pm2301_ststus_work);
- //(3)、中斷執行緒化
- chip->irq_online = gpio_to_irq(chip->pdata->gpio_wakeup);
- chip->irq_status = gpio_to_irq(chip->pdata->gpio_irq);
- /* Request IRQ for PM2301 */
- ret = request_threaded_irq(chip->irq_online,
- NULL, pm2301_dcin,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "PM2301 DC IN", chip);
- if (ret) {
- printk(KERN_ERR "Cannot request irq %d for DC (%d)\n",
- chip->irq_online, ret);
- goto err_hw_failed;
- }
- #ifdef PM2301_REPORT_STATUS_BY_IRQ
- ret = request_threaded_irq(chip->irq_status,
- NULL, pm2301_status,
- IRQF_TRIGGER_FALLING,
- "PM2301 STATUS", chip);
- if (ret) {
- printk(KERN_ERR "Cannot request irq %d for CHARGE STATUS (%d)\n",
- chip->irq_status, ret);
- goto err_hw_failed;
- }
- #endif
- charger_initial = 1;
- g_has_charged = 0;
- g_has_charging_full_or_stop = 0;
- #ifdef PM2301_REPORT_STATUS_BY_IRQ
- /* Set wakeup source for online pin*/
- irq_set_irq_wake(chip->irq_status, 1);
- #endif
- /* Set wakeup source for online pin*/
- irq_set_irq_wake(chip->irq_online, 1);
- /* Init default interrupt route for PM2301 */
- pm2301_reg_init(chip->client);
- /* Init online & status value */
- chip->online = pm2301_charger_online(chip);
- g_pm2301_online = chip->online; /* Sync to global */
- pm2301_charger_enable(chip->client, chip->online);
- pm2301_charger_status(chip);
- printk(KERN_INFO "PM2301 probe success!!\n");
- return 0;
- err_hw_failed:
- dev_err(&client->dev, "failed: power supply register\n");
- i2c_set_clientdata(client, NULL);
- kfree(chip);
- return ret;
- }
(1)、前面這部分是對IIC的初始化
這部分就不再多說了,搞來搞去都是這個老樣子;
(2)、任務初始化巨集
- INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online, pm2301_online_work);
- INIT_DELAYED_WORK_DEFERRABLE(&chip->work_status, pm2301_ststus_work);
把pm2301_online_work加入佇列chip->work_online, pm2301_ststus_work加入chip->work_status佇列。
(3)、中斷執行緒化 request_threaded_irq
為什麼要提出中斷執行緒化?
在 Linux 中,中斷具有最高的優先順序。不論在任何時刻,只要產生中斷事件,核心將立即執行相應的中斷處理程式,等到所有掛起的中斷和軟中斷處理完畢後才能執行正常的任務,因此有可能造成實時任務得不到及時的處理。中斷執行緒化之後,中斷將作為核心執行緒執行而且被賦予不同的實時優先順序,實時任務可以有比中斷執行緒更高的優先順序。這樣,具有最高優先順序的實時任務就能得到優先處理,即使在嚴重負載下仍有實時性保證。但是,並不是所有的中斷都可以被執行緒化,比如時鐘中斷,主要用來維護系統時間以及定時器等,其中定時器是作業系統的脈搏,一旦被執行緒化,就有可能被掛起,這樣後果將不堪設想,所以不應當被執行緒化。
看下我們程式中如何把中斷執行緒化的:
- chip->irq_online = gpio_to_irq(chip->pdata->gpio_wakeup);
- chip->irq_status = gpio_to_irq(chip->pdata->gpio_irq);
看到這裡是否想起:
- static struct pm2301_platform_data pm2301_platform_data = {
- ………………
- .gpio_lpn = GPIO_PM2301_LP,
- .gpio_irq = GPIO_CHARGER_STATUS,
- .gpio_enn = GPIO_CHARGER_ENABLE,
- .gpio_wakeup = GPIO_CHARGER_ONLINE,
- };
- #define GPIO_CHARGER_ONLINE EXYNOS4_GPX0(7)
- #define GPIO_CHARGER_STATUS EXYNOS4_GPX1(3)
- #define GPIO_CHARGER_ENABLE EXYNOS4_GPL2(0)
- #define GPIO_PM2301_LP EXYNOS4_GPX1(7)
感覺申請個中斷腳,這樣有點費勁呀;
中斷執行緒化:
- /* Request IRQ for PM2301 */
- ret = request_threaded_irq(chip->irq_online,
- NULL, pm2301_dcin,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "PM2301 DC IN", chip);
當有插入DC中斷出發時呼叫:
- static irqreturn_t pm2301_dcin(int irq, void *_data)
- {
- struct pm2301_chip *chip = _data;
- schedule_delayed_work(&chip->work_online, PM2301_DELAY);
- return IRQ_HANDLED;
- }
Pm2301_dcin排程佇列:chip->work_online執行:pm2301_online_work函式
- static void pm2301_online_work(struct work_struct *work)
- {
- struct pm2301_chip *chip;
- chip = container_of(work, struct pm2301_chip, work_online.work);
- int new_online = pm2301_charger_online(chip);
- if (chip->online != new_online) {
- chip->online = new_online;
- g_pm2301_online = chip->online; /* Sync to global */
- pm2301_charger_enable(chip->client, chip->online);//①、初始化充電IC;
- #ifdef PM2301_REPORT_STATUS_BY_IRQ
- /*To avoid status pin keep low*/
- schedule_delayed_work(&chip->work_status, 1000);
- #endif
- #if defined(CONFIG_BATTERY_MAX17040)
- TriggerGasgaugeUpdate();//②、把DC狀態更新到max17040;
- #endif
- }
- }
①、初始化電IC
這裡面主要是寫一些暫存器
- static void pm2301_charger_enable(struct i2c_client *client, int online)
- {
- if (online) { /* Enabled Charging*/
- int batt_capacity = 0;
- batt_capacity = GetGasgaugeCapacity();
- /* Don't start charging if battery capacity above 95% when DC plug in*/
- if(0) {
- //if( batt_capacity >= 95 ) {
- pm2301_write_reg(client, 0x01, 0x02);
- pm2301_write_reg(client, 0x26, 0x00); /* always keep the register to 0 */
- } else {
- pm2301_write_reg(client, 0x00, 0x01); /* force resume of charging */
- pm2301_write_reg(client, 0x01, 0x06); /* ChEn=1, AutoResume=1 */
- pm2301_write_reg(client, 0x05, 0x7A); /* ChEoccurrentLevel:150mA, ChPrechcurrentLevel:100mA, ChCCcurrentLevel:1000mA/2000mA */
- pm2301_write_reg(client, 0x06, 0x0A); /* ChVersumeVot:3.6V ChPrechVoltLevel:2.9V */
- pm2301_write_reg(client, 0x07, 0x1E); /* ChVoltLevel:4.25V */
- pm2301_write_reg(client, 0x26, 0x00); /* always keep the register to 0 */
- }
- g_has_charged = 1;
- } else { /* Disable Charging*/
- pm2301_write_reg(client, 0x01, 0x02);
- pm2301_write_reg(client, 0x26, 0x00); /* always keep the register to 0 */
- g_has_charged = 0;
- }
- }
②、把DC狀態更新到max17040
- TriggerGasgaugeUpdate()
相關文章
- 電池 並聯、串聯、充電、放電
- 新型iPhone電池充電只需5秒iPhone
- DIY多快充協議太陽能充電器!----鋰電池充電電路協議
- 【MTK/Chargeing/Charger/驅動/JEITA】新增電池節點改變電池溫度
- win10電腦電池不充電怎麼回事 win10電池連線電源不充電如何處理Win10
- 聯想筆記本電池0%充不進電怎麼辦 聯想電腦電池顯示0充不進電筆記
- lenovothinkpadt460sopensuselinux保護電池設定電池充電閥值ThinkPadLinux
- win10電池不充電怎麼處理_win10電源接通電池不充電的解決教程Win10
- 自動駕駛汽車的未來,“鋰電池先驅”:需要更強勁的電池自動駕駛
- 全新到貨IBM DS4500電池/FASTT900電池/DS4400電池/FASTT700電池/FASTT500電池IBMAST
- 電池、電容內阻手動計算
- AlDente for Mac(mac電池最大充電限制工具)中文版Mac
- RK3288 手動設定電池電量
- 陣列卡快取電池充放電問題詳解陣列快取
- mac電池最大充電限制工具AlDente Pro中文啟用版Mac
- mac電池最大充電限制工具:AlDente mac中文免費版Mac
- mac電池最大充電限制工具:AlDente for Mac中文免費版Mac
- iPhone 7電池容量是多少?蘋果7電池容量猛增?iPhone蘋果
- Battery Charging Alert for Mac(電腦電池充電提醒工具) v3.00啟用版BATMac
- Batteries for Mac(電池電量管理軟體)BATMac
- 1.5V電池多少伏沒電
- 怎麼保護電腦電池,AlDente免fei版讓Mac電腦不會過度充電Mac
- 電池最大充電限制工具:AlDente Pro for Mac v1.24啟用版Mac
- 蘋果iPhone XR電池續航能力詳解 iPhone XR電池容量多大?蘋果iPhone
- 筆記本主機板CMOS電池怎麼放電?膝上型電腦主機板電池放電圖解教程筆記圖解
- 電池管理系統(BMS)
- 紐扣電池分類
- 電池最大充電限制工具:AlDente Pro for Mac v1.23啟用版Mac
- Nipper應急神器!能讓你用普通AA電池給手機充電
- AlDente Pro如何延長mac電池壽命?AlDente Pro mac智慧充電管理Mac
- 【新能源】從“材料”到“電池組”一文讀懂動力電池生產全流程!
- 蘋果電池新政策:不再需要進行額外的電池檢測蘋果
- 為什麼電池正負極短接會導致電池燒燬
- win10電池損耗怎麼看_如何檢視win10電腦電池損耗Win10
- 筆記本win10系統下電池不能充電怎麼解決筆記Win10
- 蘋果iOS 14.7正式釋出!可支援MagSafe外接電池無線充電蘋果iOS
- Battery Indicator 電池指示燈工具BATIndicator
- Dealroom:2023 年英國電動汽車電池技術報告OOM