android充电这块,有的电源管理芯片内部包含充电管理,如s5pv210上常用的AT8937。我们这次用的max77686没有充电控制这块,所以我们加入一个充电IC来控制,选用PM2301.

一、PM2301和主控、电池的逻辑

如下图所示:


android电池(五):电池 充电IC(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硬件电路

如下所示:

android电池(五):电池 充电IC(PM2301)驱动分析篇【转】_#define_02

 

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电源部分的切换控制。

android电池(五):电池 充电IC(PM2301)驱动分析篇【转】_#define_03

 

下图为整个电池充电的过程控制:

Trickle mode、Constant current mode (CC mode or fast charge mode)、Constant voltage mode (CV mode) 、End of charge feature

android电池(五):电池 充电IC(PM2301)驱动分析篇【转】_#define_04

 

三、PL2301驱动部分

PL2301的硬件、工作原理做简单的解释,接下来我们分析驱动程序:

驱动用到知识点:

IIC的注册;

      任务初始化宏(在上一篇我们简单提过);

中断线程化;

1、IIC的注册

这个和上一篇所说的电量计相似;

(1)、pm2301驱动部分

[cpp] ​​view plain​​​ ​​copy​​


1. static const struct i2c_device_id pm2301_id[] = {
2. "pm2301", 0 },
3. { }
4. };
5. MODULE_DEVICE_TABLE(i2c, pm2301_id);
6.
7. static struct i2c_driver pm2301_i2c_driver = {
8. .driver = {
9. "pm2301",
10. },
11. .probe = pm2301_probe,
12. .remove = __devexit_p(pm2301_remove),
13. .suspend = pm2301_suspend,
14. .resume = pm2301_resume,
15. .id_table = pm2301_id,
16. };
17.
18. static int __init pm2301_init(void)
19. {
20. "pm2301_init !!\n");
21. return i2c_add_driver(&pm2301_i2c_driver);
22. }
23. module_init(pm2301_init);

(2)、平台驱动部分

arch/arm/mach-exynos/mach-smdk4x12.c

[cpp] ​​view plain​​​ ​​copy​​


1. static struct i2c_board_info i2c_devs1[] __initdata = {
2.
3. …………
4. #ifdef CONFIG_CHARGER_PM2301
5. {
6. "pm2301", 0x2c),
7. .platform_data = &pm2301_platform_data,
8. },
9. #endif
10. …………
11. };

下图就是我们IIC驱动注册生成的文件;

/sys/bus/i2c/drivers/pm2301

android电池(五):电池 充电IC(PM2301)驱动分析篇【转】_初始化_05

 

2、关于:pm2301_platform_data这个结构体

    [cpp] ​​view plain​​​ ​​copy​​


    1. static struct pm2301_platform_data pm2301_platform_data = {
    2. //(1)、硬件接口初始化化;
    3. //(2)、结构体初始化;
    4. .gpio_irq = GPIO_CHARGER_STATUS,
    5. .gpio_enn = GPIO_CHARGER_ENABLE,
    6. .gpio_wakeup = GPIO_CHARGER_ONLINE,
    7. };

    arch/arm/mach-exynos/mach-smdk4x12.c

    (1)、硬件接口初始化

    [cpp] ​​view plain​​​ ​​copy​​


    1. static int pm2301_hw_init(void)
    2. {
    3. "pm2301_hw_init !!\n");
    4.
    5. if (gpio_request(GPIO_CHARGER_ONLINE, "GPIO_CHARGER_ONLINE")) {
    6. "%s :GPIO_CHARGER_ONLINE request port error!\n", __func__);
    7. goto err_gpio_failed;
    8. else {
    9. s3c_gpio_setpull(GPIO_CHARGER_ONLINE, S3C_GPIO_PULL_NONE);
    10. s3c_gpio_cfgpin(GPIO_CHARGER_ONLINE, S3C_GPIO_SFN(0));
    11. gpio_direction_input(GPIO_CHARGER_ONLINE);
    12. gpio_free(GPIO_CHARGER_ONLINE);
    13. }
    14.
    15. if (gpio_request(GPIO_CHARGER_STATUS, "GPIO_CHARGER_STATUS")) {
    16. "%s :GPIO_CHARGER_STATUS request port error!\n", __func__);
    17. goto err_gpio_failed;
    18. else {
    19. s3c_gpio_setpull(GPIO_CHARGER_STATUS, S3C_GPIO_PULL_NONE);
    20. s3c_gpio_cfgpin(GPIO_CHARGER_STATUS, S3C_GPIO_SFN(0));
    21. gpio_direction_input(GPIO_CHARGER_STATUS);
    22. gpio_free(GPIO_CHARGER_STATUS);
    23. }
    24.
    25.
    26. if (gpio_request(GPIO_CHARGER_ENABLE, "GPIO_CHARGER_ENABLE")) {
    27. "%s :GPIO_CHARGER_ENABLE request port error!\n", __func__);
    28. goto err_gpio_failed;
    29. else {
    30. s3c_gpio_setpull(GPIO_CHARGER_ENABLE, S3C_GPIO_PULL_NONE);
    31. s3c_gpio_cfgpin(GPIO_CHARGER_ENABLE, S3C_GPIO_SFN(1));
    32. gpio_direction_output(GPIO_CHARGER_ENABLE, 0);
    33. gpio_free(GPIO_CHARGER_ENABLE);
    34. }
    35.
    36. if (gpio_request(GPIO_PM2301_LP, "GPIO_PM2301_LP")) {
    37. "%s :GPIO_PM2301_LP request port error!\n", __func__);
    38. goto err_gpio_failed;
    39. else {
    40. s3c_gpio_setpull(GPIO_PM2301_LP, S3C_GPIO_PULL_NONE);
    41. s3c_gpio_cfgpin(GPIO_PM2301_LP, S3C_GPIO_SFN(1));
    42. gpio_direction_output(GPIO_PM2301_LP, 1);
    43. gpio_free(GPIO_PM2301_LP);
    44. }
    45.
    46. return 1;
    47.
    48. err_gpio_failed:
    49. return 0;
    50. }

    (2)、结构体初始化

    Include/​​Linux​​/pm2301_charger.h


    [cpp] ​​view plain​​​ ​​copy​​


    1. #define GPIO_CHARGER_ONLINE EXYNOS4_GPX0(7)//对应控制脚的主控接口
    2. #define GPIO_CHARGER_STATUS EXYNOS4_GPX1(3)
    3. #define GPIO_CHARGER_ENABLE EXYNOS4_GPL2(0)
    4. #define GPIO_PM2301_LP EXYNOS4_GPX1(7)
    5. struct pm2301_platform_data {
    6. int (*hw_init)(void);
    7. int gpio_enn;
    8. int gpio_wakeup;
    9. int gpio_irq;
    10. int gpio_lpn;
    11. };
    12. extern int pm2301_get_online(void);
    13. extern int pm2301_get_status(void);

    3、probe函数分析

    如果你是初学者,建议多看程序,你会发现,其实驱动程序的格式大多都是相同的,如这个IIC 器件的, 队列、定时器之类的东西。


    [cpp] ​​view plain​​​ ​​copy​​


    1. static int __devinit pm2301_probe(struct i2c_client *client,
    2. const struct i2c_device_id *id)
    3. {
    4. struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
    5. struct pm2301_chip *chip;
    6. int ret;
    7. "PM2301 probe !!\n");
    8. //(1)、前面这部分是对IIC的初始化;
    9. if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
    10. return -EIO;
    11.
    12. sizeof(*chip), GFP_KERNEL);
    13.
    14. if (!chip)
    15. return -ENOMEM;
    16.
    17. g_chip = chip;
    18. chip->client = client;
    19. chip->pdata = client->dev.platform_data;
    20. i2c_set_clientdata(client, chip);
    21.
    22. /* Hardware Init for PM2301 */
    23. if (chip->pdata->hw_init && !(chip->pdata->hw_init())) {
    24. "hardware initial failed.\n");
    25. goto err_hw_failed;
    26. }
    27.
    28. mutex_init(&i2c_lock);
    29. //(2)、初始化两个队列
    30. INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online, pm2301_online_work);
    31. INIT_DELAYED_WORK_DEFERRABLE(&chip->work_status, pm2301_ststus_work);
    32. //(3)、中断线程化
    33. chip->irq_online = gpio_to_irq(chip->pdata->gpio_wakeup);
    34. chip->irq_status = gpio_to_irq(chip->pdata->gpio_irq);
    35. /* Request IRQ for PM2301 */
    36. ret = request_threaded_irq(chip->irq_online,
    37. NULL, pm2301_dcin,
    38. IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
    39. "PM2301 DC IN", chip);
    40.
    41. if (ret) {
    42. "Cannot request irq %d for DC (%d)\n",
    43. chip->irq_online, ret);
    44. goto err_hw_failed;
    45. }
    46.
    47. #ifdef PM2301_REPORT_STATUS_BY_IRQ
    48.
    49. ret = request_threaded_irq(chip->irq_status,
    50. NULL, pm2301_status,
    51. IRQF_TRIGGER_FALLING,
    52. "PM2301 STATUS", chip);
    53.
    54. if (ret) {
    55. "Cannot request irq %d for CHARGE STATUS (%d)\n",
    56. chip->irq_status, ret);
    57. goto err_hw_failed;
    58. }
    59. #endif
    60.
    61.
    62. charger_initial = 1;
    63. g_has_charged = 0;
    64. g_has_charging_full_or_stop = 0;
    65.
    66. #ifdef PM2301_REPORT_STATUS_BY_IRQ
    67. /* Set wakeup source for online pin*/
    68. irq_set_irq_wake(chip->irq_status, 1);
    69. #endif
    70. /* Set wakeup source for online pin*/
    71. irq_set_irq_wake(chip->irq_online, 1);
    72.
    73. /* Init default interrupt route for PM2301 */
    74. pm2301_reg_init(chip->client);
    75. /* Init online & status value */
    76. chip->online = pm2301_charger_online(chip);
    77. /* Sync to global */
    78. pm2301_charger_enable(chip->client, chip->online);
    79. pm2301_charger_status(chip);
    80.
    81. "PM2301 probe success!!\n");
    82. return 0;
    83. err_hw_failed:
    84. "failed: power supply register\n");
    85. i2c_set_clientdata(client, NULL);
    86. kfree(chip);
    87. return ret;
    88. }

    (1)、前面这部分是对IIC的初始化

    这部分就不再多说了,搞来搞去都是这个老样子;

    (2)、任务初始化宏

    [cpp] ​​view plain​​​ ​​copy​​


    1. INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online, pm2301_online_work);
    2. 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 plain​​​ ​​copy​​


    1. chip->irq_online = gpio_to_irq(chip->pdata->gpio_wakeup);
    2. chip->irq_status = gpio_to_irq(chip->pdata->gpio_irq);

    看到这里是否想起:


    [cpp] ​​view plain​​​ ​​copy​​


    1. static struct pm2301_platform_data pm2301_platform_data = {
    2. ………………
    3. .gpio_lpn = GPIO_PM2301_LP,
    4. .gpio_irq = GPIO_CHARGER_STATUS,
    5. .gpio_enn = GPIO_CHARGER_ENABLE,
    6. .gpio_wakeup = GPIO_CHARGER_ONLINE,
    7. };
    8.
    9. #define GPIO_CHARGER_ONLINE EXYNOS4_GPX0(7)
    10. #define GPIO_CHARGER_STATUS EXYNOS4_GPX1(3)
    11. #define GPIO_CHARGER_ENABLE EXYNOS4_GPL2(0)
    12. #define GPIO_PM2301_LP EXYNOS4_GPX1(7)

    感觉申请个中断脚,这样有点费劲呀;

    中断线程化:


    [cpp] ​​view plain​​​ ​​copy​​


    1. /* Request IRQ for PM2301 */
    2. ret = request_threaded_irq(chip->irq_online,
    3. NULL, pm2301_dcin,
    4. IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
    5. "PM2301 DC IN", chip);

    当有插入DC中断出发时调用:


    [cpp] ​​view plain​​​ ​​copy​​


    1. static irqreturn_t pm2301_dcin(int irq, void *_data)
    2. {
    3. struct pm2301_chip *chip = _data;
    4. schedule_delayed_work(&chip->work_online, PM2301_DELAY);
    5. return IRQ_HANDLED;
    6. }

    Pm2301_dcin调度队列:chip->work_online执行:pm2301_online_work函数


    [cpp] ​​view plain​​​ ​​copy​​


    1. static void pm2301_online_work(struct work_struct *work)
    2. {
    3. struct pm2301_chip *chip;
    4. struct pm2301_chip, work_online.work);
    5. int new_online = pm2301_charger_online(chip);
    6.
    7. if (chip->online != new_online) {
    8. chip->online = new_online;
    9. /* Sync to global */
    10. //①、初始化充电IC;
    11. #ifdef PM2301_REPORT_STATUS_BY_IRQ
    12.
    13. /*To avoid status pin keep low*/
    14. schedule_delayed_work(&chip->work_status, 1000);
    15. #endif
    16. #if defined(CONFIG_BATTERY_MAX17040)
    17. //②、把DC状态更新到max17040;
    18. #endif
    19. }
    20. }

    ①、初始化电IC

    这里面主要是写一些寄存器


    [cpp] ​​view plain​​​ ​​copy​​


    1. static void pm2301_charger_enable(struct i2c_client *client, int online)
    2. {
    3. if (online) { /* Enabled Charging*/
    4. int batt_capacity = 0;
    5. batt_capacity = GetGasgaugeCapacity();
    6. /* Don't start charging if battery capacity above 95% when DC plug in*/
    7. if(0) {
    8. //if( batt_capacity >= 95 ) {
    9. pm2301_write_reg(client, 0x01, 0x02);
    10. /* always keep the register to 0 */
    11. else {
    12. /* force resume of charging */
    13. /* ChEn=1, AutoResume=1 */
    14. /* ChEoccurrentLevel:150mA, ChPrechcurrentLevel:100mA, ChCCcurrentLevel:1000mA/2000mA */
    15. /* ChVersumeVot:3.6V ChPrechVoltLevel:2.9V */
    16. /* ChVoltLevel:4.25V */
    17. /* always keep the register to 0 */
    18. }
    19. g_has_charged = 1;
    20. else { /* Disable Charging*/
    21. pm2301_write_reg(client, 0x01, 0x02);
    22. /* always keep the register to 0 */
    23. g_has_charged = 0;
    24. }
    25. }

    ②、把DC状态更新到max17040

     

    [cpp] ​​view plain​​​ ​​copy​​


    1. TriggerGasgaugeUpdate()

    插入DC这部流程如下:

     

    android电池(五):电池 充电IC(PM2301)驱动分析篇【转】_初始化_06