update cw2015 battery driver
authorxuhuicong <xhc@rock-chips.com>
Thu, 21 Mar 2013 14:46:22 +0000 (22:46 +0800)
committerxuhuicong <xhc@rock-chips.com>
Thu, 21 Mar 2013 14:46:22 +0000 (22:46 +0800)
drivers/power/cw2015_battery.c [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index da3dcaf..55388b6
 #include <mach/gpio.h>
 #include <linux/delay.h>
 #include <linux/power/cw2015_battery.h>
+#include <linux/time.h>
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <mach/board.h>
 
 #define REG_VERSION             0x0
 #define REG_VCELL               0x2
 
 #define CW_I2C_SPEED            100000          // default i2c speed set 100khz
 
+#define BATTERY_UP_MAX_CHANGE   600             // the max time allow battery change quantity
+#define BATTERY_DOWN_MIN_CHANGE_RUN 60          // the min time allow battery change quantity when run
+#define BATTERY_DOWN_MIN_CHANGE_SLEEP 3600      // the min time allow battery change quantity when run 1h
+
 #define Enable_BATDRV_LOG       0
 
 #if Enable_BATDRV_LOG
@@ -50,12 +59,19 @@ struct cw_battery {
         struct i2c_client *client;
         struct workqueue_struct *battery_workqueue;
         struct delayed_work battery_delay_work;
+        struct delayed_work dc_wakeup_work;
         const struct cw_bat_platform_data *plat_data;
 
         struct power_supply rk_bat;
         struct power_supply rk_ac;
         struct power_supply rk_usb;
 
+        long sleep_time_capacity;
+        long run_time_capacity;
+
+        long sleep_time_ac_online;
+        long run_time_ac_online;
+
         int dc_online;
         int usb_online;
         int capacity;
@@ -85,6 +101,7 @@ static int cw_update_config_info(struct cw_battery *cw_bat)
         int ret;
         u8 reg_val;
         int i;
+        u8 reset_val;
 
         printk("func: %s-------\n", __func__);
         /* make sure no in sleep mode */
@@ -92,6 +109,7 @@ static int cw_update_config_info(struct cw_battery *cw_bat)
         if (ret < 0)
                 return ret;
 
+        reset_val = reg_val;
         if((reg_val & MODE_SLEEP_MASK) == MODE_SLEEP) {
                 printk("Error, device in sleep mode, cannot update battery info\n");
                 return -1;
@@ -116,19 +134,17 @@ static int cw_update_config_info(struct cw_battery *cw_bat)
         }
 
         
-        /* set alert info */
+        /* set cw2015 to use new battery info */
         ret = cw_read(cw_bat->client, REG_CONFIG, &reg_val);
         if (ret < 0)
                 return ret;
 
-        if ((reg_val & 0xf8) != ATHD) {
-                printk("the new ATHD have not set\n");
-                reg_val &= 0x07;    // clear ATHD
-                reg_val |= ATHD;    // set ATHD
-                ret = cw_write(cw_bat->client, REG_CONFIG, &reg_val);
-                if (ret < 0)
-                        return ret;
-        }
+        reg_val |= CONFIG_UPDATE_FLG;/* set UPDATE_FLAG */
+        reg_val &= 0x07;    // clear ATHD
+        reg_val |= ATHD;    // set ATHD
+        ret = cw_write(cw_bat->client, REG_CONFIG, &reg_val);
+        if (ret < 0)
+                return ret;
 
         /* check 2015 for ATHD & update_flag */ 
         ret = cw_read(cw_bat->client, REG_CONFIG, &reg_val);
@@ -144,17 +160,14 @@ static int cw_update_config_info(struct cw_battery *cw_bat)
         }
 
         /* reset */
-        ret = cw_read(cw_bat->client, REG_MODE, &reg_val);
-        if (ret < 0)
-                return ret;
-
-        reg_val |= MODE_RESTART;
+        reset_val &= ~(MODE_RESTART);
+        reg_val = reset_val | MODE_RESTART;
         ret = cw_write(cw_bat->client, REG_MODE, &reg_val);
         if (ret < 0)
                 return ret;
 
         msleep(10);
-        ret = cw_write(cw_bat->client, REG_MODE, &reg_val);
+        ret = cw_write(cw_bat->client, REG_MODE, &reset_val);
         if (ret < 0)
                 return ret;
         
@@ -234,19 +247,99 @@ static int cw_init(struct cw_battery *cw_bat)
         return 0;
 }
 
+static void cw_update_time_member_ac_online(struct cw_battery *cw_bat)
+{
+        struct timespec ts;
+        int new_run_time;
+        int new_sleep_time;
 
-static int cw_get_capacity(struct cw_battery *cw_bat)
+        ktime_get_ts(&ts);
+        new_run_time = ts.tv_sec;
+
+        get_monotonic_boottime(&ts);
+        new_sleep_time = ts.tv_sec - new_run_time;
+
+        cw_bat->run_time_ac_online = new_run_time;
+        cw_bat->sleep_time_ac_online = new_sleep_time; 
+}
+
+static void cw_update_time_member_capacity(struct cw_battery *cw_bat)
 {
+        struct timespec ts;
+        int new_run_time;
+        int new_sleep_time;
 
+        ktime_get_ts(&ts);
+        new_run_time = ts.tv_sec;
+
+        get_monotonic_boottime(&ts);
+        new_sleep_time = ts.tv_sec - new_run_time;
+
+        cw_bat->run_time_capacity = new_run_time;
+        cw_bat->sleep_time_capacity = new_sleep_time; 
+}
+
+static int cw_get_capacity(struct cw_battery *cw_bat)
+{
+        int cw_capacity;
         int ret;
         u8 reg_val;
+
+        struct timespec ts;
+        long new_run_time;
+        long new_sleep_time;
+        long capacity_or_aconline_time;
+        int allow_change;
+        int allow_capacity;
+
+
         ret = cw_read(cw_bat->client, REG_SOC, &reg_val);
         if (ret < 0)
                 return ret;
 
-        return reg_val;
-        return 0;
+        cw_capacity = reg_val;
+        if ((cw_capacity < 0) || (cw_capacity > 100)) {
+                printk("get cw_capacity error; cw_capacity = %d\n", cw_capacity);
+                return cw_capacity;
+        } 
+
+        if (cw_capacity == 0) 
+                printk("the cw201x capacity is 0 !!!!!!!, funciton: %s, line: %d\n", __func__, __LINE__);
+        else 
+                xprintk("the cw201x capacity is %d, funciton: %s\n", cw_capacity, __func__);
+
+        ret = cw_read(cw_bat->client, REG_SOC + 1, &reg_val);
+
+        ktime_get_ts(&ts);
+        new_run_time = ts.tv_sec;
 
+        get_monotonic_boottime(&ts);
+        new_sleep_time = ts.tv_sec - new_run_time;
+
+        if ((cw_bat->dc_online == 1) && (cw_capacity >= 95) && (cw_capacity <= cw_bat->capacity) && (cw_bat->capacity != 100)) {     // avoid no charge full
+
+                capacity_or_aconline_time = (cw_bat->sleep_time_capacity > cw_bat->sleep_time_ac_online) ? cw_bat->sleep_time_capacity : cw_bat->sleep_time_ac_online;
+                capacity_or_aconline_time += (cw_bat->run_time_capacity > cw_bat->run_time_ac_online) ? cw_bat->run_time_capacity : cw_bat->run_time_ac_online;
+                allow_change = (new_sleep_time + new_run_time - capacity_or_aconline_time) / BATTERY_UP_MAX_CHANGE;
+                if (allow_change > 0) {
+                        allow_capacity = cw_bat->capacity + allow_change; 
+                        cw_capacity = (allow_capacity <= 100) ? allow_capacity : 100;
+                }
+
+        } else if (((cw_bat->dc_online == 1) && (cw_capacity == (cw_bat->capacity - 1)))
+                        || ((cw_bat->dc_online == 0) && (cw_capacity == (cw_bat->capacity + 1)))) {             // modify battery level swing
+
+                cw_capacity = cw_bat->capacity;
+
+        } else if ((cw_capacity == 0) && (cw_bat->capacity > 1)) {              // avoid battery level jump to 0% at a moment from more than 2%
+                allow_change = ((new_run_time - cw_bat->run_time_capacity) / BATTERY_DOWN_MIN_CHANGE_RUN);
+                allow_change += ((new_sleep_time - cw_bat->sleep_time_capacity) / BATTERY_DOWN_MIN_CHANGE_SLEEP);
+
+                allow_capacity = cw_bat->capacity - allow_change;
+                cw_capacity = (allow_capacity >= cw_capacity) ? allow_capacity: cw_capacity;
+        } 
+        return cw_capacity;
 }
 
 static int cw_get_vol(struct cw_battery *cw_bat)
@@ -270,7 +363,6 @@ static int cw_get_vol(struct cw_battery *cw_bat)
         voltage = value16 * 312 / 1024;
 
         return voltage;
-
 }
 
 static int cw_get_time_to_empty(struct cw_battery *cw_bat)
@@ -295,14 +387,22 @@ static int cw_get_time_to_empty(struct cw_battery *cw_bat)
 
 static void rk_bat_update_capacity(struct cw_battery *cw_bat)
 {
-        int ret;
-        ret = cw_get_capacity(cw_bat);
-        if ((ret >= 0) && (ret <= 100) && (cw_bat->capacity != ret)) {
-                cw_bat->capacity = ret;
+        int cw_capacity;
+
+        cw_capacity = cw_get_capacity(cw_bat);
+        if ((cw_capacity >= 0) && (cw_capacity <= 100) && (cw_bat->capacity != cw_capacity)) {
+                cw_bat->capacity = cw_capacity;
                 cw_bat->bat_change = 1;
+                cw_update_time_member_capacity(cw_bat);
+
+                if (cw_bat->capacity == 0)
+                        printk("report battery capacity 0 and will shutdown if no changing");
+
         }
 }
 
+
+
 static void rk_bat_update_vol(struct cw_battery *cw_bat)
 {
         int ret;
@@ -316,6 +416,8 @@ static void rk_bat_update_vol(struct cw_battery *cw_bat)
 static void rk_bat_update_status(struct cw_battery *cw_bat)
 {
         int status;
+
+
         if ((cw_bat->dc_online == 1) || (cw_bat->usb_online == 1)) {
                 if (cw_bat->capacity >= 100) 
                         status=POWER_SUPPLY_STATUS_FULL;
@@ -328,6 +430,7 @@ static void rk_bat_update_status(struct cw_battery *cw_bat)
         if (cw_bat->status != status) {
                 cw_bat->status = status;
                 cw_bat->bat_change = 1;
+       
         } 
 }
 
@@ -346,11 +449,13 @@ static int rk_ac_update_online(struct cw_battery *cw_bat)
 {
         if (gpio_get_value(cw_bat->plat_data->dc_det_pin) == cw_bat->plat_data->dc_det_level) {
                 if (cw_bat->dc_online != 1) {
+                        cw_update_time_member_ac_online(cw_bat);
                         cw_bat->dc_online = 1;
                         return 1;
                 }
         } else {
                 if (cw_bat->dc_online != 0) {
+                        cw_update_time_member_ac_online(cw_bat);
                         cw_bat->dc_online = 0;
                         return 1;
                 }
@@ -374,19 +479,8 @@ static void cw_bat_work(struct work_struct *work)
         delay_work = container_of(work, struct delayed_work, work);
         cw_bat = container_of(delay_work, struct cw_battery, battery_delay_work);
 
-        rk_bat_update_capacity(cw_bat);
-        rk_bat_update_vol(cw_bat);
-        rk_bat_update_status(cw_bat);
-        rk_bat_update_time_to_empty(cw_bat);
         
 
-        xprintk("cw_bat->bat_change = %d, cw_bat->time_to_empty = %d, cw_bat->capacity = %d, cw_bat->voltage = %d\n", cw_bat->bat_change, cw_bat->time_to_empty, cw_bat->capacity, cw_bat->voltage);
-        if (cw_bat->bat_change) {
-                power_supply_changed(&cw_bat->rk_bat);
-                cw_bat->bat_change = 0;
-        }
-
-
         ret = rk_ac_update_online(cw_bat);
         if (ret == 1) {
                 power_supply_changed(&cw_bat->rk_ac);
@@ -397,7 +491,20 @@ static void cw_bat_work(struct work_struct *work)
                 power_supply_changed(&cw_bat->rk_usb);
         }
 
+        rk_bat_update_status(cw_bat);
+        rk_bat_update_capacity(cw_bat);
+        rk_bat_update_vol(cw_bat);
+        rk_bat_update_time_to_empty(cw_bat);
+
+        if (cw_bat->bat_change) {
+                power_supply_changed(&cw_bat->rk_bat);
+                cw_bat->bat_change = 0;
+        }
+
         queue_delayed_work(cw_bat->battery_workqueue, &cw_bat->battery_delay_work, msecs_to_jiffies(1000));
+
+        xprintk("cw_bat->bat_change = %d, cw_bat->time_to_empty = %d, cw_bat->capacity = %d, cw_bat->voltage = %d, cw_bat->dc_online = %d, cw_bat->usb_online = %d\n",\
+                        cw_bat->bat_change, cw_bat->time_to_empty, cw_bat->capacity, cw_bat->voltage, cw_bat->dc_online, cw_bat->usb_online);
 }
 
 static int rk_usb_get_property (struct power_supply *psy,
@@ -454,7 +561,6 @@ static int rk_battery_get_property(struct power_supply *psy,
 
         cw_bat = container_of(psy, struct cw_battery, rk_bat); 
         switch (psp) {
-
         case POWER_SUPPLY_PROP_CAPACITY:
                 val->intval = cw_bat->capacity;
                 break;
@@ -489,7 +595,12 @@ static int rk_battery_get_property(struct power_supply *psy,
 
 static enum power_supply_property rk_battery_properties[] = {
         POWER_SUPPLY_PROP_CAPACITY,
-
+        POWER_SUPPLY_PROP_STATUS,
+        POWER_SUPPLY_PROP_HEALTH,
+        POWER_SUPPLY_PROP_PRESENT,
+        POWER_SUPPLY_PROP_VOLTAGE_NOW,
+        POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+        POWER_SUPPLY_PROP_TECHNOLOGY,
 };
 
 static int cw_bat_gpio_init(struct cw_battery *cw_bat)
@@ -544,20 +655,59 @@ static int cw_bat_gpio_init(struct cw_battery *cw_bat)
 
         
 request_chg_ok_pin_fail:
-        gpio_free(cw_bat->plat_data->bat_low_pin);
+        if (cw_bat->plat_data->bat_low_pin != INVALID_GPIO)
+                gpio_free(cw_bat->plat_data->bat_low_pin);
+
 request_bat_low_pin_fail:
-        gpio_free(cw_bat->plat_data->dc_det_pin);
+        if (cw_bat->plat_data->dc_det_pin != INVALID_GPIO) 
+                gpio_free(cw_bat->plat_data->dc_det_pin);
+
 request_dc_det_pin_fail:
         return ret;
 
 }
 
+
+static void dc_detect_do_wakeup(struct work_struct *work)
+{
+        int ret;
+        int irq;
+        unsigned int type;
+
+        struct delayed_work *delay_work;
+        struct cw_battery *cw_bat;
+
+        delay_work = container_of(work, struct delayed_work, work);
+        cw_bat = container_of(delay_work, struct cw_battery, dc_wakeup_work);
+
+        rk28_send_wakeup_key();
+
+        irq = gpio_to_irq(cw_bat->plat_data->dc_det_pin);
+        type = gpio_get_value(cw_bat->plat_data->dc_det_pin) ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
+        ret = irq_set_irq_type(irq, type);
+        if (ret < 0) {
+                pr_err("%s: irq_set_irq_type(%d, %d) failed\n", __func__, irq, type);
+        }
+        enable_irq(irq);
+}
+
+static irqreturn_t dc_detect_irq_handler(int irq, void *dev_id)
+{
+        struct cw_battery *cw_bat = dev_id;
+        disable_irq_nosync(irq); // for irq debounce
+        //wake_lock_timeout(&usb_wakelock, WAKE_LOCK_TIMEOUT);
+        //schedule_delayed_work(&wakeup_work, HZ / 10);
+        queue_delayed_work(cw_bat->battery_workqueue, &cw_bat->dc_wakeup_work, msecs_to_jiffies(20));
+        return IRQ_HANDLED;
+}
+
+
 static int cw_bat_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
         struct cw_battery *cw_bat;
         int ret;
-
-        xprintk();
+        int irq;
+        int irq_flags;
 
         cw_bat = devm_kzalloc(&client->dev, sizeof(*cw_bat), GFP_KERNEL);
         if (!cw_bat) {
@@ -568,8 +718,10 @@ static int cw_bat_probe(struct i2c_client *client, const struct i2c_device_id *i
         i2c_set_clientdata(client, cw_bat);
         cw_bat->plat_data = client->dev.platform_data;
         ret = cw_bat_gpio_init(cw_bat);
-        //if (ret) 
-        //        return ret;
+        if (ret) {
+                printk("cw_bat_gpio_init error\n");
+                return ret;
+        }
         
         cw_bat->client = client;
         ret = cw_init(cw_bat);
@@ -611,16 +763,29 @@ static int cw_bat_probe(struct i2c_client *client, const struct i2c_device_id *i
 
         cw_bat->dc_online = 0;
         cw_bat->usb_online = 0;
-        cw_bat->capacity = 50;
+        cw_bat->capacity = 2;
         cw_bat->voltage = 0;
         cw_bat->status = 0;
         cw_bat->time_to_empty = 0;
         cw_bat->bat_change = 0;
 
+        cw_update_time_member_capacity(cw_bat);
+        cw_update_time_member_ac_online(cw_bat);
+
         cw_bat->battery_workqueue = create_singlethread_workqueue("rk_battery");
         INIT_DELAYED_WORK(&cw_bat->battery_delay_work, cw_bat_work);
-        queue_delayed_work(cw_bat->battery_workqueue, &cw_bat->battery_delay_work, msecs_to_jiffies(1000));
+        INIT_DELAYED_WORK(&cw_bat->dc_wakeup_work, dc_detect_do_wakeup);
+        queue_delayed_work(cw_bat->battery_workqueue, &cw_bat->battery_delay_work, msecs_to_jiffies(10));
+        
+        irq = gpio_to_irq(cw_bat->plat_data->dc_det_pin);
+        irq_flags = gpio_get_value(cw_bat->plat_data->dc_det_pin) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
+        ret = request_irq(irq, dc_detect_irq_handler, irq_flags, "usb_detect", cw_bat);
+        if (ret < 0) {
+                pr_err("%s: request_irq(%d) failed\n", __func__, irq);
+        }
+        enable_irq_wake(irq);
 
+        printk("cw2015 driver v1.0 probe sucess\n");
         return 0;
 
 rk_usb_register_fail:
@@ -628,6 +793,7 @@ rk_usb_register_fail:
 rk_ac_register_fail:
         power_supply_unregister(&cw_bat->rk_ac);
 rk_bat_register_fail:
+        printk("cw2015 driver v1.0 probe error!!!!\n");
         return ret;
 }
 
@@ -639,16 +805,19 @@ static int cw_bat_remove(struct i2c_client *client)
         return 0;
 }
 
-static int cw_bat_suspend(struct i2c_client *client, pm_message_t mesg)
+#ifdef CONFIG_PM
+static int cw_bat_suspend(struct device *dev)
 {
+        struct i2c_client *client = to_i2c_client(dev);
         struct cw_battery *cw_bat = i2c_get_clientdata(client);
         xprintk();
         cancel_delayed_work(&cw_bat->battery_delay_work);
         return 0;
 }
 
-static int cw_bat_resume(struct i2c_client *client)
+static int cw_bat_resume(struct device *dev)
 {
+        struct i2c_client *client = to_i2c_client(dev);
         struct cw_battery *cw_bat = i2c_get_clientdata(client);
         xprintk();
         queue_delayed_work(cw_bat->battery_workqueue, &cw_bat->battery_delay_work, msecs_to_jiffies(100));
@@ -660,16 +829,22 @@ static const struct i2c_device_id cw_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, cw_id);
 
+static const struct dev_pm_ops cw_bat_pm_ops = {
+        .suspend  = cw_bat_suspend,
+        .resume   = cw_bat_resume,
+};
+#endif
 
 static struct i2c_driver cw_bat_driver = {
         .driver         = {
                 .name   = "cw201x",
+#ifdef CONFIG_PM
+                .pm     = &cw_bat_pm_ops,
+#endif
         },
         
         .probe          = cw_bat_probe,
         .remove         = cw_bat_remove,
-        .suspend        = cw_bat_suspend,
-        .resume         = cw_bat_resume, 
        .id_table       = cw_id,
 };
 
@@ -685,7 +860,7 @@ static void __exit cw_bat_exit(void)
         i2c_del_driver(&cw_bat_driver);
 }
 
-module_init(cw_bat_init);
+fs_initcall(cw_bat_init);
 module_exit(cw_bat_exit);
 
 MODULE_AUTHOR("xhc<xhc@rock-chips.com>");