power_supply子系统

目录

  • 作用
  • 结构体及api
    • 核心结构体
      • psy type
      • PSY property
    • 向具体的PSY driver提供的API
    • PSY状态改变时通知power supply core的API
    • 其它杂项接口
    • 向其它driver提供的用于接收PSY状态改变notifier的API
    • 向用户空间传值
  • driver流程
    • power_supply_sysfs.c
    • battery_probe
  • 添加一个新的节点

作用

psy driver的主要功能,就是向用户空间程序汇整各类状态信息。

将这些状态信息,抽象为properties,由于状态信息的类型是有限的,properties的个数也是有限的。

psy driver只需要负责:该psy设备具有哪些“属性”;这些“属性”的value是什么;当“属性值”发生改变时,通知power supply class。

power supply class负责:将某个PSY设备支持的属性及其value,以sysfs的形式,提供给用户空间;当属性值改变时,以uevent的形式,广播给用户空间程序。

结构体及api

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/目录下。

核心结构体

path:kernel-4.4/include/linux/power_supply.h

struct power_supply {
        const struct power_supply_desc *desc;

        char **supplied_to;//一个字符串数组,保存了由该PSY供电的PSY列表,以此可将PSY组织成相互级联的PSY链。这些“被供电”的PSY,称作supplicant(客户端、乞求者);
        size_t num_supplicants;//supplicant的个数;

        char **supplied_from;//一个字符串数组,保存了向该PSY供电的PSY列表,也称作supply(提供者)。从另一个方向,组织PSY之间的级联关系;
        size_t num_supplies;//supply的个数;
        struct device_node *of_node;//用于保存device、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;
//一个用于处理状态改变的workqueue,主要思路是:当该PSY的状态发生改变,启动一个workqueue,查询并通知所有的supplicants;
        atomic_t use_cnt;
#ifdef CONFIG_THERMAL
        struct thermal_zone_device *tzd;
        struct thermal_cooling_device *tcd;
//如果该PSY具有温度等属性,则需要借助linux generic thermal sysfs drivers(温控子系统)的框架,注册相应的thermal设备
#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_desc 结构体

struct power_supply_desc {
        const char *name;    //设备名称    /sys/class/power_supply/name
        enum power_supply_type type; //电源类型
        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);//get property
        int (*set_property)(struct power_supply *psy,
                            enum power_supply_property psp,
                            const union power_supply_propval *val);
//PSY driver需要重点实现的两个回调函数,用于获取/设置属性值;
        /*
         * 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);//返回指定的属性值是否可写(用于sysfs);
        void (*external_power_changed)(struct power_supply *psy);//当一个PSY设备存在supply PSY,且该supply PSY的属性发生改变(如online、offline)时,power supply core会调用该回调函数,通知PSY driver,以便让它做出相应的处理;
        void (*set_charged)(struct power_supply *psy);//外部模块通知PSY driver,该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;
};

psy type

power_supply_type结构体
在mtk平台

enum power_supply_type {
        POWER_SUPPLY_TYPE_UNKNOWN = 0,
        POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */
        POWER_SUPPLY_TYPE_WIRELESS, /* Wireless Charger */
};

标准的linux

 enum power_supply_type {
        OWER_SUPPLY_TYPE_UNKNOWN = 0,
        POWER_SUPPLY_TYPE_BATTERY,//电池,嵌入式设备、手持式智能设备常用的供电形式;
        POWER_SUPPLY_TYPE_UPS,//Uninterruptible Power System/Uninterruptible Power Supply,不间断式供电设备,通过将交流电和蓄电池连接,正常情况下由交流电供电,同时向蓄电池充电。当交流电断电时,由蓄电池紧急供电。一般用于服务器等设备;
        POWER_SUPPLY_TYPE_MAINS,//主供电设备,如笔记本电脑的适配器,其特点是可以单独供电,当其断电时,再由辅助供电设备供电(如battery);
        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 */
 };

PSY property

enum power_supply_property

向具体的PSY driver提供的API

 extern int power_supply_register(struct device *parent,  struct power_supply *psy); //有wakeup system的能力
 extern int power_supply_register_no_ws(struct device *parent,struct power_supply *psy); //无wakeup system的能力
 
 extern void power_supply_unregister(struct power_supply *psy);

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

extern void power_supply_changed(struct power_supply *psy);
/**********************
当PSY driver检测到该设备某些属性值改变时,需要调用这个接口,通知power supply core,power supply core会有如下动作:

如果该PSY是其它PSY的供电源,调用这些PSY的external_power_changed回调函数,通知它们(这些PSY具体要做些什么,由它们的自身逻辑决定);

如果配置了CONFIG_LEDS_TRIGGERS,调用power_supply_update_leds,更新该PSY有关的led状态;

发送notifier,通知那些关心PSY设备状态的drivers;

以统一的格式,向用户空间发送uevent

***************************/

其它杂项接口

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

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

extern int power_supply_reg_notifier(struct notifier_block *nb); //通过notifier注册接口注册notifier之后,系统任何PSY设备的状态发生改变,并调用了power_supply_changed接口,power supply core就是通知notifier的监听者。
extern void power_supply_unreg_notifier(struct notifier_block *nb);

向用户空间传值

  1. uevnt
POWER_SUPPLY_NAME=xxx                     /* power supply name */ 
POWER_SUPPLY_xxx1=xxx                       /* property = value */ 
POWER_SUPPLY_xxx2=xxx 
…
  1. sysfs
    power supply class在power_supply_sysfs.c中,定义了相当多的默认attribute(见下面),如果某个PSY设备具有某个属性,该属性对应的attribute就会体现在sysfs中
/* Must be in the same order as POWER_SUPPLY_PROP_* */
static struct device_attribute power_supply_attrs[] = {
        /* Properties of type `int' */
        POWER_SUPPLY_ATTR(status),
        POWER_SUPPLY_ATTR(charge_type),
        POWER_SUPPLY_ATTR(health),
        POWER_SUPPLY_ATTR(present),
        POWER_SUPPLY_ATTR(online),
        POWER_SUPPLY_ATTR(authentic),
        POWER_SUPPLY_ATTR(technology),
        POWER_SUPPLY_ATTR(cycle_count),
        POWER_SUPPLY_ATTR(voltage_max),
        POWER_SUPPLY_ATTR(voltage_min),
        POWER_SUPPLY_ATTR(voltage_max_design),
        POWER_SUPPLY_ATTR(voltage_min_design),
        POWER_SUPPLY_ATTR(voltage_now),
        POWER_SUPPLY_ATTR(voltage_avg),
        POWER_SUPPLY_ATTR(voltage_ocv),
        POWER_SUPPLY_ATTR(voltage_boot),
        POWER_SUPPLY_ATTR(current_max),
        POWER_SUPPLY_ATTR(current_now),
        POWER_SUPPLY_ATTR(current_avg),
        POWER_SUPPLY_ATTR(current_boot),
        POWER_SUPPLY_ATTR(power_now),

driver流程

创建一个名为power_supply的class,挂上一个uevent,之后填充device_type

power_supply_core.c

static struct device_type power_supply_dev_type;
static int __init power_supply_class_init(void)
{
 power_supply_class = class_create(THIS_MODULE, "power_supply");
 if (IS_ERR(power_supply_class))
  return PTR_ERR(power_supply_class);
 power_supply_class->dev_uevent = power_supply_uevent;
 power_supply_init_attrs(&power_supply_dev_type);
 return 0;
}
subsys_initcall(power_supply_class_init);

add_uevent_var中添加发送msg到uevent,如battery,后面的for循环中是将power_supply_attrs中的对应的属性和值,以POWER_SUPPLY_XXXX=xxxx的方式添加到发送的数据中。这个回调函数就是做了这个操作,即将系统中的变化数据组织成特定的格式,通过uevent发送出去

power_supply_sysfs.c

int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
{
 struct power_supply *psy = dev_get_drvdata(dev);
 int ret = 0, j;
 char *prop_buf;
 char *attrname;
 dev_dbg(dev, "uevent\n");
 if (!psy || !psy->desc) {
  dev_dbg(dev, "No power supply yet\n");
  return ret;
 }
 dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->desc->name);
 ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name);
 if (ret)
  return ret;
 prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
 if (!prop_buf)
  return -ENOMEM;
 for (j = 0; j < psy->desc->num_properties; j++) {
  struct device_attribute *attr;
  char *line;
  attr = &power_supply_attrs[psy->desc->properties[j]];
  ret = power_supply_show_property(dev, attr, prop_buf);
  if (ret == -ENODEV || ret == -ENODATA) {
   /* When a battery is absent, we expect -ENODEV. Don't abort;
      send the uevent with at least the the PRESENT=0 property */
   ret = 0;
   continue;
  }
  if (ret < 0)
   goto out;
  line = strchr(prop_buf, '\n');
  if (line)
   *line = 0;
  attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
  if (!attrname) {
   ret = -ENOMEM;
   goto out;
  }
  dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
  ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
  kfree(attrname);
  if (ret)
   goto out;
 }
out:
 free_page((unsigned long)prop_buf);
 return ret;
}

填充sysfs

static struct attribute_group power_supply_attr_group = {
 .attrs = __power_supply_attrs,
 .is_visible = power_supply_attr_is_visible,
};
static const struct attribute_group *power_supply_attr_groups[] = {
 &power_supply_attr_group,
 NULL,
};
void power_supply_init_attrs(struct device_type *dev_type)
{
 int i;
 dev_type->groups = power_supply_attr_groups;
 for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
  __power_supply_attrs[i] = &power_supply_attrs[i].attr;
}

POWER_SUPPLY_ATTR

static struct device_attribute power_supply_attrs[] = {
 /* Properties of type `int' */
 POWER_SUPPLY_ATTR(status),
 POWER_SUPPLY_ATTR(charge_type),
 POWER_SUPPLY_ATTR(health),
 POWER_SUPPLY_ATTR(present),
 POWER_SUPPLY_ATTR(online),
 POWER_SUPPLY_ATTR(authentic),
 POWER_SUPPLY_ATTR(technology),
 POWER_SUPPLY_ATTR(cycle_count),
 POWER_SUPPLY_ATTR(voltage_max),
 POWER_SUPPLY_ATTR(voltage_min),
 POWER_SUPPLY_ATTR(voltage_max_design),
 POWER_SUPPLY_ATTR(voltage_min_design),
 POWER_SUPPLY_ATTR(voltage_now),
 POWER_SUPPLY_ATTR(voltage_avg),
......

battery_probe

在static int __init battery_probe(struct platform_device *dev)中
battery_main.psy = power_supply_register(&(dev->dev), &battery_main.psd, NULL);

battery_main

static struct battery_data battery_main = {
 .psd = {
  .name = "battery",
  .type = POWER_SUPPLY_TYPE_BATTERY,
  .properties = battery_props,
  .num_properties = ARRAY_SI
ZE(battery_props),
  .get_property = battery_get_property,
  },
 .BAT_STATUS = POWER_SUPPLY_STATUS_DISCHARGING,
 .BAT_HEALTH = POWER_SUPPLY_HEALTH_GOOD,
 .BAT_PRESENT = 1,
 .BAT_TECHNOLOGY = POWER_SUPPLY_TECHNOLOGY_LION,
 .BAT_CAPACITY = -1,
 .BAT_batt_vol = 0,
 .BAT_batt_temp = 0,
 /* Dual battery */
 .status_smb = POWER_SUPPLY_STATUS_DISCHARGING,
 .capacity_smb = 50,
 .present_smb = 0,
 /* ADB CMD discharging */
 .adjust_power = -1,
};

battery_data

struct battery_data {
 struct power_supply_desc psd;
 struct power_supply *psy;
 int BAT_STATUS;
 int BAT_HEALTH;
 int BAT_PRESENT;
 int BAT_TECHNOLOGY;
 int BAT_CAPACITY;
 /* Add for Battery Service */
 int BAT_batt_vol;
 int BAT_batt_temp;
 /* Add for EM */
 int BAT_TemperatureR;
 int BAT_TempBattVoltage;
 int BAT_InstatVolt;
 int BAT_BatteryAverageCurrent;
 int BAT_BatterySenseVoltage;
 int BAT_ISenseVoltage;
 int BAT_ChargerVoltage;
 /* Dual battery */
 int status_smb;
 int capacity_smb;
 int present_smb;
 int adjust_power;
};

填充battery_get_property 回调函数

static int battery_get_property(struct power_supply *psy,
    enum power_supply_property psp, union power_supply_propval *val)
{
 int ret = 0;
 int fgcurrent = 0;
 bool b_ischarging = 0;
 struct battery_data *data = container_of(psy->desc, struct battery_data, psd);
 switch (psp) {
 case POWER_SUPPLY_PROP_STATUS:
  val->intval = data->BAT_STATUS;
  break;
 case POWER_SUPPLY_PROP_HEALTH:
  val->intval = data->BAT_HEALTH;/* do not change before*/
  break;
 case POWER_SUPPLY_PROP_PRESENT:
  val->intval = data->BAT_PRESENT;/* do not change before*/
  break;
 case POWER_SUPPLY_PROP_TECHNOLOGY:
  val->intval = data->BAT_TECHNOLOGY;
  break;
 case POWER_SUPPLY_PROP_CAPACITY:
  val->intval = data->BAT_CAPACITY;
  break;
 case POWER_SUPPLY_PROP_CURRENT_NOW:
  b_ischarging = gauge_get_current(&fgcurrent);
  if (b_ischarging == false)
   fgcurrent = 0 - fgcurrent;
  val->intval = fgcurrent / 10;
  break;
 case POWER_SUPPLY_PROP_CURRENT_MAX:
  val->intval = 3000000;
  /* 3A */
  break;
.......

添加一个新的节点

添加一个新的节点主要就是填充sysfs以及battery_get_property

power_supply_sysfs.c

static struct device_attribute power_supply_attrs[] = {
 /* Properties of type `int' */
 POWER_SUPPLY_ATTR(status),
 POWER_SUPPLY_ATTR(charge_type),
 POWER_SUPPLY_ATTR(health),
 POWER_SUPPLY_ATTR(present),
 POWER_SUPPLY_ATTR(online),
 POWER_SUPPLY_ATTR(authentic),
 POWER_SUPPLY_ATTR(technology),
 POWER_SUPPLY_ATTR(cycle_count),
.......
#if 1
 POWER_SUPPLY_ATTR(BatteryVoltage),
 POWER_SUPPLY_ATTR(FGVoltage),
 POWER_SUPPLY_ATTR(BatteryCurrent),
 POWER_SUPPLY_ATTR(FGCurrent),
 POWER_SUPPLY_ATTR(BatInstTemp),
 POWER_SUPPLY_ATTR(BasSoc),
 POWER_SUPPLY_ATTR(PreSoc),
 POWER_SUPPLY_ATTR(BoardTemp),
 POWER_SUPPLY_ATTR(CPUTemp),
#endif
......

填充battery_get_property

static int battery_get_property(struct power_supply *psy,
    enum power_supply_property psp, union power_supply_propval *val)
{
 int ret = 0;
 int fgcurrent = 0;
 bool b_ischarging = 0;
 struct battery_data *data = container_of(psy->desc, struct battery_data, psd);
 switch (psp) {
 case POWER_SUPPLY_PROP_STATUS:
  val->intval = data->BAT_STATUS;
  break;
 case POWER_SUPPLY_PROP_HEALTH:
  val->intval = data->BAT_HEALTH;/* do not change before*/
  break;

填充battery_get_property首先需要填充case 也就是enum power_supply_property 即相应properties

static enum power_supply_property battery_props[] = {
 POWER_SUPPLY_PROP_STATUS,
 POWER_SUPPLY_PROP_HEALTH,
 POWER_SUPPLY_PROP_PRESENT,
 POWER_SUPPLY_PROP_TECHNOLOGY,
......
 POWER_SUPPLY_PROP_BatteryVoltage,
 POWER_SUPPLY_PROP_FGVoltage,
 POWER_SUPPLY_PROP_BatteryCurrent,
 POWER_SUPPLY_PROP_FGCurrent,
 POWER_SUPPLY_PROP_BatInstTemp,
 POWER_SUPPLY_PROP_BasSoc,
 POWER_SUPPLY_PROP_PreSoc,
 POWER_SUPPLY_PROP_BoardTemp,
 POWER_SUPPLY_PROP_CPUTemp,
.......

之后填充battery_data的值
struct battery_data *data = container_of(psy->desc, struct battery_data,

struct battery_data {
 struct power_supply_desc psd;
 struct power_supply *psy;
 int BAT_STATUS;
 int BAT_HEALTH;
 int BAT_PRESENT;
 int BAT_TECHNOLOGY;
......
int BatteryVoltage;
 int FGVoltage;
 int BatteryCurrent;
 int FGCurrent;
 int BatInstTemp;
 int BasSoc;
 int PreSoc;
 int BoardTemp;
 int CPUTemp; 
.....

填充完毕后如下

static int battery_get_property(struct power_supply *psy,
    enum power_supply_property psp, union power_supply_propval *val)
{
 int ret = 0;
 int fgcurrent = 0;
 bool b_ischarging = 0;
 struct battery_data *data = container_of(psy->desc, struct battery_data, psd);
 switch (psp) {
 case POWER_SUPPLY_PROP_STATUS:
  val->intval = data->BAT_STATUS;
.......
 case POWER_SUPPLY_PROP_BatteryVoltage:
  val->intval = data->BatteryVoltage;
  break;
 case POWER_SUPPLY_PROP_FGVoltage:
  val->intval = data->FGVoltage;
  break;
 case POWER_SUPPLY_PROP_BatteryCurrent:
  val->intval = data->BatteryCurrent;
  break;
 case POWER_SUPPLY_PROP_FGCurrent:
  val->intval = data->FGCurrent;
  break;
 case POWER_SUPPLY_PROP_BatInstTemp:
  val->intval = data->BatInstTemp;
  break;
 case POWER_SUPPLY_PROP_BasSoc:
  val->intval = data->BasSoc;
  break;
 case POWER_SUPPLY_PROP_PreSoc:
  val->intval = data->PreSoc;
  break;
 case POWER_SUPPLY_PROP_BoardTemp:
  val->intval = data->BoardTemp;
  break;
 case POWER_SUPPLY_PROP_CPUTemp:
  val->intval = data->CPUTemp;
  break; 
.......

最后需要去实现,想get的value
如在fg_drv_update_hw_status 线程中定期更新这些value

void fg_drv_update_hw_status(void)
{
 static bool fg_current_state;
 signed int chr_vol;
 int fg_current, fg_coulomb, bat_vol, plugout_status, tmp, bat_plugout_time;
 int fg_current_iavg;
 bool valid = false;
 static unsigned int cnt;

 struct battery_data *bat_data = &battery_main;
 static signed int previous_SOC = -1;
...........
bat_data->BatteryVoltage = bat_vol; // 瞬间电压
 bat_data->FGVoltage = pmic_get_battery_voltage();    // FG电压
 bat_data->BatteryCurrent = fg_current_iavg;    // 瞬时充电电流
 bat_data->FGCurrent = fg_current;    // FG电流
 bat_data->BatInstTemp = battery_meter_get_battery_temperature(); // 瞬间电池温度
 bat_data->BasSoc = battery_get_bat_soc();    // 平均SOC
 bat_data->PreSoc = previous_SOC;    // 前SOC
 bat_data->CPUTemp = mtk_thermal_get_temp(MTK_THERMAL_SENSOR_CPU);    // cpu 温度
 bat_data->BoardTemp = mtk_thermal_get_temp(MTK_THERMAL_SENSOR_AP);    // board 温度
...........

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

你可能感兴趣的:(power)