Linux 4.19.111 供电(power supply )子系统
时间:2022-07-17 12:30:00
Linux 内核为了方便 battery 管理专门提供power supply framework。battery 管理分为两部分,一部分是电池监控(fuelgauge),二是充放电管理(charger)。
fuelgauge 驱动主要负责上层 android 系统提供当前电池的功率和健康状况信息,也提供 charger 驱动提供电池信息;
charger 驱动主要负责电源线的插拔检测和充放电过程管理。battery 管理,硬件上有电量计 IC 和充放电 IC。
power supply 设备的目的很简单,就是为系统供电,但是因为供电设备可以来自电池或者电池USB 电源、电源适配器电源、无线电源等,涉及充放电管理。此外,核心还需要向用户空间导出必要的信息,然后通知用户程序,如电池功率变化、充放电状态、电池类型等。因此,核心抽象 power supply 作为基础组件支撑。
所以 power supply 设备的主要功能是向用户空间报告各种状态信息,因此 power supply framework 的核心思想是抽象这些状态信息properties——属性。由于状态信息类型有限,属性数量也有限。
power supply 设备驱动只需要负责该 power supply 设备的属性是什么?这些属性的值是什么?当属性值发生变化时,通知 power supply framework。将某个power supply 设备支持的属性和对应值为 sysfs 以属性值变化时,以 的形式为用户提供空间uevent 的形式广播给用户空间程序。另外,power supply framework 也有助于处理power supply 设备级联。
一、Power Supply 软件框架
power supply framework 存储在 中drivers/power/ 路径下。内核抽象 power supply 子系统为驱动提供了统一的框架。功能包括:
-
抽象 power supply 设备的共性为用户空间提供了统一的 API;
-
为 power supply 设备驱动的编写提供了简单统一的方式。
power supply framework 主要由三部分组成:
-
power supply core,抽象核心数据结构,实现公共逻辑的一般电源监控。drivers/power/power_supply_core.c 中。
-
power supply sysfs, 通用电源监控Sysfs 接口,实现 Sysfs 以及 uevent 功能。位于 drivers/power/power_supply_sysfs.c 中。
-
power supply leds,基于 Linux LED class,提供 power supply 设备状态指示的通用实现。drivers/power/power_suppply_leds.c 中。
最后,驱动工程师可以基于 power supply framework,实现具体 power supply 设备驱动程序用于处理平台和硬件的逻辑。这些驱动程序位于 drivers/power/ 目录下。
二、Power Supply 关键数据结构
power_supply_config 结构代表运行过程中特定的 power supply 配置。
of_node:严格来说,这种数据结构不在设备模型中,而是 DTS 中间节点对应的内存中的设备描述通常代表一个设备。这里代表 power supply 设备。
fwnode:fwnode 指固件节点,通常代表设备树或 ACPI(通常是 DSDT 表中的一个项目。设备树和 ACPI 是两种不同的方法来定义设备及其属性和设备之间的连接。它们都使用树形结构来编码这些信息。
在一个给定的结构设备上的 fwnode 成员是设备对应固件表中的节点。ACPI 在基于 x86/UEFI 的系统很常见,设备树在 ARM 在系统中很常见。
fwnode 可接受 fwnode 句柄内核 api 一起使用。
supplied_to:由 保存的字符串数组power supply 设备供电power supply 设备列表可用于 power supply 设备组织成相互级联的 power supply 设备链。这些供电 power supply 设备,称为 supplicant(客户端,乞讨者)。
num_supplicants:supplicant 数量。
power_supply_desc 结构体代表 power supply 说明(详细)。
name:设备名称。
type:设备类型。
usb_types:支持的 USB 类型(TYPE C 接口、专用充电端口、下游充电端口等。
num_usb_types:支持的 USB 类型数量。
properties:设备属性列表。
num_properties:属性数。
get_property/set_property:实现 用于驱动程序power supply class 函数。这些不应该被其他驱动程序直接调用来访问 power supply。取而代之的是使用 power_supply_*() 函数(如 power_supply_get_property())。
property_is_writeable:是否可以写回指定的属性值(用于 Sysfs),注册 power supply 设备被调用。如果发生在设备检测过程中,则不得访问设备内部数据(因为检测尚未结束)。
external_power_changed:当一个 power supply 设备存在 supply 设备, power supply 设备的属性发生变化(如online、offline)时,power supply core 将调用回调函数通知 power supply 设备 driver,让它做出相应的处理。
set_charged:通知 外部模块power supply 设备 driver,该 power supply 设备状态发生了变化。
no_thermal:是否为 power supply 创建设备thermal zone。
use_for_apm:For APM emulation。
power_supply 结构体为 power supply class抽象 的核心数据结构power supply 设备。
desc:power_supply_desc 结构。
supplied_to:由 保存的字符串数组power supply 设备供电power supply 设备列表可用于 power supply 设备组织成相互级联的 power supply 设备链。这些供电 power supply 设备,称为 supplicant(客户端,乞讨者)。
num_supplicants:supplicant 个数。
supplied_from:向 power supply 设备供电power supply 设备列表,又称 supply(提供者)。从另一个方向组织 power supply 设备之间的级联关系。
num_supplies:supply 的个数。
of_node:power supply 设备(是 DTS 中的一个节点)。
dev:power supply 设备。
changed_work/changed_lock/changed: 用于处理状态变化的 workqueue,主要思路是要 power supply 设备状态发生变化,提交 changed_work 到 workqueue,然后查询并通知所有 supplicants。
deferred_register_work:power supply 注册结束时调用,推迟任务执行。工作队列中有一个 timer 定时器结构,实现延迟工作。
initialized:power supply 设备是否初始化。
removing:power supply 设备正在移除。
use_cnt:用户计数。
tzd/tcd:如果该 power supply 设备具有温度等属性,则需要借助 Linux Generic Thermal Sysfs Drivers(温控子系统)的框架,注册相应的 Thermal 设备。
led_trigger:如果配置了 CONFIG_LEDS_TRIGGERS,则调用 Linux Led Class 的接口,注册相应的 LED 设备,用于power supply 设备状态指示。
include/linux/power_supply.h
/* Run-time specific power supply configuration */struct power_supply_config {
struct device_node *of_node; struct fwnode_handle *fwnode; /* Driver private data */ void *drv_data; char **supplied_to; size_t num_supplicants;};/* Description of power supply */struct power_supply_desc {
const char *name; enum power_supply_type type; enum power_supply_usb_type *usb_types; size_t num_usb_types; enum power_supply_property *properties; 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, enum power_supply_property psp, union power_supply_propval *val); int (*set_property)(struct power_supply *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, enum power_supply_property psp); void (*external_power_changed)(struct power_supply *psy); 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;};struct power_supply {
const struct power_supply_desc *desc; 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};
power supply framework 将所有可能 power supply 设备属性,以枚举(enum power_supply_property )的形式抽象出来,power supply 设备 driver 可以根据设备的实际情况,从中选取一些。
POWER_SUPPLY_PROP_STATUS,该 power supply 设备的 status,主要是充电状态,包括“Unknown”, “Charging”,“Discharging”, “Not charging”,“Full”,由枚举变量(POWER_SUPPLY_STATUS_*)定义。根据设计方案的不同,充电类型的 power supply 设备,或者 battery 类型的 power supply 设备,都可能具备该属性。
POWER_SUPPLY_PROP_CHARGE_TYPE,充电类型,包括:“Unknown”, “N/A”, “Trickle”,“Fast”,由枚举型变量(POWER_SUPPLY_CHARGE_TYPE_*)定义,同理根据设计方案的不同,充电类型的 power supply 设备,或者 battery 类型的 power supply 设备,都可能具备该属性。
POWER_SUPPLY_PROP_HEALTH,“健康”情况,包括“Unknown”,“Good”,“Overheat”,“Dead”,“Over voltage”等,由枚举型变量(POWER_SUPPLY_HEALTH_*)定义。一般用于 battery 类型的 power supply 设备。
POWER_SUPPLY_PROP_TECHNOLOGY,采用的技术,包括“Unknown”,“NiMH”,“Li-ion”,“Li-poly”, “LiFe”, “NiCd”,“LiMn”,由枚举型变量(POWER_SUPPLY_TECHNOLOGY_*)定义。一般用于 battery 类型的 power supply 设备。
POWER_SUPPLY_PROP_CAPACITY_LEVEL,容量,包括“Unknown”,“Critical”,“Low”,“Normal”,“High”,“Full”,由枚举型变量(POWER_SUPPLY_CAPACITY_LEVEL_*)定义。一般用于 battery 类型的 power supply 设备。
power supply 设备类型由 enum power_supply_type 枚举定义。
POWER_SUPPLY_TYPE_UNKOWN,未知。
POWER_SUPPLY_TYPE_BATTERY,电池,嵌入式设备、手持式智能设备常用的供电形式。
POWER_SUPPLY_TYPE_UPS,Uninterruptible Power System/Uninterruptible Power Supply,不间断式供电设备,通过将交流电和蓄电池连接,正常情况下由交流电供电,同时向蓄电池充电。当交流电断电时,由蓄电池紧急供电。一般用于服务器等设备。
POWER_SUPPLY_TYPE_MAINS,主供电设备,如笔记本电脑的适配器,其特点是可以单独供电,当其断电时,再由辅助供电设备供电(如 battery)。
POWER_SUPPLY_TYPE_USB/POWER_SUPPLY_TYPE_USB_DCP/POWER_SUPPLY_TYPE_USB_CDP/POWER_SUPPLY_TYPE_USB_ACA/POWER_SUPPLY_TYPE_USB_PD/POWER_SUPPLY_TYPE_USB_PD_DRP,USB 类型的供电,不同点在于充电电流的限制,由 USB Battery Charge Spec 规定。
POWER_SUPPLY_TYPE_USB_TYPE_C USB Type C 接口供电。
POWER_SUPPLY_TYPE_APPLE_BRICK_ID 苹果供电。
power supply USB 类型由 power_supply_usb_type 枚举定义。
标准下行端口(SDP)
这种端口的D+和D-线上具有15kΩ下拉电阻。限流值为:挂起时2.5mA,连接时为100mA,连接并配置为较高功率时为500mA。它其实就是一种普通的USB模式,当USB处于这种模式时,既可以为外部设备(手机充电、充电宝)充电,也可以起到数据连接的作用(U盘、手机上传/下载)。
专用充电端口(DCP)
这种端口不支持任何数据传输,但能够提供1.5A以上的电流。端口的D+和D-线之间短路。这种类型的端口支持较高充电能力的墙上充电器和车载充电器,无需枚举。它其实就是简单的充电器,当USB处于这种模式时只能进行充电而不能进行数据连接。
充电下行端口(CDP)
这种端口既支持大电流充电,也支持完全兼容USB 2.0的数据传输。端口具有D+和D-通信所必需的15kΩ下拉电阻,也具有充电器检测阶段切换的内部电路。内部电路允许便携设备将CDP与其它类型端口区分开来。它其实就是带有快充功能(1.5A)的USB接口,当USB处于这种模式时既可以进行快充,也可以起到数据连接的作用。
USB-PD(Power Delivery)
基于USB Type-C的一种电源供电标准,最大供电功率可达100瓦(W);随着USB Type-C的普及,越来越多的设备(手机、平板、显示器、工作站、充电器等)使用USB-PD快速充电方案。
USB PD 1.0
PD1.0把传输功率从之前的7.5W提高到了最大100W,输出电压最高达到20V,最大电流达到5A。当一个用电设备连接到主机的USB口,初始功率为10W (5V / 2A),在最终的功率规格选定之后,传输功率相应的转换到18W,36W,60W或者100W。
USB PD 2.0
随着Type-C接口规范的发布,USB-IF将USB PD升级到了2.0版本。BUS电压根据需求在5V,9V,15V和20V之间切换。
Type-C是一种接口规范,默认最大支持5V/3A。Type-C接口带有专用的通信线,即CC(channel configure)线。CC线可以传输USB PD协议,同时支持DRP (dual role port) type C接口,可以在电源和负载之间进行角色转换,进而支持功率双向传输。
USB PD 3.0
是目前USB PD的最新版本。在PD2.0的基础上,PD3.0增加了PPS(programmable power supply)功能,BUS电压能够以20mV/step进行调整,电流限值能够以50mA/step进行调整。电压电流细分调整可以优化电力传输策略,让power变得更加智能化。
include/linux/power_supply.h
enum {
POWER_SUPPLY_STATUS_UNKNOWN = 0, POWER_SUPPLY_STATUS_CHARGING, POWER_SUPPLY_STATUS_DISCHARGING, POWER_SUPPLY_STATUS_NOT_CHARGING, POWER_SUPPLY_STATUS_FULL,};enum {
POWER_SUPPLY_CHARGE_TYPE_UNKNOWN = 0, POWER_SUPPLY_CHARGE_TYPE_NONE, POWER_SUPPLY_CHARGE_TYPE_TRICKLE, POWER_SUPPLY_CHARGE_TYPE_FAST,};enum {
POWER_SUPPLY_HEALTH_UNKNOWN = 0, POWER_SUPPLY_HEALTH_GOOD, POWER_SUPPLY_HEALTH_OVERHEAT, POWER_SUPPLY_HEALTH_DEAD, POWER_SUPPLY_HEALTH_OVERVOLTAGE, POWER_SUPPLY_HEALTH_UNSPEC_FAILURE, POWER_SUPPLY_HEALTH_COLD, POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE, POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,};enum {
POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0, POWER_SUPPLY_TECHNOLOGY_NiMH, POWER_SUPPLY_TECHNOLOGY_LION, POWER_SUPPLY_TECHNOLOGY_LIPO, POWER_SUPPLY_TECHNOLOGY_LiFe, POWER_SUPPLY_TECHNOLOGY_NiCd, POWER_SUPPLY_TECHNOLOGY_LiMn,};enum {
POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0, POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL, POWER_SUPPLY_CAPACITY_LEVEL_LOW, POWER_SUPPLY_CAPACITY_LEVEL_NORMAL, POWER_SUPPLY_CAPACITY_LEVEL_HIGH, POWER_SUPPLY_CAPACITY_LEVEL_FULL,};enum {
POWER_SUPPLY_SCOPE_UNKNOWN = 0, POWER_SUPPLY_SCOPE_SYSTEM, POWER_SUPPLY_SCOPE_DEVICE,};enum power_supply_property {
/* Properties of type `int' */ POWER_SUPPLY_PROP_STATUS = 0, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_AUTHENTIC, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_VOLTAGE_MIN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_AVG, POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_VOLTAGE_BOOT, POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_CURRENT_BOOT, POWER_SUPPLY_PROP_POWER_NOW, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_EMPTY, POWER_SU