調試cw2015電量計+GPIO DC檢測 +兼容bq27541


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM