天天看點

[Android6.0][RK3399] 電池系統(四)不插電池無法開機的問題

Platform: RK3399

OS: Android 6.0

Kernel: 4.4

Version: v2017.04

IC: TI BQ25700、CW2015、RK808

    • 已知問題與需求分析
    • 解決思路
    • 代碼移植
      • dts
      • Makefile 和 Kconfig
      • cw2015_batteryc
      • bq25700_chargerc

在前面我們

1. 分析了 Charger IC BQ25700 的驅動流程

2. 添加了 BQ IC 的 DC 充電功能

3. 分析了 電量計 CW2015 的驅動流程

在這一章,我們完成電量計的移植,并且整合電量計與充電IC。使其協同為電池工作。

已知問題與需求分析

現在的 BQ IC Driver 有一個大 Bug。

在接上電池的時候,Battery、Battery+TypeC、Battery+DC 都是可以正常開機的。

但是沒有電池的時候,單獨 TypeC 或者 DC 開機是有問題的。

RK 回報他們那邊也有這個問題,定位問題在于 bq25600_hw_init ,BQ IC 的初始化配置。

當屏蔽該函數的時候,單獨 TypeC 或者 DC 是可以正常開機的。

但是電池的正常工作又必須依賴于它的初始化。

解決思路

三條:

1. 尋找 DC / TypeC 開機時對 BQ IC 寄存器初始化的操作 和 bq_hw_init 中進行的操作對比。

2. 在 dts 中配置一個标志位,has_battery,當 has_battery = 1 的時候,加載 bq_hw_init ,沒有的時候屏蔽 bq_hw_init

3. 利用 cw2015 電量計 IC ,檢測是否有無電池,并根據檢測結果決定是否屏蔽 bq_hw_init。

1 當然是正解。

但是尋求了 BQ IC FAE 的幫助,無果,他們回報理論上 bq_hw_init 中的配置是沒問題的,不因采用哪種供電方式而發生改變。

2 缺陷是會生成兩套 resource.img ,根據不同的條件使用不同的 resource.img ,這顯然是不好的

3 需要移植 cw2015。

綜上,隻能選擇第三種方式了

代碼移植

dts

dts 中 對于 cw2015 的配置

[email protected] {
        compatible = "cw201x";
        reg = <0x62>;
        dc_det_gpio = <&gpio1 23 GPIO_ACTIVE_LOW>; //  DC_DET_H GPIO1_C7
        //bat_low_gpio = <&gpio0 GPIO_A7 GPIO_ACTIVE_LOW>; 
        //chg_ok_gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>; // CHG_OK_H GPIO1_A1
        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>;
        is_dc_charge = <1>;
        is_usb_charge = <1>;
};
           

Makefile 和 Kconfig

//Todo

obj-y

cw2015_battery.c

修改 cw2015_battery 的代碼是适用于 rk3288 平台的,有些接口進行了更新。

包括 i2c_master_reg8_send/write 和 register_power_supply

并且我們還 定義了 have_battery 标志位,判斷出電池有無,并将标志位送給 bq 進行下一步電池初始化的操作。

diff --git a/drivers/power/cw2015_battery.c b/drivers/power/cw2015_battery.c
index aaf01fc. 
--- a/drivers/power/cw2015_battery.c
+++ b/drivers/power/cw2015_battery.c
@@ -, +, @@

 #include <linux/power/cw2015_battery.h>

+int have_battery;
+
+#define VCELL_VOLTAGE 3000000
+#define DEBUG 1
+#define DOUBLE_SERIES_BATTERY 1
+
 static int i2c_master_reg8_send(const struct i2c_client *client, const char reg,
                const char *buf, int count, int scl_rate)
 {
@@ -, +, @@ static int i2c_master_reg8_send(const struct i2c_client *client, const char reg,
    msg.flags = client->flags;
    msg.len = count + ;
    msg.buf = (char *)tx_buf;
-   msg.scl_rate = scl_rate;
+   //msg.scl_rate = scl_rate;

    ret = i2c_transfer(adap, &msg, );
    kfree(tx_buf);
@@ -, +, @@ static int i2c_master_reg8_recv(const struct i2c_client *client, const char reg,
    msgs[].flags = client->flags;
    msgs[].len = ;
    msgs[].buf = &reg_buf;
-   msgs[].scl_rate = scl_rate;
+   //msgs[0].scl_rate = scl_rate;

    msgs[].addr = client->addr;
    msgs[].flags = client->flags | I2C_M_RD;
    msgs[].len = count;
    msgs[].buf = (char *)buf;
-   msgs[].scl_rate = scl_rate;
+   //msgs[1].scl_rate = scl_rate;

    ret = i2c_transfer(adap, msgs, );

@@ -, +, @@ static int cw_get_vol(struct cw_battery *cw_bat)
    }

    voltage = value16_1 * ;
+   
+   if(DOUBLE_SERIES_BATTERY)
+       voltage = voltage * ;

    dev_dbg(&cw_bat->client->dev, "the cw201x voltage=%d,reg_val=%x %x\n",
        voltage, reg_val[], reg_val[]);
@@ -, +, @@ static void cw_bat_work(struct work_struct *work)
    if (cw_bat->plat_data.is_dc_charge == ) {
        ret = rk_ac_update_online(cw_bat);
        if (ret == )
-           power_supply_changed(&cw_bat->rk_ac);
+           power_supply_changed(cw_bat->rk_ac);
    }

    if (cw_bat->plat_data.is_usb_charge == ) {
        ret = rk_usb_update_online(cw_bat);
        if (ret == ) {
-           power_supply_changed(&cw_bat->rk_usb);
-           power_supply_changed(&cw_bat->rk_ac);
+           power_supply_changed(cw_bat->rk_usb);
+           power_supply_changed(cw_bat->rk_ac);
        }
    }

@@ -, +, @@ static void cw_bat_work(struct work_struct *work)
    rk_bat_update_time_to_empty(cw_bat);

    if (cw_bat->bat_change) {
-       power_supply_changed(&cw_bat->rk_bat);
+       power_supply_changed(cw_bat->rk_bat);
        cw_bat->bat_change = ;
    }

    queue_delayed_work(cw_bat->battery_workqueue,
-              &cw_bat->battery_delay_work, msecs_to_jiffies());
+              &cw_bat->battery_delay_work, msecs_to_jiffies());

    dev_dbg(&cw_bat->client->dev,
        "cw_bat->bat_change = %d, cw_bat->time_to_empty = %d, cw_bat->capacity = %d\n",
@@ -, +, @@ static int rk_usb_get_property(struct power_supply *psy,
                   union power_supply_propval *val)
 {
    int ret = ;
-   struct cw_battery *cw_bat;
+   struct cw_battery *cw_bat = power_supply_get_drvdata(psy);

-   cw_bat = container_of(psy, struct cw_battery, rk_usb);
+   //cw_bat = container_of(psy, struct cw_battery, rk_usb);
    switch (psp) {
    case POWER_SUPPLY_PROP_ONLINE:
        val->intval = (cw_bat->charger_mode == USB_CHARGER_MODE);
@@ -, +, @@ static int rk_ac_get_property(struct power_supply *psy,
                  union power_supply_propval *val)
 {
    int ret = ;
-   struct cw_battery *cw_bat;
+   struct cw_battery *cw_bat = power_supply_get_drvdata(psy);

-   cw_bat = container_of(psy, struct cw_battery, rk_ac);
+   //cw_bat = container_of(psy, struct cw_battery, rk_ac);
    switch (psp) {
    case POWER_SUPPLY_PROP_ONLINE:
        val->intval = (cw_bat->charger_mode == AC_CHARGER_MODE);
@@ -, +, @@ static int rk_battery_get_property(struct power_supply *psy,
                   union power_supply_propval *val)
 {
    int ret = ;
-   struct cw_battery *cw_bat;
+   struct cw_battery *cw_bat = power_supply_get_drvdata(psy);

-   cw_bat = container_of(psy, struct cw_battery, rk_bat);
+   //cw_bat = container_of(psy, struct cw_battery, rk_bat);
    switch (psp) {
    case POWER_SUPPLY_PROP_CAPACITY:
        val->intval = cw_bat->capacity;
@@ -, +, @@ static enum power_supply_property rk_battery_properties[] = {
    POWER_SUPPLY_PROP_TECHNOLOGY,
 };

+
+static  const struct power_supply_desc cw_bat_desc = {
+   .name   = "rk-bat",
+   .type   = POWER_SUPPLY_TYPE_BATTERY,
+   .properties = rk_battery_properties,
+   .num_properties = ARRAY_SIZE(rk_battery_properties),
+   .get_property = rk_battery_get_property,
+};
+
+static  const struct power_supply_desc cw_ac_desc = {
+   .name   = "rk-ac",
+   .type   = POWER_SUPPLY_TYPE_MAINS,
+   .properties = rk_ac_properties,
+   .num_properties = ARRAY_SIZE(rk_ac_properties),
+   .get_property = rk_ac_get_property,
+};
+
+static  const struct power_supply_desc cw_usb_desc = {
+   .name   = "rk-usb",
+   .type   = POWER_SUPPLY_TYPE_USB,
+   .properties = rk_usb_properties,
+   .num_properties = ARRAY_SIZE(rk_usb_properties),
+   .get_property = rk_usb_get_property,
+};
+
+
+static int cw_init_power_supply(struct cw_battery *bat)
+{
+   struct power_supply_config psy_cfg = {.drv_data = bat, };
+   
+   bat->rk_bat = power_supply_register(&bat->client->dev, &cw_bat_desc, &psy_cfg);
+   if (IS_ERR(bat->rk_bat)) {
+       dev_err(&bat->client->dev,
+           "power supply register rk_bat error\n");
+       return PTR_ERR(bat->rk_bat);
+   }
+   
+   bat->rk_ac = power_supply_register(&bat->client->dev, &cw_ac_desc, &psy_cfg);
+   if (IS_ERR(bat->rk_ac)) {
+       dev_err(&bat->client->dev,
+           "power supply register rk_ac error\n");
+       return PTR_ERR(bat->rk_ac);
+   }
+
+   if(bat->plat_data.is_usb_charge == ) {
+       bat->rk_usb = power_supply_register(&bat->client->dev, &cw_usb_desc, &psy_cfg);
+       if (IS_ERR(bat->rk_usb)) {
+           dev_err(&bat->client->dev,
+               "power supply register rk_usb error\n");
+           return PTR_ERR(bat->rk_usb);
+       }
+       bat->charger_init_mode = dwc_otg_check_dpdm();
+       pr_info("%s cw2015 support charger by usb. usb_mode=%d\n",
+           __func__, bat->charger_init_mode);
+   }
+   return ;
+}
+
 static int cw_bat_gpio_init(struct cw_battery *cw_bat)
 {
    int ret;

@@ -, +, @@ static int cw_bat_probe(struct i2c_client *client,
        return ret;
    }

-   cw_bat->rk_bat.name = "rk-bat";
-   cw_bat->rk_bat.type = POWER_SUPPLY_TYPE_BATTERY;
-   cw_bat->rk_bat.properties = rk_battery_properties;
-   cw_bat->rk_bat.num_properties = ARRAY_SIZE(rk_battery_properties);
-   cw_bat->rk_bat.get_property = rk_battery_get_property;
-   ret = power_supply_register(&client->dev, &cw_bat->rk_bat);
-   if (ret < ) {
-       dev_err(&cw_bat->client->dev,
-           "power supply register rk_bat error\n");
-       goto rk_bat_register_fail;
-   }
-
-   cw_bat->rk_ac.name = "rk-ac";
-   cw_bat->rk_ac.type = POWER_SUPPLY_TYPE_MAINS;
-   cw_bat->rk_ac.properties = rk_ac_properties;
-   cw_bat->rk_ac.num_properties = ARRAY_SIZE(rk_ac_properties);
-   cw_bat->rk_ac.get_property = rk_ac_get_property;
-   ret = power_supply_register(&client->dev, &cw_bat->rk_ac);
-   if (ret < ) {
-       dev_err(&cw_bat->client->dev,
-           "power supply register rk_ac error\n");
-       goto rk_ac_register_fail;
-   }
-
-   if (cw_bat->plat_data.is_usb_charge == ) {
-       cw_bat->rk_usb.name = "rk-usb";
-       cw_bat->rk_usb.type = POWER_SUPPLY_TYPE_USB;
-       cw_bat->rk_usb.properties = rk_usb_properties;
-       cw_bat->rk_usb.num_properties = ARRAY_SIZE(rk_usb_properties);
-       cw_bat->rk_usb.get_property = rk_usb_get_property;
-       ret = power_supply_register(&client->dev, &cw_bat->rk_usb);
-       if (ret < ) {
-           dev_err(&cw_bat->client->dev,
-               "power supply register rk_usb error\n");
-           goto rk_usb_register_fail;
-       }
-       cw_bat->charger_init_mode = dwc_otg_check_dpdm();
-       pr_info("%s cw2015 support charger by usb. usb_mode=%d\n",
-           __func__, cw_bat->charger_init_mode);
+   ret = cw_init_power_supply(cw_bat);
+   if (ret) {
+       dev_err(&cw_bat->client->dev, "init power supply fail!\n");
+       return ret;
    }

    cw_bat->dc_online = ;
@@ -, +, @@ static int cw_bat_probe(struct i2c_client *client,
        }
        irq_flags = level ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
        ret =
-           request_irq(irq, dc_detect_irq_handler, irq_flags,
+           request_irq(irq, dc_detect_irq_handler, irq_flags | IRQF_SHARED,
                "dc_detect", cw_bat);
        if (ret < )
            pr_err("%s: request_irq(%d) failed\n", __func__, irq);
@@ -, +, @@ static int cw_bat_probe(struct i2c_client *client,

    dev_info(&cw_bat->client->dev,
         "cw2015/cw2013 driver v1.2 probe sucess\n");
+
+   ret = cw_get_vol(cw_bat);
+   if(ret > VCELL_VOLTAGE){        
+       have_battery = ;
+       dev_info(&cw_bat->client->dev,"FOUND BAT! have_battery = %d\n",have_battery);
+   }else{
+       have_battery = ;
+       dev_dbg(&cw_bat->client->dev,"NOT FOUND BAT! have_battery = %d\n",have_battery);
+   }
    return ;

-rk_usb_register_fail:
-   power_supply_unregister(&cw_bat->rk_usb);
-rk_ac_register_fail:
-   power_supply_unregister(&cw_bat->rk_ac);
-rk_bat_register_fail:
-   power_supply_unregister(&cw_bat->rk_bat);
 pdate_fail:
    dev_info(&cw_bat->client->dev,
         "cw2015/cw2013 driver v1.2 probe error!!!!\n");

diff --git a/include/linux/power/cw2015_battery.h b/include/linux/power/cw2015_battery.h
index 14aa7.a3c203 
--- a/include/linux/power/cw2015_battery.h
+++ b/include/linux/power/cw2015_battery.h
@@ -, +, @@ struct cw_battery {
    struct delayed_work bat_low_wakeup_work;
    struct cw_bat_platform_data plat_data;

-   struct power_supply rk_bat;
-   struct power_supply rk_ac;
-   struct power_supply rk_usb;
+   struct power_supply *rk_bat;
+   struct power_supply *rk_ac;
+   struct power_supply *rk_usb;

    long sleep_time_capacity_change;
    long run_time_capacity_change;
@@ -, +, @@ struct cw_battery {
 };

 #if defined(CONFIG_ARCH_ROCKCHIP)
-int get_gadget_connect_flag(void);
+int get_gadget_connect_flag(void)
+{
+   return ;
+}
 int dwc_otg_check_dpdm(void);
 int dwc_vbus_status(void);
 #else
@@ -, +, @@ static inline int dwc_otg_check_dpdm(bool wait)
    return ;
 }

-static inline int dwc_vbus_status(void);
+static inline int dwc_vbus_status(void)
 {
    return ;
 }
+
 #endif

 #endif
           

bq25700_charger.c

在 bq 中我們會根據 cw 中獲得到的電池的狀态來操作 是否進行 BQ IC 寄存器的初始化(hw_init)

--- a/drivers/power/bq25700_charger.c
+++ b/drivers/power/bq25700_charger.c
@@ -33,6 +33,8 @@
 #include <linux/of_gpio.h>
 #include <linux/rk_keys.h>

+extern int have_battery;
+
 static int dbg_enable = 1;
 module_param_named(dbg_level, dbg_enable, int, 0644);

@@ -741,7 +743,7 @@ static int bq25700_chip_reset(struct bq25700_device *charger)
 {
    int ret;
    int rst_check_counter = 10;
-   return 0;
+   //return 0;  // hw_init 中不需要 chip reset ,關掉

    DBG("BQ25700: bq25700_chip_reset\n");
    ret = bq25700_field_write(charger, RESET_REG, 1);
@@ -781,7 +783,14 @@ static int bq25700_hw_init(struct bq25700_device *charger)
        {OTG_CURRENT,    charger->init_data.otg_current},
    };

-   DBG("BQ25700: bq25700_hw_init.\n");
+   if(!have_battery)  //如果沒有電池,就 bq25700_hw_init 就直接傳回
+   {
+       DBG("BQ25700: CW said NO BAT FOUND!\n");
+       return -1;
+   }
+
+   DBG("BQ25700: We have BAT,bq25700_hw_init.\n");
    ret = bq25700_chip_reset(charger);
    if (ret < 0)
        return ret;
@@ -887,6 +896,46 @@ static int bq25700_hw_init(struct bq25700_device *charger)
        return ret;
    }


 // 将中斷設為 共享中斷
@@ -1079,7 +1139,7 @@ static irqreturn_t bq25700_irq_handler_thread(int irq, void *private)
        charger->typec1_status = USB_STATUS_NONE;
        DBG("BQ25700: irq_flag = IRQF_TRIGGER_HIGH\n");
    }
-   irq_set_irq_type(irq, irq_flag | IRQF_ONESHOT);
+   irq_set_irq_type(irq, irq_flag | IRQF_ONESHOT | IRQF_SHARED);
    rk_send_wakeup_key();

    DBG("BQ25700: bq25700_irq_handler_thread done\n");
@@ -1815,7 +1875,7 @@ while(1) {

    ret = devm_request_threaded_irq(dev, client->irq, NULL,
                    bq25700_irq_handler_thread,
-                   irq_flag | IRQF_ONESHOT,
+                   irq_flag | IRQF_ONESHOT | IRQF_SHARED,
                    "bq25700_irq", charger);
    if (ret)
        goto irq_fail;
@@ -1825,18 +1885,6 @@ while(1) {

    DBG("BQ25700: enable_irq_wake done.\n");
           

## 驗證結果

ls sys/class/power_supply/ bq25700_charger rk_ac rk_usb rk_bat

可以看到四個裝置

bq25700_charger rk_ac rk_usb rk_bat

bq25700_charger 是在 bq 中注冊的 Type = POWER_SUPPLY_TYPE_USB 的裝置

rk_ac 是在 cw 中注冊的 Type = POWER_SUPPLY_TYPE_MAINS 的裝置

rk_usb 是在 cw 中注冊的 Type = POWER_SUPPLY_TYPE_USB 的裝置

rk_bat 是在 cw 中注冊的 Type = POWER_SUPPLY_TYPE_BATTERY 的裝置

均注冊正常。

而且無論是否接電池,現在都可以正常開機。

歡迎轉載,轉載請著名作者 Younix 和原文位址:

http://blog.csdn.net/dearsq/article/details/72770347

謝謝。

繼續閱讀