From: 张晴 Date: Wed, 12 Mar 2014 07:54:36 +0000 (+0800) Subject: rk31:linux3.10:support bq27320 fg and bq24296 charger ic X-Git-Tag: firefly_0821_release~6122 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=19f3753f60f939dd5c46b1ab6ee4c7a41dffb773;p=firefly-linux-kernel-4.4.55.git rk31:linux3.10:support bq27320 fg and bq24296 charger ic --- diff --git a/arch/arm/boot/dts/rk3188-tb.dts b/arch/arm/boot/dts/rk3188-tb.dts old mode 100755 new mode 100644 index e3d379939cff..d66bfc225547 --- a/arch/arm/boot/dts/rk3188-tb.dts +++ b/arch/arm/boot/dts/rk3188-tb.dts @@ -194,6 +194,19 @@ reg = <0x1b>; status = "okay"; }; + bq24296: bq24296@6b { + compatible = "ti,bq24296"; + reg = <0x6b>; +/* gpios = <&gpio0 GPIO_A7 GPIO_ACTIVE_HIGH>; */ + bq24296,chg_current = <1000 500 2000>; + status = "disable"; + }; + bq27320: bq27320@55 { + compatible = "ti,bq27320"; + reg = <0x55>; +/* gpios = <&gpio0 GPIO_A7 GPIO_ACTIVE_HIGH>; */ + status = "disable"; + }; }; &i2c2 { diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 7b8979c63f48..41ab1b16e962 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -171,6 +171,17 @@ config BATTERY_BQ27X00_PLATFORM help Say Y here to enable support for batteries with BQ27000 (HDQ) chips. +config BATTERY_BQ24296 + tristate "BQ24296 chargeIC driver" + help + Say Y here to enable support for batteries with BQ24296 (I2C/HDQ) chips. + +config BATTERY_BQ27320 + tristate "BQ27320 battery driver" + depends on I2C + help + Say Y here to enable support for batteries with BQ27320(I2C) chip. + config BATTERY_DA9030 tristate "DA9030 battery driver" depends on PMIC_DA903X diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 653bf6ceff30..c5801e891bf8 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -28,6 +28,8 @@ obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o +obj-$(CONFIG_BATTERY_BQ24296) += bq24296_charger.o +obj-$(CONFIG_BATTERY_BQ27320) += bq27320_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o diff --git a/drivers/power/bq24296_charger.c b/drivers/power/bq24296_charger.c new file mode 100755 index 000000000000..3f9ab81488b4 --- /dev/null +++ b/drivers/power/bq24296_charger.c @@ -0,0 +1,610 @@ +/* + * BQ24296 battery driver + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct bq24296_device_info *bq24296_di; +struct bq24296_board *bq24296_pdata; +static int bq24296_int = 0; +int bq24296_mode = 0; +int bq24296_chag_down ; +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) do { } while (0) +#endif + +/* + * Common code for BQ24296 devices read + */ +static int bq24296_i2c_reg8_read(const struct i2c_client *client, const char reg, char *buf, int count, int scl_rate) +{ + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msgs[2]; + int ret; + char reg_buf = reg; + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = 1; + msgs[0].buf = ®_buf; + msgs[0].scl_rate = scl_rate; +// msgs[0].udelay = client->udelay; + + msgs[1].addr = client->addr; + msgs[1].flags = client->flags | I2C_M_RD; + msgs[1].len = count; + msgs[1].buf = (char *)buf; + msgs[1].scl_rate = scl_rate; +// msgs[1].udelay = client->udelay; + + ret = i2c_transfer(adap, msgs, 2); + + return (ret == 2)? count : ret; +} +EXPORT_SYMBOL(bq24296_i2c_reg8_read); + +static int bq24296_i2c_reg8_write(const struct i2c_client *client, const char reg, const char *buf, int count, int scl_rate) +{ + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + int ret; + char *tx_buf = (char *)kmalloc(count + 1, GFP_KERNEL); + if(!tx_buf) + return -ENOMEM; + tx_buf[0] = reg; + memcpy(tx_buf+1, buf, count); + + msg.addr = client->addr; + msg.flags = client->flags; + msg.len = count + 1; + msg.buf = (char *)tx_buf; + msg.scl_rate = scl_rate; +// msg.udelay = client->udelay; + + ret = i2c_transfer(adap, &msg, 1); + kfree(tx_buf); + return (ret == 1) ? count : ret; + +} +EXPORT_SYMBOL(bq24296_i2c_reg8_write); + +static int bq24296_read(struct i2c_client *client, u8 reg, u8 buf[], unsigned len) +{ + int ret; + ret = bq24296_i2c_reg8_read(client, reg, buf, len, BQ24296_SPEED); + return ret; +} + +static int bq24296_write(struct i2c_client *client, u8 reg, u8 const buf[], unsigned len) +{ + int ret; + ret = bq24296_i2c_reg8_write(client, reg, buf, (int)len, BQ24296_SPEED); + return ret; +} + +static ssize_t bat_param_read(struct device *dev,struct device_attribute *attr, char *buf) +{ + int i; + u8 buffer; + struct bq24296_device_info *di=bq24296_di; + + for(i=0;i<11;i++) + { + bq24296_read(di->client,i,&buffer,1); + DBG("reg %d value %x\n",i,buffer); + } + return 0; +} +DEVICE_ATTR(battparam, 0664, bat_param_read,NULL); + +static int bq24296_update_reg(struct i2c_client *client, int reg, u8 value, u8 mask ) +{ + int ret =0; + u8 retval = 0; + + ret = bq24296_read(client, reg, &retval, 1); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + return ret; + } + + if ((retval & mask) != value) { + retval = ((retval & ~mask) | value) | value; + ret = bq24296_write(client, reg, &retval, 1); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + return ret; + } + } + + return ret; +} + +static int bq24296_init_registers(void) +{ + int ret = 0; + + /* reset the register */ + ret = bq24296_update_reg(bq24296_di->client, + POWE_ON_CONFIGURATION_REGISTER, + REGISTER_RESET_ENABLE << REGISTER_RESET_OFFSET, + REGISTER_RESET_MASK << REGISTER_RESET_OFFSET); + if (ret < 0) { + dev_err(&bq24296_di->client->dev, "%s(): Failed to reset the register \n", + __func__); + goto final; + } + + mdelay(5); + + /* Disable the watchdog */ + ret = bq24296_update_reg(bq24296_di->client, + TERMINATION_TIMER_CONTROL_REGISTER, + WATCHDOG_DISABLE << WATCHDOG_OFFSET, + WATCHDOG_MASK << WATCHDOG_OFFSET); + if (ret < 0) { + dev_err(&bq24296_di->client->dev, "%s(): Failed to disable the watchdog \n", + __func__); + goto final; + } + + /* Set Pre-Charge Current Limit as 128mA */ + ret = bq24296_update_reg(bq24296_di->client, + PRE_CHARGE_TERMINATION_CURRENT_CONTROL_REGISTER, + PRE_CHARGE_CURRENT_LIMIT_128MA << PRE_CHARGE_CURRENT_LIMIT_OFFSET, + PRE_CHARGE_CURRENT_LIMIT_MASK << PRE_CHARGE_CURRENT_LIMIT_OFFSET); + if (ret < 0) { + dev_err(&bq24296_di->client->dev, "%s(): Failed to set pre-charge limit 128mA \n", + __func__); + goto final; + } + + /* Set Termination Current Limit as 128mA */ + ret = bq24296_update_reg(bq24296_di->client, + PRE_CHARGE_TERMINATION_CURRENT_CONTROL_REGISTER, + TERMINATION_CURRENT_LIMIT_128MA << TERMINATION_CURRENT_LIMIT_OFFSET, + TERMINATION_CURRENT_LIMIT_MASK << TERMINATION_CURRENT_LIMIT_OFFSET); + if (ret < 0) { + dev_err(&bq24296_di->client->dev, "%s(): Failed to set termination limit 128mA \n", + __func__); + goto final; + } + +final: + return ret; +} + +static int bq24296_get_limit_current(int value) +{ + u8 data; + if (value < 120) + data = 0; + else if(value < 400) + data = 1; + else if(value < 700) + data = 2; + else if(value < 1000) + data = 3; + else if(value < 1200) + data = 4; + else if(value < 1800) + data = 6; + else + data = 7; + data &= 0xff; + return data; + +} + +static int bq24296_get_chg_current(int value) +{ + u8 data; + + data = (value)/64; + data &= 0xff; + return data; +} +static int bq24296_update_input_current_limit(u8 value) +{ + int ret = 0; + ret = bq24296_update_reg(bq24296_di->client, + INPUT_SOURCE_CONTROL_REGISTER, + ((value << IINLIM_OFFSET) | (EN_HIZ_DISABLE << EN_HIZ_OFFSET)), + ((IINLIM_MASK << IINLIM_OFFSET) | (EN_HIZ_MASK << EN_HIZ_OFFSET))); + if (ret < 0) { + dev_err(&bq24296_di->client->dev, "%s(): Failed to set input current limit (0x%x) \n", + __func__, value); + } + + return ret; +} + static int bq24296_set_charge_current(u8 value) +{ + int ret = 0; + + ret = bq24296_update_reg(bq24296_di->client, + CHARGE_CURRENT_CONTROL_REGISTER, + (value << CHARGE_CURRENT_OFFSET) ,(CHARGE_CURRENT_MASK <client->dev, "%s(): Failed to set charge current limit (0x%x) \n", + __func__, value); + } + return ret; +} + +static int bq24296_update_en_hiz_disable(void) +{ + int ret = 0; + + ret = bq24296_update_reg(bq24296_di->client, + INPUT_SOURCE_CONTROL_REGISTER, + EN_HIZ_DISABLE << EN_HIZ_OFFSET, + EN_HIZ_MASK << EN_HIZ_OFFSET); + if (ret < 0) { + dev_err(&bq24296_di->client->dev, "%s(): Failed to set en_hiz_disable\n", + __func__); + } + return ret; +} + +int bq24296_set_input_current(int on) +{ + if(!bq24296_int) + return 0; + + if(1 == on){ +#ifdef CONFIG_BATTERY_RK30_USB_AND_CHARGE + bq24296_update_input_current_limit(IINLIM_3000MA); +#else + bq24296_update_input_current_limit(IINLIM_3000MA); +#endif + }else{ + bq24296_update_input_current_limit(IINLIM_500MA); + } + DBG("bq24296_set_input_current %s\n", on ? "3000mA" : "500mA"); + + return 0; +} +EXPORT_SYMBOL_GPL(bq24296_set_input_current); + +static int bq24296_update_charge_mode(u8 value) +{ + int ret = 0; + + ret = bq24296_update_reg(bq24296_di->client, + POWE_ON_CONFIGURATION_REGISTER, + value << CHARGE_MODE_CONFIG_OFFSET, + CHARGE_MODE_CONFIG_MASK << CHARGE_MODE_CONFIG_OFFSET); + if (ret < 0) { + dev_err(&bq24296_di->client->dev, "%s(): Failed to set charge mode(0x%x) \n", + __func__, value); + } + + return ret; +} + +static int bq24296_update_otg_mode_current(u8 value) +{ + int ret = 0; + + ret = bq24296_update_reg(bq24296_di->client, + POWE_ON_CONFIGURATION_REGISTER, + value << OTG_MODE_CURRENT_CONFIG_OFFSET, + OTG_MODE_CURRENT_CONFIG_MASK << OTG_MODE_CURRENT_CONFIG_OFFSET); + if (ret < 0) { + dev_err(&bq24296_di->client->dev, "%s(): Failed to set otg current mode(0x%x) \n", + __func__, value); + } + return ret; +} + +static int bq24296_charge_mode_config(int on) +{ + + if(!bq24296_int) + return 0; + + if(1 == on) + { + bq24296_update_en_hiz_disable(); + mdelay(5); + bq24296_update_charge_mode(CHARGE_MODE_CONFIG_OTG_OUTPUT); + mdelay(10); + bq24296_update_otg_mode_current(OTG_MODE_CURRENT_CONFIG_1300MA); + }else{ + bq24296_update_charge_mode(CHARGE_MODE_CONFIG_CHARGE_BATTERY); + } + + DBG("bq24296_charge_mode_config is %s\n", on ? "OTG Mode" : "Charge Mode"); + + return 0; +} + int bq24296_charge_otg_en(int chg_en,int otg_en) +{ + int ret = 0; + + if ((chg_en ==0) && (otg_en ==0)){ + ret = bq24296_update_reg(bq24296_di->client,POWE_ON_CONFIGURATION_REGISTER,0x00 << 4,0x03 << 4); + } + else if ((chg_en ==0) && (otg_en ==1)) + bq24296_charge_mode_config(1); + else + bq24296_charge_mode_config(0); + return ret; +} + +extern int dwc_vbus_status(void); +//extern int get_gadget_connect_flag(void); + +static void usb_detect_work_func(struct work_struct *work) +{ + struct delayed_work *delayed_work = (struct delayed_work *)container_of(work, struct delayed_work, work); + struct bq24296_device_info *pi = (struct bq24296_device_info *)container_of(delayed_work, struct bq24296_device_info, usb_detect_work); + u8 retval = 0; + int ret ; + + ret = bq24296_read(bq24296_di->client, 0x08, &retval, 1); + if (ret < 0) { + dev_err(&bq24296_di->client->dev, "%s: err %d\n", __func__, ret); + } + if ((retval & 0x30) ==0x30){ + bq24296_chag_down =1; + }else + bq24296_chag_down =0; + + DBG("%s: retval = %08x bq24296_chag_down = %d\n", __func__,retval,bq24296_chag_down); + + mutex_lock(&pi->var_lock); + DBG("%s: dwc_vbus_status %d\n", __func__, dwc_vbus_status()); + switch(dwc_vbus_status()) + { + case 2: // USB Wall charger + bq24296_update_input_current_limit(bq24296_di->adp_input_current); + bq24296_set_charge_current(bq24296_di->chg_current); + bq24296_charge_mode_config(0); + DBG("bq24296: detect usb wall charger\n"); + break; + case 1: //normal USB + #if 0 + if (0 == get_gadget_connect_flag()){ // non-standard AC charger + bq24296_update_input_current_limit(IINLIM_2000MA); + bq24296_set_charge_current(CHARGE_CURRENT_1024MA); + bq24296_charge_mode_config(0);; + }else{ + #endif + // connect to pc + bq24296_update_input_current_limit(bq24296_di->usb_input_current); + bq24296_set_charge_current(CHARGE_CURRENT_512MA); + bq24296_charge_mode_config(0); + DBG("bq24296: detect normal usb charger\n"); + // } + break; + default: + DBG("bq24296: detect no usb \n"); + break; + } + mutex_unlock(&pi->var_lock); + + schedule_delayed_work(&pi->usb_detect_work, 1*HZ); +} + + +static void irq_work_func(struct work_struct *work) +{ +// struct bq24296_device_info *info= container_of(work, struct bq24296_device_info, irq_work); +} + +static irqreturn_t chg_irq_func(int irq, void *dev_id) +{ +// struct bq24296_device_info *info = dev_id; + DBG("%s\n", __func__); + +// queue_work(info->workqueue, &info->irq_work); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_OF +static struct bq24296_board *bq24296_parse_dt(struct bq24296_device_info *di) +{ + struct bq24296_board *pdata; + struct device_node *bq24296_np; + + DBG("%s,line=%d\n", __func__,__LINE__); + bq24296_np = of_node_get(di->dev->of_node); + if (!bq24296_np) { + printk("could not find bq24296-node\n"); + return NULL; + } + pdata = devm_kzalloc(di->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + if (of_property_read_u32_array(bq24296_np,"bq24296,chg_current",pdata->chg_current, 3)) { + printk("dcdc sleep voltages not specified\n"); + return NULL; + } + + pdata->chg_irq_pin = of_get_named_gpio(bq24296_np,"gpios",0); + if (!gpio_is_valid(pdata->chg_irq_pin)) { + printk("invalid gpio: %d\n", pdata->chg_irq_pin); + } + + return pdata; +} + +#else +static struct rk808_board *bq24296_parse_dt(struct bq24296_device_info *di) +{ + return NULL; +} +#endif + +#ifdef CONFIG_OF +static struct of_device_id bq24296_battery_of_match[] = { + { .compatible = "ti,bq24296"}, + { }, +}; +MODULE_DEVICE_TABLE(of, bq24296_battery_of_match); +#endif + +static int bq24296_battery_probe(struct i2c_client *client,const struct i2c_device_id *id) +{ + struct bq24296_device_info *di; + u8 retval = 0; + struct bq24296_board *pdev; + struct device_node *bq24296_node; + int ret=0,irq=0; + + DBG("%s,line=%d\n", __func__,__LINE__); + + bq24296_node = of_node_get(client->dev.of_node); + if (!bq24296_node) { + printk("could not find bq24296-node\n"); + } + + di = devm_kzalloc( &client->dev,sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&client->dev, "failed to allocate device info data\n"); + retval = -ENOMEM; + goto batt_failed_2; + } + i2c_set_clientdata(client, di); + di->dev = &client->dev; + di->client = client; + if (bq24296_node) + pdev = bq24296_parse_dt(di); + + bq24296_pdata = pdev; + + DBG("%s,line=%d chg_current =%d usb_input_current = %d adp_input_current =%d \n", __func__,__LINE__, + pdev->chg_current[0],pdev->chg_current[1],pdev->chg_current[2]); + + /******************get set current******/ + if (pdev->chg_current[0] && pdev->chg_current[1] && pdev->chg_current[2]){ + di->chg_current = bq24296_get_chg_current(pdev->chg_current[0] ); + di->usb_input_current = bq24296_get_limit_current(pdev->chg_current[1]); + di->adp_input_current = bq24296_get_limit_current(pdev->chg_current[2]); + } + else { + di->chg_current = bq24296_get_chg_current(1000); + di->usb_input_current = bq24296_get_limit_current(500); + di->adp_input_current = bq24296_get_limit_current(2000); + } + /****************************************/ + bq24296_di = di; + /* get the vendor id */ + ret = bq24296_read(di->client, VENDOR_STATS_REGISTER, &retval, 1); + if (ret < 0) { + dev_err(&di->client->dev, "%s(): Failed in reading register" + "0x%02x\n", __func__, VENDOR_STATS_REGISTER); + goto batt_failed_4; + } + di->workqueue = create_singlethread_workqueue("bq24296_irq"); + INIT_WORK(&di->irq_work, irq_work_func); + mutex_init(&di->var_lock); + INIT_DELAYED_WORK(&di->usb_detect_work, usb_detect_work_func); + schedule_delayed_work(&di->usb_detect_work, 0); + bq24296_init_registers(); + + if (gpio_is_valid(pdev->chg_irq_pin)){ + irq = gpio_to_irq(pdev->chg_irq_pin); + ret = request_threaded_irq(irq, NULL,chg_irq_func, IRQF_TRIGGER_FALLING| IRQF_ONESHOT, "bq24296_chg_irq", di); + if (ret) { + ret = -EINVAL; + printk("failed to request bq24296_chg_irq\n"); + goto err_chgirq_failed; + } + } + + bq24296_int =1; + + DBG("bq24296_battery_probe ok"); + return 0; + +batt_failed_4: + kfree(di); +batt_failed_2: + +err_chgirq_failed: + free_irq(gpio_to_irq(pdev->chg_irq_pin), NULL); + return retval; +} + +static void bq24296_battery_shutdown(struct i2c_client *client) +{ + free_irq(gpio_to_irq(bq24296_pdata->chg_irq_pin), NULL); + +} +static int bq24296_battery_remove(struct i2c_client *client) +{ + struct bq24296_device_info *di = i2c_get_clientdata(client); + kfree(di); + return 0; +} + +static const struct i2c_device_id bq24296_id[] = { + { "bq24296", 0 }, +}; + +static struct i2c_driver bq24296_battery_driver = { + .driver = { + .name = "bq24296", + .owner = THIS_MODULE, + .of_match_table =of_match_ptr(bq24296_battery_of_match), + }, + .probe = bq24296_battery_probe, + .remove = bq24296_battery_remove, + .shutdown = bq24296_battery_shutdown, + .id_table = bq24296_id, +}; + +static int __init bq24296_battery_init(void) +{ + int ret; + + ret = i2c_add_driver(&bq24296_battery_driver); + if (ret) + printk(KERN_ERR "Unable to register BQ24296 driver\n"); + + return ret; +} +module_init(bq24296_battery_init); + +static void __exit bq24296_battery_exit(void) +{ + i2c_del_driver(&bq24296_battery_driver); +} +module_exit(bq24296_battery_exit); + +MODULE_AUTHOR("Rockchip"); +MODULE_DESCRIPTION("BQ24296 battery monitor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/bq27320_battery.c b/drivers/power/bq27320_battery.c new file mode 100755 index 000000000000..b06d5b558510 --- /dev/null +++ b/drivers/power/bq27320_battery.c @@ -0,0 +1,1402 @@ +/* + * BQ27320 battery driver + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "1.0.0" +#define BQ27x00_REG_TEMP 0x06 +#define BQ27x00_REG_VOLT 0x08 +#define BQ27x00_REG_AI 0x14 +#define BQ27x00_REG_BATTERYSTATUS 0x0A +#define BQ27x00_REG_TTE 0x16 +#define BQ27x00_REG_TTF 0x18 +#define BQ27320_REG_SOC 0x2c + +#define BQ27320_BATTERYSTATUS_DSC BIT(0) +#define BQ27320_BATTERYSTATUS_SYSDOWN BIT(1) +//#define BQ27320_BATTERYSTATUS_CHGS BIT(8) +#define BQ27320_BATTERYSTATUS_FC BIT(9) +#define BQ27320_BATTERYSTATUS_OTD BIT(10) +#define BQ27320_BATTERYSTATUS_OTC BIT(11) +#define BQ27320_CURRENT BIT(15) + +#define BQ27320_SPEED 100 * 1000 + +/*define for firmware update*/ +#define BSP_I2C_MAX_TRANSFER_LEN 128 +#define BSP_MAX_ASC_PER_LINE 400 +#define BSP_ENTER_ROM_MODE_CMD 0x00 +#define BSP_ENTER_ROM_MODE_DATA 0x0F00 +#define BSP_ROM_MODE_I2C_ADDR 0x0B +#define BSP_NORMAL_MODE_I2C_ADDR 0x55 +#define BSP_FIRMWARE_FILE_SIZE (3290*400) + +/*define for power detect*/ +#define BATTERY_LOW_CAPACITY 2 +#define BATTERY_LOW_VOLTAGE 3500000 +#define BATTERY_RECHARGER_CAPACITY 97 +#define BATTERY_LOW_TEMPRETURE 0 +#define BATTERY_HIGH_TEMPRETURE 650 + +struct bq27320_device_info { + struct device *dev; + struct power_supply bat; + struct power_supply usb; + struct power_supply ac; + struct delayed_work work; + struct i2c_client *client; + unsigned int interval; + unsigned int dc_check_pin; + unsigned int bat_num; + unsigned ac_charging; + unsigned usb_charging; + unsigned online; + unsigned int irq_pin; + int soc_full; + int rsoc; + int bat_tempreture; + int bat_status; + int bat_present; + struct workqueue_struct *workqueue; + struct delayed_work chg_down_work; + struct mutex battery_mutex; + +}; +struct bq27320_board { + unsigned int irq_pin; + struct device_node *of_node; +}; + +struct i2c_client* g_bq27320_i2c_client = NULL; +static struct i2c_driver bq27320_battery_driver; + +int virtual_battery_enable = 0; +extern int dwc_vbus_status(void); +//extern int get_gadget_connect_flag(void); +//extern int dwc_otg_check_dpdm(void); + +static void bq27320_set(void); + +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) do { } while (0) +#endif + +/* If the system has several batteries we need a different name for each + * of them... + */ +static DEFINE_MUTEX(battery_mutex); + +static struct bq27320_device_info *bq27320_di; +static enum power_supply_property bq27320_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, +// POWER_SUPPLY_PROP_TECHNOLOGY, +// POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + //POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, +}; + +static enum power_supply_property rk3190_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static enum power_supply_property rk3190_usb_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static ssize_t battery_proc_write(struct file *file,const char __user *buffer, + size_t count, loff_t *ppos) +{ + char c; + int rc; + printk("USER:\n"); + printk("echo x >/proc/driver/power\n"); + printk("x=1,means just print log ||x=2,means log and data ||x= other,means close log\n"); + + rc = get_user(c,buffer); + if(rc) + return rc; + + if(c == '1') + virtual_battery_enable = 1; + else if(c == '2') + virtual_battery_enable = 2; + else if(c == '3') + virtual_battery_enable = 3; + else + virtual_battery_enable = 0; + printk("%s,count(%d),virtual_battery_enable(%d)\n",__FUNCTION__,(int)count,virtual_battery_enable); + return count; +} + +static const struct file_operations battery_proc_fops = { + .owner = THIS_MODULE, + .write = battery_proc_write, +}; + +/* + * Common code for BQ27320 devices read + */ + + int bq27320_i2c_master_reg8_read(const struct i2c_client *client, const char reg, char *buf, int count, int scl_rate) +{ + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msgs[2]; + int ret; + char reg_buf = reg; + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = 1; + msgs[0].buf = ®_buf; + msgs[0].scl_rate = scl_rate; +// msgs[0].udelay = client->udelay; + + msgs[1].addr = client->addr; + msgs[1].flags = client->flags | I2C_M_RD; + msgs[1].len = count; + msgs[1].buf = (char *)buf; + msgs[1].scl_rate = scl_rate; +// msgs[1].udelay = client->udelay; + + ret = i2c_transfer(adap, msgs, 2); + return (ret == 2)? count : ret; +} +EXPORT_SYMBOL(bq27320_i2c_master_reg8_read); + +int bq27320_i2c_master_reg8_write(const struct i2c_client *client, const char reg, const char *buf, int count, int scl_rate) +{ + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + int ret; + char *tx_buf = (char *)kmalloc(count + 1, GFP_KERNEL); + if(!tx_buf) + return -ENOMEM; + tx_buf[0] = reg; + memcpy(tx_buf+1, buf, count); + + msg.addr = client->addr; + msg.flags = client->flags; + msg.len = count + 1; + msg.buf = (char *)tx_buf; + msg.scl_rate = scl_rate; +// msg.udelay = client->udelay; + + ret = i2c_transfer(adap, &msg, 1); + kfree(tx_buf); + return (ret == 1) ? count : ret; + +} +EXPORT_SYMBOL(bq27320_i2c_master_reg8_write); +static int bq27320_read(struct i2c_client *client, u8 reg, u8 buf[], unsigned len) +{ + int ret; + mutex_lock(&battery_mutex); + ret = bq27320_i2c_master_reg8_read(client, reg, buf, len, BQ27320_SPEED); + mutex_unlock(&battery_mutex); + return ret; +} + +static int bq27320_write(struct i2c_client *client, u8 reg, u8 const buf[], unsigned len) +{ + int ret; + mutex_lock(&battery_mutex); + ret = bq27320_i2c_master_reg8_write(client, reg, buf, (int)len, BQ27320_SPEED); + mutex_unlock(&battery_mutex); + return ret; +} +#if 1 +static int bq27320_read_and_compare(struct i2c_client *client, u8 reg, u8 *pSrcBuf, u8 *pDstBuf, u16 len) +{ + int i2c_ret; + + i2c_ret = bq27320_read(client, reg, pSrcBuf, len); + if(i2c_ret < 0) + { + printk(KERN_ERR "[%s,%d,%08x] bq27320_read failed\n",__FUNCTION__,__LINE__,reg); + return i2c_ret; + } + + i2c_ret = strncmp(pDstBuf, pSrcBuf, len); + + return i2c_ret; +} + +static int bq27320_atoi(const char *s) +{ + int k = 0; + + k = 0; + while (*s != '\0' && *s >= '0' && *s <= '9') { + k = 10 * k + (*s - '0'); + s++; + } + return k; +} + +static unsigned long bq27320_strtoul(const char *cp, unsigned int base) +{ + unsigned long result = 0,value; + + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) + { + result = result*base + value; + cp++; + } + + return result; +} + +static int bq27320_firmware_program(struct i2c_client *client, const unsigned char *pgm_data, unsigned int filelen) +{ + unsigned int i = 0, j = 0, ulDelay = 0, ulReadNum = 0; + unsigned int ulCounter = 0, ulLineLen = 0; + unsigned char temp = 0; + unsigned char *p_cur; + unsigned char pBuf[BSP_MAX_ASC_PER_LINE] = { 0 }; + unsigned char p_src[BSP_I2C_MAX_TRANSFER_LEN] = { 0 }; + unsigned char p_dst[BSP_I2C_MAX_TRANSFER_LEN] = { 0 }; + unsigned char ucTmpBuf[16] = { 0 }; + +bq275x0_firmware_program_begin: + if(ulCounter > 10) + { + return -1; + } + + p_cur = (unsigned char *)pgm_data; + + while(1) + { + if((p_cur - pgm_data) >= filelen) + { + printk("Download success\n"); + break; + } + + while (*p_cur == '\r' || *p_cur == '\n') + { + p_cur++; + } + + i = 0; + ulLineLen = 0; + + memset(p_src, 0x00, sizeof(p_src)); + memset(p_dst, 0x00, sizeof(p_dst)); + memset(pBuf, 0x00, sizeof(pBuf)); + + /*»ñÈ¡Ò»ÐÐÊý¾Ý£¬È¥³ý¿Õ¸ñ*/ + while(i < BSP_MAX_ASC_PER_LINE) + { + temp = *p_cur++; + i++; + if(('\r' == temp) || ('\n' == temp)) + { + break; + } + if(' ' != temp) + { + pBuf[ulLineLen++] = temp; + } + } + + + p_src[0] = pBuf[0]; + p_src[1] = pBuf[1]; + + if(('W' == p_src[0]) || ('C' == p_src[0])) + { + for(i=2,j=0; i>8) & 0x00ff; + + /*Enter Rom Mode */ + iRet = bq27320_write(client, BSP_ENTER_ROM_MODE_CMD, &ucTmpBuf[0], 2); + if(0 > iRet) + { + printk(KERN_ERR "[%s,%d] bq27320_write failed\n",__FUNCTION__,__LINE__); + } + mdelay(10); + + /*change i2c addr*/ + g_bq27320_i2c_client->addr = BSP_ROM_MODE_I2C_ADDR; + + /*program bqfs*/ + iRet = bq27320_firmware_program(g_bq27320_i2c_client, pgm_data, len); + if(0 != iRet) + { + printk(KERN_ERR "[%s,%d] bq275x0_firmware_program failed\n",__FUNCTION__,__LINE__); + } + + /*change i2c addr*/ + g_bq27320_i2c_client->addr = BSP_NORMAL_MODE_I2C_ADDR; + + return iRet; + +} + +static int bq27320_update_firmware(struct i2c_client *client, const char *pFilePath) +{ + char *buf; + struct file *filp; + struct inode *inode = NULL; + mm_segment_t oldfs; + unsigned int length; + int ret = 0; + + /* open file */ + oldfs = get_fs(); + set_fs(KERNEL_DS); + filp = filp_open(pFilePath, O_RDONLY, S_IRUSR); + if (IS_ERR(filp)) + { + printk(KERN_ERR "[%s,%d] filp_open failed\n",__FUNCTION__,__LINE__); + set_fs(oldfs); + return -1; + } + + if (!filp->f_op) + { + printk(KERN_ERR "[%s,%d] File Operation Method Error\n",__FUNCTION__,__LINE__); + filp_close(filp, NULL); + set_fs(oldfs); + return -1; + } + + inode = filp->f_path.dentry->d_inode; + if (!inode) + { + printk(KERN_ERR "[%s,%d] Get inode from filp failed\n",__FUNCTION__,__LINE__); + filp_close(filp, NULL); + set_fs(oldfs); + return -1; + } + + /* file's size */ + length = i_size_read(inode->i_mapping->host); + printk("bq27320 firmware image size is %d \n",length); + if (!( length > 0 && length < BSP_FIRMWARE_FILE_SIZE)) + { + printk(KERN_ERR "[%s,%d] Get file size error\n",__FUNCTION__,__LINE__); + filp_close(filp, NULL); + set_fs(oldfs); + return -1; + } + + /* allocation buff size */ + buf = vmalloc(length+(length%2)); /* buf size if even */ + if (!buf) + { + printk(KERN_ERR "[%s,%d] Alloctation memory failed\n",__FUNCTION__,__LINE__); + filp_close(filp, NULL); + set_fs(oldfs); + return -1; + } + + /* read data */ + if (filp->f_op->read(filp, buf, length, &filp->f_pos) != length) + { + printk(KERN_ERR "[%s,%d] File read error\n",__FUNCTION__,__LINE__); + filp_close(filp, NULL); + filp_close(filp, NULL); + set_fs(oldfs); + vfree(buf); + return -1; + } + + ret = bq27320_firmware_download(client, (const char*)buf, length); + + //if(0 == ret) + //ret = 1; + + filp_close(filp, NULL); + set_fs(oldfs); + vfree(buf); + + return ret; +} + +static u8 get_child_version(void) +{ + u8 data[32]; + + data[0] = 0x39; + if(bq27320_write(g_bq27320_i2c_client, 0x3e, data, 1) < 0) + return -1; + mdelay(2); + + data[0] = 0x00; + if(bq27320_write(g_bq27320_i2c_client, 0x3f, data, 1) < 0) + return -1; + mdelay(2); + + data[0] = 0x00; + if(bq27320_write(g_bq27320_i2c_client, 0x61, data, 1) < 0) + return -1; + mdelay(2); + + bq27320_read(g_bq27320_i2c_client, 0x60, data, 1); + mdelay(2); + + bq27320_read(g_bq27320_i2c_client, 0x40, data, 32); + + return data[0]; +} + +static ssize_t bq27320_attr_store(struct device_driver *driver,const char *buf, size_t count) +{ + int iRet = 0; + unsigned char path_image[255]; + + if(NULL == buf || count >255 || count == 0 || strnchr(buf, count, 0x20)) + return -1; + memcpy (path_image, buf, count); + /* replace '\n' with '\0' */ + if((path_image[count-1]) == '\n') + path_image[count-1] = '\0'; + else + path_image[count] = '\0'; + + /*enter firmware bqfs download*/ + virtual_battery_enable = 1; + iRet = bq27320_update_firmware(g_bq27320_i2c_client, path_image); + msleep(3000); + virtual_battery_enable = 0; + + if (iRet == 0) { + pr_err("Update firemware finish, then update battery status..."); + return count; + } + + return iRet; +} + +static ssize_t bq27320_attr_show(struct device_driver *driver, char *buf) +{ + u8 ver; + + if(NULL == buf) + { + return -1; + } + + ver = get_child_version(); + + if(ver < 0) + { + return sprintf(buf, "%s", "Coulometer Damaged or Firmware Error"); + } + else + { + + return sprintf(buf, "%x", ver); + } + +} + +static DRIVER_ATTR(state, 0664, bq27320_attr_show, bq27320_attr_store); + + +#endif +/* + * Return the battery temperature in tenths of degree Celsius + * Or < 0 if something fails. + */ +static int bq27320_battery_temperature(struct bq27320_device_info *di) +{ + int ret; + int temp = 0; + u8 buf[2]; + + #if defined (CONFIG_NO_BATTERY_IC) + return 258; + #endif + + if(virtual_battery_enable == 1) + return 125/*258*/; + ret = bq27320_read(di->client,BQ27x00_REG_TEMP,buf,2); + if (ret<0) { + dev_err(di->dev, "error reading temperature\n"); + return ret; + } + temp = get_unaligned_le16(buf); + temp = temp - 2731; + DBG("Enter:%s--temp = %d\n",__FUNCTION__,temp); + di ->bat_tempreture = temp; + return temp; +} + +/* + * Return the battery Voltage in milivolts + * Or < 0 if something fails. + */ +static int bq27320_battery_voltage(struct bq27320_device_info *di) +{ + int ret; + u8 buf[2]; + int volt = 0; + + #if defined (CONFIG_NO_BATTERY_IC) + return 4000000; + #endif + if(virtual_battery_enable == 1) + return 2000000/*4000000*/; + + ret = bq27320_read(di->client,BQ27x00_REG_VOLT,buf,2); + if (ret<0) { + dev_err(di->dev, "error reading voltage\n"); + return ret; + } + volt = get_unaligned_le16(buf); + + //bp27510 can only measure one li-lion bat + if(di->bat_num == 2){ + volt = volt * 1000 * 2; + }else{ + volt = volt * 1000; + } + + DBG("Enter:%s--volt = %d\n",__FUNCTION__,volt); + return volt; +} + +/* + * Return the battery average current + * Note that current can be negative signed as well + * Or 0 if something fails. + */ +static int bq27320_battery_current(struct bq27320_device_info *di) +{ + int ret; + int curr = 0; + u8 buf[2]; + + #if defined (CONFIG_NO_BATTERY_IC) + return 22000; + #endif + if(virtual_battery_enable == 1) + return 11000/*22000*/; + ret = bq27320_read(di->client,BQ27x00_REG_AI,buf,2); + if (ret<0) { + dev_err(di->dev, "error reading current\n"); + return 0; + } + + curr = get_unaligned_le16(buf); + DBG("curr = %x \n",curr); + if(curr>0x8000){ + curr = 0xFFFF^(curr-1); + DBG("curr = -%d \n",curr*1000); + } + else + DBG("curr = %d \n",curr*1000); + curr = curr * 1000; + return curr; +} + +/* + * Return the battery Relative State-of-Charge + * Or < 0 if something fails. + + */ +static int bq27320_battery_rsoc(struct bq27320_device_info *di) +{ + int ret; + int rsoc = 0; + #if 0 + int nvcap = 0,facap = 0,remcap=0,fccap=0,full=0,cnt=0; + int art = 0, artte = 0, ai = 0, tte = 0, ttf = 0, si = 0; + int stte = 0, mli = 0, mltte = 0, ae = 0, ap = 0, ttecp = 0, cc = 0; + #endif + u8 buf[2]; + + #if defined (CONFIG_NO_BATTERY_IC) + return 50; + #endif + if(virtual_battery_enable == 1) + return 50/*100*/; + + ret = bq27320_read(di->client,BQ27320_REG_SOC,buf,2); + if (ret<0) { + dev_err(di->dev, "error reading relative State-of-Charge\n"); + return ret; + } + rsoc = get_unaligned_le16(buf); + DBG("Enter:%s --rsoc = %d\n",__FUNCTION__,rsoc); + + #if defined (CONFIG_NO_BATTERY_IC) + rsoc = 100; + #endif + #if 0 //other register information, for debug use + ret = bq27320_read(di->client,0x0c,buf,2); //NominalAvailableCapacity + nvcap = get_unaligned_le16(buf); + DBG("\nEnter:%s %d--nvcap = %d\n",__FUNCTION__,__LINE__,nvcap); + ret = bq27320_read(di->client,0x0e,buf,2); //FullAvailableCapacity + facap = get_unaligned_le16(buf); + DBG("Enter:%s %d--facap = %d\n",__FUNCTION__,__LINE__,facap); + ret = bq27320_read(di->client,0x10,buf,2); //RemainingCapacity + remcap = get_unaligned_le16(buf); + DBG("Enter:%s %d--remcap = %d\n",__FUNCTION__,__LINE__,remcap); + ret = bq27320_read(di->client,0x12,buf,2); //FullChargeCapacity + fccap = get_unaligned_le16(buf); + DBG("Enter:%s %d--fccap = %d\n",__FUNCTION__,__LINE__,fccap); + ret = bq27320_read(di->client,0x3c,buf,2); //DesignCapacity + full = get_unaligned_le16(buf); + DBG("Enter:%s %d--DesignCapacity = %d\n",__FUNCTION__,__LINE__,full); + + buf[0] = 0x00; //CONTROL_STATUS + buf[1] = 0x00; + bq27320_write(di->client,0x00,buf,2); + ret = bq27320_read(di->client,0x00,buf,2); + cnt = get_unaligned_le16(buf); + DBG("Enter:%s %d--Control status = %x\n",__FUNCTION__,__LINE__,cnt); + + ret = bq27320_read(di->client,0x02,buf,2); //AtRate + art = get_unaligned_le16(buf); + DBG("Enter:%s %d--AtRate = %d\n",__FUNCTION__,__LINE__,art); + ret = bq27320_read(di->client,0x04,buf,2); //AtRateTimeToEmpty + artte = get_unaligned_le16(buf); + DBG("Enter:%s %d--AtRateTimeToEmpty = %d\n",__FUNCTION__,__LINE__,artte); + ret = bq27320_read(di->client,0x14,buf,2); //AverageCurrent + ai = get_unaligned_le16(buf); + DBG("Enter:%s %d--AverageCurrent = %d\n",__FUNCTION__,__LINE__,ai); + ret = bq27320_read(di->client,0x16,buf,2); //TimeToEmpty + tte = get_unaligned_le16(buf); + DBG("Enter:%s %d--TimeToEmpty = %d\n",__FUNCTION__,__LINE__,tte); + ret = bq27320_read(di->client,0x18,buf,2); //TimeToFull + ttf = get_unaligned_le16(buf); + DBG("Enter:%s %d--TimeToFull = %d\n",__FUNCTION__,__LINE__,ttf); + ret = bq27320_read(di->client,0x1a,buf,2); //StandbyCurrent + si = get_unaligned_le16(buf); + DBG("Enter:%s %d--StandbyCurrent = %d\n",__FUNCTION__,__LINE__,si); + ret = bq27320_read(di->client,0x1c,buf,2); //StandbyTimeToEmpty + stte = get_unaligned_le16(buf); + DBG("Enter:%s %d--StandbyTimeToEmpty = %d\n",__FUNCTION__,__LINE__,stte); + ret = bq27320_read(di->client,0x1e,buf,2); //MaxLoadCurrent + mli = get_unaligned_le16(buf); + DBG("Enter:%s %d--MaxLoadCurrent = %d\n",__FUNCTION__,__LINE__,mli); + ret = bq27320_read(di->client,0x20,buf,2); //MaxLoadTimeToEmpty + mltte = get_unaligned_le16(buf); + DBG("Enter:%s %d--MaxLoadTimeToEmpty = %d\n",__FUNCTION__,__LINE__,mltte); + ret = bq27320_read(di->client,0x22,buf,2); //AvailableEnergy + ae = get_unaligned_le16(buf); + DBG("Enter:%s %d--AvailableEnergy = %d\n",__FUNCTION__,__LINE__,ae); + ret = bq27320_read(di->client,0x24,buf,2); //AveragePower + ap = get_unaligned_le16(buf); + DBG("Enter:%s %d--AveragePower = %d\n",__FUNCTION__,__LINE__,ap); + ret = bq27320_read(di->client,0x26,buf,2); //TTEatConstantPower + ttecp = get_unaligned_le16(buf); + DBG("Enter:%s %d--TTEatConstantPower = %d\n",__FUNCTION__,__LINE__,ttecp); + ret = bq27320_read(di->client,0x2a,buf,2); //CycleCount + cc = get_unaligned_le16(buf); + DBG("Enter:%s %d--CycleCount = %d\n",__FUNCTION__,__LINE__,cc); + #endif + return rsoc; +} + +static int bq27320_battery_status(struct bq27320_device_info *di, + union power_supply_propval *val) +{ + u8 buf[2]; + int flags = 0; + int status; + int ret; + + #if defined (CONFIG_NO_BATTERY_IC) + val->intval = POWER_SUPPLY_STATUS_FULL; + return 0; + #endif + + if(virtual_battery_enable == 1) + { + val->intval = POWER_SUPPLY_STATUS_FULL; + return 0; + } + + ret = bq27320_read(di->client,BQ27x00_REG_BATTERYSTATUS, buf, 2); + if (ret < 0) { + dev_err(di->dev, "error reading flags\n"); + return ret; + } + + flags = get_unaligned_le16(buf); + DBG("Enter:%s %d--status = %x\n",__FUNCTION__,__LINE__,flags); + + if ((flags & BQ27320_BATTERYSTATUS_FC) ||(bq27320_di ->rsoc ==100)){ + status = POWER_SUPPLY_STATUS_FULL; + di->soc_full = 1; + DBG("status =POWER_SUPPLY_STATUS_FULL \n"); + } + else if (flags & BQ27320_BATTERYSTATUS_DSC){ + status = POWER_SUPPLY_STATUS_DISCHARGING; + DBG("status =POWER_SUPPLY_STATUS_DISCHARGING \n"); + } + else { + status = POWER_SUPPLY_STATUS_CHARGING; + DBG("status =POWER_SUPPLY_STATUS_CHARGING \n"); + } + + if (((status==POWER_SUPPLY_STATUS_FULL)||(status==POWER_SUPPLY_STATUS_CHARGING)) + && ((bq27320_di->ac_charging ==0) && (bq27320_di->usb_charging ==0) )) + status = POWER_SUPPLY_STATUS_DISCHARGING; + else if ((status==POWER_SUPPLY_STATUS_DISCHARGING) && ((bq27320_di->ac_charging ==1) || (bq27320_di->usb_charging ==1) )) + status = POWER_SUPPLY_STATUS_CHARGING; + + di ->bat_status = status; + val->intval = status; + return 0; +} + +static int bq27320_health_status(struct bq27320_device_info *di, + union power_supply_propval *val) +{ + u8 buf[2]; + int flags = 0; + int status; + int ret; + + #if defined (CONFIG_NO_BATTERY_IC) + val->intval = POWER_SUPPLY_HEALTH_GOOD; + return 0; + #endif + + if(virtual_battery_enable == 1) + { + val->intval = POWER_SUPPLY_HEALTH_GOOD; + return 0; + } + ret = bq27320_read(di->client,BQ27x00_REG_BATTERYSTATUS, buf, 2); + if (ret < 0) { + dev_err(di->dev, "error reading flags\n"); + return ret; + } + flags = get_unaligned_le16(buf); + DBG("Enter:%s--health status = %x\n",__FUNCTION__,flags); + if ((flags & BQ27320_BATTERYSTATUS_OTD)||(flags & BQ27320_BATTERYSTATUS_OTC)){ + status = POWER_SUPPLY_HEALTH_OVERHEAT; + DBG("health =POWER_SUPPLY_HEALTH_OVERHEAT \n"); + } + else{ + status = POWER_SUPPLY_HEALTH_GOOD; + DBG("health =POWER_SUPPLY_HEALTH_GOOD \n"); + } + + val->intval = status; + return 0; +} + + +/* + * Read a time register. + * Return < 0 if something fails. + */ +static int bq27320_battery_time(struct bq27320_device_info *di, int reg, + union power_supply_propval *val) +{ + u8 buf[2]; + int tval = 0; + int ret; + + ret = bq27320_read(di->client,reg,buf,2); + if (ret<0) { + dev_err(di->dev, "error reading register %02x\n", reg); + return ret; + } + tval = get_unaligned_le16(buf); + DBG("Enter:%s--tval=%d\n",__FUNCTION__,tval); + if (tval == 65535) + return -ENODATA; + + val->intval = tval * 60; + DBG("Enter:%s val->intval = %d\n",__FUNCTION__,val->intval); + return 0; +} + +#define to_bq27320_device_info(x) container_of((x), \ + struct bq27320_device_info, bat); + +static int bq27320_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct bq27320_device_info *di = to_bq27320_device_info(psy); + DBG("Enter:%s %d psp= %d\n",__FUNCTION__,__LINE__,psp); + + switch (psp) { + + case POWER_SUPPLY_PROP_STATUS: + ret = bq27320_battery_status(di, val); + break; + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = bq27320_di ->ac_charging; + else if (psy->type == POWER_SUPPLY_TYPE_USB) + val->intval = bq27320_di ->usb_charging; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = bq27320_battery_voltage(di); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bq27320_battery_voltage(di); + val->intval = val->intval <= 0 ? 0 : 1; + di->bat_present =val->intval; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = bq27320_battery_current(di); + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = bq27320_battery_rsoc(di); + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = bq27320_battery_temperature(di); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = bq27320_health_status(di, val); + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + ret = bq27320_battery_time(di, BQ27x00_REG_TTE, val); + break; +// case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: +// ret = bq27320_battery_time(di, BQ27x00_REG_TTECP, val); +// break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + ret = bq27320_battery_time(di, BQ27x00_REG_TTF, val); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int bq27320_get_usb_status(void){ + int usb_status = 0; // 0--dischage ,1 ---usb charge, 2 ---ac charge + int vbus_status = dwc_vbus_status(); + + if (1 == vbus_status) { + // if (0 == get_gadget_connect_flag()){ +// usb_status = 2; // non-standard AC charger +// }else + usb_status = 1; // connect to pc + }else{ + if (2 == vbus_status) + usb_status = 2; //standard AC charger + else + usb_status = 0; + } + return usb_status; +} +static int bq27320_battery_get_status(void) +{ + int charge_on = 0; + int usb_ac_charging = 0; +/* + if(dwc_otg_check_dpdm() == 0){ + bq27320_di->usb_charging = 0; + bq27320_di->ac_charging = 0; + }else if(dwc_otg_check_dpdm() == 1){ + bq27320_di->usb_charging = 1; + bq27320_di->ac_charging = 0; + }else if(dwc_otg_check_dpdm() == 2 || dwc_otg_check_dpdm() == 3){ + bq27320_di->usb_charging = 0; + bq27320_di->ac_charging = 1; + } + if(( 1 == bq27320_di->usb_charging)||(1 == bq27320_di ->ac_charging)) + charge_on =1; +*/ + if (charge_on == 0){ + usb_ac_charging = bq27320_get_usb_status(); //0 --discharge, 1---usb charging,2----AC charging; + if(1 == usb_ac_charging){ + bq27320_di->usb_charging = 1; + bq27320_di->ac_charging = 0; + } + else if(2 == usb_ac_charging){ + bq27320_di->usb_charging = 0; + bq27320_di->ac_charging = 1; + } + else{ + bq27320_di->usb_charging = 0; + bq27320_di->ac_charging = 0; + } + } + return 0; + +} +static int rk3190_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = bq27320_di ->ac_charging; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + bq27320_di->online =val->intval; + DBG("%s:rk3190_ac_get_property %d val->intval = %d\n",__FUNCTION__,__LINE__,val->intval); + + return ret; +} + +static int rk3190_usb_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_USB) + val->intval = bq27320_di ->usb_charging; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + bq27320_di->online =val->intval; + DBG("%s:%d rk3190_usb_get_property val->intval = %d\n",__FUNCTION__,__LINE__,val->intval); + + return ret; +} + +static void bq27320_powersupply_init(struct bq27320_device_info *di) +{ + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; + di->bat.properties = bq27320_battery_props; + di->bat.num_properties = ARRAY_SIZE(bq27320_battery_props); + di->bat.get_property = bq27320_battery_get_property; + + di->usb.name = "bq27320-usb"; + di->usb.type = POWER_SUPPLY_TYPE_USB; + di->usb.properties = rk3190_usb_props; + di->usb.num_properties = ARRAY_SIZE(rk3190_usb_props); + di->usb.get_property = rk3190_usb_get_property; + + di->ac.name = "bq27320-ac"; + di->ac.type = POWER_SUPPLY_TYPE_MAINS; + di->ac.properties = rk3190_ac_props; + di->ac.num_properties = ARRAY_SIZE(rk3190_ac_props); + di->ac.get_property =rk3190_ac_get_property; +} + + +static void bq27320_battery_update_status(struct bq27320_device_info *di) +{ + + bq27320_battery_get_status(); + power_supply_changed(&di->bat); + power_supply_changed(&di->usb); + power_supply_changed(&di->ac); +} +#ifdef CONFIG_CHARGER_BQ24161 +#include +static void bq27320_for_charging(struct bq27320_device_info *di) +{ + if ((bq27320_di->usb_charging || bq27320_di->ac_charging) && (di->bat_present == 1)) {//ÓгäµçÆ÷½ÓÈ룬ÇÒµç³Ø´æÔÚ + //tempreture out of safe range, do not charge + if ((di->bat_tempreture < BATTERY_LOW_TEMPRETURE) + || (di->bat_tempreture > BATTERY_HIGH_TEMPRETURE)) { + printk(KERN_INFO "battery tempreture is out of safe range\n"); + di->soc_full = 0; + bq24296_charge_otg_en(0, 0);//disable charging + return ; + } +/* + if (otg_is_host_mode() || mhl_vbus_power_on()) { + printk("**********usb is otg mode, otg lock***********\n"); + bq24296_charge_otg_en(1, 1, 1); + } + + else + bq24296_charge_otg_en(1, 1, 0); +*/ + if (di->bat_status==POWER_SUPPLY_STATUS_FULL) {//³äÂú + di->soc_full = 1; + printk(KERN_INFO "**********charger ok*********\n"); + } + else if ((di->soc_full==1) && (di->rsoc<=BATTERY_RECHARGER_CAPACITY)) {//ÒѳäÂú¹ý£¬ÇÒµçÁ¿Ð¡ÓÚ95%£¬ÐèÒªÐø³ä + bq24296_charge_otg_en(0, 0); + msleep(1000); + bq24296_charge_otg_en(1, 0); + di->soc_full = 0; + printk(KERN_INFO "**********recharger*********\n"); + } + } + /* + else if (otg_is_host_mode() || mhl_vbus_power_on()) { + bq24296_charge_otg_en(1, 0, 1); + } + else { + di->bat_full = 0; + bq24296_charge_otg_en(); + } + */ +} + +#endif + + +static void bq27320_battery_work(struct work_struct *work) +{ + struct bq27320_device_info *di = container_of(work, struct bq27320_device_info, work.work); + bq27320_battery_update_status(di); + /* reschedule for the next time */ + #ifdef CONFIG_CHARGER_BQ24296 + bq27320_for_charging(di); + #endif + schedule_delayed_work(&di->work, 1*HZ); +} + +static void bq27320_set(void) +{ + struct bq27320_device_info *di; + int i = 0; + u8 buf[2]; + + di = bq27320_di; + printk("enter 0x41\n"); + buf[0] = 0x41; + buf[1] = 0x00; + bq27320_write(di->client,0x00,buf,2); + + msleep(1500); + + printk("enter 0x21\n"); + buf[0] = 0x21; + buf[1] = 0x00; + bq27320_write(di->client,0x00,buf,2); + + buf[0] = 0; + buf[1] = 0; + bq27320_read(di->client,0x00,buf,2); + + // printk("%s: Enter:BUF[0]= 0X%x BUF[1] = 0X%x\n",__FUNCTION__,buf[0],buf[1]); + + while((buf[0] & 0x04)&&(i<5)) + { + printk("enter more 0x21 times i = %d\n",i); + mdelay(1000); + buf[0] = 0x21; + buf[1] = 0x00; + bq27320_write(di->client,0x00,buf,2); + + buf[0] = 0; + buf[1] = 0; + bq27320_read(di->client,0x00,buf,2); + i++; + } + + if(i>5) + printk("write 0x21 error\n"); + else + printk("bq27320 write 0x21 success\n"); +} + + +static int bq27320_battery_suspend(struct i2c_client *client, pm_message_t mesg) +{ + cancel_delayed_work_sync(&bq27320_di->work); + return 0; +} + +static int bq27320_battery_resume(struct i2c_client *client) +{ + schedule_delayed_work(&bq27320_di->work, msecs_to_jiffies(50)); + return 0; +} +static int bq27320_is_in_rom_mode(void) +{ + int ret = 0; + unsigned char data = 0x0f; + + bq27320_di->client->addr = BSP_ROM_MODE_I2C_ADDR; + ret = bq27320_write(bq27320_di->client, 0x00, &data, 1); + bq27320_di->client->addr = BSP_NORMAL_MODE_I2C_ADDR; + + if (ret == 1) + return 1; + else + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id bq27320_battery_of_match[] = { + { .compatible = "ti,bq27320"}, + { }, +}; +MODULE_DEVICE_TABLE(of, bq27320_battery_of_match); +#endif + +static int bq27320_battery_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bq27320_device_info *di; + int retval = 0; + struct bq27320_board *pdev; + struct device_node *bq27320_node; + + DBG("%s,line=%d\n", __func__,__LINE__); + + bq27320_node = of_node_get(client->dev.of_node); + if (!bq27320_node) { + printk("could not find bq27320-node\n"); + } + + di = devm_kzalloc(&client->dev,sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&client->dev, "failed to allocate device info data\n"); + retval = -ENOMEM; + goto batt_failed_2; + } + i2c_set_clientdata(client, di); + di->dev = &client->dev; + di->bat.name = "bq27320-battery"; + di->client = client; + /* 4 seconds between monotor runs interval */ + di->interval = msecs_to_jiffies(4 * 1000); + di->ac_charging = 1; + di->usb_charging =1; + di->online =1; + di->soc_full = 0; + bq27320_di = di; + + mutex_init(&di->battery_mutex); + + bq27320_powersupply_init(di); + retval = power_supply_register(&client->dev, &di->bat); + if (retval) { + dev_err(&client->dev, "failed to register battery\n"); + goto batt_failed_4; + } + + retval = power_supply_register(&client->dev, &di->usb); + if (retval) { + dev_err(&client->dev, "failed to register ac\n"); + goto batt_failed_4; + } + + retval = power_supply_register(&client->dev, &di->ac); + if (retval) { + dev_err(&client->dev, "failed to register ac\n"); + goto batt_failed_4; + } + + g_bq27320_i2c_client = client; + + retval = driver_create_file(&(bq27320_battery_driver.driver), &driver_attr_state); + if (0 != retval) + { + printk("failed to create sysfs entry(state): %d\n", retval); + goto batt_failed_3; + } + + INIT_DELAYED_WORK(&di->work, bq27320_battery_work); +// schedule_delayed_work(&di->work, di->interval); + schedule_delayed_work(&di->work, 1*HZ); + dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION); + + return 0; + +batt_failed_4: + kfree(di); +batt_failed_3: + driver_remove_file(&(bq27320_battery_driver.driver), &driver_attr_state); +batt_failed_2: + return retval; + +} + +static int bq27320_battery_remove(struct i2c_client *client) +{ + struct bq27320_device_info *di = i2c_get_clientdata(client); + + driver_remove_file(&(bq27320_battery_driver.driver), &driver_attr_state); + + power_supply_unregister(&di->bat); + power_supply_unregister(&di->usb); + power_supply_unregister(&di->ac); + kfree(di->bat.name); + kfree(di->usb.name); + kfree(di->ac.name); + kfree(di); + return 0; +} + +static const struct i2c_device_id bq27320_id[] = { + { "bq27320", 0 }, +}; + +static struct i2c_driver bq27320_battery_driver = { + .driver = { + .name = "bq27320", + .of_match_table =of_match_ptr(bq27320_battery_of_match), + }, + .probe = bq27320_battery_probe, + .remove = bq27320_battery_remove, + .suspend = bq27320_battery_suspend, + .resume = bq27320_battery_resume, + .id_table = bq27320_id, +}; + +static int __init bq27320_battery_init(void) +{ + int ret; + struct proc_dir_entry * battery_proc_entry; + + ret = i2c_add_driver(&bq27320_battery_driver); + if (ret) + printk(KERN_ERR "Unable to register BQ27320 driver\n"); + + battery_proc_entry = proc_create("driver/power",0777,NULL,&battery_proc_fops); + return ret; +} +module_init(bq27320_battery_init); + +static void __exit bq27320_battery_exit(void) +{ + i2c_del_driver(&bq27320_battery_driver); +} +module_exit(bq27320_battery_exit); + +MODULE_AUTHOR("Rockchip"); +MODULE_DESCRIPTION("BQ27320 battery monitor driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/power/bq24296_charger.h b/include/linux/power/bq24296_charger.h new file mode 100755 index 000000000000..79fac3d8d283 --- /dev/null +++ b/include/linux/power/bq24296_charger.h @@ -0,0 +1,164 @@ +/* + * Definitions for mma8452 compass chip. + */ +#ifndef BQ24296_H +#define BQ24296_H +#include + +/* I2C register define */ +#define INPUT_SOURCE_CONTROL_REGISTER 0x00 +#define POWE_ON_CONFIGURATION_REGISTER 0x01 +#define CHARGE_CURRENT_CONTROL_REGISTER 0x02 +#define PRE_CHARGE_TERMINATION_CURRENT_CONTROL_REGISTER 0x03 +#define CHARGE_VOLTAGE_CONTROL_REGISTER 0x04 +#define TERMINATION_TIMER_CONTROL_REGISTER 0x05 +#define THERMAIL_REGULATOION_CONTROL_REGISTER 0x06 +#define MISC_OPERATION_CONTROL_REGISTER 0x07 +#define SYSTEM_STATS_REGISTER 0x08 +#define FAULT_STATS_REGISTER 0x09 +#define VENDOR_STATS_REGISTER 0x0A + +/* power-on configuration register value */ +#define REGISTER_RESET_ENABLE 1 +#define REGISTER_RESET_DISABLE 0 +#define REGISTER_RESET_OFFSET 7 +#define REGISTER_RESET_MASK 1 + +/* input source control register value */ +#define EN_HIZ_ENABLE 1 +#define EN_HIZ_DISABLE 0 +#define EN_HIZ_OFFSET 7 +#define EN_HIZ_MASK 1 + +#define IINLIM_100MA 0 +#define IINLIM_150MA 1 +#define IINLIM_500MA 2 +#define IINLIM_900MA 3 +#define IINLIM_1200MA 4 +#define IINLIM_1500MA 5 +#define IINLIM_2000MA 6 +#define IINLIM_3000MA 7 +#define IINLIM_OFFSET 0 +#define IINLIM_MASK 7 + +#define CHARGE_CURRENT_64MA 0x01 +#define CHARGE_CURRENT_128MA 0x02 +#define CHARGE_CURRENT_256MA 0x04 +#define CHARGE_CURRENT_512MA 0x08 +#define CHARGE_CURRENT_1024MA 0x10 +#define CHARGE_CURRENT_1536MA 0x18 +#define CHARGE_CURRENT_2048MA 0x20 +#define CHARGE_CURRENT_OFFSET 2 +#define CHARGE_CURRENT_MASK 0x3f + +/* Charge Termination/Timer control register value */ +#define WATCHDOG_DISABLE 0 +#define WATCHDOG_40S 1 +#define WATCHDOG_80S 2 +#define WATCHDOG_160S 3 +#define WATCHDOG_OFFSET 4 +#define WATCHDOG_MASK 3 + +/* misc operation control register value */ +#define DPDM_ENABLE 1 +#define DPDM_DISABLE 0 +#define DPDM_OFFSET 7 +#define DPDM_MASK 1 + +/* system status register value */ +#define VBUS_UNKNOWN 0 +#define VBUS_USB_HOST 1 +#define VBUS_ADAPTER_PORT 2 +#define VBUS_OTG 3 +#define VBUS_OFFSET 6 +#define VBUS_MASK 3 + +#define CHRG_NO_CHARGING 0 +#define CHRG_PRE_CHARGE 1 +#define CHRG_FAST_CHARGE 2 +#define CHRG_CHRGE_DONE 3 +#define CHRG_OFFSET 4 +#define CHRG_MASK 3 + +/* vendor status register value */ +#define CHIP_BQ24190 0 +#define CHIP_BQ24191 1 +#define CHIP_BQ24192 2 +#define CHIP_BQ24192I 3 +#define CHIP_BQ24190_DEBUG 4 +#define CHIP_BQ24192_DEBUG 5 +#define CHIP_BQ24296 10 +#define CHIP_OFFSET 3 +#define CHIP_MASK 7 + +/* Pre-Charge/Termination Current Control Register value */ +/* Pre-Charge Current Limit */ +#define PRE_CHARGE_CURRENT_LIMIT_128MA 0x00 +#define PRE_CHARGE_CURRENT_LIMIT_256MA 0x01 +#define PRE_CHARGE_CURRENT_LIMIT_OFFSET 4 +#define PRE_CHARGE_CURRENT_LIMIT_MASK 0x0f +/* Termination Current Limit */ +#define TERMINATION_CURRENT_LIMIT_128MA 0x00 +#define TERMINATION_CURRENT_LIMIT_256MA 0x01 +#define TERMINATION_CURRENT_LIMIT_OFFSET 0 +#define TERMINATION_CURRENT_LIMIT_MASK 0x0f +/* Charge Mode Config */ +#define CHARGE_MODE_CONFIG_CHARGE_DISABLE 0x00 +#define CHARGE_MODE_CONFIG_CHARGE_BATTERY 0x01 +#define CHARGE_MODE_CONFIG_OTG_OUTPUT 0x02 +#define CHARGE_MODE_CONFIG_OFFSET 4 +#define CHARGE_MODE_CONFIG_MASK 0x03 +/* OTG Mode Current Config */ +#define OTG_MODE_CURRENT_CONFIG_500MA 0x00 +#define OTG_MODE_CURRENT_CONFIG_1300MA 0x01 +#define OTG_MODE_CURRENT_CONFIG_OFFSET 0 +#define OTG_MODE_CURRENT_CONFIG_MASK 0x01 + +#define BQ24296_CHG_COMPELET 0x03 +#define BQ24296_NO_CHG 0x00 + +#define BQ24296_DC_CHG 0x02 +#define BQ24296_USB_CHG 0x01 + +#define BQ24296_SPEED 300 * 1000 + +enum { + AC_NOT_INSERT = 0, + AC_INSERT = 1, +}; + +struct bq24296_device_info { + struct device *dev; + struct delayed_work usb_detect_work; + struct i2c_client *client; + unsigned int interval; + struct mutex var_lock; + struct workqueue_struct *freezable_work; + struct work_struct irq_work; /* for Charging & VUSB/VADP */ + + struct workqueue_struct *workqueue; + u8 chg_current; + u8 usb_input_current; + u8 adp_input_current; + //struct timer_list timer; +}; + +struct bq24296_platform_data { + unsigned int otg_usb_pin; + unsigned int chg_irq_pin; + unsigned int psel_pin; + int (*irq_init)(void); +}; + +struct bq24296_board { + unsigned int otg_usb_pin; + unsigned int chg_irq_pin; + unsigned int psel_pin; + struct device_node *of_node; + unsigned int chg_current[3]; +}; + + int bq24296_charge_otg_en(int chg_en,int otg_en); +#endif + +