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 輸出幫助判斷。