From e535c3aa343763bca641bb663e4c632f30f65737 Mon Sep 17 00:00:00 2001 From: xuhuicong Date: Sun, 9 Jun 2013 15:49:34 +0800 Subject: [PATCH] battery: add usb chage support to cw2015 battery driver and some other improve --- arch/arm/mach-rk30/board-rk3168-tb.c | 9 +- drivers/power/Kconfig | 12 +- drivers/power/cw2015_battery.c | 276 +++++++++++++++++++++------ include/linux/power/cw2015_battery.h | 7 + 4 files changed, 240 insertions(+), 64 deletions(-) diff --git a/arch/arm/mach-rk30/board-rk3168-tb.c b/arch/arm/mach-rk30/board-rk3168-tb.c index f5b3b48d29b4..11d3ff4a3ad0 100755 --- a/arch/arm/mach-rk30/board-rk3168-tb.c +++ b/arch/arm/mach-rk30/board-rk3168-tb.c @@ -1172,12 +1172,17 @@ static u8 config_info[SIZE_BATINFO] = { static struct cw_bat_platform_data cw_bat_platdata = { .dc_det_pin = RK30_PIN0_PB2, - .bat_low_pin = RK30_PIN0_PB1, - .chg_ok_pin = RK30_PIN0_PA6, .dc_det_level = GPIO_LOW, + + .bat_low_pin = RK30_PIN0_PB1, .bat_low_level = GPIO_LOW, + .chg_ok_pin = INVALID_GPIO, .chg_ok_level = GPIO_HIGH, + .is_usb_charge = 0, + .chg_mode_sel_pin = INVALID_GPIO, + .chg_mode_sel_level = GPIO_HIGH, + .cw_bat_config_info = config_info, }; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 4935297f3bfc..edda96ac0dc7 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -336,13 +336,6 @@ config BATTERY_RK30_ADC_FAC help Say Y to enable support for the battery on the RK30. -config CW2015_BATTERY - tristate "CW2015 battery driver" - help - Say Y to enable support for the cw2015 on the Rockchip - - - config BATTERY_RK30_AC_CHARGE tristate "RK30 AC CHARGE" depends on BATTERY_RK30_ADC||BATTERY_RK30_ADC_FAC @@ -360,6 +353,11 @@ config BATTERY_RK30_VOL3V8 depends on BATTERY_RK30_ADC||BATTERY_RK30_ADC_FAC help say Y to enable suspport for the battery voltage 3.8V + +config CW2015_BATTERY + tristate "CW2015 battery driver" + help + Say Y to enable support for the cw2015 on the Rockchip config POWER_ON_CHARGER_DISPLAY bool "Support charger display" diff --git a/drivers/power/cw2015_battery.c b/drivers/power/cw2015_battery.c index f09f215a30db..869dff3356c4 100755 --- a/drivers/power/cw2015_battery.c +++ b/drivers/power/cw2015_battery.c @@ -19,7 +19,6 @@ #include #include #include - #include #include #include @@ -39,24 +38,33 @@ #define MODE_RESTART (0xf<<0) #define CONFIG_UPDATE_FLG (0x1<<1) -#define ATHD (0xa<<3) //ATHD =10% +#define ATHD (0x0<<3) //ATHD =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_UP_MAX_CHANGE 420 // the max time allow battery change quantity +#define BATTERY_DOWN_CHANGE 60 // the max time allow battery change quantity #define BATTERY_DOWN_MIN_CHANGE_RUN 30 // the min time allow battery change quantity when run #define BATTERY_DOWN_MIN_CHANGE_SLEEP 1800 // the min time allow battery change quantity when run 30min -#define BATTERY_DOWN_MAX_CHANGE_RUN_AC_ONLINE 1800 +#define BATTERY_DOWN_MAX_CHANGE_RUN_AC_ONLINE 3600 #define Enable_BATDRV_LOG 0 +#define USB_CHARGER_MODE 1 +#define AC_CHARGER_MODE 2 + + #if Enable_BATDRV_LOG #define xprintk(format, arg...) printk("func: %s\n" format, __FUNCTION__, ##arg) #else #define xprintk(format, ...) #endif +extern int dwc_otg_check_dpdm(void); +extern int get_gadget_connect_flag(void); +extern int dwc_vbus_status( void ); + struct cw_battery { struct i2c_client *client; struct workqueue_struct *battery_workqueue; @@ -76,10 +84,13 @@ struct cw_battery { int dc_online; int usb_online; + int charger_mode; + int charger_init_mode; int capacity; int voltage; int status; int time_to_empty; + int alt; int bat_change; }; @@ -97,6 +108,23 @@ static int cw_write(struct i2c_client *client, u8 reg, u8 const buf[]) ret = i2c_master_reg8_send(client, reg, buf, 1, CW_I2C_SPEED); return ret; } +static int cw_read_word(struct i2c_client *client, u8 reg, u8 buf[]) +{ + int ret; + ret = i2c_master_reg8_recv(client, reg, buf, 2, CW_I2C_SPEED); + return ret; +} + +#if 0 +static int cw_write_word(struct i2c_client *client, u8 reg, u8 const buf[]) +{ + int ret; + ret = i2c_master_reg8_send(client, reg, buf, 2, CW_I2C_SPEED); + return ret; +} +#endif + + static int cw_update_config_info(struct cw_battery *cw_bat) { @@ -284,7 +312,7 @@ static void cw_update_time_member_capacity(struct cw_battery *cw_bat) static int cw_quickstart(struct cw_battery *cw_bat) { int ret = 0; - u8 reg_val = MODE_QUICK_START | MODE_NORMAL; + u8 reg_val = MODE_QUICK_START; ret = cw_write(cw_bat->client, REG_MODE, ®_val); //(MODE_QUICK_START | MODE_NORMAL)); // 0x30 if(ret < 0) { @@ -305,7 +333,7 @@ static int cw_get_capacity(struct cw_battery *cw_bat) { int cw_capacity; int ret; - u8 reg_val; + u8 reg_val[2]; struct timespec ts; long new_run_time; @@ -314,13 +342,15 @@ static int cw_get_capacity(struct cw_battery *cw_bat) int allow_change; int allow_capacity; static int if_quickstart = 0; + static int jump_flag =0; - ret = cw_read(cw_bat->client, REG_SOC, ®_val); + // ret = cw_read(cw_bat->client, REG_SOC, ®_val); + ret = cw_read_word(cw_bat->client, REG_SOC, reg_val); if (ret < 0) return ret; - cw_capacity = reg_val; + cw_capacity = reg_val[0]; if ((cw_capacity < 0) || (cw_capacity > 100)) { printk("get cw_capacity error; cw_capacity = %d\n", cw_capacity); return cw_capacity; @@ -331,7 +361,7 @@ static int cw_get_capacity(struct cw_battery *cw_bat) else xprintk("the cw201x capacity is %d, funciton: %s\n", cw_capacity, __func__); - ret = cw_read(cw_bat->client, REG_SOC + 1, ®_val); + // ret = cw_read(cw_bat->client, REG_SOC + 1, ®_val); ktime_get_ts(&ts); new_run_time = ts.tv_sec; @@ -339,7 +369,7 @@ static int cw_get_capacity(struct cw_battery *cw_bat) 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)) { // avoid no charge full + if ((cw_bat->charger_mode > 0) && (cw_capacity >= 95) && (cw_capacity <= cw_bat->capacity)) { // 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; @@ -347,10 +377,11 @@ static int cw_get_capacity(struct cw_battery *cw_bat) if (allow_change > 0) { allow_capacity = cw_bat->capacity + allow_change; cw_capacity = (allow_capacity <= 100) ? allow_capacity : 100; + jump_flag =1; } - } 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 + } else if (((cw_bat->charger_mode > 0) && (cw_capacity == (cw_bat->capacity - 1))) + || ((cw_bat->charger_mode == 0) && (cw_capacity == (cw_bat->capacity + 1)))) { // modify battery level swing if (!(cw_capacity == 0 && cw_bat->capacity == 1)) { cw_capacity = cw_bat->capacity; @@ -363,24 +394,43 @@ static int cw_get_capacity(struct cw_battery *cw_bat) allow_capacity = cw_bat->capacity - allow_change; cw_capacity = (allow_capacity >= cw_capacity) ? allow_capacity: cw_capacity; - } + reg_val[0] = MODE_NORMAL; + ret = cw_write(cw_bat->client, REG_MODE, reg_val); + if (ret < 0) + return ret; + + } else if ((cw_bat->charger_mode == 0) && (cw_capacity <= cw_bat->capacity ) && (cw_capacity >= 90) && (jump_flag == 1)) { // avoid battery level jump to CW_BAT + 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_DOWN_CHANGE; + if (allow_change > 0) { + allow_capacity = cw_bat->capacity - allow_change; + if (cw_capacity >= allow_capacity){ + jump_flag =0; + } + else{ + cw_capacity = (allow_capacity <= 100) ? allow_capacity : 100; + } + } + } #if 1 - if((cw_bat->dc_online == 1) &&(cw_capacity == 0)) + if((cw_bat->charger_mode > 0) &&(cw_capacity == 0)) { if (((new_sleep_time + new_run_time - cw_bat->sleep_time_ac_online - cw_bat->run_time_ac_online) > BATTERY_DOWN_MAX_CHANGE_RUN_AC_ONLINE) && (if_quickstart == 0)) { cw_quickstart(cw_bat); // if the cw_capacity = 0 the cw2015 will qstrt if_quickstart = 1; - } else if (if_quickstart == 1) { - if_quickstart = 0; } - } else if (if_quickstart == 1) { - if_quickstart = 0; + } else if ((if_quickstart == 1)&&(cw_bat->charger_mode == 0)) { + if_quickstart = 0; } + #endif - if((cw_capacity == 100) && (gpio_get_value(cw_bat->plat_data->chg_ok_pin) != cw_bat->plat_data->chg_ok_level)) - cw_capacity = 99; + if (cw_bat->plat_data->chg_ok_pin != INVALID_GPIO) { + if((cw_capacity == 100) && (gpio_get_value(cw_bat->plat_data->chg_ok_pin) != cw_bat->plat_data->chg_ok_level)) + cw_capacity = 99; + } return cw_capacity; } @@ -388,45 +438,24 @@ static int cw_get_capacity(struct cw_battery *cw_bat) static int cw_get_vol(struct cw_battery *cw_bat) { int ret; - u8 reg_val; + u8 reg_val[2]; u16 value16, value16_1, value16_2, value16_3; int voltage; - ret = cw_read(cw_bat->client, REG_VCELL, ®_val); + ret = cw_read_word(cw_bat->client, REG_VCELL, reg_val); if (ret < 0) return ret; - - value16 = reg_val; - - ret = cw_read(cw_bat->client, REG_VCELL + 1, ®_val); - if (ret < 0) - return ret; - - value16 = (value16 << 8) + reg_val; - - ret = cw_read(cw_bat->client, REG_VCELL, ®_val); - if (ret < 0) - return ret; - - value16_1 = reg_val; - - ret = cw_read(cw_bat->client, REG_VCELL + 1, ®_val); - if (ret < 0) - return ret; - - value16_1 = (value16_1 << 8) + reg_val; + value16 = (reg_val[0] << 8) + reg_val[1]; - ret = cw_read(cw_bat->client, REG_VCELL, ®_val); + ret = cw_read_word(cw_bat->client, REG_VCELL, reg_val); if (ret < 0) return ret; + value16_1 = (reg_val[0] << 8) + reg_val[1]; - value16_2 = reg_val; - - ret = cw_read(cw_bat->client, REG_VCELL + 1, ®_val); + ret = cw_read_word(cw_bat->client, REG_VCELL, reg_val); if (ret < 0) return ret; - - value16_2 = (value16_2 << 8) + reg_val; + value16_2 = (reg_val[0] << 8) + reg_val[1]; if(value16 > value16_1) @@ -455,6 +484,34 @@ static int cw_get_vol(struct cw_battery *cw_bat) return voltage; } +#if 0 +static int cw_get_alt(struct cw_battery *cw_bat) +{ + int ret = 0; + u8 reg_val; + u8 value8 = 0; + int alrt; + + ret = cw_read(cw_bat->client, REG_RRT_ALERT, ®_val); + if (ret < 0) + return ret; + value8 = reg_val; + alrt = value8 >>7; + + //dev_info(&client->dev, "read RRT %d%%. value16 0x%x\n", alrt, value16); + value8 = value8&0x7f; + reg_val = value8; + ret = cw_write(cw_bat->client, REG_RRT_ALERT, ®_val); + if(ret < 0) { + printk("Error clear ALRT\n"); + return ret; + } + + return alrt; +} +#endif + + static int cw_get_time_to_empty(struct cw_battery *cw_bat) { int ret; @@ -537,16 +594,24 @@ static void rk_bat_update_time_to_empty(struct cw_battery *cw_bat) static int rk_ac_update_online(struct cw_battery *cw_bat) { + if(cw_bat->plat_data->dc_det_pin == INVALID_GPIO) + return 0; 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; + if (cw_bat->charger_mode != AC_CHARGER_MODE) + cw_bat->charger_mode = AC_CHARGER_MODE; + return 1; } } else { if (cw_bat->dc_online != 0) { cw_update_time_member_ac_online(cw_bat); cw_bat->dc_online = 0; + if ((cw_bat->usb_online == 0)&&(cw_bat->charger_mode != 0)) + cw_bat->charger_mode = 0; + return 1; } } @@ -556,7 +621,67 @@ static int rk_ac_update_online(struct cw_battery *cw_bat) static int rk_usb_update_online(struct cw_battery *cw_bat) { - cw_bat->usb_online = 0; + int usb_status = dwc_vbus_status(); + int gadget_status =get_gadget_connect_flag(); + + xprintk("%s usb_status=[%d],cw_bat->charger_mode=[%d],cw_bat->gadget_status=[%d], cw_bat->charger_init_mode = [%d]\n",__func__,usb_status,cw_bat->charger_mode,gadget_status, cw_bat->charger_init_mode); + + + if (cw_bat->plat_data->is_usb_charge == 0) { + cw_bat->usb_online = 0; + return 0; + + } + + if (((usb_status == 2) || (cw_bat->charger_init_mode==2)) && (cw_bat->charger_mode != AC_CHARGER_MODE)){ + + if (cw_bat->charger_mode != AC_CHARGER_MODE) + cw_bat->charger_mode = AC_CHARGER_MODE; + if (cw_bat->plat_data->chg_mode_sel_pin != INVALID_GPIO) + gpio_direction_output(cw_bat->plat_data->chg_mode_sel_pin, (cw_bat->plat_data->chg_mode_sel_level==GPIO_HIGH) ? GPIO_HIGH : GPIO_LOW); + if (cw_bat->usb_online != 1){ + cw_bat->usb_online = 1; + cw_update_time_member_ac_online(cw_bat); + return 1; + } + } else if ((usb_status == 1) || ((cw_bat->charger_init_mode==1))) { +#if 1 + if (0 == gadget_status){ + if (cw_bat->charger_mode != AC_CHARGER_MODE) + cw_bat->charger_mode = AC_CHARGER_MODE; + if (cw_bat->plat_data->chg_mode_sel_pin != INVALID_GPIO) + gpio_direction_output(cw_bat->plat_data->chg_mode_sel_pin, (cw_bat->plat_data->chg_mode_sel_level==GPIO_HIGH) ? GPIO_HIGH : GPIO_LOW); + if (cw_bat->usb_online != 1){ + cw_bat->usb_online = 1; + cw_update_time_member_ac_online(cw_bat); + return 1; + } + } + else{ +#endif + if (cw_bat->charger_mode != USB_CHARGER_MODE) + cw_bat->charger_mode = USB_CHARGER_MODE; + if (cw_bat->plat_data->chg_mode_sel_pin != INVALID_GPIO) + gpio_direction_output(cw_bat->plat_data->chg_mode_sel_pin, (cw_bat->plat_data->chg_mode_sel_level==GPIO_HIGH) ? GPIO_LOW : GPIO_HIGH); + if (cw_bat->usb_online != 1){ + cw_bat->usb_online = 1; + cw_update_time_member_ac_online(cw_bat); + return 1; + } + } + } else {//if (usb_status == 0){ + if (cw_bat->plat_data->chg_mode_sel_pin != INVALID_GPIO) + gpio_direction_output(cw_bat->plat_data->chg_mode_sel_pin, (cw_bat->plat_data->chg_mode_sel_level==GPIO_HIGH) ? GPIO_LOW : GPIO_HIGH); + if ((cw_bat->dc_online == 0)&&(cw_bat->charger_mode != 0)) + cw_bat->charger_mode = 0; + if (cw_bat->usb_online != 0) { + cw_update_time_member_ac_online(cw_bat); + cw_bat->usb_online = 0; + return 1; + } + } + + return 0; } @@ -566,21 +691,32 @@ static void cw_bat_work(struct work_struct *work) struct cw_battery *cw_bat; int ret; + struct timespec ts; + int time_from_boot; + delay_work = container_of(work, struct delayed_work, work); cw_bat = container_of(delay_work, struct cw_battery, battery_delay_work); - + get_monotonic_boottime(&ts); + time_from_boot = ts.tv_sec; + if ((time_from_boot > 30) && cw_bat->charger_init_mode) { + cw_bat->charger_init_mode = 0; + } ret = rk_ac_update_online(cw_bat); if (ret == 1) { power_supply_changed(&cw_bat->rk_ac); } - ret = rk_usb_update_online(cw_bat); - if (ret == 1) { - power_supply_changed(&cw_bat->rk_usb); + if (cw_bat->plat_data->is_usb_charge == 1) { + ret = rk_usb_update_online(cw_bat); + if (ret == 1) { + power_supply_changed(&cw_bat->rk_usb); + power_supply_changed(&cw_bat->rk_ac); ///////////////////////////////////////////////////// + } } + rk_bat_update_status(cw_bat); rk_bat_update_capacity(cw_bat); rk_bat_update_vol(cw_bat); @@ -607,7 +743,8 @@ static int rk_usb_get_property (struct power_supply *psy, cw_bat = container_of(psy, struct cw_battery, rk_usb); switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - val->intval = cw_bat->usb_online; + // val->intval = cw_bat->usb_online; + val->intval = (cw_bat->charger_mode == USB_CHARGER_MODE); break; default: break; @@ -630,7 +767,8 @@ static int rk_ac_get_property (struct power_supply *psy, cw_bat = container_of(psy, struct cw_battery, rk_ac); switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - val->intval = cw_bat->dc_online; + // val->intval = cw_bat->dc_online; + val->intval = (cw_bat->charger_mode == AC_CHARGER_MODE); break; default: break; @@ -741,6 +879,21 @@ static int cw_bat_gpio_init(struct cw_battery *cw_bat) goto request_chg_ok_pin_fail; } } + + if ((cw_bat->plat_data->is_usb_charge == 1) && (cw_bat->plat_data->chg_mode_sel_pin!= INVALID_GPIO)) { + ret = gpio_request(cw_bat->plat_data->chg_mode_sel_pin, NULL); + if (ret) { + printk("failed to request chg_mode_sel_pin gpio\n"); + goto request_chg_ok_pin_fail; + } + ret = gpio_direction_output(cw_bat->plat_data->chg_mode_sel_pin, (cw_bat->plat_data->chg_mode_sel_level==GPIO_HIGH) ? GPIO_LOW : GPIO_HIGH); + if (ret) { + printk("failed to set chg_mode_sel_pin input\n"); + gpio_free(cw_bat->plat_data->chg_mode_sel_pin); + goto request_chg_ok_pin_fail; + } + } + return 0; @@ -772,6 +925,10 @@ static void dc_detect_do_wakeup(struct work_struct *work) rk28_send_wakeup_key(); + /* this assume if usb insert or extract dc_det pin is change */ + if(cw_bat->charger_init_mode) + cw_bat->charger_init_mode=0; + 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); @@ -798,6 +955,7 @@ static int cw_bat_probe(struct i2c_client *client, const struct i2c_device_id *i int ret; int irq; int irq_flags; + int loop = 0; cw_bat = devm_kzalloc(&client->dev, sizeof(*cw_bat), GFP_KERNEL); if (!cw_bat) { @@ -814,7 +972,12 @@ static int cw_bat_probe(struct i2c_client *client, const struct i2c_device_id *i } cw_bat->client = client; + ret = cw_init(cw_bat); + while ((loop++ < 200) && (ret != 0)) { + ret = cw_init(cw_bat); + } + if (ret) return ret; @@ -851,8 +1014,11 @@ static int cw_bat_probe(struct i2c_client *client, const struct i2c_device_id *i goto rk_usb_register_fail; } + cw_bat->charger_init_mode = dwc_otg_check_dpdm(); + cw_bat->dc_online = 0; cw_bat->usb_online = 0; + cw_bat->charger_mode = 0; cw_bat->capacity = 2; cw_bat->voltage = 0; cw_bat->status = 0; diff --git a/include/linux/power/cw2015_battery.h b/include/linux/power/cw2015_battery.h index a3acd2882a65..aaa88b3a1a68 100644 --- a/include/linux/power/cw2015_battery.h +++ b/include/linux/power/cw2015_battery.h @@ -18,8 +18,15 @@ #define SIZE_BATINFO 64 struct cw_bat_platform_data { + + int is_dc_charge; int dc_det_pin; int dc_det_level; + + int is_usb_charge; + int chg_mode_sel_pin; + int chg_mode_sel_level; + int bat_low_pin; int bat_low_level; int chg_ok_pin; -- 2.34.1