Power supply 子系统之最简驱动实现
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,其操作流程如下:
- WAKE UP使其退出上电默认的sleep状态
- 设置容量门限值ATHD
- 检查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 子系统之最简驱动实现相关推荐
- Linux Power supply子系统分析
1.概述 对于便携式设备,如手机或者pad来说,battery都是必不可少的一个组成部分.kernel中为了方便对battery的管理,专门提供了power supply framework. bat ...
- Linux Power supply子系统分析之一
转自:http://www.wowotech.net/pm_subsystem/psy_class_overview.html 1.概述 对于便携式设备,如手机或者pad来说,battery都是必不可 ...
- Linux Power supply子系统分析之二
1.概述 在上一篇博文中参考窝窝科技的文章分析了linux Power Supply子系统的框架.这篇我们以一个实际的例子来看一下PSY driver的编程方式,比便于更深刻理解power suppl ...
- Linux 4.19.111 供电(power supply )子系统
Linux 内核中为了方便对 battery 的管理,专门提供了power supply framework.battery 管理分开为两个部分,一个是电池监控(fuelgauge),另一个是充放电管 ...
- power supply框架
概述 对于便携式设备,如手机或者pad来说,battery都是必不可少的一个组成部分.kernel中为了方便对battery的管理,专门提供了power supply framework.batter ...
- power supply frameware 框架
一.前言: power supply framework给power supply(供电设备psy)提供统一框架,来管理battery,psy设备目的就是给系统充电,将一些必要信息给到上层用户,如充电 ...
- power supply是如何上报电池信息的
一.引文 作为一个内核初学者,经常容易进入"知其然但不知其所以然"的状态,在power supply子系统中就是这样,知道如何去添加一个属性prop,知道psy可以创建一堆文件节点 ...
- Linux 中power supply软件架构和相关API
一. 概述 电源管理整体上可以分为两个部分,一个是电池监控(fuel gauge),另外一个是充放电管理.这两部分在内核中也是分为两个驱动来管理.fuelgauge驱动的功能主要是负责向上层Andro ...
- Power Supply驱动框架
Power Supply驱动框架和具体驱动 Power Supply驱动程序用于让用户空间可以读取系统中的供电设备信息.供电设备可以是直流电源(AC).USB或者电池等. 17.5.1 Power ...
最新文章
- 还需要“attention”吗?一堆“前馈层”在ImageNet上表现得出奇得好
- android 日历下面备注,怎样在日历的下面加备注?
- python列表知识点_Python列表知识点
- 正则表达式三 :编译
- 《JAVA与模式》之装饰模式
- CMS 站点可能发生稳定性和性能问题
- 6410 spi 设备驱动
- C# Note21: 扩展方法(Extension Method)及其应用
- 自动化设计-框架介绍 TestCase
- python文件目录提取_python-按日志提取文件并创建相关目录
- Effective C++(6) 如何拒绝编译器的自动生成函数
- VTK:交互与Widget——观察者/命令模式
- ROC曲线面积AUC详解
- mysql mtq_Mysql常用简介 - osc_r3mtqivi的个人空间 - OSCHINA - 中文开源技术交流社区
- 苏强SN系列服务器说明书,SN2000交流伺服驱动器使用手册.pdf
- 由浅入深MFC学习摘记--第三部分
- 喜讯 | 联诚发斩获2020年度LED显示屏十佳品牌
- 如何用python画流程图_Markdown笔记:如何画流程图
- wireshark学习系列————4、实时捕捉数据包
- 微信聊天记录备份:当前网络状况复杂和连接失败的解决办法
热门文章
- 2019年诺贝尔生理医学奖揭晓 |动图展示历年生理学奖
- MATLAB显函数作图 参数方程作图 极坐标方程作图绘图实例 用 Matlab 绘制高颜值函数图像 放大看告别浓浓锯齿风
- 屏幕尺寸,分辨率,ppi换算分析
- 【java-日志组件】slf4j+logback配置及详解
- Tarena代码-一些代码碎片
- C#读IC卡程序 Mwic_32.dll
- 02、【江科大自化协stm32F103c8t6】笔记之【入门32单片机及EXTI外部中断初始化参数配置】
- Java缓存知识汇总
- shape格式、tiff格式地图免费下载网站转载
- 全新2022强大的趣味心理测试小程序源码,趣味测试引流裂变神器,流量主激励广告实现管道收益