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

阅读: 评论:0

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

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

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

平台信息:
内核:linux2.6/linux3.0
系统:android/android4.0 
平台:samsung exynos 4210、exynos 4412 、exynos 5250

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

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

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

 

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

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

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

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

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

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驱动部分

static const struct i2c_device_id pm2301_id[] = {{ "pm2301", 0 },{ }
};
MODULE_DEVICE_TABLE(i2c, pm2301_id);static struct i2c_driver pm2301_i2c_driver = {.driver    = {.name    = "pm2301",},.probe        = pm2301_probe,.remove        = __devexit_p(pm2301_remove),.suspend    = pm2301_suspend,.resume        = pm2301_resume,.id_table    = pm2301_id,
};static int __init pm2301_init(void)
{printk(KERN_INFO "pm2301_init !!n");return i2c_add_driver(&pm2301_i2c_driver);
}
module_init(pm2301_init);

(2)、平台驱动部分

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

static struct i2c_board_info i2c_devs1[] __initdata = {…………
#ifdef CONFIG_CHARGER_PM2301{I2C_BOARD_INFO("pm2301", 0x2c),.platform_data    = &pm2301_platform_data,},
#endif
…………
};

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

/sys/bus/i2c/drivers/pm2301

2、关于:pm2301_platform_data这个结构体

static struct pm2301_platform_data pm2301_platform_data = {.hw_init = pm2301_hw_init,//(1)、硬件接口初始化化;.gpio_lpn = GPIO_PM2301_LP,//(2)、结构体初始化;.gpio_irq = GPIO_CHARGER_STATUS,.gpio_enn = GPIO_CHARGER_ENABLE,.gpio_wakeup = GPIO_CHARGER_ONLINE,
};

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

(1)、硬件接口初始化

static int pm2301_hw_init(void)
{printk("pm2301_hw_init !!n");if (gpio_request(GPIO_CHARGER_ONLINE, "GPIO_CHARGER_ONLINE"))    {printk(KERN_ERR "%s :GPIO_CHARGER_ONLINE request port error!n", __func__);goto err_gpio_failed;} else {s3c_gpio_setpull(GPIO_CHARGER_ONLINE, S3C_GPIO_PULL_NONE);s3c_gpio_cfgpin(GPIO_CHARGER_ONLINE, S3C_GPIO_SFN(0));gpio_direction_input(GPIO_CHARGER_ONLINE);gpio_free(GPIO_CHARGER_ONLINE);}if (gpio_request(GPIO_CHARGER_STATUS, "GPIO_CHARGER_STATUS"))    {printk(KERN_ERR "%s :GPIO_CHARGER_STATUS request port error!n", __func__);goto err_gpio_failed;} else {s3c_gpio_setpull(GPIO_CHARGER_STATUS, S3C_GPIO_PULL_NONE);s3c_gpio_cfgpin(GPIO_CHARGER_STATUS, S3C_GPIO_SFN(0));gpio_direction_input(GPIO_CHARGER_STATUS);gpio_free(GPIO_CHARGER_STATUS);}if (gpio_request(GPIO_CHARGER_ENABLE, "GPIO_CHARGER_ENABLE"))    {printk(KERN_ERR "%s :GPIO_CHARGER_ENABLE request port error!n", __func__);goto err_gpio_failed;} else {s3c_gpio_setpull(GPIO_CHARGER_ENABLE, S3C_GPIO_PULL_NONE);s3c_gpio_cfgpin(GPIO_CHARGER_ENABLE, S3C_GPIO_SFN(1));gpio_direction_output(GPIO_CHARGER_ENABLE, 0);gpio_free(GPIO_CHARGER_ENABLE);}if (gpio_request(GPIO_PM2301_LP, "GPIO_PM2301_LP"))    {printk(KERN_ERR "%s :GPIO_PM2301_LP request port error!n", __func__);goto err_gpio_failed;} else {s3c_gpio_setpull(GPIO_PM2301_LP, S3C_GPIO_PULL_NONE);s3c_gpio_cfgpin(GPIO_PM2301_LP, S3C_GPIO_SFN(1));gpio_direction_output(GPIO_PM2301_LP, 1);gpio_free(GPIO_PM2301_LP);}return 1;err_gpio_failed:return 0;
}

(2)、结构体初始化

Include/linux/pm2301_charger.h

#define GPIO_CHARGER_ONLINE        EXYNOS4_GPX0(7)//对应控制脚的主控接口
#define GPIO_CHARGER_STATUS        EXYNOS4_GPX1(3)
#define GPIO_CHARGER_ENABLE     EXYNOS4_GPL2(0)
#define GPIO_PM2301_LP             EXYNOS4_GPX1(7)
struct pm2301_platform_data {int (*hw_init)(void);int gpio_enn;int gpio_wakeup;int gpio_irq;int gpio_lpn;
};
extern int pm2301_get_online(void);
extern int pm2301_get_status(void);

3、probe函数分析

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

static int __devinit pm2301_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);struct pm2301_chip *chip;int ret;printk(KERN_INFO "PM2301 probe !!n");
//(1)、前面这部分是对IIC的初始化;if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))return -EIO;chip = kzalloc(sizeof(*chip), GFP_KERNEL);if (!chip)return -ENOMEM;g_chip = chip;chip->client = client;chip->pdata = client->dev.platform_data;i2c_set_clientdata(client, chip);/* Hardware Init for PM2301 */if (chip->pdata->hw_init && !(chip->pdata->hw_init())) {dev_err(&client->dev, "hardware initial failed.n");goto err_hw_failed;}mutex_init(&i2c_lock);
//(2)、初始化两个队列INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online, pm2301_online_work);INIT_DELAYED_WORK_DEFERRABLE(&chip->work_status, pm2301_ststus_work);
//(3)、中断线程化chip->irq_online = gpio_to_irq(chip->pdata->gpio_wakeup);chip->irq_status = gpio_to_irq(chip->pdata->gpio_irq);/* Request IRQ for PM2301 */ret = request_threaded_irq(chip->irq_online,NULL, pm2301_dcin,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"PM2301 DC IN", chip);if (ret) {printk(KERN_ERR "Cannot request irq %d for DC (%d)n",chip->irq_online, ret);goto err_hw_failed;}#ifdef PM2301_REPORT_STATUS_BY_IRQret = request_threaded_irq(chip->irq_status,NULL, pm2301_status,IRQF_TRIGGER_FALLING,"PM2301 STATUS", chip);if (ret) {printk(KERN_ERR "Cannot request irq %d for CHARGE STATUS (%d)n",chip->irq_status, ret);goto err_hw_failed;}
#endifcharger_initial = 1;g_has_charged = 0;g_has_charging_full_or_stop = 0;#ifdef PM2301_REPORT_STATUS_BY_IRQ/* Set wakeup source for online pin*/irq_set_irq_wake(chip->irq_status, 1);
#endif/* Set wakeup source for online pin*/irq_set_irq_wake(chip->irq_online, 1);/* Init default interrupt route for PM2301 */pm2301_reg_init(chip->client);/* Init online & status value */chip->online = pm2301_charger_online(chip);g_pm2301_online = chip->online;    /* Sync to global */pm2301_charger_enable(chip->client, chip->online);pm2301_charger_status(chip);printk(KERN_INFO "PM2301 probe success!!n");return 0;
err_hw_failed:dev_err(&client->dev, "failed: power supply registern");i2c_set_clientdata(client, NULL);kfree(chip);return ret;
}

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

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

(2)、任务初始化宏

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

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

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

看到这里是否想起:

static struct pm2301_platform_data pm2301_platform_data = {……………….gpio_lpn = GPIO_PM2301_LP,.gpio_irq = GPIO_CHARGER_STATUS,.gpio_enn = GPIO_CHARGER_ENABLE,.gpio_wakeup = GPIO_CHARGER_ONLINE,
};#define GPIO_CHARGER_ONLINE        EXYNOS4_GPX0(7)
#define GPIO_CHARGER_STATUS        EXYNOS4_GPX1(3)
#define GPIO_CHARGER_ENABLE         EXYNOS4_GPL2(0)
#define GPIO_PM2301_LP             EXYNOS4_GPX1(7)

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

中断线程化:

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

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

static irqreturn_t pm2301_dcin(int irq, void *_data)
{struct pm2301_chip *chip = _data;schedule_delayed_work(&chip->work_online, PM2301_DELAY);return IRQ_HANDLED;
}

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

static void pm2301_online_work(struct work_struct *work)
{struct pm2301_chip *chip;chip = container_of(work, struct pm2301_chip, work_online.work);int new_online = pm2301_charger_online(chip);if (chip->online != new_online) {chip->online = new_online;g_pm2301_online = chip->online;    /* Sync to global */pm2301_charger_enable(chip->client, chip->online);//①、初始化充电IC;
#ifdef PM2301_REPORT_STATUS_BY_IRQ/*To avoid status pin keep low*/schedule_delayed_work(&chip->work_status, 1000);
#endif
#if defined(CONFIG_BATTERY_MAX17040)TriggerGasgaugeUpdate();//②、把DC状态更新到max17040;
#endif}
}

①、初始化电IC

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

static void pm2301_charger_enable(struct i2c_client *client, int online)
{if (online) {    /* Enabled Charging*/int batt_capacity = 0;batt_capacity = GetGasgaugeCapacity();/* Don't start charging if battery capacity above 95% when DC plug in*/if(0) {//if( batt_capacity >= 95 ) {pm2301_write_reg(client, 0x01, 0x02);pm2301_write_reg(client, 0x26, 0x00);   /* always keep the register to 0 */} else {pm2301_write_reg(client, 0x00, 0x01);   /* force resume of charging */pm2301_write_reg(client, 0x01, 0x06);    /* ChEn=1, AutoResume=1 */pm2301_write_reg(client, 0x05, 0x7A);     /* ChEoccurrentLevel:150mA, ChPrechcurrentLevel:100mA, ChCCcurrentLevel:1000mA/2000mA */pm2301_write_reg(client, 0x06, 0x0A);    /* ChVersumeVot:3.6V ChPrechVoltLevel:2.9V */pm2301_write_reg(client, 0x07, 0x1E);    /* ChVoltLevel:4.25V */pm2301_write_reg(client, 0x26, 0x00);     /* always keep the register to 0 */}g_has_charged = 1;} else {    /* Disable Charging*/pm2301_write_reg(client, 0x01, 0x02);pm2301_write_reg(client, 0x26, 0x00);   /* always keep the register to 0 */g_has_charged = 0;}
}

②、把DC状态更新到max17040

TriggerGasgaugeUpdate()

插入DC这部流程如下:

 



转载于:.html

本文发布于:2024-02-04 05:09:40,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170699800552348.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:电池   android   IC
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23