Skip to main content
 首页 » 操作系统

Linux 电池——power supply class

2022年07月19日25mfrbuaa

1. power supply class 简介

  power supply class负责将某个PSY设备支持的属性及其value,以sysfs的形式,提供给用户空间;当属性值改变时,以uevent的形式,广播给用户空间程序。另外,power supply class也会协助处理PSY级联的情况。


2. 软件架构

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_supply_leds.c中。

驱动工程师可以基于power supply class,实现具体的PSY drivers,主要处理平台相关、硬件相关的逻辑。这些drivers都位于drivers/power/目录下。

3. 核心数据结构

(1) struct power_supply:抽象PSY设备

struct power_supply { 
    /*见下面注释*/ 
    const struct power_supply_desc *desc; 
 
    /* 
     * 一个字符串数组,保存了由该PSY供电的PSY列表,以此可将PSY组织成相互级联的PSY链。 
     * 这些“被供电”的PSY,称作supplicant(客户端、乞求者) 
     */ 
    char **supplied_to; 
    /*supplicant的个数*/ 
    size_t num_supplicants; 
 
    /* 
     * 一个字符串数组,保存了向该PSY供电的PSY列表,也称作supply(提供者)。 
     * 从另一个方向,组织PSY之间的级联关系. 
     */ 
    char **supplied_from; 
    /*supply的个数*/ 
    size_t num_supplies; 
    struct device_node *of_node; 
 
    /* Driver private data */ 
    void *drv_data; 
 
    /* private */ 
    struct device dev; 
    /* 
     * changed_work/changed_lock/changed,changed_work用于处理状态改变的workqueue, 
     * 主要思路是:当该PSY的状态发生改变,启动一个workqueue,查询并通知所有的supplicants 
     */ 
    struct work_struct changed_work; 
    struct delayed_work deferred_register_work; 
    spinlock_t changed_lock; 
    bool changed; 
    atomic_t use_cnt; 
#ifdef CONFIG_THERMAL 
    /* 
     * 如果该PSY具有温度等属性,则需要借助linux generic thermal sysfs drivers(温控子系统) 
     * 的框架,注册相应的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的名称*/ 
    const char *name; 
    /*该PSY的类型,枚举型,包括:battery、USB charger等等*/ 
    enum power_supply_type type; 
    /*该PSY具有的属性列表*/ 
    enum power_supply_property *properties; 
    /*属性个数*/ 
    size_t num_properties; 
 
    /*PSY driver需要重点实现的两个回调函数,用于获取/设置属性值*/ 
    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); 
     
    /*返回指定的属性值是否可写(用于sysfs)*/ 
    int (*property_is_writeable)(struct power_supply *psy, enum power_supply_property psp); 
    /* 
     * 当一个PSY设备存在supply PSY,且该supply PSY的属性发生改变(如online、offline)时, 
     * power supply core会调用该回调函数,通知PSY driver,以便让它做出相应的处理 
     */ 
    void (*external_power_changed)(struct power_supply *psy); 
    /* 
     * 该回调函数的应用场景有点奇怪:外部模块通知PSY driver,该PSY设备的状态改变了。 
     * 自己改变了自己不知道,要外部通知,希望大家在实际工作中不要遇到,不然太纠结了 
    */ 
    void (*set_charged)(struct power_supply *psy); 
 
    bool no_thermal; 
    /* For APM emulation, think legacy userspace. */ 
    int use_for_apm; 
};

(2) enum power_supply_type:描述PSY类型

enum power_supply_type { 
    /*未知*/ 
    POWER_SUPPLY_TYPE_UNKNOWN = 0, 
    /*电池,嵌入式设备、手持式智能设备常用的供电形式*/ 
    POWER_SUPPLY_TYPE_BATTERY, 
    /* 
     * Uninterruptible Power System/Uninterruptible Power Supply,不间断式供电设备, 
     * 通过将交流电和蓄电池连接,正常情况下由交流电供电,同时向蓄电池充电。当交流电 
     * 断电时,由蓄电池紧急供电。一般用于服务器等设备. 
     */ 
    POWER_SUPPLY_TYPE_UPS, 
    /* 
     * 主供电设备,如笔记本电脑的适配器,其特点是可以单独供电,当其断电时, 
     * 再由辅助供电设备供电(如battery). 
     */ 
    POWER_SUPPLY_TYPE_MAINS, 
    /* 
     * USB类型的供电,不同点在于充电电流的限制,由USB Battery Charge Spec规定, 
     * 具体可参考USB组织的规范,或者参考博客:http://www.cash.idv.tw/wordpress/?p=8334 
     */ 
    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_HVDCP,    /* High Voltage DCP */ 
    POWER_SUPPLY_TYPE_USB_HVDCP_3,    /* Efficient High Voltage DCP */ 
    POWER_SUPPLY_TYPE_USB_PD,    /* Power Delivery */ 
    POWER_SUPPLY_TYPE_WIRELESS,    /* Accessory Charger Adapters */ 
    POWER_SUPPLY_TYPE_USB_FLOAT,    /* Floating charger */ 
    POWER_SUPPLY_TYPE_BMS,        /* Battery Monitor System */ 
    POWER_SUPPLY_TYPE_PARALLEL,    /* Parallel Path */ 
    POWER_SUPPLY_TYPE_MAIN,        /* Main Path */ 
    POWER_SUPPLY_TYPE_WIPOWER,    /* Wipower */ 
    POWER_SUPPLY_TYPE_TYPEC,    /* Type-C */ 
    POWER_SUPPLY_TYPE_UFP,        /* Type-C UFP */ 
    POWER_SUPPLY_TYPE_DFP,        /* TYpe-C DFP */ 
};

(3) enum power_supply_property: PSY属性类型枚举

power supply class将所有可能PSY属性,以枚举型变量(enum power_supply_property )的形式抽象出来,PSY driver可以根据设备的实际情况,从中选取一些。

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_STATUS:
  该PSY的status,主要是充电状态,包括:"Unknown", "Charging", "Discharging", "Not charging", "Full",由枚举型变量(POWER_SUPPLY_STATUS_*)定义。根据设计方案的不同,充电类型的PSY,或者battery类型的PSY,都可能具备该属性;

POWER_SUPPLY_PROP_CHARGE_TYPE:
  充电类型,包括:"Unknown", "N/A", "Trickle", "Fast",由枚举型变量(POWER_SUPPLY_CHARGE_TYPE_*)定义,同理根据设计方案的不同,充电类型的PSY,或者battery类型的PSY,都可能具备该属性;

POWER_SUPPLY_PROP_HEALTH:
  “健康”情况,包括:"Unknown", "Good", "Overheat", "Dead", "Over voltage"等等, 由枚举型变量(POWER_SUPPLY_HEALTH_*)定义。一般用于battery类型的PSY;

POWER_SUPPLY_PROP_TECHNOLOGY:
  采用的技术,包括:"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd", "LiMn",由枚举型变量(POWER_SUPPLY_TECHNOLOGY_*)定义。一般用于battery类型的PSY;

POWER_SUPPLY_PROP_CAPACITY_LEVEL:
  容量,包括:"Unknown", "Critical", "Low", "Normal", "High", "Full",由枚举型变量(POWER_SUPPLY_CAPACITY_LEVEL_*)定义。一般用于battery类型的PSY;

POWER_SUPPLY_PROP_TYPE:
  PSY类型,比较特殊,保存在“psy->type”变量中,而不在properties数组中。

4. 向具体的PSY driver提供的API

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

(1) PSY的register/unregister API

struct power_supply *__must_check power_supply_register(struct device *parent, const struct power_supply_desc *desc,  
        const struct power_supply_config *cfg) 
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) 
void power_supply_unregister(struct power_supply *psy)

  其中power_supply_register和power_supply_register_no_ws的区别是:power_supply_register注册的PSY,具备wakeup系统的能力,而power_supply_register_no_ws不具备。

(2) PSY状态改变时通知power supply core的API

void power_supply_changed(struct power_supply *psy)

当PSY driver检测到该设备某些属性值改变时,需要调用这个接口,通知power supply core,power supply core会有如下动作:
  (1) 如果该PSY是其它PSY的供电源,调用这些PSY的external_power_changed回调函数,通知它们,这些PSY具体要做些什么,由它们的自身逻辑决定;
  (2) 如果配置了CONFIG_LEDS_TRIGGERS,调用power_supply_update_leds,更新该PSY有关的led状态;
  (3) 发送notifier,通知那些关心PSY设备状态的drivers;
  (4) 以统一的格式,向用户空间发送uevent,这就是设备模型中class的魅力,对外接口由class core提供,可以节省driver的工作量,同时确保了接口的一致性。

(3) 向其它driver提供的用于接收PSY状态改变notifier的API

int power_supply_reg_notifier(struct notifier_block *nb) 
void power_supply_unreg_notifier(struct notifier_block *nb)

通过notifier注册接口注册notifier之后,系统任何PSY设备的状态发生改变,并调用了power_supply_changed接口,power supply core就是通知notifier的监听者。

(4) 其它杂项接口

/*通过名字获取PSY指针*/ 
struct power_supply *power_supply_get_by_name(const char *name) 
/*从DTS中,解析出对应dePSY指针*/ 
struct power_supply *power_supply_get_by_phandle(struct device_node *np, const char *property) 
/*查询自己是否由其它PSY供电*/ 
int power_supply_am_i_supplied(struct power_supply *psy) 
/*调用指定PSY的set_charged回调*/ 
int power_supply_set_battery_charged(struct power_supply *psy) 
/*查询系统是否有有效的或者处于online状态的PSY,如果没有,可能为桌面系统*/ 
int power_supply_is_system_supplied(void) 
/*在指定设备(通常是该PSY设备)的sysfs目录(/sys/devices/xxx/)下,创建指定PSY的符号链接(/sys/devices/xxx/powers)*/ 
int power_supply_powers(struct power_supply *psy, struct device *dev)

5. power supply class通过两种形式向用户空间提供接口

(1) uevent: 以“名字=value”的形式,上报所有property的值,格式如下:

POWER_SUPPLY_NAME=xxx        /* power supply name */  
POWER_SUPPLY_xxx1=xxx        /* property = value */  
POWER_SUPPLY_xxx2=xxx  
......

uevent一般会在PSY设备添加到kernel时,或者PSY属性发生改变时发送。

(2) sysfs
  power supply class在power_supply_sysfs.c中,定义了相当多的默认attributes,如果某个PSY设备具有某个属性,该属性对应的attribute就会体现在sysfs中(一般位于“/sys/class/power_supply/xxx/”中)。

static struct device_attribute power_supply_attrs[] = { 
    POWER_SUPPLY_ATTR(status), 
    POWER_SUPPLY_ATTR(charge_type), 
    POWER_SUPPLY_ATTR(health), 
    ...... 
};

6. 怎样基于power supply class编写PSY driver

最后从PSY driver的角度,说明一下怎么基于power supply class,编写驱动:
  1)根据硬件spec,确定该PSY设备具备哪些特性,并把它们和enum power_supply_property 中所定义的property对应。
  2)根据实际情况,实现这些properties的get/set接口。
  3)定义一个struct power_supply变量,并初始化必要的字段后,调用power_supply_register或者power_supply_register_no_ws,将其注册到kernel中。
  4)根据实际情况,启动设备属性变化的监控逻辑,例如中断、轮询等,并在发生改变时,调用power_supply_changed,通知power supply core。

7. 补充:USB 充电补充

目前 USB 的充电规范以 USB应用者论坛(USB-IF) 所制定 BC (Battery Charge Spec.) 1.2 为主, 它定义了每个类型充电器的可用电流上限, 以及充电器的类型.

原先都是 USB 要支持 500mA 的电流. 但是实际上, 这种电流可能连外接硬盘都推不动. 于是大家可能都 "偷偷" 可以供到 1A. 从 BC 1.1 开始, 规范中就直接让 USB 可以输出 1.5 A 了.

在充电类型方面, 一共有三种定义:
(1) 标准下行口(Standard Downstream Port, SDP)
所谓的 SDP 就是标准的 USB HUB 输出, 像是计算机或是屏幕基座上的 USB 输出大抵都支持 SDP. 它的充电电流是 500 mA, 所以不致于充得太快! 甚至在被充电装置完全
没电时, 可以先用 100m A 的小电流输出, 等到充至 0.5~0.7 V 时才改回 500 mA.

(2) 充电下行口(Charging Downstream Port, CDP)
至于 CDP 可以提供 500mA 和 1.5A 两种电流, 算是有 "两下子" 的 USB 接口. 一般会先用 500mA, 若是符合 1.5A 的快充, 就会切换过去.

(3) 专用充电口(Dedicated Charging Port, DCP)
DCP 只能充电, 不能传 data.

上面提到的都是 USB 2.0, 而一般 USB 3.0 都是用 USB 2.0 的规范充电. 特别强调 USB 3.0 充电的话, 指得是 BC1.2, 特别是 DCP 快充.

USB 便携设备(portable device, 简称 PD) 要怎么知道连上的是哪一种 USB Port, 并从而决定要抽多少电呢?判断方式可以参考 ref 3 或 4. 简单地说:

PD 先把 D+ 设为 0.6V:
(1) 若 D- 变成 0.6V, HOST 可能是 DCP 或 CDP.

PD 再把 D+ 或 D- (只有慢速设备会拉 D-) 拉高到高电位, 然后去看另外一根 D- 或 D+:
(1) D- (D+) 还在低电位, 表示这是 CDP.
(2) D- (D+) 也变成高电位, 表示 D+ 和 D- 短路, 一定是 DCP.

因此 PD 有办法知道它所连接的 port 是哪一种, 再进一步决定抽 0.5A 还是 1.5A.

最后, 一个设备可能有时支持 CDP, DCP, 或 SDP , 这叫做 multi-role port. 若是不只是能支持充电, 还可以当 OTG 用, 这个叫做 ACA (Accessary Charge Adapter).
PD 只要有一个 micro-ACA, 它的 3 个 port 就同时扮演 device 当别人的 USB Storage (OTG Port), 当 host 外接键盘鼠标 (Accessary Port) , 又被充电 (Charger Port).
一孔多用是未来的趋势, 因此 ACA 早在 BC 1.1 的时候就被列入规范了.

参考: http://www.wowotech.net/pm_subsystem/rpm_overview.html


本文参考链接:https://www.cnblogs.com/hellokitty2/p/11300783.html