Power supply 子系统之最简驱动实现

目录:
1.概述
2.电源驱动设计思路
3.CW2015例程分析
4.最简电源驱动Demo
5.设备树使用补充

1.概述
  power supply的目的就是为系统供电。那系统如何获取供电?其实是通过 power supply class 将某个PSY设备支持的属性及其value,以sysfs的形式提供给用户空间;当属性值改变时,以uevent的形式,广播给用户空间程序。另外,power supply class也会协助处理PSY级联的情况。那什么是级联,比如出现多个电源节点,vantron-ac ,vantron-bat,而给系统电源数据的只vantron-bat这个节点。

2.电源驱动设计思路

2.1 power supply 表征关系结构
HW(电源芯片) -> PSYDEV(驱动) ->attribute(属性值) -> sysfs(节点值)->user

通过表征关系结构,我们明白通过读取电源芯片数据到驱动中,以sysfs的形式,提供给用户空间,也就是power supply 未来需要做电量检测和充电管理,最终将结果上报应用层
2.2 软件架构分析
power supply class位于drivers/power/目录下,主要由2部分组成:
(1) power supply core: 用于抽象核心数据结构、实现公共逻辑。位于drivers/power/power_supply_core.c中。
(2) power supply sysfs: 实现sysfs以及uevent功能。位于drivers/power/power_supply_sysfs.c中。
驱动工程师可以基于power supply class,实现具体的PSY drivers,主要处理平台相关、硬件相关的逻辑。这些drivers都位于drivers/power/目录下。
2.3 核心结构体分析
(1) struct power_supply:抽象PSY设备

struct power_supply {const struct power_supply_desc *desc; // 一个字符串数组,保存了由该PSY供电的PSY列表,以此可将PSY组织成相互级联的PSY链。char **supplied_to;// size_t num_supplicants;  //supplicant的个数char **supplied_from;//一个字符串数组,保存了向该PSY供电的PSY列表,也称作supply(提供者)从另一个方向,组织PSY之间的级联关系.
size_t num_supplies; /*supply的个数*/struct device_node *of_node;
void *drv_data;  /* Driver private data */
struct device dev;    /* private */struct work_struct changed_work;//当该PSY的状态发生改变,启动一个workqueu查询并通知所有的supplicants
struct delayed_work deferred_register_work;spinlock_t changed_lock;bool changed;atomic_t use_cnt;
#ifdef CONFIG_THERMALstruct thermal_zone_device *tzd;struct thermal_cooling_device *tcd;
#endif#ifdef CONFIG_LEDS_TRIGGERSstruct 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 {const char *name;  /*该PSY的名称*/enum power_supply_type type;  /*该PSY的类型,包括:battery、USB,AC
enum power_supply_property *properties; /*该PSY具有的属性列表*/
size_t num_properties; /*属性个数*/int (*get_property)(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val);   /*PSY driver需要重点实现的两个回调函数,用于获取/设置属性值*/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规定,*/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属性类型枚举

enum power_supply_property {/* Properties of type `int' */POWER_SUPPLY_PROP_STATUS = 0, //该PSY的status,主要是充电状态POWER_SUPPLY_PROP_CHARGE_TYPE,//充电类型,包括:"Unknown", "N/A"POWER_SUPPLY_PROP_HEALTH,  //“健康”情况,包括:"Unknown", "Good",POWER_SUPPLY_PROP_PRESENT,//POWER_SUPPLY_PROP_ONLINE,//POWER_SUPPLY_PROP_AUTHENTIC,POWER_SUPPLY_PROP_TECHNOLOGY,
};

(4). 向具体的PSY driver提供的API,可以通过这两个API 实现驱动。

PSY状态改变时通知power supply core的API
void power_supply_changed(struct power_supply *psy)
PSY 申请电源节点 API
struct power_supply *__must_check power_supply_register(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg)

3.CW2015例程分析

分析驱动毫无疑问从probe()函数开始,函数原型:

static int cw_bat_probe();

*进入probe()后,首先需要获取电量计相关gpio,调用函数cw2015_parse_dt(),目的:
获取电池信息
DC检测脚
低电量中断脚
是否支持DC充电
是否支持USB充电
然后调用cw_bat_gpio_init()初始化各gpio的值。随后,进入电量计的初始化函数cw_init()。
cw_init()
cw2015的初始化流程可参考datasheet,其操作流程如下:

  1. WAKE UP使其退出上电默认的sleep状态
  2. 设置容量门限值ATHD
  3. 检查UPDATE_FLAG标志位
    • 如果UPDATE_FLAG有置位,检查电池信息是否一致
    • 如果没有置位,写入电池信息,并置位UPDATE_FLAG
      4.判断SOC是否合法,不合法cw2015进入sleep状态*

CW2015初始化成功后,填充struct power_supply各字段,注册三种power_supply设备,这里只关注battery部分:

    cw_bat->rk_bat.name = "rk-bat";cw_bat->rk_bat.type = POWER_SUPPLY_TYPE_BATTERY;cw_bat->rk_bat.properties = rk_battery_properties;cw_bat->rk_bat.num_properties = ARRAY_SIZE(rk_battery_properties);cw_bat->rk_bat.get_property = rk_battery_get_property;ret = power_supply_register(&client->dev, &cw_bat->rk_bat);if (ret < 0) {dev_err(&cw_bat->client->dev,"power supply register rk_bat error\n");goto rk_bat_register_fail;
}

get_property:get_property方法提供了sys用户接口获取电池信息,调用rk_battery_get_property。该方法通过power_supply_register注册进power supply core,用户层读取时再回调。
get_property的sys接口可参考power supply sys
power_supply_register:初始化电池uevent change工作队列,注册power supply设备。
INIT_WORK(&psy->changed_work, power_supply_changed_work);

注册完power supply设备,各个功能就可以正常工作了,然后通过工作队列更新电池信息。
电池相关处理

电池信息相关处理主要由几个工作队列和中断来完成:

cw_bat_work-更新电池信息
dc_detect_do_wakeup-DC状态中断函数
bat_low_detect_do_wakeup-低电压处理

本文只分析cw_bat_work()电池信息更新部分。
cw_bat_work

首先会判断是否支持DC充电和USB充电(dts配置),如下:

if (cw_bat->plat_data.is_dc_charge == 1) {   //支持DC充电ret = rk_ac_update_online(cw_bat);if (ret == 1)power_supply_changed(&cw_bat->rk_ac);}
if (cw_bat->plat_data.is_usb_charge == 1) {  //支持usb充电ret = rk_usb_update_online(cw_bat);if (ret == 1) {power_supply_changed(&cw_bat->rk_usb);power_supply_changed(&cw_bat->rk_ac);}
}

支持DC充电时(usb充电暂不分析),调用rk_ac_update_online(),进行状态的切换,如下:

  /*判断dc_det_pin是否有效*/if (!gpio_is_valid(cw_bat->plat_data.dc_det_pin)) {  cw_bat->dc_online = 0;pr_info("%s dc charger without dc_det_pin\n", __func__);return 0;}/*判断dc是否插入,接入DC时该脚拉低,只接电池或OTG时该脚为高电平dc_online只判断一次,也就是DC插拔时电池status只切换一次*/if (gpio_get_value(cw_bat->plat_data.dc_det_pin) ==    //为0表示接入了DC,DC充电模式cw_bat->plat_data.dc_det_level) {   //dc_det_level为GPIO_ACTIVE_LOWif (cw_bat->dc_online != 1) {cw_update_time_member_charge_start(cw_bat);cw_bat->dc_online = 1;if (cw_bat->charger_mode != AC_CHARGER_MODE)cw_bat->charger_mode = AC_CHARGER_MODE;ret = 1;}} else {    //为1表示未接入DC,bat放电模式if (cw_bat->dc_online != 0) {cw_update_time_member_charge_start(cw_bat);cw_bat->dc_online = 0;if (cw_bat->usb_online == 0)cw_bat->charger_mode = 0;ret = 1;}}

然后会调用rk_bat_update_status()更新电池状态:充电,充满,放电

if (cw_bat->charger_mode > 0) {if (cw_bat->capacity >= 100)status = POWER_SUPPLY_STATUS_FULL;    //充满elsestatus = POWER_SUPPLY_STATUS_CHARGING;   //充电
} else {status = POWER_SUPPLY_STATUS_NOT_CHARGING;   //放电
}if (cw_bat->status != status) {cw_bat->status = status;cw_bat->bat_change = 1;
}

如果电池状态发生改变时,调用power_supply_changed(),该函数中调度changed_work工作队列,实际任务函数为power_supply_changed_work(),在注册power supply时初始化。
调用rk_bat_update_capacity()更新电池容量SOC,电池容量的获取主要是读取CW2015的0x4,0x5寄存器。实现逻辑为函数cw_get_capacity()。
调用rk_bat_update_vol()更新电池电压VCELL,实现逻辑为函数cw_get_vol()。
调用rk_bat_update_time_to_empty()更新系统可运行时间RRT,实现逻辑为函数cw_get_time_to_empty()。
最后判断电池状态是否改变,若电池状态改变则调度changed_work工作队列。
power_supply_changed_work

每当电池状态或电池容量SOC,电压VCELL等发生改变时,最终都会调用power_supply_changed_work()通知uevent事件给上层。Android HAL由healthd负责监听。函数如下:

static void power_supply_changed_work(struct work_struct *work)
{unsigned long flags;struct power_supply *psy = container_of(work, struct power_supply,changed_work);dev_dbg(psy->dev, "%s\n", __func__);spin_lock_irqsave(&psy->changed_lock, flags);if (psy->changed) {psy->changed = false;spin_unlock_irqrestore(&psy->changed_lock, flags);class_for_each_device(power_supply_class, NULL, psy,__power_supply_changed_work);power_supply_update_leds(psy);kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);spin_lock_irqsave(&psy->changed_lock, flags);}if (!psy->changed)pm_relax(psy->dev);spin_unlock_irqrestore(&psy->changed_lock, flags);
}

至此,CW2015驱动主要部分已分析完。但是在调试时需要注意:CW2015电量计需要配置电池信息,电池信息bat_config_info需由原厂配合电量计调校得出正确的电池信息,并在初始化时将电池信息写入CW2015,否则重启后获取到的电池容量SOC不准确。
SYS节点
CW201X驱动提供了以下几个字段来获取battery状态,实际上HAL uevent也是获取这几个接口的值,接口如下:

ls  /sys/class/power_supply/rk-bat/
capacity
device
health
power
present
status
subsystem
technology
time_to_empty_now
type
uevent
voltage_now

Capacity:电池容量百分比,该值会上报Android,设置中打开时,状态栏即显示该值。
Health:电池健康情况,平台默认为POWER_SUPPLY_HEALTH_GOOD,即返回Good。
Status:电池充电状态。
Technology:电池采用的技术,平台默认为POWER_SUPPLY_TECHNOLOGY_LION,即返回Li-ion。
Time_to_empty_now:电池电压。
Type:电池充电类型。
以上值除了平台默认的,其余的均从struct cw_battery中获取,电池信息的工作队列会一直更新struct cw_battery中的相关值。
4.最简电源驱动Demo
通过上述驱动分析,我整理出最简驱动Demo,便于方便快速写出驱动,下面例子可直接使用。

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>static enum power_supply_property rk_bat_properties[] = {//电源属性节点结构体
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CAPACITY,
};
//电源属性值获取回调,此函数被应用层调用获取节点值
static int rk_bat_get_property(struct power_supply *psy,enum power_supply_property psp, union power_supply_propval *val)
{switch (psp) {case POWER_SUPPLY_PROP_ONLINE:
// val->intval =1;
//获取芯片电池状态上报到val->intval
break;
case POWER_SUPPLY_PROP_CAPACITY:
//val->intval =100;
//获取芯片电池电量上报到val->intval
//
break;
default:
return -EINVAL;
}
return 0;
}
static struct power_supply_desc bat_desc =
{.name = "vantron-bat",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = rk_bat_properties,
.num_properties = ARRAY_SIZE(rk_bat_properties),
.get_property = rk_bat_get_property,
};static struct power_supply_config bat_cfg;static struct power_supply *bat_psy;
static int power_probe(struct platform_device *pdev)
{//此处电源芯片的初始化bat_psy = power_supply_register(&pdev->dev,&bat_desc, &bat_cfg);
if (IS_ERR(bat_psy)) {pr_notice("%s Failed to register power supply: %ld\n",__func__, PTR_ERR(bat_psy));
return PTR_ERR(bat_psy);
}//此处开启电源芯片中断
//在中断中获取更新电池各项数值属性 ,通过power_supply_changed() 更新数据return 0;
}
static int power_remove(struct platform_device *pdev)
{return 0;
}
static struct platform_device power_devices =
{.name = "vantron_power",
.id = 0,
};
static struct platform_driver power_driver =
{.probe = power_probe,
.remove = power_remove,
.driver = {.name = "vantron_power",
.owner = THIS_MODULE,
},
};
static int power_init(void)
{platform_device_register(&power_devices);
platform_driver_register(&power_driver);
return 0;
}
static void __exit power_exit(void)
{platform_device_unregister(&power_devices);
platform_driver_unregister(&power_driver);
}
module_init(power_init);
module_exit(power_exit);
MODULE_LICENSE("GPL");

5.设备树使用补充
此处不再反复阐述,下面提供firefly 官网使用手册链接
5.1GPIO 设备树参考

https://wiki.t-firefly.com/zh_CN/Firefly-RK3288/driver_gpio.html

5.1I2C 设备树参考

https://wiki.t-firefly.com/zh_CN/Firefly-RK3288/driver_i2c.html

Power supply 子系统之最简驱动实现相关推荐

  1. Linux Power supply子系统分析

    1.概述 对于便携式设备,如手机或者pad来说,battery都是必不可少的一个组成部分.kernel中为了方便对battery的管理,专门提供了power supply framework. bat ...

  2. Linux Power supply子系统分析之一

    转自:http://www.wowotech.net/pm_subsystem/psy_class_overview.html 1.概述 对于便携式设备,如手机或者pad来说,battery都是必不可 ...

  3. Linux Power supply子系统分析之二

    1.概述 在上一篇博文中参考窝窝科技的文章分析了linux Power Supply子系统的框架.这篇我们以一个实际的例子来看一下PSY driver的编程方式,比便于更深刻理解power suppl ...

  4. Linux 4.19.111 供电(power supply )子系统

    Linux 内核中为了方便对 battery 的管理,专门提供了power supply framework.battery 管理分开为两个部分,一个是电池监控(fuelgauge),另一个是充放电管 ...

  5. power supply框架

    概述 对于便携式设备,如手机或者pad来说,battery都是必不可少的一个组成部分.kernel中为了方便对battery的管理,专门提供了power supply framework.batter ...

  6. power supply frameware 框架

    一.前言: power supply framework给power supply(供电设备psy)提供统一框架,来管理battery,psy设备目的就是给系统充电,将一些必要信息给到上层用户,如充电 ...

  7. power supply是如何上报电池信息的

    一.引文 作为一个内核初学者,经常容易进入"知其然但不知其所以然"的状态,在power supply子系统中就是这样,知道如何去添加一个属性prop,知道psy可以创建一堆文件节点 ...

  8. Linux 中power supply软件架构和相关API

    一. 概述 电源管理整体上可以分为两个部分,一个是电池监控(fuel gauge),另外一个是充放电管理.这两部分在内核中也是分为两个驱动来管理.fuelgauge驱动的功能主要是负责向上层Andro ...

  9. Power Supply驱动框架

    Power Supply驱动框架和具体驱动 Power Supply驱动程序用于让用户空间可以读取系统中的供电设备信息.供电设备可以是直流电源(AC).USB或者电池等. 17.5.1  Power ...

最新文章

  1. 还需要“attention”吗?一堆“前馈层”在ImageNet上表现得出奇得好
  2. android 日历下面备注,怎样在日历的下面加备注?
  3. python列表知识点_Python列表知识点
  4. 正则表达式三 :编译
  5. 《JAVA与模式》之装饰模式
  6. CMS 站点可能发生稳定性和性能问题
  7. 6410 spi 设备驱动
  8. C# Note21: 扩展方法(Extension Method)及其应用
  9. 自动化设计-框架介绍 TestCase
  10. python文件目录提取_python-按日志提取文件并创建相关目录
  11. Effective C++(6) 如何拒绝编译器的自动生成函数
  12. VTK:交互与Widget——观察者/命令模式
  13. ROC曲线面积AUC详解
  14. mysql mtq_Mysql常用简介 - osc_r3mtqivi的个人空间 - OSCHINA - 中文开源技术交流社区
  15. 苏强SN系列服务器说明书,SN2000交流伺服驱动器使用手册.pdf
  16. 由浅入深MFC学习摘记--第三部分
  17. 喜讯 | 联诚发斩获2020年度LED显示屏十佳品牌
  18. 如何用python画流程图_Markdown笔记:如何画流程图
  19. wireshark学习系列————4、实时捕捉数据包
  20. 微信聊天记录备份:当前网络状况复杂和连接失败的解决办法

热门文章

  1. 2019年诺贝尔生理医学奖揭晓 |动图展示历年生理学奖
  2. MATLAB显函数作图 参数方程作图 极坐标方程作图绘图实例 用 Matlab 绘制高颜值函数图像 放大看告别浓浓锯齿风
  3. 屏幕尺寸,分辨率,ppi换算分析
  4. 【java-日志组件】slf4j+logback配置及详解
  5. Tarena代码-一些代码碎片
  6. C#读IC卡程序 Mwic_32.dll
  7. 02、【江科大自化协stm32F103c8t6】笔记之【入门32单片机及EXTI外部中断初始化参数配置】
  8. Java缓存知识汇总
  9. shape格式、tiff格式地图免费下载网站转载
  10. 全新2022强大的趣味心理测试小程序源码,趣味测试引流裂变神器,流量主激励广告实现管道收益