platform: RK3288
OS: Android7.1
kernel:4.4
cw2015電量計:
dts中添加cw2015的設備節點:
cw2015@62 { status = "okay"; compatible = "cw201x"; reg = <0x62>; bat_config_info = <0x15 0x42 0x60 0x59 0x52 0x58 0x4D 0x48 0x48 0x44 0x44 0x46 0x49 0x48 0x32 0x24 0x20 0x17 0x13 0x0F 0x19 0x3E 0x51 0x45 0x08 0x76 0x0B 0x85 0x0E 0x1C 0x2E 0x3E 0x4D 0x52 0x52 0x57 0x3D 0x1B 0x6A 0x2D 0x25 0x43 0x52 0x87 0x8F 0x91 0x94 0x52 0x82 0x8C 0x92 0x96 0xFF 0x7B 0xBB 0xCB 0x2F 0x7D 0x72 0xA5 0xB5 0xC1 0x46 0xAE>; monitor_sec = <5>; virtual_power = <0>; divider_res1 = <200>; divider_res2 = <200>; };
對應驅動在drivers/power/cw2015_battery.c,但內核默認是沒有編譯該驅動的,需要進入menuconfig里面進行設置:make ARCH=arm menuconfig
Device Drivers --->
-*- Power supply class support --->
[*] CW2015 Battery driver
選擇好驅動后,硬件正常的話,一般都能正常跑起來,可以正常上報電池電量。但還不能檢測充電器的拔插狀態,這個功能下面會加上。
兼容bq27541:
考慮到之前舊的主板電量計(bq27541,電量計是附帶在電池里面的)可能也要維護使用,所以dts和驅動里面也要打開
bq27320: bq27320@55 { compatible = "ti,bq27541-g1"; reg = <0x55>; status = "okay"; };
調試發現,當兩個驅動都打開,實際上接的是不帶電量計(bq27541)的電池時,這個時候無論接不接AC電源適配器,充電圖標都是充電狀態。這是因為bq27541的驅動有讀取bq27541寄存器的代碼,不接該電量計電池的時候,就會報錯,導致上報的狀態不對。我們需要把它屏蔽掉:
diff --git a/drivers/power/bq27541_battery.c b/drivers/power/bq27541_battery.c index f46b719..3d84022 100755 --- a/drivers/power/bq27541_battery.c +++ b/drivers/power/bq27541_battery.c @@ -40,7 +40,7 @@ static int bq27541_read_reg(unsigned int reg){ static void bq27541_battery_work(struct work_struct *work){ - power_supply_changed(g_di->ac); + // power_supply_changed(g_di->ac); power_supply_changed(g_di->battery); schedule_delayed_work(&g_di->work, 6*HZ); } @@ -64,7 +64,7 @@ static int bq27541_battery_status(void){ int status = 0; status = bq27541_read_reg(0x0a);//REG_FLAGS if(status < 0){ - printk("battery read flags error\n"); + //printk("battery read flags error\n"); //return 0; } status = (status<<0)&0x01; @@ -203,8 +203,8 @@ static int bq27541_battery_probe(struct i2c_client *client, di->battery = power_supply_register(di->dev,&bq27541_bat_desc, &battery_psy_cfg); - di->ac = power_supply_register(di->dev, &bq27541_ac_desc, - &battery_psy_cfg); + // di->ac = power_supply_register(di->dev, &bq27541_ac_desc, + // &battery_psy_cfg); INIT_DELAYED_WORK(&di->work, bq27541_battery_work); schedule_delayed_work(&di->work, 15*HZ); @@ -217,7 +217,7 @@ static int bq27541_battery_probe(struct i2c_client *client, static int bq27541_battery_remove(struct i2c_client *client){ cancel_delayed_work_sync(&g_di->work); - power_supply_unregister(g_di->ac); + // power_supply_unregister(g_di->ac); power_supply_unregister(g_di->battery); return 0; }
但這樣就沒有充電檢測功能了,硬件電路里面有加DC檢查功能的gpio口,需要把這個功能用起來。
GPIO DC檢測:
由於有兩個電量計驅動,為了兼容,所以這個gpio檢測DC變化的功能就不能做在某一個單獨的驅動里面,在 /kernel/drivers/power目錄下剛好存在一個gpio-charger.c文件,這個剛好可以符合我們的這個需求。
menuconfig里面選擇編譯該驅動:
Device Drivers --->
-*- Power supply class support --->
<*> GPIO charger
dts中添加設備節點:
gpio-charger { status="okay"; compatible = "gpio-charger"; gpios = <&gpio0 8 GPIO_ACTIVE_LOW>; charger-type = "mains"; };
這里的 charger-type選擇的是mains,在代碼中的解析如下:

參考cw2015_battery.c和bq27541_battery.c這兩個驅動,ac模式時候type的注冊都是POWER_SUPPLY_TYPE_MAINS


附上gpio-charger.c的代碼:
1 /* 2 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> 3 * Driver for chargers which report their online status through a GPIO pin 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the 7 * Free Software Foundation; either version 2 of the License, or (at your 8 * option) any later version. 9 * 10 * You should have received a copy of the GNU General Public License along 11 * with this program; if not, write to the Free Software Foundation, Inc., 12 * 675 Mass Ave, Cambridge, MA 02139, USA. 13 * 14 */ 15 16 #include <linux/device.h> 17 #include <linux/gpio.h> 18 #include <linux/init.h> 19 #include <linux/interrupt.h> 20 #include <linux/kernel.h> 21 #include <linux/module.h> 22 #include <linux/platform_device.h> 23 #include <linux/power_supply.h> 24 #include <linux/slab.h> 25 #include <linux/of.h> 26 #include <linux/of_gpio.h> 27 28 #include <linux/power/gpio-charger.h> 29 30 struct gpio_charger { 31 const struct gpio_charger_platform_data *pdata; 32 unsigned int irq; 33 bool wakeup_enabled; 34 35 struct power_supply *charger; 36 struct power_supply_desc charger_desc; 37 }; 38 39 static irqreturn_t gpio_charger_irq(int irq, void *devid) 40 { 41 struct power_supply *charger = devid; 42 43 power_supply_changed(charger); 44 45 return IRQ_HANDLED; 46 } 47 48 static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) 49 { 50 return power_supply_get_drvdata(psy); 51 } 52 53 static int gpio_charger_get_property(struct power_supply *psy, 54 enum power_supply_property psp, union power_supply_propval *val) 55 { 56 struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy); 57 const struct gpio_charger_platform_data *pdata = gpio_charger->pdata; 58 59 switch (psp) { 60 case POWER_SUPPLY_PROP_ONLINE: 61 val->intval = !!gpio_get_value_cansleep(pdata->gpio); 62 val->intval ^= pdata->gpio_active_low; 63 break; 64 default: 65 return -EINVAL; 66 } 67 68 return 0; 69 } 70 71 static enum power_supply_property gpio_charger_properties[] = { 72 POWER_SUPPLY_PROP_ONLINE, 73 }; 74 75 static 76 struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev) 77 { 78 struct device_node *np = dev->of_node; 79 struct gpio_charger_platform_data *pdata; 80 const char *chargetype; 81 enum of_gpio_flags flags; 82 int ret; 83 84 if (!np) 85 return ERR_PTR(-ENOENT); 86 87 pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 88 if (!pdata) 89 return ERR_PTR(-ENOMEM); 90 91 pdata->name = np->name; 92 93 pdata->gpio = of_get_gpio_flags(np, 0, &flags); 94 if (pdata->gpio < 0) { 95 if (pdata->gpio != -EPROBE_DEFER) 96 dev_err(dev, "could not get charger gpio\n"); 97 return ERR_PTR(pdata->gpio); 98 } 99 100 pdata->gpio_active_low = !!(flags & OF_GPIO_ACTIVE_LOW); 101 102 pdata->type = POWER_SUPPLY_TYPE_UNKNOWN; 103 ret = of_property_read_string(np, "charger-type", &chargetype); 104 if (ret >= 0) { 105 if (!strncmp("unknown", chargetype, 7)) 106 pdata->type = POWER_SUPPLY_TYPE_UNKNOWN; 107 else if (!strncmp("battery", chargetype, 7)) 108 pdata->type = POWER_SUPPLY_TYPE_BATTERY; 109 else if (!strncmp("ups", chargetype, 3)) 110 pdata->type = POWER_SUPPLY_TYPE_UPS; 111 else if (!strncmp("mains", chargetype, 5)) 112 pdata->type = POWER_SUPPLY_TYPE_MAINS; 113 else if (!strncmp("usb-sdp", chargetype, 7)) 114 pdata->type = POWER_SUPPLY_TYPE_USB; 115 else if (!strncmp("usb-dcp", chargetype, 7)) 116 pdata->type = POWER_SUPPLY_TYPE_USB_DCP; 117 else if (!strncmp("usb-cdp", chargetype, 7)) 118 pdata->type = POWER_SUPPLY_TYPE_USB_CDP; 119 else if (!strncmp("usb-aca", chargetype, 7)) 120 pdata->type = POWER_SUPPLY_TYPE_USB_ACA; 121 else 122 dev_warn(dev, "unknown charger type %s\n", chargetype); 123 } 124 125 return pdata; 126 } 127 128 static int gpio_charger_probe(struct platform_device *pdev) 129 { 130 const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data; 131 struct power_supply_config psy_cfg = {}; 132 struct gpio_charger *gpio_charger; 133 struct power_supply_desc *charger_desc; 134 int ret; 135 int irq; 136 137 if (!pdata) { 138 pdata = gpio_charger_parse_dt(&pdev->dev); 139 if (IS_ERR(pdata)) { 140 ret = PTR_ERR(pdata); 141 if (ret != -EPROBE_DEFER) 142 dev_err(&pdev->dev, "No platform data\n"); 143 return ret; 144 } 145 } 146 147 if (!gpio_is_valid(pdata->gpio)) { 148 dev_err(&pdev->dev, "Invalid gpio pin\n"); 149 return -EINVAL; 150 } 151 152 gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger), 153 GFP_KERNEL); 154 if (!gpio_charger) { 155 dev_err(&pdev->dev, "Failed to alloc driver structure\n"); 156 return -ENOMEM; 157 } 158 159 charger_desc = &gpio_charger->charger_desc; 160 161 charger_desc->name = pdata->name ? pdata->name : "gpio-charger"; 162 charger_desc->type = pdata->type; 163 charger_desc->properties = gpio_charger_properties; 164 charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties); 165 charger_desc->get_property = gpio_charger_get_property; 166 167 psy_cfg.supplied_to = pdata->supplied_to; 168 psy_cfg.num_supplicants = pdata->num_supplicants; 169 psy_cfg.of_node = pdev->dev.of_node; 170 psy_cfg.drv_data = gpio_charger; 171 172 ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); 173 if (ret) { 174 dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret); 175 goto err_free; 176 } 177 ret = gpio_direction_input(pdata->gpio); 178 if (ret) { 179 dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret); 180 goto err_gpio_free; 181 } 182 183 gpio_charger->pdata = pdata; 184 185 gpio_charger->charger = power_supply_register(&pdev->dev, 186 charger_desc, &psy_cfg); 187 if (IS_ERR(gpio_charger->charger)) { 188 ret = PTR_ERR(gpio_charger->charger); 189 dev_err(&pdev->dev, "Failed to register power supply: %d\n", 190 ret); 191 goto err_gpio_free; 192 } 193 194 irq = gpio_to_irq(pdata->gpio); 195 if (irq > 0) { 196 ret = request_any_context_irq(irq, gpio_charger_irq, 197 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 198 dev_name(&pdev->dev), gpio_charger->charger); 199 if (ret < 0) 200 dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret); 201 else 202 gpio_charger->irq = irq; 203 } 204 205 platform_set_drvdata(pdev, gpio_charger); 206 207 device_init_wakeup(&pdev->dev, 1); 208 209 return 0; 210 211 err_gpio_free: 212 gpio_free(pdata->gpio); 213 err_free: 214 return ret; 215 } 216 217 static int gpio_charger_remove(struct platform_device *pdev) 218 { 219 struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); 220 221 if (gpio_charger->irq) 222 free_irq(gpio_charger->irq, gpio_charger->charger); 223 224 power_supply_unregister(gpio_charger->charger); 225 226 gpio_free(gpio_charger->pdata->gpio); 227 228 return 0; 229 } 230 231 #ifdef CONFIG_PM_SLEEP 232 static int gpio_charger_suspend(struct device *dev) 233 { 234 struct gpio_charger *gpio_charger = dev_get_drvdata(dev); 235 236 if (device_may_wakeup(dev)) 237 gpio_charger->wakeup_enabled = 238 !enable_irq_wake(gpio_charger->irq); 239 240 return 0; 241 } 242 243 static int gpio_charger_resume(struct device *dev) 244 { 245 struct platform_device *pdev = to_platform_device(dev); 246 struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); 247 248 if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled) 249 disable_irq_wake(gpio_charger->irq); 250 power_supply_changed(gpio_charger->charger); 251 252 return 0; 253 } 254 #endif 255 256 static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops, 257 gpio_charger_suspend, gpio_charger_resume); 258 259 static const struct of_device_id gpio_charger_match[] = { 260 { .compatible = "gpio-charger" }, 261 { } 262 }; 263 MODULE_DEVICE_TABLE(of, gpio_charger_match); 264 265 static struct platform_driver gpio_charger_driver = { 266 .probe = gpio_charger_probe, 267 .remove = gpio_charger_remove, 268 .driver = { 269 .name = "gpio-charger", 270 .pm = &gpio_charger_pm_ops, 271 .of_match_table = gpio_charger_match, 272 }, 273 }; 274 275 module_platform_driver(gpio_charger_driver); 276 277 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 278 MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO"); 279 MODULE_LICENSE("GPL"); 280 MODULE_ALIAS("platform:gpio-charger");
這樣,兩個電量計的電量讀取上報以及充電檢測的功能就實現了。
參考:https://blog.csdn.net/oliverJ/article/details/104018558
