本文转载自: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这部流程如下:

android电池(五):电池 充电IC(PM2301)驱动分析篇【转】的更多相关文章

  1. 【转】android电池(五):电池 充电IC(PM2301)驱动分析篇

    关键词:android 电池  电量计  PL2301任务初始化宏 power_supply 中断线程化 平台信息:内核:linux2.6/linux3.0系统:android/android4.0  ...

  2. 【转】android电池(四):电池 电量计(MAX17040)驱动分析篇

    关键词:android 电池  电量计  MAX17040 任务初始化宏 power_supply 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台: ...

  3. android电池(四):电池 电量计(MAX17040)驱动分析篇【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/8969369 电池电量计,库仑计,用max17040这颗电量IC去计量电池电量,这种方法 ...

  4. 新款iPad Pro4的电池续航和充电速度对比

    3月18日晚苹果官网上架了两款新iPad Pro,两款iPad Pro 2020外观大小分别为11英寸和12.9英寸,搭载苹果A12Z仿生芯片,起售价分别为6288元和7899元.那么两款iPad P ...

  5. 【转】android 电容屏(三):驱动调试之驱动程序分析篇

    关键词:android  电容屏 tp 工作队列 中断 坐点计算  电容屏主要参数平台信息:内核:linux2.6/linux3.0系统:android/android4.0  平台:S5PV310( ...

  6. Android 8.1 关机充电动画(三)Android模式

    system:Android 8.1 platform:RK3326/PX30 uboot kernel system/core/healthd Android 8.1 关机充电动画(一)模式选择 A ...

  7. 【转】android 电容屏(二):驱动调试之基本概念篇

    关键词:android  电容屏 tp 工作队列 中断 多点触摸协议平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310(samsung ...

  8. 充电 IC 对 0V 电池充电问题

    只讨论锂聚合物电池,由于设置漏电流原因,有些锂电池会放电到 0V(正常是不应该的). 但是在时候就是会发电到 0V,这里就要考虑充电 IC 是否有对 0V 电池充电的功能,还有保护 IC 是否允许 0 ...

  9. 【转】Android LCD(四):LCD驱动调试篇

    关键词:android LCD TFTSN75LVDS83B  TTL-LVDS LCD电压背光电压 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台 ...

随机推荐

  1. WCF实现上传图片功能

    初次学习实现WCF winform程序的通信,主要功能是实现图片的传输. 下面是实现步骤: 第一步: 首先建立一个类库项目TransferPicLib,导入wcf需要的引用System.Service ...

  2. 使用AlloyLever来搞定开发调试发布,错误监控上报,用户问题定位

    传送门: # gituhbhttps://github.com/AlloyTeam/AlloyLever # 官网https://alloyteam.github.io/AlloyLever/ 下载和 ...

  3. window 效率神器:Wox

    官方网站 http://www.getwox.com/ 下载后以管理员身份运行,右下角可以看到Wox的图标.点击setting可以进入主界面 如果看不懂可以将语言设置为中文 默认快捷键是Alt + s ...

  4. 更改 Nginx 服务的默认用户

    为什么要更改 Nginx 服务的默认用户:就像更改 ssh 的默认 22 端口一样,增加安全性,Nginx 服务的默认用户是 nobody ,我们更改为 nginx 1.添加 nginx 用户 use ...

  5. JavaScript的slice()

    JavaScript slice() 方法 JavaScript Array 对象 定义和用法 slice() 方法可从已有的数组中返回选定的元素. 语法 arrayObject.slice(star ...

  6. oracle中避免sort操作

    1.升序排列 create index ix_table1 on table1 (column1,column2); select column1,column2 from table1 order ...

  7. 怎样取消不能改动(仅仅读打开)的word文件的password

    作者:iamlaosong 朋友给我一个文档,是加了防改动password的,希望我能帮其取消.由于须要原文档的格式,取消方法例如以下(office2007环境): 1.打开文件.文件打开时,提演示样 ...

  8. C语言include预处理命令与多文件编译

    #include预处理命令几乎使我们在第一次接触C的时候就会碰到的预处理命令,可我现在还不怎么清楚,这次争取一次搞懂. 一.#include预处理指令的基本使用 预处理指令可以将别处的源代码内容插入到 ...

  9. 【深入JVM】JVM工具之JMAP

    一.工具介绍 假设把java\bin文件夹配置到环境变量.在cmd输入jmap会有例如以下提示: 翻译:打印出某个java进程(使用pid)内存内的,全部'对象'的情况(如:产生那些对象,及其数量). ...

  10. Java线程—-Runnable和Callable的区别和联系

    Java 提供了三种创建线程的方法 1.继承Thread接口 public class Thread2Thread { public static void main(String[] args) { ...