本文转载自: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驱动部分

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

(2)、平台驱动部分

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

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

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

/sys/bus/i2c/drivers/pm2301

2、关于:pm2301_platform_data这个结构体

  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)、硬件接口初始化

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

(2)、结构体初始化

Include/Linux/pm2301_charger.h

  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 器件的, 队列、定时器之类的东西。

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

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

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

(2)、任务初始化宏

  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 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。但是,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。

看下我们程序中如何把中断线程化的:

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

看到这里是否想起:

  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. #define GPIO_CHARGER_ONLINE     EXYNOS4_GPX0(7)
  9. #define GPIO_CHARGER_STATUS     EXYNOS4_GPX1(3)
  10. #define GPIO_CHARGER_ENABLE         EXYNOS4_GPL2(0)
  11. #define GPIO_PM2301_LP          EXYNOS4_GPX1(7)

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

中断线程化:

  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中断出发时调用:

  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函数

  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. if (chip->online != new_online) {
  7. chip->online = new_online;
  8. g_pm2301_online = chip->online;  /* Sync to global */
  9. pm2301_charger_enable(chip->client, chip->online);//①、初始化充电IC;
  10. #ifdef PM2301_REPORT_STATUS_BY_IRQ
  11. /*To avoid status pin keep low*/
  12. schedule_delayed_work(&chip->work_status, 1000);
  13. #endif
  14. #if defined(CONFIG_BATTERY_MAX17040)
  15. TriggerGasgaugeUpdate();//②、把DC状态更新到max17040;
  16. #endif
  17. }
  18. }

①、初始化电IC

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

  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

  1. TriggerGasgaugeUpdate()

插入DC这部流程如下:

最新文章

  1. centos6.3与jexus5.4.4配置支持php(wordpress)
  2. Discuz X3.2 网站快照被劫持的解决方法
  3. 根目录97 <input file>标签,把图片上传到服务器(跟增删改查一起实现)
  4. Asp.Net异步编程-使用了异步,性能就提升了吗?
  5. HTML&CSS----练习做网页
  6. c++相关的类型推导
  7. 在集群环境中使用 EhCache 缓存系统|RMI 集群模式
  8. Hibernate中的对象状态,及自动更新原因
  9. hdu 1005 java(System.out.println();与System.out.println(“\n”);)
  10. 《APUE》第6章笔记
  11. PAT 1003. Emergency (25)
  12. Round Numbers (排列组合)
  13. php不区分大小写
  14. python Tkinter 全屏显示
  15. javascript 检测密码强度 美化版
  16. 第13天 JSTL标签、MVC设计模式、BeanUtils工具类
  17. ZYThumbnailTableView类似于小型阅读器
  18. CodeForces 935E Fafa and Ancient Mathematics (树形DP)
  19. StringBuilder与String有哪些区别?
  20. Cow Acrobats [POJ3045] [贪心]

热门文章

  1. 每日一招:如何才能在A股持续盈利?
  2. 关于浮点数的json解析
  3. Redis的订阅发布
  4. Pentaho 免费版本下载地址列表
  5. ntp服务及其配置
  6. Java Web开发笔记
  7. wamp 两个不同的php.ini
  8. UML类图中的关系表示
  9. Windows下安装redis和在php中使用phpredis扩展
  10. mysql如何监测是否命中索引?