1. 需求描述
cat /sys/class/power_supply/sy6974/enable # 读取充电启用状态
echo 0 > /sys/class/power_supply/sy6974/enable # 禁止充电
echo 1 > /sys/class/power_supply/sy6974/enable # 启用充电
2. SY6974 充电芯片简介
- 厂商:Silergy (China)
- 通信方式:I2C
- 功能:
- 支持常压充电
- 支持温度、电压、电流等相关监测
- 多级充电结束条件
3. 工作原理 & 软件流程
- 设备通过 I2C 形成 Linux 中的 /dev/i2c-X
- SY6974 驱动在 probe 时连接 I2C client,初始化 GPIO 与 power_supply
- 通过 power_supply_register 注册一个 sy6974名称的 power supply 设备
- Linux power_supply 框架会自动创建 /sys/class/power_supply/sy6794/ 节点
- 属性访问由 power_supply_class 接管,通过标准 get_property 接口提供信息
4. 原始驱动框架
- 使用简单版 power_supply_register()
struct power_supply charger;
power_supply_register(dev, &charger);
- 无 power_supply_desc 和 power_supply_config
- charger.dev 为 NULL,需要后期根据 name 查找 device
5. 可行方案分析
方案 1:改为 power_supply_desc 模型
- ✅ 优点:规范,正确
- ❌ 缺点:需要重写多数代码,不适合现有已使用 power_supply_register() 的组织
方案 2:把 enable 节点挂载到 i2c client 节点
- ✅ 简单易行
- ❌ 但节点路径不符合需求(在 /sys/devices/.../enable)
方案 3(已采用):查找 power_supply 对应 device,挂载 sysfs 节点
- ✅ 保持原驱动结构不变
- ✅ 节点出现在 /sys/class/power_supply/sy6794/enable
- ✅ 兼容 power_supply 框架
- ❌ 需要多一步查找名称,不能直接传递 driver_data
6. 最终方案详解
修改清单
- 修改函数:sy6974_power_supply_init()、sy6974_power_supply_exit()
- 新增函数:enable_show、enable_store、power_supply_match_by_name
- 使用静态全局变量保存 charger 设备指针,避免覆盖 power_supply 框架数据
1). 实现方法
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/power_supply.h>
static struct sy6974_device *g_sy6974 = NULL;
实现 sysfs 操作函数:
static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{if (!g_sy6974)return -ENODEV;return sprintf(buf, "%d\n", g_sy6974->charge_enabled);
}static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{unsigned long val;if (!g_sy6974)return -ENODEV;if (kstrtoul(buf, 10, &val))return -EINVAL;sy6974_set_charge_enable(g_sy6974, !!val);return count;
}static DEVICE_ATTR(enable, 0664, enable_show, enable_store);
2). 查找 power_supply class 的 device,并添加属性文件
在 sy6974_power_supply_init() 注册完成 power_supply 后添加:
extern struct class *power_supply_class;
static int power_supply_match_by_name(struct device *dev, const void *data)
{ const char *name = data; struct power_supply *psy = dev_get_drvdata(dev); if (!psy || !psy->name) return 0; return strcmp(psy->name, name) == 0;
}
然后添加以下代码:
// 保存设备指针
g_sy6974 = bq;
struct device *psy_dev = class_find_device(power_supply_class, NULL, bq->charger.name, power_supply_match_by_name);
if (!psy_dev)
{ dev_err(bq->dev, "Failed to find power_supply device\n"); return -ENODEV;
} ret = device_create_file(psy_dev, &dev_attr_enable);
if (ret) dev_err(bq->dev, "Failed to create enable sysfs node\n");
else bq->charger.dev = psy_dev;
3). 清理函数中移除 sysfs 节点
在 sy6974_power_supply_exit() 函数中添加:
if (bq->charger.dev) device_remove_file(bq->charger.dev, &dev_attr_enable); g_sy6974 = NULL;
4). 测试路径
ls /sys/class/power_supply/sy6974/enable
cat /sys/class/power_supply/sy6974/enable # 输出 1 或 0
echo 1 > /sys/class/power_supply/sy6974/enable # 启用充电
echo 0 > /sys/class/power_supply/sy6974/enable # 禁用充电
dmesg | tail
7. 测试与验证
测试项 | 操作 | 预期 |
1. 节点创建 | ls /sys/class/power_supply/sy6974/enable | 正常显示 |
2. 状态读取 | cat enable | 输出 0 或 1 |
3. 禁用充电 | echo 0 > enable | 芯片停止充电,log打印“DISABLED” |
4. 启用充电 | echo 1 > enable | 芯片开始充电,log打印“ENABLED” |
5. 充电状态监测 | 使用电表或测试桩 | 充电电流变化 |
8 FAQ 常见问题
Q1: 为何节点不在 /sys/class/power_supply/ 下?
请确保你没有使用 dev_set_drvdata() 覆盖了 power_supply 框架的 drvdata,此方案是通过 class_find_device 查找原始节点并挂载的。
Q2: 为什么 cat enable 会崩溃?
通常是 g_sy6974 为 NULL,可能是在调用前未初始化或退出时未清空,请检查 init/exit 的赋值与清理。
Q3: 有多个 charger 怎么处理?
不要使用全局变量 g_sy6974,改为一个以 name 哈希或数组形式维护的指针表,根据 dev 获取。
Q4: 如何调试节点写入失败?
检查 device_create_file() 返回值是否成功,并确认 power_supply_class 正确初始化,使用 dmesg 输出帮助判断。