本帖最后由 mzy2364 于 2020-7-30 14:47 编辑
在多数情况下,作为用户是不需要写Linux驱动代码的,因为芯片厂或者方案厂会为我们提供,但是作为嵌入式Linux开发者,学会看驱动甚至学会写驱动会大大提高开发效率。这里就cw2015的驱动代码,带领读者简单了解下i2c驱动框架下的电源管理驱动。 需要完成的功能: 在设备树下面添加CW2015; 将cw2015注册为power_supply设备,在/sys/class/power_supply下面可以看到cw2015; 通过power_supply提供的接口函数利用i2c读取电池的电量信息和电压信息; 通过power_supply提供的接口函数利用GPIO函数读取电池充电状态。 开始写驱动 1、准备工作linux内核的电源驱动文件在内核目录的/drivers/power文件夹下面,这里面有很多电源芯片的驱动文件,这些文件对我们编写驱动有非常大的参考意义,读者在编写的驱动的时候可以多看一看。 2、驱动的基本框架接下来我们来看一下power_supply的基本驱动框架,power_supply的3个核心文件在内核目录的/drivers/power/下面 power_supply_core.c 用于抽象核心数据结构、实现公共逻辑 power_supply_leds.c 实现sysfs以及uevent功能 power_supply_sysfs.c 基于linux led class,提供PSY设备状态指示的通用实现 来看一下power_supply的结构体:(在kernel/include/power_supply.h) - 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;
- 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设备的属性,包括电源设备的名称、类型以及接口函数。 power_supply_desc的结构体: - /* Description of power supply */
- struct power_supply_desc {
- const char *name; /* 名称 */
- enum power_supply_type type; /* 电源的类型 */
- enum power_supply_property *properties; /* 属性列表 */
- size_t num_properties; /* 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);
- 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;
- };
复制代码对于电源类型、电源属性这些枚举类型建议读者去阅读源码去获取。 power_supply_config结构体:
- /* Run-time specific power supply configuration */
- struct power_supply_config {
- struct device_node *of_node;
- /* Driver private data */
- void *drv_data;
- char **supplied_to;
- size_t num_supplicants;
- };
复制代码这3个重要的结构体之间的关系由一个函数进行建立: - struct power_supply *__must_check power_supply_register(struct device *parent,const struct power_supply_desc *desc,const struct power_supply_config *cfg)
复制代码power_supply的简要驱动步骤一般为: l 填充 power_supply_desc 结构体,指定驱动的名称、类型、参数获取函数等; l 调用函数 power_supply_register 在 /sys/class/power_supply 下面注册对应的设备; l 内核通过 get_property 函数获取指定的参数(电池健康状态、充电状态等等)。[url=]恢复数据[/url]
修改设备树
驱动文件写完以后需要修改设备树,才能匹配硬件
- &i2c2 {
- clock_frequency = <100000>;
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c2>;
- status = "okay";
- ...
- cw2015: cw2015@62{
- compatible = "cw2015";
- cw2015_charge_gpio = <&gpio1 1 GPIO_ACTIVE_LOW>;
- reg = <0x62>;
- };
- };
复制代码 编译新的内核和设备树
我们将cw2015的驱动添加进内核,然后编译新的内核和设备树替换到目标板,使用以下命令可以查看电量
- cat /sys/class/power_supply/cw2015-battery/status
复制代码
由于字数限制,完整内容请查看pdf
imx6ull移植cw2015驱动.pdf
(578.84 KB, 下载次数: 53)
|