资讯详情

Linux 4.19.111 供电(power supply )子系统

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 子系统为驱动提供了统一的框架。功能包括:

  1. 抽象 power supply 设备的共性为用户空间提供统一 API;

  2. 为 power supply 编写设备驱动提供简单统一的方法。

power supply framework 主要由三部分组成:

  1. power supply core,抽象核心数据结构,实现公共逻辑的一般电源监控。 drivers/power/power_supply_core.c 中。

  2. power supply sysfs,通用电源监控 Sysfs 接口,实现 Sysfs 以及 uevent 功能。位于 drivers/power/power_supply_sysfs.c 中。

  3. 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 配置。

:严格来说,这种数据结构不在设备模型中,而是一种 DTS 中间节点对应的内存中的设备描述通常代表一个设备。这里代表 power supply 设备。

:fwnode 指固件节点,通常代表设备树或 ACPI(通常是 DSDT 表)中的一个项目。设备树和 ACPI 定义设备及其属性和设备之间的两种不同方式。它们都使用树形结构来编码这些信息。

在给定的结构设备上 fwnode 成员是设备对应固件表中的节点。ACPI 在基于 x86/UEFI 设备树在系统中很常见,设备树在系统中很常见 ARM 在系统中很常见。

fwnode 可以与接受 fwnode 句柄的内核 api 一起使用。

:由字符串数组保存 power supply 设备供电的 power supply 可以将设备列表 power supply 设备组织成相互级联 power supply 设备链。这些供电 power supply 设备,称作 supplicant(客户端,乞讨者)。

:supplicant 数量。


power_supply_desc 结构体代表 power supply 说明(详细)。

:设备名称。

:设备类型。

:支持的 USB 类型(TYPE C 接口、专用充电端口、下游充电端口等。

:支持的 USB 类型数量。

:设备属性列表。

:属性数。

:实现驱动程序 power supply class 函数。其他驱动程序不应直接调用这些函数 power supply。取而代之的是使用 power_supply_*() 函数(例如 power_supply_get_property())。

:返回指定的属性值是否可写(用于 Sysfs),它将在注册 power supply 设备被调用。如果发生在设备检测过程中,则不得访问设备内部数据(因为检测尚未结束)。

:当一个 power supply 设备存在 supply 设备,且该 power supply 改变设备的属性(如online、offline)时,power supply core 调用回调函数通知 power supply 设备 driver,让它做出相应的处理。

:通知外部模块 power supply 设备 driver,该 power supply 设备状态发生了变化。

:是否为 power supply 设备创建 thermal zone。

:For APM emulation。


power_supply 结构体是 power supply class 抽象核心数据结构 power supply 设备。

:power_supply_desc 结构。

:由字符串数组保存 power supply 设备供电的 power supply 可以将设备列表 power supply 设备组织成相互级联 power supply 设备链。这些供电 power supply 设备,称作 supplicant(客户端,乞讨者)。

:supplicant 个数。

:向该保存了一个字符串数组 power supply 设备供电的 power supply 设备列表,也 supply(提供者)。从另一个方向组织 power supply 设备之间的等级关系。

:supply 的个数。

:power supply 设备(它是 DTS 一个节点)。

:power supply 设备。

:用于处理状态变化 workqueue,主要思路是当该 power supply 设备状态发生变化,提交 changed_work 到 workqueue,然后查询并通知所有人 supplicants。

:power supply 注册结束时调用,推迟执行任务。工作队列中有一个 timer 为实现延迟工作,定时器结构体。

:power supply 设备是否初始化。

:power supply 设备正在移除。

:用户计数。

:如果该 power supply 设备具有温度等属性,则需要借助 Linux Generic Thermal Sysfs Drivers(温控子系统)的框架,注册相应的 Thermal 设备。

:如果配置了 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 枚举定义。

这种端口的D+和D-线上具有15kΩ下拉电阻。限流值为:挂起时2.5mA,连接时为100mA,连接并配置为较高功率时为500mA。它其实就是一种普通的USB模式,当USB处于这种模式时,既可以为外部设备(手机充电、充电宝)充电,也可以起到数据连接的作用(U盘、手机上传/下载)。

这种端口不支持任何数据传输,但能够提供1.5A以上的电流。端口的D+和D-线之间短路。这种类型的端口支持较高充电能力的墙上充电器和车载充电器,无需枚举。它其实就是简单的充电器,当USB处于这种模式时只能进行充电而不能进行数据连接。

这种端口既支持大电流充电,也支持完全兼容USB 2.0的数据传输。端口具有D+和D-通信所必需的15kΩ下拉电阻,也具有充电器检测阶段切换的内部电路。内部电路允许便携设备将CDP与其它类型端口区分开来。它其实就是带有快充功能(1.5A)的USB接口,当USB处于这种模式时既可以进行快充,也可以起到数据连接的作用。

基于USB Type-C的一种电源供电标准,最大供电功率可达100瓦(W);随着USB Type-C的普及,越来越多的设备(手机、平板、显示器、工作站、充电器等)使用USB-PD快速充电方案。

PD1.0把传输功率从之前的7.5W提高到了最大100W,输出电压最高达到20V,最大电流达到5A。当一个用电设备连接到主机的USB口,初始功率为10W (5V / 2A),在最终的功率规格选定之后,传输功率相应的转换到18W,36W,60W或者100W。

随着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的最新版本。在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_SUPPLY_PROP_CHARGE_NOW,
	POWER_SUPPLY_PROP_CHARGE_AVG,
	POWER_SUPPLY_PROP_CHARGE_COUNTER,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
	POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
	POWER_SUPPLY_PROP_ENERGY_FULL,
	POWER_SUPPLY_PROP_ENERGY_EMPTY,
	POWER_SUPPLY_PROP_ENERGY_NOW,
	POWER_SUPPLY_PROP_ENERGY_AVG,
	POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
	POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */
	POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
	POWER_SUPPLY_PROP_TEMP,
	POWER_SUPPLY_PROP_TEMP_MAX,
	POWER_SUPPLY_PROP_TEMP_MIN,
	POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
	POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
	POWER_SUPPLY_PROP_TEMP_AMBIENT,
	POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN,
	POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX,
	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
	POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
	POWER_SUPPLY_PROP_USB_TYPE,
	POWER_SUPPLY_PROP_SCOPE,
	POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
	POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
	POWER_SUPPLY_PROP_CALIBRATE,
	/* Properties of type `const char *' */
	POWER_SUPPLY_PROP_MODEL_NAME,
	POWER_SUPPLY_PROP_MANUFACTURER,
	POWER_SUPPLY_PROP_SERIAL_NUMBER,
};

enum power_supply_type { 
        
	POWER_SUPPLY_TYPE_UNKNOWN = 0,
	POWER_SUPPLY_TYPE_BATTERY,
	POWER_SUPPLY_TYPE_UPS,
	POWER_SUPPLY_TYPE_MAINS,
	POWER_SUPPLY_TYPE_USB,			/* Standard Downstream Port */
	POWER_SUPPLY_TYPE_USB_DCP,		/* Dedicated Charging Port */
	POWER_SUPPLY_TYPE_USB_CDP,		/* Charging Downstream Port */
	POWER_SUPPLY_TYPE_USB_ACA,		/* Accessory Charger Adapters */
	POWER_SUPPLY_TYPE_USB_TYPE_C,		/* Type C Port */
	POWER_SUPPLY_TYPE_USB_PD,		/* Power Delivery Port */
	POWER_SUPPLY_TYPE_USB_PD_DRP,		/* PD Dual Role Port */
	POWER_SUPPLY_TYPE_APPLE_BRICK_ID,	/* Apple Charging Method */
};

enum power_supply_usb_type { 
        
	POWER_SUPPLY_USB_TYPE_UNKNOWN = 0,
	POWER_SUPPLY_USB_TYPE_SDP,		/* Standard Downstream Port */
	POWER_SUPPLY_USB_TYPE_DCP,		/* Dedicated Charging Port */
	POWER_SUPPLY_USB_TYPE_CDP,		/* Charging Downstream Port */
	POWER_SUPPLY_USB_TYPE_ACA,		/* Accessory Charger Adapters */
	POWER_SUPPLY_USB_TYPE_C,		/* Type C Port */
	POWER_SUPPLY_USB_TYPE_PD,		/* Power Delivery Port */
	POWER_SUPPLY_USB_TYPE_PD_DRP,		/* PD Dual Role Port */
	POWER_SUPPLY_USB_TYPE_PD_PPS,		/* PD Programmable Power Supply */
	POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID,	/* Apple Charging Method */
};

三、Power Supply 核心实现

power supply framework 首要任务,是向 power supply 设备 driver 提供统一的驱动编写接口,主要包括:

power supply 设备的

其中 power_supply_register 和 power_supply_register_no_ws 的区别是 power_supply_register 注册的设备,具备 wakeup 系统的能力,而 power_supply_register_no_ws 不具备。power_supply_register 和 devm_power_supply_register 的区别是 power_supply_register 需要对返回的 power_supply 指针使用 power_supply_unregister 来释放资源,而 devm_power_supply_register 返回的 power_supply 指针将在驱动程序 detach 时自动注销。

include/linux/power_supply.h

extern struct power_supply *__must_check
power_supply_register(struct device *parent,
				 const struct power_supply_desc *desc,
				 const struct power_supply_config *cfg);
extern struct power_supply *__must_check
power_supply_register_no_ws(struct device *parent,
				 const struct power_supply_desc *desc,
				 const struct power_supply_config *cfg);
extern struct power_supply *__must_check
devm_power_supply_register(struct device *parent,
				 const struct power_supply_desc *desc,
				 const struct power_supply_config *cfg);
extern struct power_supply *__must_check
devm_power_supply_register_no_ws(struct device *parent,
				 const struct power_supply_desc *desc,
				 const struct power_supply_config *cfg);
extern void power_supply_unregister(struct power_supply *psy);

power_supply_register、power_supply_register_no_ws、devm_power_supply_register 和 devm_power_supply_register_no_ws,内部都调用了 __power_supply_register,下面分析它的关键工作流程:

  1. 遍历 desc 指向的 power_supply_desc 结构中的属性字段,如果存在 USB 供电属性,那么就去查看是否存在 USB 类型,不存在的话返回 ERR_PTR(-EINVAL)。
  2. 调用 kzalloc 给 power_supply 结构分配内存,并调用 device_initialize 初始化 device 结构,接着给 dev 指向的 device 结构字段赋值。
  3. 调用 dev_set_drvdata 给 device 设置私有数据,指向刚刚分配的 power_supply 结构。
  4. 形参 cfg 不为空的情况下,将 cfg 携带的数据赋给 power_supply 结构中的相应字段。
  5. 调用 dev_set_name 设置 device name。
  6. 调用 INIT_WORK 和 INIT_DELAYED_WORK 宏初始化工作队列,并添加任务,INIT_DELAYED_WORK 添加的任务会延时执行。
  7. 调用 power_supply_check_supplies 检查给本 power supply 设备供电的 power supply 设备,这个函数可能到设备树去检索 supplies。
  8. 调用 spin_lock_init 初始化 power_supply 结构中的自旋
  9. 调用 device_add 添加这个 device。
  10. 调用 device_init_wakeup 赋予设备是否支持唤醒系统,和是否使用唤醒系统的特性。
  11. 调用 psy_register_thermal 给 power supply 设备注册 thermal,和温控相关。
  12. 调用 psy_register_cooler 给 power supply 设备注册 cooler,和温控相关。
  13. 调用 power_supply_create_triggers 创建触发器
  14. power_supply 结构中的 use_cnt 原子的加 1(在任何 uevents 之后更新 use_cnt,最明显的来自 device_add() )。
  15. power_supply 结构中的 initialized 字段赋值为 true,表明 power_supply 结构已经初始化完成。
  16. 调用 queue_delayed_work 启动 delayed work 执行,延迟 POWER_SUPPLY_DEFERRED_REGISTER_TIME 后执行。
  17. 没有异常的情况下,现在返回指向 power_supply 结构的指针。

power_supply_unregister 主要工作流程:

  1. 调用 atomic_dec_return 将 power_supply 结构中的 use_cnt 原子地减一。
  2. power_supply 结构中的 removing 字段赋值为 true,表征正在移除 power supply 设备。
  3. 调用 cancel_work_sync 和 cancel_delayed_work_sync 取消两个工作队列里的任务。
  4. 调用 sysfs_remove_link 删除 kobj 目录下名为 powers 的软链接文件。
  5. 调用 power_supply_remove_triggers 移除触发器。
  6. 调用 psy_unregister_cooler 和 psy_unregister_thermal 取消注册的 cooler 和 thermal。
  7. 调用 device_init_wakeup 取消 power supply 设备唤醒系统的能力。
  8. 调用 device_unregister 移除设备。

drivers/power/supply/power_supply_core.c

static struct power_supply *__must_check
__power_supply_register(struct device *parent,
				   const struct power_supply_desc *desc,
				   const struct power_supply_config *cfg,
				   bool ws)
{ 
        
	struct device *dev;
	struct power_supply *psy;
	int i, rc;

	if (!parent)
		pr_warn("%s: Expected proper parent device for '%s'\n",
			__func__, desc->name);

	if (!desc || !desc->name || !desc->properties || !desc->num_properties)
		return ERR_PTR(-EINVAL);

	for (i = 0; i < desc->num_properties; ++i) { 
        
		if ((desc->properties[i] == POWER_SUPPLY_PROP_USB_TYPE) &&
		    (!desc->usb_types || !desc->num_usb_types))
			return ERR_PTR(-EINVAL);
	}

	psy = kzalloc(sizeof(*psy), GFP_KERNEL);
	if (!psy)
		return ERR_PTR(-ENOMEM);

	dev = &psy->dev;

	device_initialize(dev);

	dev->class = power_supply_class;
	dev->type = &power_supply_dev_type;
	dev->parent = parent;
	dev->release = power_supply_dev_release;
	dev_set_drvdata(dev, psy);
	psy->desc = desc;
	if (cfg) { 
        
		psy->drv_data = cfg->drv_data;
		psy->of_node =
			cfg->fwnode ? to_of_node(cfg->fwnode) : cfg->of_node;
		psy->supplied_to = cfg->supplied_to;
		psy->num_supplicants = cfg->num_supplicants;
	}

	rc = dev_set_name(dev, "%s", desc->name);
	if (rc)
		goto dev_set_name_failed;

	INIT_WORK(&psy->changed_work, power_supply_changed_work);
	INIT_DELAYED_WORK(&psy->deferred_register_work,
			  power_supply_deferred_register_work);

	rc = power_supply_check_supplies(psy);
	if (rc) { 
        
		dev_info(dev, "Not all required supplies found, defer probe\n");
		goto check_supplies_failed;
	}

	spin_lock_init(&psy->changed_lock);
	rc = device_add(dev);
	if (rc)
		goto device_add_failed;

	rc = device_init_wakeup(dev, ws);
	if (rc)
		goto wakeup_init_failed;

	rc = psy_register_thermal(psy);
	if (rc)
		goto register_thermal_failed;

	rc = psy_register_cooler(psy);
	if (rc)
		goto register_cooler_failed;

	rc = power_supply_create_triggers(psy);
	if (rc)
		goto create_triggers_failed;

	/* * Update use_cnt after any uevents (most notably from device_add()). * We are here still during driver's probe but * the power_supply_uevent() calls back driver's get_property * method so: * 1. Driver did not assigned the returned struct power_supply, * 2. Driver could not finish initialization (anything in its probe * after calling power_supply_register()). */
	atomic_inc(&psy->use_cnt);
	psy->initialized = true;

	queue_delayed_work(system_power_efficient_wq,
			   &psy->deferred_register_work,
			   POWER_SUPPLY_DEFERRED_REGISTER_TIME);

	return psy;

create_triggers_failed:
	psy_unregister_cooler(psy);
register_cooler_failed:
	psy_unregister_thermal(psy);
register_thermal_failed:
	device_del(dev);
wakeup_init_failed:
device_add_failed:
check_supplies_failed:
dev_set_name_failed:
	put_device(dev);
	return ERR_PTR(rc);
}

struct power_supply *__must_check power_supply_register(struct device *parent,
		const struct power_supply_desc *desc,
		const struct power_supply_config *cfg)
{ 
        
	return __power_supply_register(parent, desc, cfg, true);
}
EXPORT_SYMBOL_GPL(power_supply_register);


struct power_supply *__must_check
power_supply_register_no_ws(struct device *parent,
		const struct power_supply_desc *desc,
		const struct power_supply_config *cfg)
{ 
        
	return __power_supply_register(parent, desc, cfg, false);
}
EXPORT_SYMBOL_GPL(power_supply_register_no_ws);

static void devm_power_supply_release(struct device *dev, void *res)
{ 
        
	struct power_supply **psy = res;

	power_supply_unregister(*psy);
}

struct power_supply *__must_check
devm_power_supply_register(struct device *parent,
		const struct power_supply_desc *desc,
		const struct power_supply_config *cfg)
{ 
        
	struct power_supply **ptr, *psy;

	ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL);

	if (!ptr)
		return ERR_PTR(-ENOMEM);
	psy = __power_supply_register(parent, desc, cfg, true);
	if (IS_ERR(psy)) { 
        
		devres_free(ptr
        标签: 平板灯连接器快速插拔电源电池连接器1812电阻一盘自动led电阻适配器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台