分类:S5PXX(三星)linuxandroid 6736人阅读 评论(4) 收藏 举报

关键词:android电池电量计PL2301任务初始化宏power_supply中断线程化

平台信息:
内核:linux2.6/linux3.0
系统:android/android4.0
平台:samsungexynos4210、exynos4412exynos5250

作者:xubin341719(欢迎转载,请注明作者)

欢迎指正错误,共同学习、共同进步!!

完整驱动代码&规格书下载:MAX17040_PL2301

android 电池(一):锂电池基本原理篇

android 电池(二):android关机充电流程、充电画面显示

android 电池(三):android电池系统

android电池(四):电池 电量计(MAX17040)驱动分析篇

android电池(五):电池 充电IC(PM2301)驱动分析篇

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

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

如下图所示:

android电池(五):电池 充电IC(PM2301)驱动分析篇_第1张图片

1、蓝色部分:IIC控制接口这个说得太多了,好多外围器件都是通过IIC控制的,这个一定要熟悉、熟悉、熟烂了,然后可以完成比较多的工作。

2、黄色部分:中断、使能控制脚,CHG_STATUSIRQ)、DC_IN_INTWAKE_UP)、PM2301_LPLPN)、CHARGER_EN(ENN)控制引脚;

IRQ:充电IC的状态,如果有动作通知主控;

WAKE_UP:如果有DC插入,产生中断通知主控;

LPN

ENN:充电IC使能;

3PM2301、电池、系统电压的大致逻辑

标号1:系统电压有PM2301提供;

标号2PM2301给电池充电;

标号3:系统电压有电池提供;

标号:1和标号:3不同时提供电压给系统,中间有一个MOS管切换;分两种情况:

(1)、不插充电器时,有电池提供电压给系统,走通道标号:3给系统供电;

(2)、插入DC后,系统侦测到DC插入,把3的通道关闭,打开1给系统供电,同时有2给电池充电;

二、PM2301硬件电路

如下所示:

android电池(五):电池 充电IC(PM2301)驱动分析篇_第2张图片

Q5这个MOS管,就是控制系统供电的,没有充电时,VBATTVBAT+提供,充电时,VBATTSENSE_COMM提供。

控制脚对应主控的引脚:

IIC

IICID2

CHG_STATUSIRQ

EXYNOS4_GPX1(3)

DC_IN_INTWAKE_UP

EXYNOS4_GPX0(7)

PM2301_LPLPN

EXYNOS4_GPX1(7)

CHARGER_EN(ENN)

EXYNOS4_GPL2(0)

下图为PM2301的参考电路解法,同样看到P1控制VSYSTEM电源部分的切换控制。

android电池(五):电池 充电IC(PM2301)驱动分析篇_第3张图片

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

TricklemodeConstantcurrentmode(CCmodeorfastchargemode)Constantvoltagemode(CVmode)Endofchargefeature

android电池(五):电池 充电IC(PM2301)驱动分析篇_第4张图片

三、PL2301驱动部分

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

驱动用到知识点:

IIC的注册;

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

中断线程化;

1、IIC的注册

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

1)、pm2301驱动部分

[cpp] view plain copy
  1. staticconststructi2c_device_idpm2301_id[]={
  2. {"pm2301",0},
  3. {}
  4. };
  5. MODULE_DEVICE_TABLE(i2c,pm2301_id);
  6. staticstructi2c_driverpm2301_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. staticint__initpm2301_init(void)
  17. {
  18. printk(KERN_INFO"pm2301_init!!\n");
  19. returni2c_add_driver(&pm2301_i2c_driver);
  20. }
  21. module_init(pm2301_init);

(2)、平台驱动部分

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

[cpp] view plain copy
  1. staticstructi2c_board_infoi2c_devs1[]__initdata={
  2. …………
  3. #ifdefCONFIG_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

android电池(五):电池 充电IC(PM2301)驱动分析篇_第5张图片

2、关于:pm2301_platform_data这个结构体

[cpp] view plain copy
  1. staticstructpm2301_platform_datapm2301_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. staticintpm2301_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_ONLINErequestporterror!\n",__func__);
  6. gotoerr_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_STATUSrequestporterror!\n",__func__);
  15. gotoerr_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_ENABLErequestporterror!\n",__func__);
  24. gotoerr_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_LPrequestporterror!\n",__func__);
  33. gotoerr_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. return1;
  41. err_gpio_failed:
  42. return0;
  43. }

2)、结构体初始化

Include/linux/pm2301_charger.h

[cpp] view plain copy
  1. #defineGPIO_CHARGER_ONLINEEXYNOS4_GPX0(7)//对应控制脚的主控接口
  2. #defineGPIO_CHARGER_STATUSEXYNOS4_GPX1(3)
  3. #defineGPIO_CHARGER_ENABLEEXYNOS4_GPL2(0)
  4. #defineGPIO_PM2301_LPEXYNOS4_GPX1(7)
  5. structpm2301_platform_data{
  6. int(*hw_init)(void);
  7. intgpio_enn;
  8. intgpio_wakeup;
  9. intgpio_irq;
  10. intgpio_lpn;
  11. };
  12. externintpm2301_get_online(void);
  13. externintpm2301_get_status(void);

3、probe函数分析

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

[cpp] view plain copy
  1. staticint__devinitpm2301_probe(structi2c_client*client,
  2. conststructi2c_device_id*id)
  3. {
  4. structi2c_adapter*adapter=to_i2c_adapter(client->dev.parent);
  5. structpm2301_chip*chip;
  6. intret;
  7. printk(KERN_INFO"PM2301probe!!\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. /*HardwareInitforPM2301*/
  19. if(chip->pdata->hw_init&&!(chip->pdata->hw_init())){
  20. dev_err(&client->dev,"hardwareinitialfailed.\n");
  21. gotoerr_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. /*RequestIRQforPM2301*/
  31. ret=request_threaded_irq(chip->irq_online,
  32. NULL,pm2301_dcin,
  33. IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
  34. "PM2301DCIN",chip);
  35. if(ret){
  36. printk(KERN_ERR"Cannotrequestirq%dforDC(%d)\n",
  37. chip->irq_online,ret);
  38. gotoerr_hw_failed;
  39. }
  40. #ifdefPM2301_REPORT_STATUS_BY_IRQ
  41. ret=request_threaded_irq(chip->irq_status,
  42. NULL,pm2301_status,
  43. IRQF_TRIGGER_FALLING,
  44. "PM2301STATUS",chip);
  45. if(ret){
  46. printk(KERN_ERR"Cannotrequestirq%dforCHARGESTATUS(%d)\n",
  47. chip->irq_status,ret);
  48. gotoerr_hw_failed;
  49. }
  50. #endif
  51. charger_initial=1;
  52. g_has_charged=0;
  53. g_has_charging_full_or_stop=0;
  54. #ifdefPM2301_REPORT_STATUS_BY_IRQ
  55. /*Setwakeupsourceforonlinepin*/
  56. irq_set_irq_wake(chip->irq_status,1);
  57. #endif
  58. /*Setwakeupsourceforonlinepin*/
  59. irq_set_irq_wake(chip->irq_online,1);
  60. /*InitdefaultinterruptrouteforPM2301*/
  61. pm2301_reg_init(chip->client);
  62. /*Initonline&statusvalue*/
  63. chip->online=pm2301_charger_online(chip);
  64. g_pm2301_online=chip->online;/*Synctoglobal*/
  65. pm2301_charger_enable(chip->client,chip->online);
  66. pm2301_charger_status(chip);
  67. printk(KERN_INFO"PM2301probesuccess!!\n");
  68. return0;
  69. err_hw_failed:
  70. dev_err(&client->dev,"failed:powersupplyregister\n");
  71. i2c_set_clientdata(client,NULL);
  72. kfree(chip);
  73. returnret;
  74. }

(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. staticstructpm2301_platform_datapm2301_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. #defineGPIO_CHARGER_ONLINEEXYNOS4_GPX0(7)
  9. #defineGPIO_CHARGER_STATUSEXYNOS4_GPX1(3)
  10. #defineGPIO_CHARGER_ENABLEEXYNOS4_GPL2(0)
  11. #defineGPIO_PM2301_LPEXYNOS4_GPX1(7)

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

中断线程化:

[cpp] view plain copy
  1. /*RequestIRQforPM2301*/
  2. ret=request_threaded_irq(chip->irq_online,
  3. NULL,pm2301_dcin,
  4. IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
  5. "PM2301DCIN",chip);

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

[cpp] view plain copy
  1. staticirqreturn_tpm2301_dcin(intirq,void*_data)
  2. {
  3. structpm2301_chip*chip=_data;
  4. schedule_delayed_work(&chip->work_online,PM2301_DELAY);
  5. returnIRQ_HANDLED;
  6. }

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

[cpp] view plain copy
  1. staticvoidpm2301_online_work(structwork_struct*work)
  2. {
  3. structpm2301_chip*chip;
  4. chip=container_of(work,structpm2301_chip,work_online.work);
  5. intnew_online=pm2301_charger_online(chip);
  6. if(chip->online!=new_online){
  7. chip->online=new_online;
  8. g_pm2301_online=chip->online;/*Synctoglobal*/
  9. pm2301_charger_enable(chip->client,chip->online);//①、初始化充电IC;
  10. #ifdefPM2301_REPORT_STATUS_BY_IRQ
  11. /*Toavoidstatuspinkeeplow*/
  12. schedule_delayed_work(&chip->work_status,1000);
  13. #endif
  14. #ifdefined(CONFIG_BATTERY_MAX17040)
  15. TriggerGasgaugeUpdate();//②、把DC状态更新到max17040;
  16. #endif
  17. }
  18. }

①、初始化电IC

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

[cpp] view plain copy
  1. staticvoidpm2301_charger_enable(structi2c_client*client,intonline)
  2. {
  3. if(online){/*EnabledCharging*/
  4. intbatt_capacity=0;
  5. batt_capacity=GetGasgaugeCapacity();
  6. /*Don'tstartchargingifbatterycapacityabove95%whenDCplugin*/
  7. if(0){
  8. //if(batt_capacity>=95){
  9. pm2301_write_reg(client,0x01,0x02);
  10. pm2301_write_reg(client,0x26,0x00);/*alwayskeeptheregisterto0*/
  11. }else{
  12. pm2301_write_reg(client,0x00,0x01);/*forceresumeofcharging*/
  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.6VChPrechVoltLevel:2.9V*/
  16. pm2301_write_reg(client,0x07,0x1E);/*ChVoltLevel:4.25V*/
  17. pm2301_write_reg(client,0x26,0x00);/*alwayskeeptheregisterto0*/
  18. }
  19. g_has_charged=1;
  20. }else{/*DisableCharging*/
  21. pm2301_write_reg(client,0x01,0x02);
  22. pm2301_write_reg(client,0x26,0x00);/*alwayskeeptheregisterto0*/
  23. g_has_charged=0;
  24. }
  25. }

②、把DC状态更新到max17040

[cpp] view plain copy
  1. TriggerGasgaugeUpdate()
插入DC这部流程如下:

android电池(五):电池 充电IC(PM2301)驱动分析篇_第6张图片

更多相关文章

  1. Android中线程同步之Mutex与Condtion的用法
  2. android 计时器线程
  3. Android SQLite是线程安全的吗?
  4. Android 进程和线程
  5. Android 子线程中更新UI
  6. Android 线程同步 ConditionVariable的用法
  7. FFmpeg In Android - tutorial-4-Spawning Threads创建线程
  8. Android学习笔记(6)————Android的线程与进程
  9. android jni 多线程编程 join thread

随机推荐

  1. [android研究联系人之二]联系人Email/Nic
  2. 解决给一组Button设置Background导致点击
  3. [转]Android中的BatteryService及相关组
  4. 让应用程序具体相应权限
  5. mac 下配置maven环境( 包括android工程使
  6. android之Android(安卓)Studio下自定义属
  7. Android数据库升级、降级、创建(onCreate
  8. android 使用opencv4 图片相似度对比
  9. Android(安卓)SurfaceHolder处理SurfaceV
  10. Android(安卓)adb 环境变量配置的坑 adb