本文轉載自:http://blog.csdn.net/xubin341719/article/details/8970363
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驅動部分
[cpp]?view plaincopy
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
[cpp]?view plaincopy
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這個結構體
[cpp]?view plaincopy
static?struct?pm2301_platform_data?pm2301_platform_data?=?{??????.hw_init?=?pm2301_hw_init,????.gpio_lpn?=?GPIO_PM2301_LP,????.gpio_irq?=?GPIO_CHARGER_STATUS,??????.gpio_enn?=?GPIO_CHARGER_ENABLE,??????.gpio_wakeup?=?GPIO_CHARGER_ONLINE,??};?? arch/arm/mach-exynos/mach-smdk4x12.c
(1)、硬件接口初始化
[cpp]?view plaincopy
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
[cpp]?view plaincopy
#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?器件的,?隊列、定時器之類的東西。
[cpp]?view plaincopy
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");??????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);????????????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);??????INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online,?pm2301_online_work);??????INIT_DELAYED_WORK_DEFERRABLE(&chip->work_status,?pm2301_ststus_work);??????chip->irq_online?=?gpio_to_irq(chip->pdata->gpio_wakeup);??????chip->irq_status?=?gpio_to_irq(chip->pdata->gpio_irq);??????????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??????????irq_set_irq_wake(chip->irq_status,?1);??#endif??????????irq_set_irq_wake(chip->irq_online,?1);????????????pm2301_reg_init(chip->client);??????????chip->online?=?pm2301_charger_online(chip);??????g_pm2301_online?=?chip->online;??????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)、任務初始化宏
[cpp]?view plaincopy
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?中,中斷具有最高的優先級。不論在任何時刻,只要產生中斷事件,內核將立即執行相應的中斷處理程序,等到所有掛起的中斷和軟中斷處理完畢后才能執行正常的任務,因此有可能造成實時任務得不到及時的處理。中斷線程化之后,中斷將作為內核線程運行而且被賦予不同的實時優先級,實時任務可以有比中斷線程更高的優先級。這樣,具有最高優先級的實時任務就能得到優先處理,即使在嚴重負載下仍有實時性保證。但是,并不是所有的中斷都可以被線程化,比如時鐘中斷,主要用來維護系統時間以及定時器等,其中定時器是操作系統的脈搏,一旦被線程化,就有可能被掛起,這樣后果將不堪設想,所以不應當被線程化。?
看下我們程序中如何把中斷線程化的:
[cpp]?view plaincopy
chip->irq_online?=?gpio_to_irq(chip->pdata->gpio_wakeup);??chip->irq_status?=?gpio_to_irq(chip->pdata->gpio_irq);?? 看到這里是否想起:
[cpp]?view plaincopy
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)?? 感覺申請個中斷腳,這樣有點費勁呀;
中斷線程化:
[cpp]?view plaincopy
ret?=?request_threaded_irq(chip->irq_online,?????????????????????????????NULL,?pm2301_dcin,?????????????????????????????IRQF_TRIGGER_FALLING?|?IRQF_TRIGGER_RISING,?????????????????????????????"PM2301?DC?IN",?chip);?? 當有插入DC中斷出發時調用:
[cpp]?view plaincopy
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函數
[cpp]?view plaincopy
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;??????????pm2301_charger_enable(chip->client,?chip->online);#ifdef?PM2301_REPORT_STATUS_BY_IRQ????????????????????schedule_delayed_work(&chip->work_status,?1000);??#endif??#if?defined(CONFIG_BATTERY_MAX17040)??????????TriggerGasgaugeUpdate();#endif??????}??}?? ①、初始化電IC
這里面主要是寫一些寄存器
[cpp]?view plaincopy
static?void?pm2301_charger_enable(struct?i2c_client?*client,?int?online)??{??????if?(online)?{???????????int?batt_capacity?=?0;??????????batt_capacity?=?GetGasgaugeCapacity();??????????????????if(0)?{??????????????????????pm2301_write_reg(client,?0x01,?0x02);??????????????pm2301_write_reg(client,?0x26,?0x00);???????????}?else?{??????????????pm2301_write_reg(client,?0x00,?0x01);???????????????pm2301_write_reg(client,?0x01,?0x06);???????????????pm2301_write_reg(client,?0x05,?0x7A);???????????????pm2301_write_reg(client,?0x06,?0x0A);???????????????pm2301_write_reg(client,?0x07,?0x1E);???????????????pm2301_write_reg(client,?0x26,?0x00);???????????}??????????g_has_charged?=?1;??????}?else?{????????????pm2301_write_reg(client,?0x01,?0x02);??????????pm2301_write_reg(client,?0x26,?0x00);???????????g_has_charged?=?0;??????}??}?? ②、把DC狀態更新到max17040
?
[cpp]?view plaincopy
TriggerGasgaugeUpdate()?? 插入DC這部流程如下:
?
總結
以上是生活随笔為你收集整理的android电池(五):电池 充电IC(PM2301)驱动分析篇【转】的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。