电子说
对于便携设备来说,电源管理更加的重要,因为电池电量有限,容易电量焦虑。除了省电管理外,还需要对电池进行监控管理和充放电管理,这样保护好电池和系统,能用的更久。
1. Power supply框架都做些什么
这里我们以安卓为例:
APP 层: 该部分属于电量上报的最后的环节。其主要工作是:监听系统广播并对 UI 作出相应更新,包括电池电量百分比,充电状态,低电提醒,led 指示灯,异常提醒等。
FrameWork 层: 本层的 Battery 服务使用 Java 代码写成,运行在 FrameWork 中的SystemServer 进程。该系统服务的主要作用是:监听电池信息变化消息,并将该消息以系统广播的形式转发至 Android 系统中各处。
Native 层: Healthd 守护进程属于 Android Native 层的一个系统服务,负责接受 Kernel Driver 层上报的 uevent 事件,对电池信息和充电状态实时监控。
Kernel 层: 本层属于电池的驱动部分,由 Charger-manager 驱动、充电 IC 驱动、Fuel 驱动构成,负责与硬件交互,注册 Power supply 属性,并生成 uevent 上报 Native 层。包含充电状态管理、电量统计与更新。
关机充电
关机充电是单独启动的一个 linux 应用,通过系统调用直接读取 sysfs 来获取电池信息,init 进程会根据启动模式来启动 charge 服务,不会启动 android 相关进程。
我们这里只关注kernel层:
power supply framework在kernel/drivers/power/下。内核抽象出来power supply子系统为驱动提供了统一的框架。功能包括:
1.抽象PSY设备的共性,向用户空间提供统一的API
2.为底层PSY驱动的编写,提供简单、统一的方式。同事封装并实现公共逻辑。
power supply class位于drivers/power/目录中,主要由3部分组成(可参考下图的软件架构):
1)power supply core,用于抽象核心数据结构、实现公共逻辑。位于drivers/power/power_supply_core.c中。
2)power supply sysfs,实现sysfs以及uevent功能。位于drivers/power/power_supply_sysfs.c中。
3)power supply leds,基于linux led class,提供PSY设备状态指示的通用实现。位于drivers/power/power_suppply_leds.c中。
最后,驱动工程师可以基于power supply class,实现具体的PSY drivers,主要处理平台相关、硬件相关的逻辑。这些drivers都位于drivers/power/目录下。
2. 相关数据结构和接口
2.1 数据结构
struct power_supply:用于抽象PSY设备
/* include/linux/power_supply.h */ struct power_supply { const struct power_supply_desc *desc;//PSY描述符 char **supplied_to; size_t num_supplicants; char **supplied_from; size_t num_supplies; struct device_node *of_node; /* Driver private data */ void *drv_data; /* private */ struct device dev; struct work_struct changed_work; struct delayed_work deferred_register_work; spinlock_t changed_lock; bool changed; bool initialized; bool removing; atomic_t use_cnt; #ifdef CONFIG_THERMAL struct thermal_zone_device *tzd; struct thermal_cooling_device *tcd; #endif #ifdef CONFIG_LEDS_TRIGGERS struct led_trigger *charging_full_trig; char *charging_full_trig_name; struct led_trigger *charging_trig; char *charging_trig_name; struct led_trigger *full_trig; char *full_trig_name; struct led_trigger *online_trig; char *online_trig_name; struct led_trigger *charging_blink_full_solid_trig; char *charging_blink_full_solid_trig_name; #endif };
struct power_supply_desc:该描述符定义了psy的属性
/* Description of power supply */ struct power_supply_desc { const char *name;//PSY name enum power_supply_type type;//PSY类型 enum power_supply_usb_type *usb_types;//usb类型 size_t num_usb_types;//usb类型个数 enum power_supply_property *properties;//该PSY具有的属性列表 size_t num_properties;//属性的个数 /* * Functions for drivers implementing power supply class. * These shouldn't be called directly by other drivers for accessing * this power supply. Instead use power_supply_*() functions (for * example power_supply_get_property()). */ int (*get_property)(struct power_supply *psy,//用于获取psy属性的回调函数 enum power_supply_property psp, union power_supply_propval *val); int (*set_property)(struct power_supply *psy,//用于设置psy属性的回调函数 enum power_supply_property psp, const union power_supply_propval *val); /* * property_is_writeable() will be called during registration * of power supply. If this happens during device probe then it must * not access internal data of device (because probe did not end). */ int (*property_is_writeable)(struct power_supply *psy,//返回指定的属性值是否可写(用于sysfs) enum power_supply_property psp); void (*external_power_changed)(struct power_supply *psy);//当一个PSY设备存在并且属性发生改变时,power supply core会调用该回调函数,通知PSY driver,以便让它做出相应的处理 void (*set_charged)(struct power_supply *psy); /* * Set if thermal zone should not be created for this power supply. * For example for virtual supplies forwarding calls to actual * sensors or other supplies. */ bool no_thermal; /* For APM emulation, think legacy userspace. */ int use_for_apm; };
power_supply_battery_info:管理静态电池参数的推荐结构
2.2 接口
power_supply_core.c主要负责设备状态变化逻辑,power_supply_sysfs.c主要负责文件节点相关逻辑。
power_supply_changed:在驱动中检测到硬件状态发生变化,会通过该函数调度起psy中的changed_work。该工作队列负责发送notifier(内核内不同模块之间)和通过uevent进行change上报。
void power_supply_changed(struct power_supply *psy) { unsigned long flags; dev_dbg(&psy->dev, "%s ", __func__); spin_lock_irqsave(&psy->changed_lock, flags); psy->changed = true; pm_stay_awake(&psy->dev); spin_unlock_irqrestore(&psy->changed_lock, flags); schedule_work(&psy->changed_work); } EXPORT_SYMBOL_GPL(power_supply_changed);
power_supply_register:通过调用__power_supply_register负责注册一个psy设备,一般在设备驱动的probe流程中调用
power_supply_get_by_name:通过名字获取PSY指针
power_supply_put:释放获取到的PSY指针,与power_supply_get_by_name成对使用
3. 充电驱动
Charge Manger、Fuel Gauge、Charge IC,这三部分作为独立的设备驱动均注册到 Power-supply 中,每一个设备为单独的 PSY。PSY 之间可以通过 power supply 属性相互访问。
fuel-gauge 跟 charge-ic是服务于 charge-manger,charge-manger 不需要了解硬件细节,仅通过获取相应功能的 PSY 设备实例,通过这个 PSY 的属性获取相应信息。
3.1 Charger Manager
Charger Manager 是充电的控制策略层,主要负责:
修复并更新电量百分比。
充电流程管理(charging,notcharging,discharging,full 充电状态转换管理)。
安全管理(Ovp,Health,Charge Time out)。
温控管理(Jeita 功能,thermal 限流)。
电池电量显示策略(充放电曲线)。
电池容量管理(容量自学习功能)。
Charger Manager 以“battery”名字注册至 Power Supply 架构,会读写 Fuel Gauge 和 Charger IC 的 Power supply 属性。
charger-manager { compatible = "charger-manager"; cm-name = "battery"; cm-poll-mode = <2>; //”_cm_monitor”轮询模式 cm-poll-interval = <15000>;//”_cm_monitor”轮询时间间隔 cm-battery-stat = <2>;//电池在位检测方法,电压法 cm-fullbatt-vchkdrop-ms = <30000>;//充满电后,检查复充条件的周期 cm-fullbatt-vchkdrop-volt = <84000>;//满电后复充电压条件 cm-fullbatt-voltage = <4350000>;//软件满电电压判断阈值,必须配置 cm-fullbatt-current = <120000>;;//软件满电电流判断阈值,必须配置 cm-fullbatt-capacity = <100>;//电池满电时百分比 cm-num-chargers = <1>;//charger ic数量 //cm-chargers = "sc2721_charger"; cm-chargers = "fan54015_charger";//charger ic名字 cm-fuel-gauge = "sc27xx-fgu";//fgu名字 /* in deci centigrade */ cm-battery-cold = <200>; cm-battery-cold-in-minus; cm-battery-hot = <800>; cm-battery-temp-diff = <100>; /* Allow charging for 6hr */ cm-charging-max = <36000000>; /* recovery charging after stop charging 45min */ cm-discharging-max = <2700000>; /* the interval to feed charger watchdog */ cm-wdt-interval = <0>; /* drop voltage in microVolts to allow shutdown */ cm-shutdown-voltage = <3470000>;//低电关机电压 /* when 99% of the time is exceeded, it will be forced to 100% */ cm-tickle-time-out = <1500>; /* how much time to allow capacity change */ cm-one-cap-time = <60>;//允许电量增加1%最快时间 /* when the safe charging voltage is exceeded, stop charging */ cm-charge-voltage-max = <6500000>;//充电器过压保护电压阈值 /* drop voltage in microVolts to restart charging */ cm-charge-voltage-drop = <700000>;//复充电压条件 //Jeita 温控策略 cm-jeita-temp-table = <1000 1030 700000 4200000>, //不同温度范围内的充电电流和充电截止电压 <1150 1180 2000000 4400000>, //默认最大充电电流为2A <1450 1420 2000000 4400000>, //充电电压为4.35V <1600 1570 700000 4200000>; regulator@0 { cm-regulator-name = "vddgen0"; cable@0 { cm-cable-name = "USB"; extcon = <&extcon_gpio>; }; }; };
充电温控策略说明
电池温度T(℃) | 充电电流 ICC(mA) | 充电截止电压 VEOC(mV) |
---|---|---|
T≤0 | 700 | 4200 |
0
2000 |
4400 |
|
15≤T<45 | 2000 | 4400 |
45≤T<60 | 700 | 4200 |
T≥60 | 0 | 4200 |
bat: battery { compatible = "simple-battery"; charge-full-design-microamp-hours = <3900000>;//电池容量uAh charge-term-current-microamp = <200000>;//截止充电电流 constant_charge_voltage_max_microvolt = <4400000>;//截止充电电压 factory-internal-resistance-micro-ohms = <115000>;//电池内阻 voltage-min-design-microvolt = <3561000>;//Vocv低报警电压 //电池容量 – 温度补偿表 capacity-temp-table = <60 100>, <40 100>, <25 100>, <0 100>, <(-10) 80>; //电池内阻值 – 温度补偿表 resistance-temp-table = <60 60>, <40 70>, <25 100>, <0 328>, <(-20) 887>; };
3.2 Fuel Gauge
PMIC部分主要负责:
库伦计电量积分
充电器类型获取
电池在位检测
开机电压管理
内阻 – 温度,容量 – 温度等补偿算法
sc27xx_fuel_gauge 以“sc27xx-fgu”名字注册至 Power supply 架构,提供属性给 Charger Manager 读写。
pmic_fgu: fgu@a00 { compatible = "sprd,sc27xx-fgu", "sprd,sc2731-fgu"; reg = <0xa00>; bat-detect-gpio = <&pmic_eic 9 0>; nvmem-cell-names = "fgu_calib"; nvmem-cells = <&fgu_calib>; io-channels = <&pmic_adc 0>, <&pmic_adc 14>, <&pmic_adc 16>; io-channel-names = "bat-temp", "charge-vol", "charger-cur"; interrupt-parent = <&sc2721_pmic>; interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; monitored-battery = <&bat>; sprd,calib-resistance-real = <20000>;//库仑计芯片真实采样电阻 sprd,calib-resistance-spec = <20000>;//库仑计芯片规格电阻 };
3.3 Charger IC
Charger IC 主要负责以下具体内容:
打开/关闭充电
设置充电电流
设置截止充电电压点
打开/关闭 OTG
以 Fan54015 为例,将“fan54015_charger”名字注册至 Power supply 架构。提供属性给 Charger Manager读写。
&i2c3 { status = "okay"; clock-frequency = <400000>; fan54015_chg: charger@6a { compatible = "fairchild,fan54015_chg"; reg = <0x6a>; phys = <&hsphy>; monitored-battery = <&bat>; extcon = <&extcon_gpio>; vddvbus:otg-vbus { regulator-name = "vddvbus"; }; }; };
4. 怎样基于power supply class编写PSY driver
最后从PSY driver的角度,说明一下怎么基于power supply class编写驱动:
(1)根据硬件spec,确定PSY设备具备哪些特性,并把他们和enum power_supply_property对应。
(2)根据实际情况,实现这些properties的get/set接口。
(3)定义一个struct power_supply 变量,并初始化必要字段后,调用power_supply_register或者power_supply_register_no_ws,将其注册到kernel中。
(4)根据实际情况,启动设备属性变化的监控逻辑,例如中断,轮询等,并在发生改变时,调用power_supply_changed,通知power suopply core。
power supply子系统的引入 以市面上一款常见的的平板方案来看一看,进入平板的sys/class/power_supply/目录下
可以看到这里有三个****PSY 设备,分别对应 USB 充电器 DC 充电器,和电池。
进入battery目录下,发现下面有各种各样的属性,另外两个atc260x-usb 、atc260x-wall目录下分别也是这样。
然后在内核中找到对应的代码,进行学习,然后仿制一个出来就可以。以battery 驱动为例来分析。
static int __init atc260x_gauge_init(void)
atc260x_gauge_probe(struct platform_device *pdev)
soc_post_process(struct atc260x_gauge_info *info)
power_supply_register(struct device *parent, struct power_supply *psy)
power_supply_changed(struct power_supply *psy)
在相关的函数上打点断点,然后就可以学习了。
后记:
Linux内核博大精深,里面的机制太多了,不调试或者工作涉及根本学不精。可以了解了解概念和数据结构。遇到了先调试,必须知道了再去查资料研究,学不完,根本学不完。。。
全部0条评论
快来发表一下你的评论吧 !