zoukankan      html  css  js  c++  java
  • android电池(五):电池 充电IC(PM2301)驱动分析篇【转】

    本文转载自: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 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.         .name   = "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.     printk(KERN_INFO "pm2301_init !! ");  
    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.         I2C_BOARD_INFO("pm2301", 0x2c),  
    7.         .platform_data  = &pm2301_platform_data,  
    8.     },  
    9. #endif  
    10. …………  
    11. };  

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

    /sys/bus/i2c/drivers/pm2301

    2、关于:pm2301_platform_data这个结构体

    [cpp] view plain copy
     
    1. static struct pm2301_platform_data pm2301_platform_data = {  
    2.     .hw_init = pm2301_hw_init,//(1)、硬件接口初始化化;  
    3.     .gpio_lpn = GPIO_PM2301_LP,//(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.     printk("pm2301_hw_init !! ");  
    4.   
    5.     if (gpio_request(GPIO_CHARGER_ONLINE, "GPIO_CHARGER_ONLINE"))   {  
    6.         printk(KERN_ERR "%s :GPIO_CHARGER_ONLINE request port error! ", __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.         printk(KERN_ERR "%s :GPIO_CHARGER_STATUS request port error! ", __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.         printk(KERN_ERR "%s :GPIO_CHARGER_ENABLE request port error! ", __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.         printk(KERN_ERR "%s :GPIO_PM2301_LP request port error! ", __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.     printk(KERN_INFO "PM2301 probe !! ");  
    8. //(1)、前面这部分是对IIC的初始化;  
    9.     if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))  
    10.         return -EIO;  
    11.   
    12.     chip = kzalloc(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.         dev_err(&client->dev, "hardware initial failed. ");  
    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.         printk(KERN_ERR "Cannot request irq %d for DC (%d) ",  
    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.         printk(KERN_ERR "Cannot request irq %d for CHARGE STATUS (%d) ",  
    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.     g_pm2301_online = chip->online;  /* Sync to global */  
    78.     pm2301_charger_enable(chip->client, chip->online);  
    79.     pm2301_charger_status(chip);  
    80.   
    81.     printk(KERN_INFO "PM2301 probe success!! ");  
    82.     return 0;  
    83. err_hw_failed:  
    84.     dev_err(&client->dev, "failed: power supply register ");  
    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.     chip = container_of(work, 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.         g_pm2301_online = chip->online;  /* Sync to global */  
    10.         pm2301_charger_enable(chip->client, chip->online);//①、初始化充电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.         TriggerGasgaugeUpdate();//②、把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.             pm2301_write_reg(client, 0x26, 0x00);   /* always keep the register to 0 */  
    11.         } else {  
    12.             pm2301_write_reg(client, 0x00, 0x01);   /* force resume of charging */  
    13.             pm2301_write_reg(client, 0x01, 0x06);   /* ChEn=1, AutoResume=1 */  
    14.             pm2301_write_reg(client, 0x05, 0x7A);   /* ChEoccurrentLevel:150mA, ChPrechcurrentLevel:100mA, ChCCcurrentLevel:1000mA/2000mA */  
    15.             pm2301_write_reg(client, 0x06, 0x0A);   /* ChVersumeVot:3.6V ChPrechVoltLevel:2.9V */  
    16.             pm2301_write_reg(client, 0x07, 0x1E);   /* ChVoltLevel:4.25V */  
    17.             pm2301_write_reg(client, 0x26, 0x00);   /* always keep the register to 0 */  
    18.         }  
    19.         g_has_charged = 1;  
    20.     } else {    /* Disable Charging*/  
    21.         pm2301_write_reg(client, 0x01, 0x02);  
    22.         pm2301_write_reg(client, 0x26, 0x00);   /* always keep the register to 0 */  
    23.         g_has_charged = 0;  
    24.     }  
    25. }  

    ②、把DC状态更新到max17040

    [cpp] view plain copy
     
    1. TriggerGasgaugeUpdate()  

    插入DC这部流程如下:

  • 相关阅读:
    接收一次性广播,开启服务永久监听
    iOS开发之主题皮肤
    Android软件版本更新
    android服务Service(上)- IntentService
    Android之条码扫描二维码扫描
    Android之Service与IntentService的比较
    强烈推荐visual c++ 2012入门经典适合初学者入门
    转载文章:Windows Azure 七月份更新:SQL 数据库、流量管理器、自动伸缩、虚拟机
    CSV 客座文章系列:KGroup 通过 Windows Azure 将 Qoob 内容管理发布到云中
    Windows Azure 网站:应用程序字符串和连接字符串的工作原理
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/6552445.html
Copyright © 2011-2022 走看看