知识屋:更实用的电脑技术知识网站
所在位置:首页 > 操作系统 > linux

Linux内核中USB充电器的解决方案

发布时间:2014-09-05 16:06:59作者:知识屋

Linux内核中USB充电器的解决方案
 
当前最新的内核(v3.5)对USB充电器的整体方案支持的不是太好,这里讨论的USB充电器的方案仅指软件
 
方案,即充电器的检测需要由软件干预(比如读取USB PHY的寄存器), 同时电池的充电值根据
 
充电器的不同类型,需要由软件来设置。 硬件检测充电器及硬件自动选择充电电流不在此讨论之列。
 
软件的方案的好处是: 可以省去一个USB充电器的检测芯片以及使用更简单(便宜)的电池管理芯片,  www.zhishiwu.com  
 
坏处是要由软件干预(可能有bug)以及不能实现低电量时高充电电流的充电方式。
 
一个整套的USB充电器方案,应该包括:
 
1. USB充电器的识别, 判断出该USB Charger是如下USB Charger中的一种:
 
- SDP (Standard Downstream Port)
 
- DCP (Dedicated Charging Port)
 
- CDP (Charging Downstream Port)
 
- ACA (Accessory Charger Adapters)
 
通常识别的比较多的是: SDP (接PC) 和 DCP (专用charger, DP和DM短接)。
 
2. USB充电器的识别与电池管理芯片的接口
 
即如何把充电结果告知电池管理芯片的驱动, 这里需要用到内核Power Supply的框架。
 
在软件实现USB充电的方案中,通常会有2个驱动,因为充电器识别模块和电池充电模块是
 
2个硬件模块。根据我对内核对应代码的理解,有如下两种方法可以实现Linux USB充电器的解决方案。  www.zhishiwu.com  
 
方法一:
1. 
1.1 在USB充电器的识别驱动里(在USB PHY里或单独的充电器驱动)设置一个notifier, 当
 
充电器识别完成后,将对应的事件和值传递给对应注册在这个notifier上的使用者。
 
probe函数中调用:
 
ATOMIC_INIT_NOTIFIER_HEAD(&notifier);
 
当检测完成后调用:
 
atomic_notifier_call_chain(&notifier, event, v);
 
1.2 同时还要实现: psy->external_power_changed (psy为struct power_supply的指针),这样当
 
充电器识别完成后,调用【1】power_supply_changed(psy)就可以改变相应的属性值,
 
然后用户程序通过/sys/class/power_supply/就可以知道所有的属性值。
 
2. 
2.1 在电池驱动里注册一个notifier,以及对应的notifier的函数
 
probe函数中:
 
usb_phy = usb_get_transceiver();
 
usb_register_notifier(usb_phy, nb);
 
nb.notifier_call = set_charging_current;
 
2.2 set_charger_current函数中:
 
将充电电流的值设置到电池管理芯片中去,通常用I2C或SPI接口完成。(根据电池管理芯片的
 
串行接口不同而不同)。  www.zhishiwu.com  
 
方案二:
 
无需在2个driver里实现notifier的接口。
 
1. 在电池管理芯片驱动里要实现,方案一中的1.2部分。
 
2. 在电池管理芯片驱动里通过power_supply_get_by_name(name)得到对应power supply的指针。
 
这里的name可以由platform data传入或写在device tree的对应的node里。 这个name必须和
 
USB充电器的识别驱动注册的power supply的名字相同。
 
【1】要实现此功能,还需要如下patch:
 
Subject: [PATCH] power_supply: add get_supplier_property
 
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/power/power_supply_core.c |   32 ++++++++++++++++++++++++++++++++
 include/linux/power_supply.h      |    3 +++
 2 files changed, 35 insertions(+), 0 deletions(-)
 
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 329b46b..fb7843b 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -94,6 +94,38 @@ int power_supply_am_i_supplied(struct power_supply *psy)
 }
 EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
 
+static int power_supply_find_supplier(struct device *dev, void *data)
+{
+    struct power_supply *psy = (struct power_supply *)data;
+    struct power_supply *epsy = dev_get_drvdata(dev);
+    int i;  www.zhishiwu.com  
+
+    for (i = 0; i < epsy->num_supplicants; i++)
+        if (!strcmp(epsy->supplied_to[i], psy->name))
+            return 1;
+            
+    return 0;
+}
+
+int power_supply_get_supplier_property(struct power_supply *psy,
+                    enum power_supply_property psp,
+                    union power_supply_propval *val)
+{
+    struct power_supply *epsy;
+    struct device *dev;
+
+    dev = class_find_device(power_supply_class, NULL, psy,
+                power_supply_find_supplier);
+    if (!dev)
+        return 1;
+
+    epsy = dev_get_drvdata(dev);
+    put_device(dev);
+
+    return epsy->get_property(epsy, psp, val);
+}
+EXPORT_SYMBOL_GPL(power_supply_get_supplier_property);
+
 static int __power_supply_is_system_supplied(struct device *dev, void *data)
 {
     union power_supply_propval ret = {0,};
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 204c18d..0dd89bf 100644  www.zhishiwu.com  
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -201,6 +201,9 @@ extern struct power_supply *power_supply_get_by_name(char *name);
 extern void power_supply_changed(struct power_supply *psy);
 extern int power_supply_am_i_supplied(struct power_supply *psy);
 extern int power_supply_set_battery_charged(struct power_supply *psy);
+extern int power_supply_get_supplier_property(struct power_supply *psy,
+                    enum power_supply_property psp,
+                    union power_supply_propval *val);
 
 #if defined(CONFIG_POWER_SUPPLY) || defined(CONFIG_POWER_SUPPLY_MODULE)
 extern int power_supply_is_system_supplied(void);
 
 
作者 hzpeterchen
(免责声明:文章内容如涉及作品内容、版权和其它问题,请及时与我们联系,我们将在第一时间删除内容,文章内容仅供参考)
收藏
  • 人气文章
  • 最新文章
  • 下载排行榜
  • 热门排行榜