From 3a7610aed1441d8a7ba885ff6df1a38013d1bafb Mon Sep 17 00:00:00 2001 From: xuhuicong Date: Thu, 21 Mar 2013 22:46:22 +0800 Subject: [PATCH] update cw2015 battery driver --- drivers/power/cw2015_battery.c | 273 +++++++++++++++++++++++++++------ 1 file changed, 224 insertions(+), 49 deletions(-) mode change 100644 => 100755 drivers/power/cw2015_battery.c diff --git a/drivers/power/cw2015_battery.c b/drivers/power/cw2015_battery.c old mode 100644 new mode 100755 index da3dcafc0fa6..55388b68c90a --- a/drivers/power/cw2015_battery.c +++ b/drivers/power/cw2015_battery.c @@ -18,6 +18,11 @@ #include #include #include +#include + +#include +#include +#include #define REG_VERSION 0x0 #define REG_VCELL 0x2 @@ -38,6 +43,10 @@ #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, ®_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, ®_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, ®_val); + if (ret < 0) + return ret; /* check 2015 for ATHD & update_flag */ ret = cw_read(cw_bat->client, REG_CONFIG, ®_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, ®_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, ®_val); if (ret < 0) return ret; msleep(10); - ret = cw_write(cw_bat->client, REG_MODE, ®_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, ®_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, ®_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"); -- 2.34.1