大家好,欢迎来到IT知识分享网。
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
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/28134.html