电源管理入门-17 Power supply子系统

对于编写设备来说,电源管理更加的重要,因为电池可能很快就被用完电了。除了省电管理外,还需要对电池进行监控管理和充放电管理,这样保护好电池和系统,能用的更久。

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设备

arduino 复制代码
/* 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的属性

c 复制代码
/* 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上报。

scss 复制代码
void power_supply_changed(struct power_supply *psy)
{
	unsigned long flags;

	dev_dbg(&psy->dev, "%s\n", __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 属性。

ini 复制代码
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<T<15 2000 4400
15≤T<45 2000 4400
45≤T<60 700 4200
T≥60 0 4200
ini 复制代码
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 读写。

ini 复制代码
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读写。

ini 复制代码
&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 驱动为例来分析。

  1. static int __init atc260x_gauge_init(void)
  2. atc260x_gauge_probe(struct platform_device *pdev)
  3. soc_post_process(struct atc260x_gauge_info *info)
  4. power_supply_register(struct device *parent, struct power_supply *psy)
  5. power_supply_changed(struct power_supply *psy)

在相关的函数上打点断点,然后就可以学习了。

参考资料:

  1. blog.csdn.net/Numeral_Lif...
  2. blog.csdn.net/weixin_4637...

后记:

Linux内核博大精深,里面的机制太多了,不调试或者工作涉及根本学不精。可以了解了解概念和数据结构。遇到了先调试,必须知道了再去查资料研究,学不完,根本学不完。。。

干啥都能干,干啥啥不是,

专业入门劝退,堪称程序员杂家"。

欢迎各位有自己公众号的留言:申请转载,多谢!

后续会继续更新,纯干货分析,欢迎分享给朋友,欢迎点赞、收藏、在看、划线和评论交流以!

相关推荐
why1513 小时前
腾讯(QQ浏览器)后端开发
开发语言·后端·golang
浪裡遊3 小时前
跨域问题(Cross-Origin Problem)
linux·前端·vue.js·后端·https·sprint
声声codeGrandMaster3 小时前
django之优化分页功能(利用参数共存及封装来实现)
数据库·后端·python·django
呼Lu噜3 小时前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
bing_1583 小时前
为什么选择 Spring Boot? 它是如何简化单个微服务的创建、配置和部署的?
spring boot·后端·微服务
学c真好玩4 小时前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia04124 小时前
GenericObjectPool——重用你的对象
后端
Piper蛋窝4 小时前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel4 小时前
招幕技术人员
前端·javascript·后端
盖世英雄酱581364 小时前
什么是MCP
后端·程序员