From 42596aeee9678cdbcfd3a9bdd19b39cf8fd16640 Mon Sep 17 00:00:00 2001 From: gongsc1 Date: Fri, 17 Sep 2021 11:19:57 +0800 Subject: [PATCH] power/charger: add init bq25890 driver add init bq25890 driver from github https://github.com/tibms/kernel-4.4/tree/release/drivers/power/bq2589x-master for milanf Change-Id: Icb7b25364a4af1e01644286dbad2f0dda5038840 Reviewed-on: https://gerrit.mot.com/2069663 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- drivers/power/bq2589x_chg_mmi/Android.mk | 13 + drivers/power/bq2589x_chg_mmi/Kbuild | 8 + drivers/power/bq2589x_chg_mmi/Makefile | 11 + .../power/bq2589x_chg_mmi/bq2589x_charger.c | 1547 +++++++++++++++++ drivers/power/bq2589x_chg_mmi/bq2589x_reg.h | 363 ++++ 5 files changed, 1942 insertions(+) create mode 100644 drivers/power/bq2589x_chg_mmi/Android.mk create mode 100644 drivers/power/bq2589x_chg_mmi/Kbuild create mode 100644 drivers/power/bq2589x_chg_mmi/Makefile create mode 100644 drivers/power/bq2589x_chg_mmi/bq2589x_charger.c create mode 100644 drivers/power/bq2589x_chg_mmi/bq2589x_reg.h diff --git a/drivers/power/bq2589x_chg_mmi/Android.mk b/drivers/power/bq2589x_chg_mmi/Android.mk new file mode 100644 index 000000000000..3e10cf984039 --- /dev/null +++ b/drivers/power/bq2589x_chg_mmi/Android.mk @@ -0,0 +1,13 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := bq2589x_charger.ko + + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + + + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/bq2589x_chg_mmi/Kbuild b/drivers/power/bq2589x_chg_mmi/Kbuild new file mode 100644 index 000000000000..4ca109dcd171 --- /dev/null +++ b/drivers/power/bq2589x_chg_mmi/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/bq2589x_chg_mmi + +obj-m += bq2589x_charger.o + + diff --git a/drivers/power/bq2589x_chg_mmi/Makefile b/drivers/power/bq2589x_chg_mmi/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/bq2589x_chg_mmi/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/bq2589x_chg_mmi/bq2589x_charger.c b/drivers/power/bq2589x_chg_mmi/bq2589x_charger.c new file mode 100644 index 000000000000..3bb610d221f0 --- /dev/null +++ b/drivers/power/bq2589x_chg_mmi/bq2589x_charger.c @@ -0,0 +1,1547 @@ +/* + * BQ2589x battery charging driver + * + * Copyright (C) 2013 Texas Instruments + * + * 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 "bq2589x_reg.h" + +enum bq2589x_vbus_type { + BQ2589X_VBUS_NONE, + BQ2589X_VBUS_USB_SDP, + BQ2589X_VBUS_USB_CDP, /*CDP for bq25890, Adapter for bq25892*/ + BQ2589X_VBUS_USB_DCP, + BQ2589X_VBUS_MAXC, + BQ2589X_VBUS_UNKNOWN, + BQ2589X_VBUS_NONSTAND, + BQ2589X_VBUS_OTG, + BQ2589X_VBUS_TYPE_NUM, +}; + +enum bq2589x_part_no { + BQ25890 = 0x03, + BQ25892 = 0x00, + BQ25895 = 0x07, +}; + + +#define BQ2589X_STATUS_PLUGIN 0x0001 +#define BQ2589X_STATUS_PG 0x0002 +#define BQ2589X_STATUS_CHARGE_ENABLE 0x0004 +#define BQ2589X_STATUS_FAULT 0x0008 + +#define BQ2589X_STATUS_EXIST 0x0100 + +struct bq2589x_config { + bool enable_auto_dpdm; +/* bool enable_12v;*/ + + int charge_voltage; + int charge_current; + + bool enable_term; + int term_current; + + bool enable_ico; + bool use_absolute_vindpm; +}; + + +struct bq2589x { + struct device *dev; + struct i2c_client *client; + enum bq2589x_part_no part_no; + int revision; + + unsigned int status; + int vbus_type; + + bool enabled; + + int vbus_volt; + int vbat_volt; + + int rsoc; + struct bq2589x_config cfg; + struct work_struct irq_work; + struct work_struct adapter_in_work; + struct work_struct adapter_out_work; + struct delayed_work monitor_work; + struct delayed_work ico_work; + struct delayed_work pe_volt_tune_work; + struct delayed_work check_pe_tuneup_work; + + struct power_supply_desc usb; + struct power_supply_desc wall; + struct power_supply *batt_psy; + struct power_supply *usb_psy; + struct power_supply *wall_psy; + struct power_supply_config usb_cfg; + struct power_supply_config wall_cfg; +}; + +struct pe_ctrl { + bool enable; + bool tune_up_volt; + bool tune_down_volt; + bool tune_done; + bool tune_fail; + int tune_count; + int target_volt; + int high_volt_level;/* vbus volt > this threshold means tune up successfully */ + int low_volt_level; /* vbus volt < this threshold means tune down successfully */ + int vbat_min_volt; /* to tune up voltage only when vbat > this threshold */ +}; + + +static struct bq2589x *g_bq; +static struct pe_ctrl pe; + +static DEFINE_MUTEX(bq2589x_i2c_lock); + +static int bq2589x_read_byte(struct bq2589x *bq, u8 *data, u8 reg) +{ + int ret; + + mutex_lock(&bq2589x_i2c_lock); + ret = i2c_smbus_read_byte_data(bq->client, reg); + if (ret < 0) { + dev_err(bq->dev, "failed to read 0x%.2x\n", reg); + mutex_unlock(&bq2589x_i2c_lock); + return ret; + } + + *data = (u8)ret; + mutex_unlock(&bq2589x_i2c_lock); + + return 0; +} + +static int bq2589x_write_byte(struct bq2589x *bq, u8 reg, u8 data) +{ + int ret; + mutex_lock(&bq2589x_i2c_lock); + ret = i2c_smbus_write_byte_data(bq->client, reg, data); + mutex_unlock(&bq2589x_i2c_lock); + return ret; +} + +static int bq2589x_update_bits(struct bq2589x *bq, u8 reg, u8 mask, u8 data) +{ + int ret; + u8 tmp; + + ret = bq2589x_read_byte(bq, &tmp, reg); + + if (ret) + return ret; + + tmp &= ~mask; + tmp |= data & mask; + + return bq2589x_write_byte(bq, reg, tmp); +} + + +static enum bq2589x_vbus_type bq2589x_get_vbus_type(struct bq2589x *bq) +{ + u8 val = 0; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0B); + if (ret < 0) + return 0; + val &= BQ2589X_VBUS_STAT_MASK; + val >>= BQ2589X_VBUS_STAT_SHIFT; + + return val; +} + + +static int bq2589x_enable_otg(struct bq2589x *bq) +{ + u8 val = BQ2589X_OTG_ENABLE << BQ2589X_OTG_CONFIG_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_03, + BQ2589X_OTG_CONFIG_MASK, val); + +} + +static int bq2589x_disable_otg(struct bq2589x *bq) +{ + u8 val = BQ2589X_OTG_DISABLE << BQ2589X_OTG_CONFIG_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_03, + BQ2589X_OTG_CONFIG_MASK, val); + +} +EXPORT_SYMBOL_GPL(bq2589x_disable_otg); + +static int bq2589x_set_otg_volt(struct bq2589x *bq, int volt) +{ + u8 val = 0; + + if (volt < BQ2589X_BOOSTV_BASE) + volt = BQ2589X_BOOSTV_BASE; + if (volt > BQ2589X_BOOSTV_BASE + (BQ2589X_BOOSTV_MASK >> BQ2589X_BOOSTV_SHIFT) * BQ2589X_BOOSTV_LSB) + volt = BQ2589X_BOOSTV_BASE + (BQ2589X_BOOSTV_MASK >> BQ2589X_BOOSTV_SHIFT) * BQ2589X_BOOSTV_LSB; + + val = ((volt - BQ2589X_BOOSTV_BASE) / BQ2589X_BOOSTV_LSB) << BQ2589X_BOOSTV_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_0A, BQ2589X_BOOSTV_MASK, val); + +} +EXPORT_SYMBOL_GPL(bq2589x_set_otg_volt); + +static int bq2589x_set_otg_current(struct bq2589x *bq, int curr) +{ + u8 temp; + + if (curr == 500) + temp = BQ2589X_BOOST_LIM_500MA; + else if (curr == 700) + temp = BQ2589X_BOOST_LIM_700MA; + else if (curr == 1100) + temp = BQ2589X_BOOST_LIM_1100MA; + else if (curr == 1600) + temp = BQ2589X_BOOST_LIM_1600MA; + else if (curr == 1800) + temp = BQ2589X_BOOST_LIM_1800MA; + else if (curr == 2100) + temp = BQ2589X_BOOST_LIM_2100MA; + else if (curr == 2400) + temp = BQ2589X_BOOST_LIM_2400MA; + else + temp = BQ2589X_BOOST_LIM_1300MA; + + return bq2589x_update_bits(bq, BQ2589X_REG_0A, BQ2589X_BOOST_LIM_MASK, temp << BQ2589X_BOOST_LIM_SHIFT); +} +EXPORT_SYMBOL_GPL(bq2589x_set_otg_current); + +static int bq2589x_enable_charger(struct bq2589x *bq) +{ + int ret; + u8 val = BQ2589X_CHG_ENABLE << BQ2589X_CHG_CONFIG_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_CHG_CONFIG_MASK, val); + if (ret == 0) + bq->status |= BQ2589X_STATUS_CHARGE_ENABLE; + return ret; +} + +static int bq2589x_disable_charger(struct bq2589x *bq) +{ + int ret; + u8 val = BQ2589X_CHG_DISABLE << BQ2589X_CHG_CONFIG_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_CHG_CONFIG_MASK, val); + if (ret == 0) + bq->status &= ~BQ2589X_STATUS_CHARGE_ENABLE; + return ret; +} +EXPORT_SYMBOL_GPL(bq2589x_disable_charger); + + +/* interfaces that can be called by other module */ +int bq2589x_adc_start(struct bq2589x *bq, bool oneshot) +{ + u8 val; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_02); + if (ret < 0) { + dev_err(bq->dev, "%s failed to read register 0x02:%d\n", __func__, ret); + return ret; + } + + if (((val & BQ2589X_CONV_RATE_MASK) >> BQ2589X_CONV_RATE_SHIFT) == BQ2589X_ADC_CONTINUE_ENABLE) + return 0; /*is doing continuous scan*/ + if (oneshot) + ret = bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_CONV_START_MASK, BQ2589X_CONV_START << BQ2589X_CONV_START_SHIFT); + else + ret = bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_CONV_RATE_MASK, BQ2589X_ADC_CONTINUE_ENABLE << BQ2589X_CONV_RATE_SHIFT); + return ret; +} +EXPORT_SYMBOL_GPL(bq2589x_adc_start); + +int bq2589x_adc_stop(struct bq2589x *bq) +{ + return bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_CONV_RATE_MASK, BQ2589X_ADC_CONTINUE_DISABLE << BQ2589X_CONV_RATE_SHIFT); +} +EXPORT_SYMBOL_GPL(bq2589x_adc_stop); + + +int bq2589x_adc_read_battery_volt(struct bq2589x *bq) +{ + uint8_t val; + int volt; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0E); + if (ret < 0) { + dev_err(bq->dev, "read battery voltage failed :%d\n", ret); + return ret; + } else{ + volt = BQ2589X_BATV_BASE + ((val & BQ2589X_BATV_MASK) >> BQ2589X_BATV_SHIFT) * BQ2589X_BATV_LSB ; + return volt; + } +} +EXPORT_SYMBOL_GPL(bq2589x_adc_read_battery_volt); + + +int bq2589x_adc_read_sys_volt(struct bq2589x *bq) +{ + uint8_t val; + int volt; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0F); + if (ret < 0) { + dev_err(bq->dev, "read system voltage failed :%d\n", ret); + return ret; + } else{ + volt = BQ2589X_SYSV_BASE + ((val & BQ2589X_SYSV_MASK) >> BQ2589X_SYSV_SHIFT) * BQ2589X_SYSV_LSB ; + return volt; + } +} +EXPORT_SYMBOL_GPL(bq2589x_adc_read_sys_volt); + +int bq2589x_adc_read_vbus_volt(struct bq2589x *bq) +{ + uint8_t val; + int volt; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_11); + if (ret < 0) { + dev_err(bq->dev, "read vbus voltage failed :%d\n", ret); + return ret; + } else{ + volt = BQ2589X_VBUSV_BASE + ((val & BQ2589X_VBUSV_MASK) >> BQ2589X_VBUSV_SHIFT) * BQ2589X_VBUSV_LSB ; + return volt; + } +} +EXPORT_SYMBOL_GPL(bq2589x_adc_read_vbus_volt); + +int bq2589x_adc_read_temperature(struct bq2589x *bq) +{ + uint8_t val; + int temp; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_10); + if (ret < 0) { + dev_err(bq->dev, "read temperature failed :%d\n", ret); + return ret; + } else{ + temp = BQ2589X_TSPCT_BASE + ((val & BQ2589X_TSPCT_MASK) >> BQ2589X_TSPCT_SHIFT) * BQ2589X_TSPCT_LSB ; + return temp; + } +} +EXPORT_SYMBOL_GPL(bq2589x_adc_read_temperature); + +int bq2589x_adc_read_charge_current(struct bq2589x *bq) +{ + uint8_t val; + int volt; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_12); + if (ret < 0) { + dev_err(bq->dev, "read charge current failed :%d\n", ret); + return ret; + } else{ + volt = (int)(BQ2589X_ICHGR_BASE + ((val & BQ2589X_ICHGR_MASK) >> BQ2589X_ICHGR_SHIFT) * BQ2589X_ICHGR_LSB) ; + return volt; + } +} +EXPORT_SYMBOL_GPL(bq2589x_adc_read_charge_current); + +int bq2589x_set_chargecurrent(struct bq2589x *bq, int curr) +{ + u8 ichg; + + ichg = (curr - BQ2589X_ICHG_BASE)/BQ2589X_ICHG_LSB; + return bq2589x_update_bits(bq, BQ2589X_REG_04, BQ2589X_ICHG_MASK, ichg << BQ2589X_ICHG_SHIFT); + +} +EXPORT_SYMBOL_GPL(bq2589x_set_chargecurrent); + +int bq2589x_set_term_current(struct bq2589x *bq, int curr) +{ + u8 iterm; + + iterm = (curr - BQ2589X_ITERM_BASE) / BQ2589X_ITERM_LSB; + + return bq2589x_update_bits(bq, BQ2589X_REG_05, BQ2589X_ITERM_MASK, iterm << BQ2589X_ITERM_SHIFT); +} +EXPORT_SYMBOL_GPL(bq2589x_set_term_current); + + +int bq2589x_set_prechg_current(struct bq2589x *bq, int curr) +{ + u8 iprechg; + + iprechg = (curr - BQ2589X_IPRECHG_BASE) / BQ2589X_IPRECHG_LSB; + + return bq2589x_update_bits(bq, BQ2589X_REG_05, BQ2589X_IPRECHG_MASK, iprechg << BQ2589X_IPRECHG_SHIFT); +} +EXPORT_SYMBOL_GPL(bq2589x_set_prechg_current); + +int bq2589x_set_chargevoltage(struct bq2589x *bq, int volt) +{ + u8 val; + + val = (volt - BQ2589X_VREG_BASE)/BQ2589X_VREG_LSB; + return bq2589x_update_bits(bq, BQ2589X_REG_06, BQ2589X_VREG_MASK, val << BQ2589X_VREG_SHIFT); +} +EXPORT_SYMBOL_GPL(bq2589x_set_chargevoltage); + + +int bq2589x_set_input_volt_limit(struct bq2589x *bq, int volt) +{ + u8 val; + val = (volt - BQ2589X_VINDPM_BASE) / BQ2589X_VINDPM_LSB; + return bq2589x_update_bits(bq, BQ2589X_REG_0D, BQ2589X_VINDPM_MASK, val << BQ2589X_VINDPM_SHIFT); +} +EXPORT_SYMBOL_GPL(bq2589x_set_input_volt_limit); + +int bq2589x_set_input_current_limit(struct bq2589x *bq, int curr) +{ + u8 val; + + val = (curr - BQ2589X_IINLIM_BASE) / BQ2589X_IINLIM_LSB; + return bq2589x_update_bits(bq, BQ2589X_REG_00, BQ2589X_IINLIM_MASK, val << BQ2589X_IINLIM_SHIFT); +} +EXPORT_SYMBOL_GPL(bq2589x_set_input_current_limit); + + +int bq2589x_set_vindpm_offset(struct bq2589x *bq, int offset) +{ + u8 val; + + val = (offset - BQ2589X_VINDPMOS_BASE)/BQ2589X_VINDPMOS_LSB; + return bq2589x_update_bits(bq, BQ2589X_REG_01, BQ2589X_VINDPMOS_MASK, val << BQ2589X_VINDPMOS_SHIFT); +} +EXPORT_SYMBOL_GPL(bq2589x_set_vindpm_offset); + +int bq2589x_get_charging_status(struct bq2589x *bq) +{ + u8 val = 0; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0B); + if (ret < 0) { + dev_err(bq->dev, "%s Failed to read register 0x0b:%d\n", __func__, ret); + return ret; + } + val &= BQ2589X_CHRG_STAT_MASK; + val >>= BQ2589X_CHRG_STAT_SHIFT; + return val; +} +EXPORT_SYMBOL_GPL(bq2589x_get_charging_status); + +void bq2589x_set_otg(struct bq2589x *bq, int enable) +{ + int ret; + + if (enable) { + ret = bq2589x_enable_otg(bq); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to enable otg-%d\n", __func__, ret); + return; + } + } else{ + ret = bq2589x_disable_otg(bq); + if (ret < 0) + dev_err(bq->dev, "%s:Failed to disable otg-%d\n", __func__, ret); + } +} +EXPORT_SYMBOL_GPL(bq2589x_set_otg); + +int bq2589x_set_watchdog_timer(struct bq2589x *bq, u8 timeout) +{ + return bq2589x_update_bits(bq, BQ2589X_REG_07, BQ2589X_WDT_MASK, (u8)((timeout - BQ2589X_WDT_BASE) / BQ2589X_WDT_LSB) << BQ2589X_WDT_SHIFT); +} +EXPORT_SYMBOL_GPL(bq2589x_set_watchdog_timer); + +int bq2589x_disable_watchdog_timer(struct bq2589x *bq) +{ + u8 val = BQ2589X_WDT_DISABLE << BQ2589X_WDT_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_07, BQ2589X_WDT_MASK, val); +} +EXPORT_SYMBOL_GPL(bq2589x_disable_watchdog_timer); + +int bq2589x_reset_watchdog_timer(struct bq2589x *bq) +{ + u8 val = BQ2589X_WDT_RESET << BQ2589X_WDT_RESET_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_WDT_RESET_MASK, val); +} +EXPORT_SYMBOL_GPL(bq2589x_reset_watchdog_timer); + +int bq2589x_force_dpdm(struct bq2589x *bq) +{ + int ret; + u8 val = BQ2589X_FORCE_DPDM << BQ2589X_FORCE_DPDM_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_FORCE_DPDM_MASK, val); + if (ret) + return ret; + + msleep(20);/*TODO: how much time needed to finish dpdm detect?*/ + return 0; + +} +EXPORT_SYMBOL_GPL(bq2589x_force_dpdm); + +int bq2589x_reset_chip(struct bq2589x *bq) +{ + int ret; + u8 val = BQ2589X_RESET << BQ2589X_RESET_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_14, BQ2589X_RESET_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2589x_reset_chip); + +int bq2589x_enter_ship_mode(struct bq2589x *bq) +{ + int ret; + u8 val = BQ2589X_BATFET_OFF << BQ2589X_BATFET_DIS_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_09, BQ2589X_BATFET_DIS_MASK, val); + return ret; + +} +EXPORT_SYMBOL_GPL(bq2589x_enter_ship_mode); + +int bq2589x_enter_hiz_mode(struct bq2589x *bq) +{ + u8 val = BQ2589X_HIZ_ENABLE << BQ2589X_ENHIZ_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_00, BQ2589X_ENHIZ_MASK, val); + +} +EXPORT_SYMBOL_GPL(bq2589x_enter_hiz_mode); + +int bq2589x_exit_hiz_mode(struct bq2589x *bq) +{ + + u8 val = BQ2589X_HIZ_DISABLE << BQ2589X_ENHIZ_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_00, BQ2589X_ENHIZ_MASK, val); + +} +EXPORT_SYMBOL_GPL(bq2589x_exit_hiz_mode); + +int bq2589x_get_hiz_mode(struct bq2589x *bq, u8 *state) +{ + u8 val; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_00); + if (ret) + return ret; + *state = (val & BQ2589X_ENHIZ_MASK) >> BQ2589X_ENHIZ_SHIFT; + + return 0; +} +EXPORT_SYMBOL_GPL(bq2589x_get_hiz_mode); + + +int bq2589x_pumpx_enable(struct bq2589x *bq, int enable) +{ + u8 val; + int ret; + + if (enable) + val = BQ2589X_PUMPX_ENABLE << BQ2589X_EN_PUMPX_SHIFT; + else + val = BQ2589X_PUMPX_DISABLE << BQ2589X_EN_PUMPX_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_04, BQ2589X_EN_PUMPX_MASK, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2589x_pumpx_enable); + +int bq2589x_pumpx_increase_volt(struct bq2589x *bq) +{ + u8 val; + int ret; + + val = BQ2589X_PUMPX_UP << BQ2589X_PUMPX_UP_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_09, BQ2589X_PUMPX_UP_MASK, val); + + return ret; + +} +EXPORT_SYMBOL_GPL(bq2589x_pumpx_increase_volt); + +int bq2589x_pumpx_increase_volt_done(struct bq2589x *bq) +{ + u8 val; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_09); + if (ret) + return ret; + + if (val & BQ2589X_PUMPX_UP_MASK) + return 1; /* not finished*/ + else + return 0; /* pumpx up finished*/ + +} +EXPORT_SYMBOL_GPL(bq2589x_pumpx_increase_volt_done); + +int bq2589x_pumpx_decrease_volt(struct bq2589x *bq) +{ + u8 val; + int ret; + + val = BQ2589X_PUMPX_DOWN << BQ2589X_PUMPX_DOWN_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_09, BQ2589X_PUMPX_DOWN_MASK, val); + + return ret; + +} +EXPORT_SYMBOL_GPL(bq2589x_pumpx_decrease_volt); + +int bq2589x_pumpx_decrease_volt_done(struct bq2589x *bq) +{ + u8 val; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_09); + if (ret) + return ret; + + if (val & BQ2589X_PUMPX_DOWN_MASK) + return 1; /* not finished*/ + else + return 0; /* pumpx down finished*/ + +} +EXPORT_SYMBOL_GPL(bq2589x_pumpx_decrease_volt_done); + +static int bq2589x_force_ico(struct bq2589x *bq) +{ + u8 val; + int ret; + + val = BQ2589X_FORCE_ICO << BQ2589X_FORCE_ICO_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_09, BQ2589X_FORCE_ICO_MASK, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2589x_force_ico); + +static int bq2589x_check_force_ico_done(struct bq2589x *bq) +{ + u8 val; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_14); + if (ret) + return ret; + + if (val & BQ2589X_ICO_OPTIMIZED_MASK) + return 1; /*finished*/ + else + return 0; /* in progress*/ +} +EXPORT_SYMBOL_GPL(bq2589x_check_force_ico_done); + +static int bq2589x_enable_term(struct bq2589x* bq, bool enable) +{ + u8 val; + int ret; + + if (enable) + val = BQ2589X_TERM_ENABLE << BQ2589X_EN_TERM_SHIFT; + else + val = BQ2589X_TERM_DISABLE << BQ2589X_EN_TERM_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_07, BQ2589X_EN_TERM_MASK, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2589x_enable_term); + +static int bq2589x_enable_auto_dpdm(struct bq2589x* bq, bool enable) +{ + u8 val; + int ret; + + if (enable) + val = BQ2589X_AUTO_DPDM_ENABLE << BQ2589X_AUTO_DPDM_EN_SHIFT; + else + val = BQ2589X_AUTO_DPDM_DISABLE << BQ2589X_AUTO_DPDM_EN_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_AUTO_DPDM_EN_MASK, val); + + return ret; + +} +EXPORT_SYMBOL_GPL(bq2589x_enable_auto_dpdm); + +static int bq2589x_use_absolute_vindpm(struct bq2589x* bq, bool enable) +{ + u8 val; + int ret; + + if (enable) + val = BQ2589X_FORCE_VINDPM_ENABLE << BQ2589X_FORCE_VINDPM_SHIFT; + else + val = BQ2589X_FORCE_VINDPM_DISABLE << BQ2589X_FORCE_VINDPM_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_0D, BQ2589X_FORCE_VINDPM_MASK, val); + + return ret; + +} +EXPORT_SYMBOL_GPL(bq2589x_use_absolute_vindpm); + +static int bq2589x_enable_ico(struct bq2589x* bq, bool enable) +{ + u8 val; + int ret; + + if (enable) + val = BQ2589X_ICO_ENABLE << BQ2589X_ICOEN_SHIFT; + else + val = BQ2589X_ICO_DISABLE << BQ2589X_ICOEN_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_ICOEN_MASK, val); + + return ret; + +} +EXPORT_SYMBOL_GPL(bq2589x_enable_ico); + + +static int bq2589x_read_idpm_limit(struct bq2589x *bq) +{ + uint8_t val; + int curr; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_13); + if (ret < 0) { + dev_err(bq->dev, "read vbus voltage failed :%d\n", ret); + return ret; + } else{ + curr = BQ2589X_IDPM_LIM_BASE + ((val & BQ2589X_IDPM_LIM_MASK) >> BQ2589X_IDPM_LIM_SHIFT) * BQ2589X_IDPM_LIM_LSB ; + return curr; + } +} +EXPORT_SYMBOL_GPL(bq2589x_read_idpm_limit); + +static bool bq2589x_is_charge_done(struct bq2589x *bq) +{ + int ret; + u8 val; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0B); + if (ret < 0) { + dev_err(bq->dev, "%s:read REG0B failed :%d\n", __func__, ret); + return false; + } + val &= BQ2589X_CHRG_STAT_MASK; + val >>= BQ2589X_CHRG_STAT_SHIFT; + + return (val == BQ2589X_CHRG_STAT_CHGDONE); +} +EXPORT_SYMBOL_GPL(bq2589x_is_charge_done); + +static int bq2589x_init_device(struct bq2589x *bq) +{ + int ret; + + /*common initialization*/ + + bq2589x_disable_watchdog_timer(bq); + + bq2589x_enable_auto_dpdm(bq, bq->cfg.enable_auto_dpdm); + bq2589x_enable_term(bq, bq->cfg.enable_term); + bq2589x_enable_ico(bq, bq->cfg.enable_ico); + /*force use absolute vindpm if auto_dpdm not enabled*/ + if (!bq->cfg.enable_auto_dpdm) + bq->cfg.use_absolute_vindpm = true; + bq2589x_use_absolute_vindpm(bq, bq->cfg.use_absolute_vindpm); + + + ret = bq2589x_set_vindpm_offset(bq, 600); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to set vindpm offset:%d\n", __func__, ret); + return ret; + } + + ret = bq2589x_set_term_current(bq, bq->cfg.term_current); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to set termination current:%d\n", __func__, ret); + return ret; + } + + ret = bq2589x_set_chargevoltage(bq, bq->cfg.charge_voltage); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to set charge voltage:%d\n", __func__, ret); + return ret; + } + + ret = bq2589x_set_chargecurrent(bq, bq->cfg.charge_current); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to set charge current:%d\n", __func__, ret); + return ret; + } + + ret = bq2589x_enable_charger(bq); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to enable charger:%d\n", __func__, ret); + return ret; + } + + bq2589x_adc_start(bq, false); + + ret = bq2589x_pumpx_enable(bq, 1); + if (ret) { + dev_err(bq->dev, "%s:Failed to enable pumpx:%d\n", __func__, ret); + return ret; + } + + bq2589x_set_watchdog_timer(bq, 160); + + return ret; +} + + +static int bq2589x_charge_status(struct bq2589x *bq) +{ + u8 val = 0; + + bq2589x_read_byte(bq, &val, BQ2589X_REG_0B); + val &= BQ2589X_CHRG_STAT_MASK; + val >>= BQ2589X_CHRG_STAT_SHIFT; + switch (val) { + case BQ2589X_CHRG_STAT_FASTCHG: + return POWER_SUPPLY_CHARGE_TYPE_FAST; + case BQ2589X_CHRG_STAT_PRECHG: + return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + case BQ2589X_CHRG_STAT_CHGDONE: + case BQ2589X_CHRG_STAT_IDLE: + return POWER_SUPPLY_CHARGE_TYPE_NONE; + default: + return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } +} + +static enum power_supply_property bq2589x_charger_props[] = { + POWER_SUPPLY_PROP_CHARGE_TYPE, /* Charger status output */ + POWER_SUPPLY_PROP_ONLINE, /* External power source */ +}; + + +static int bq2589x_usb_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + + struct bq2589x *bq = power_supply_get_drvdata(psy); + u8 type = bq2589x_get_vbus_type(bq); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (type == BQ2589X_VBUS_USB_SDP || type == BQ2589X_VBUS_USB_DCP) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = bq2589x_charge_status(bq); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int bq2589x_wall_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + + struct bq2589x *bq = power_supply_get_drvdata(psy); + u8 type = bq2589x_get_vbus_type(bq); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (type == BQ2589X_VBUS_MAXC || type == BQ2589X_VBUS_UNKNOWN || type == BQ2589X_VBUS_NONSTAND) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = bq2589x_charge_status(bq); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int bq2589x_psy_register(struct bq2589x *bq) +{ + int ret; + + bq->usb.name = "bq2589x-usb"; + bq->usb.type = POWER_SUPPLY_TYPE_USB; + bq->usb.properties = bq2589x_charger_props; + bq->usb.num_properties = ARRAY_SIZE(bq2589x_charger_props); + bq->usb.get_property = bq2589x_usb_get_property; + bq->usb.external_power_changed = NULL; + + bq->usb_cfg.drv_data = bq; + bq->usb_cfg.of_node = bq->dev->of_node; + + bq->usb_psy = power_supply_register(bq->dev, &bq->usb, &bq->usb_cfg); + if (IS_ERR(bq->usb_psy)) { + ret = PTR_ERR(bq->usb_psy); + dev_err(bq->dev, "%s:failed to register usb psy:%d\n", __func__, ret); + return ret; + } + + bq->wall.name = "bq2589x-Wall"; + bq->wall.type = POWER_SUPPLY_TYPE_MAINS; + bq->wall.properties = bq2589x_charger_props; + bq->wall.num_properties = ARRAY_SIZE(bq2589x_charger_props); + bq->wall.get_property = bq2589x_wall_get_property; + bq->wall.external_power_changed = NULL; + + bq->wall_cfg.drv_data = bq; + bq->wall_cfg.of_node = bq->dev->of_node; + + bq->wall_psy = power_supply_register(bq->dev, &bq->wall, &bq->wall_cfg); + if (IS_ERR(bq->wall_psy)) { + ret = PTR_ERR(bq->usb_psy); + dev_err(bq->dev, "%s:failed to register wall psy:%d\n", __func__, ret); + goto fail_1; + } + + return 0; + +fail_1: + power_supply_unregister(bq->usb_psy); + + return ret; +} + +static void bq2589x_psy_unregister(struct bq2589x *bq) +{ + power_supply_unregister(bq->usb_psy); + power_supply_unregister(bq->wall_psy); +} + +static ssize_t bq2589x_show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 addr; + u8 val; + u8 tmpbuf[300]; + int len; + int idx = 0; + int ret ; + + idx = snprintf(buf, PAGE_SIZE, "%s:\n", "Charger 1"); + for (addr = 0x0; addr <= 0x14; addr++) { + ret = bq2589x_read_byte(g_bq, &val, addr); + if (ret == 0) { + len = snprintf(tmpbuf, PAGE_SIZE - idx,"Reg[0x%.2x] = 0x%.2x\n", addr, val); + memcpy(&buf[idx], tmpbuf, len); + idx += len; + } + } + + return idx; +} + +static DEVICE_ATTR(registers, S_IRUGO, bq2589x_show_registers, NULL); + +static struct attribute *bq2589x_attributes[] = { + &dev_attr_registers.attr, + NULL, +}; + +static const struct attribute_group bq2589x_attr_group = { + .attrs = bq2589x_attributes, +}; + + +static int bq2589x_parse_dt(struct device *dev, struct bq2589x *bq) +{ + int ret; + struct device_node *np = dev->of_node; + + ret = of_property_read_u32(np, "ti,bq2589x,vbus-volt-high-level", &pe.high_volt_level); + if (ret) + return ret; + + ret = of_property_read_u32(np, "ti,bq2589x,vbus-volt-low-level", &pe.low_volt_level); + if (ret) + return ret; + + ret = of_property_read_u32(np, "ti,bq2589x,vbat-min-volt-to-tuneup", &pe.vbat_min_volt); + if (ret) + return ret; + + bq->cfg.enable_auto_dpdm = of_property_read_bool(np, "ti,bq2589x,enable-auto-dpdm"); + bq->cfg.enable_term = of_property_read_bool(np, "ti,bq2589x,enable-termination"); + bq->cfg.enable_ico = of_property_read_bool(np, "ti,bq2589x,enable-ico"); + bq->cfg.use_absolute_vindpm = of_property_read_bool(np, "ti,bq2589x,use-absolute-vindpm"); + + ret = of_property_read_u32(np, "ti,bq2589x,charge-voltage",&bq->cfg.charge_voltage); + if (ret) + return ret; + + ret = of_property_read_u32(np, "ti,bq2589x,charge-current",&bq->cfg.charge_current); + if (ret) + return ret; + + ret = of_property_read_u32(np, "ti,bq2589x,term-current",&bq->cfg.term_current); + if (ret) + return ret; + + return 0; +} + +static int bq2589x_detect_device(struct bq2589x *bq) +{ + int ret; + u8 data; + + ret = bq2589x_read_byte(bq, &data, BQ2589X_REG_14); + if (ret == 0) { + bq->part_no = (data & BQ2589X_PN_MASK) >> BQ2589X_PN_SHIFT; + bq->revision = (data & BQ2589X_DEV_REV_MASK) >> BQ2589X_DEV_REV_SHIFT; + } + + return ret; +} + +static int bq2589x_read_batt_rsoc(struct bq2589x *bq) +{ + union power_supply_propval ret = {0,}; + + if (!bq->batt_psy) + bq->batt_psy = power_supply_get_by_name("battery"); + + if (bq->batt_psy) { + power_supply_get_property(bq->batt_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); + return ret.intval; + } else { + return 50; + } +} + +static void bq2589x_adjust_absolute_vindpm(struct bq2589x *bq) +{ + u16 vbus_volt; + u16 vindpm_volt; + int ret; + + ret = bq2589x_disable_charger(bq); + if (ret < 0) { + dev_err(bq->dev,"%s:failed to disable charger\n",__func__); + /*return;*/ + } + /* wait for new adc data */ + msleep(1000); + vbus_volt = bq2589x_adc_read_vbus_volt(bq); + ret = bq2589x_enable_charger(bq); + if (ret < 0) { + dev_err(bq->dev, "%s:failed to enable charger\n",__func__); + return; + } + + if (vbus_volt < 6000) + vindpm_volt = vbus_volt - 600; + else + vindpm_volt = vbus_volt - 1200; + ret = bq2589x_set_input_volt_limit(bq, vindpm_volt); + if (ret < 0) + dev_err(bq->dev, "%s:Set absolute vindpm threshold %d Failed:%d\n", __func__, vindpm_volt, ret); + else + dev_info(bq->dev, "%s:Set absolute vindpm threshold %d successfully\n", __func__, vindpm_volt); + +} + +static void bq2589x_adapter_in_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, adapter_in_work); + int ret; + + if (bq->vbus_type == BQ2589X_VBUS_MAXC) { + dev_info(bq->dev, "%s:HVDCP or Maxcharge adapter plugged in\n", __func__); + ret = bq2589x_set_chargecurrent(bq, bq->cfg.charge_current); + if (ret < 0) + dev_err(bq->dev, "%s:Failed to set charge current:%d\n", __func__, ret); + else + dev_info(bq->dev, "%s: Set charge current to %dmA successfully\n",__func__,bq->cfg.charge_current); + schedule_delayed_work(&bq->ico_work, 0); + } else if (bq->vbus_type == BQ2589X_VBUS_USB_DCP) {/* DCP, let's check if it is PE adapter*/ + dev_info(bq->dev, "%s:usb dcp adapter plugged in\n", __func__); + ret = bq2589x_set_chargecurrent(bq, bq->cfg.charge_current); + if (ret < 0) + dev_err(bq->dev, "%s:Failed to set charge current:%d\n", __func__, ret); + else + dev_info(bq->dev, "%s: Set charge current to %dmA successfully\n",__func__,bq->cfg.charge_current); + schedule_delayed_work(&bq->check_pe_tuneup_work, 0); + } else if (bq->vbus_type == BQ2589X_VBUS_USB_SDP || bq->vbus_type == BQ2589X_VBUS_UNKNOWN) { + if (bq->vbus_type == BQ2589X_VBUS_USB_SDP) + dev_info(bq->dev, "%s:host SDP plugged in\n", __func__); + else + dev_info(bq->dev, "%s:unknown adapter plugged in\n", __func__); + + ret = bq2589x_set_chargecurrent(bq, 500); + if (ret < 0) + dev_err(bq->dev, "%s:Failed to set charge current:%d\n", __func__, ret); + else + dev_info(bq->dev, "%s: Set charge current to %dmA successfully\n",__func__,500); + } + else { + dev_info(bq->dev, "%s:other adapter plugged in,vbus_type is %d\n", __func__, bq->vbus_type); + ret = bq2589x_set_chargecurrent(bq, 1000); + if (ret < 0) + dev_err(bq->dev, "%s:Failed to set charge current:%d\n", __func__, ret); + else + dev_info(bq->dev, "%s: Set charge current to %dmA successfully\n",__func__,1000); + schedule_delayed_work(&bq->ico_work, 0); + } + + if (bq->cfg.use_absolute_vindpm) + bq2589x_adjust_absolute_vindpm(bq); + + if (pe.enable) { + schedule_delayed_work(&bq->monitor_work, 0); + } +} + +static void bq2589x_adapter_out_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, adapter_out_work); + int ret; + + ret = bq2589x_set_input_volt_limit(bq, 4400); + if (ret < 0) + dev_err(bq->dev,"%s:reset vindpm threshold to 4400 failed:%d\n",__func__,ret); + else + dev_info(bq->dev,"%s:reset vindpm threshold to 4400 successfully\n",__func__); + + if (pe.enable) { + cancel_delayed_work_sync(&bq->monitor_work); + } +} + +static void bq2589x_ico_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, ico_work.work); + int ret; + int idpm; + u8 status; + static bool ico_issued; + + if (!ico_issued) { + ret = bq2589x_force_ico(bq); + if (ret < 0) { + schedule_delayed_work(&bq->ico_work, HZ); /* retry 1 second later*/ + dev_info(bq->dev, "%s:ICO command issued failed:%d\n", __func__, ret); + } else { + ico_issued = true; + schedule_delayed_work(&bq->ico_work, 3 * HZ); + dev_info(bq->dev, "%s:ICO command issued successfully\n", __func__); + } + } else { + ico_issued = false; + ret = bq2589x_check_force_ico_done(bq); + if (ret) {/*ico done*/ + ret = bq2589x_read_byte(bq, &status, BQ2589X_REG_13); + if (ret == 0) { + idpm = ((status & BQ2589X_IDPM_LIM_MASK) >> BQ2589X_IDPM_LIM_SHIFT) * BQ2589X_IDPM_LIM_LSB + BQ2589X_IDPM_LIM_BASE; + dev_info(bq->dev, "%s:ICO done, result is:%d mA\n", __func__, idpm); + } + } + } +} + +static void bq2589x_check_pe_tuneup_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, check_pe_tuneup_work.work); + + if (!pe.enable) { + schedule_delayed_work(&bq->ico_work, 0); + return; + } + + bq->vbat_volt = bq2589x_adc_read_battery_volt(bq); + bq->rsoc = bq2589x_read_batt_rsoc(bq); + + if (bq->vbat_volt > pe.vbat_min_volt && bq->rsoc < 95) { + dev_info(bq->dev, "%s:trying to tune up vbus voltage\n", __func__); + pe.target_volt = pe.high_volt_level; + pe.tune_up_volt = true; + pe.tune_down_volt = false; + pe.tune_done = false; + pe.tune_count = 0; + pe.tune_fail = false; + schedule_delayed_work(&bq->pe_volt_tune_work, 0); + } else if (bq->rsoc >= 95) { + schedule_delayed_work(&bq->ico_work, 0); + } else { + /* wait battery voltage up enough to check again */ + schedule_delayed_work(&bq->check_pe_tuneup_work, 2*HZ); + } +} + +static void bq2589x_tune_volt_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, pe_volt_tune_work.work); + int ret; + static bool pumpx_cmd_issued; + + bq->vbus_volt = bq2589x_adc_read_vbus_volt(bq); + + dev_info(bq->dev, "%s:vbus voltage:%d, Tune Target Volt:%d\n", __func__, bq->vbus_volt, pe.target_volt); + + if ((pe.tune_up_volt && bq->vbus_volt > pe.target_volt) || + (pe.tune_down_volt && bq->vbus_volt < pe.target_volt)) { + dev_info(bq->dev, "%s:voltage tune successfully\n", __func__); + pe.tune_done = true; + bq2589x_adjust_absolute_vindpm(bq); + if (pe.tune_up_volt) + schedule_delayed_work(&bq->ico_work, 0); + return; + } + + if (pe.tune_count > 10) { + dev_info(bq->dev, "%s:voltage tune failed,reach max retry count\n", __func__); + pe.tune_fail = true; + bq2589x_adjust_absolute_vindpm(bq); + + if (pe.tune_up_volt) + schedule_delayed_work(&bq->ico_work, 0); + return; + } + + if (!pumpx_cmd_issued) { + if (pe.tune_up_volt) + ret = bq2589x_pumpx_increase_volt(bq); + else if (pe.tune_down_volt) + ret = bq2589x_pumpx_decrease_volt(bq); + if (ret) { + schedule_delayed_work(&bq->pe_volt_tune_work, HZ); + } else { + dev_info(bq->dev, "%s:pumpx command issued.\n", __func__); + pumpx_cmd_issued = true; + pe.tune_count++; + schedule_delayed_work(&bq->pe_volt_tune_work, 3*HZ); + } + } else { + if (pe.tune_up_volt) + ret = bq2589x_pumpx_increase_volt_done(bq); + else if (pe.tune_down_volt) + ret = bq2589x_pumpx_decrease_volt_done(bq); + if (ret == 0) { + dev_info(bq->dev, "%s:pumpx command finishedd!\n", __func__); + bq2589x_adjust_absolute_vindpm(bq); + pumpx_cmd_issued = 0; + } + schedule_delayed_work(&bq->pe_volt_tune_work, HZ); + } +} + + +static void bq2589x_monitor_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, monitor_work.work); + u8 status = 0; + int ret; + int chg_current; + + bq2589x_reset_watchdog_timer(bq); + + bq->rsoc = bq2589x_read_batt_rsoc(bq); + + bq->vbus_volt = bq2589x_adc_read_vbus_volt(bq); + bq->vbat_volt = bq2589x_adc_read_battery_volt(bq); + chg_current = bq2589x_adc_read_charge_current(bq); + + dev_info(bq->dev, "%s:vbus volt:%d,vbat volt:%d,charge current:%d\n", __func__,bq->vbus_volt,bq->vbat_volt,chg_current); + + ret = bq2589x_read_byte(bq, &status, BQ2589X_REG_13); + if (ret == 0 && (status & BQ2589X_VDPM_STAT_MASK)) + dev_info(bq->dev, "%s:VINDPM occurred\n", __func__); + if (ret == 0 && (status & BQ2589X_IDPM_STAT_MASK)) + dev_info(bq->dev, "%s:IINDPM occurred\n", __func__); + + if (bq->vbus_type == BQ2589X_VBUS_USB_DCP && bq->vbus_volt > pe.high_volt_level && + bq->rsoc > 95 && !pe.tune_down_volt) { + pe.tune_down_volt = true; + pe.tune_up_volt = false; + pe.target_volt = pe.low_volt_level; + pe.tune_done = false; + pe.tune_count = 0; + pe.tune_fail = false; + schedule_delayed_work(&bq->pe_volt_tune_work, 0); + } + + /* read temperature,or any other check if need to decrease charge current*/ + + schedule_delayed_work(&bq->monitor_work, 10 * HZ); +} + + + +static void bq2589x_charger_irq_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, irq_work); + u8 status = 0; + u8 fault = 0; + u8 charge_status = 0; + int ret; + + msleep(5); + + /* Read STATUS and FAULT registers */ + ret = bq2589x_read_byte(bq, &status, BQ2589X_REG_0B); + if (ret) + return; + + ret = bq2589x_read_byte(bq, &fault, BQ2589X_REG_0C); + if (ret) + return; + + bq->vbus_type = (status & BQ2589X_VBUS_STAT_MASK) >> BQ2589X_VBUS_STAT_SHIFT; + + if (((bq->vbus_type == BQ2589X_VBUS_NONE) || (bq->vbus_type == BQ2589X_VBUS_OTG)) && (bq->status & BQ2589X_STATUS_PLUGIN)) { + dev_info(bq->dev, "%s:adapter removed\n", __func__); + bq->status &= ~BQ2589X_STATUS_PLUGIN; + schedule_work(&bq->adapter_out_work); + } else if (bq->vbus_type != BQ2589X_VBUS_NONE && (bq->vbus_type != BQ2589X_VBUS_OTG) && !(bq->status & BQ2589X_STATUS_PLUGIN)) { + dev_info(bq->dev, "%s:adapter plugged in\n", __func__); + bq->status |= BQ2589X_STATUS_PLUGIN; + schedule_work(&bq->adapter_in_work); + } + + + if ((status & BQ2589X_PG_STAT_MASK) && !(bq->status & BQ2589X_STATUS_PG)) + bq->status |= BQ2589X_STATUS_PG; + else if (!(status & BQ2589X_PG_STAT_MASK) && (bq->status & BQ2589X_STATUS_PG)) + bq->status &= ~BQ2589X_STATUS_PG; + + if (fault && !(bq->status & BQ2589X_STATUS_FAULT)) + bq->status |= BQ2589X_STATUS_FAULT; + else if (!fault && (bq->status & BQ2589X_STATUS_FAULT)) + bq->status &= ~BQ2589X_STATUS_FAULT; + + charge_status = (status & BQ2589X_CHRG_STAT_MASK) >> BQ2589X_CHRG_STAT_SHIFT; + if (charge_status == BQ2589X_CHRG_STAT_IDLE) + dev_info(bq->dev, "%s:not charging\n", __func__); + else if (charge_status == BQ2589X_CHRG_STAT_PRECHG) + dev_info(bq->dev, "%s:precharging\n", __func__); + else if (charge_status == BQ2589X_CHRG_STAT_FASTCHG) + dev_info(bq->dev, "%s:fast charging\n", __func__); + else if (charge_status == BQ2589X_CHRG_STAT_CHGDONE) + dev_info(bq->dev, "%s:charge done!\n", __func__); + + if (fault) + dev_info(bq->dev, "%s:charge fault:%02x\n", __func__,fault); +} + + +static irqreturn_t bq2589x_charger_interrupt(int irq, void *data) +{ + struct bq2589x *bq = data; + + schedule_work(&bq->irq_work); + return IRQ_HANDLED; +} + + +//#define GPIO_IRQ 42 +static int bq2589x_charger_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bq2589x *bq; + //int irqn; + + int ret; + + bq = devm_kzalloc(&client->dev, sizeof(struct bq2589x), GFP_KERNEL); + if (!bq) { + dev_err(&client->dev, "%s: out of memory\n", __func__); + return -ENOMEM; + } + + bq->dev = &client->dev; + bq->client = client; + i2c_set_clientdata(client, bq); + + ret = bq2589x_detect_device(bq); + if (!ret && bq->part_no == BQ25890) { + bq->status |= BQ2589X_STATUS_EXIST; + dev_info(bq->dev, "%s: charger device bq25890 detected, revision:%d\n", __func__, bq->revision); + } else { + dev_info(bq->dev, "%s: no bq25890 charger device found:%d\n", __func__, ret); + return -ENODEV; + } + + bq->batt_psy = power_supply_get_by_name("battery"); + + g_bq = bq; + + if (client->dev.of_node) + bq2589x_parse_dt(&client->dev, bq); + + ret = bq2589x_init_device(bq); + if (ret) { + dev_err(bq->dev, "device init failure: %d\n", ret); + goto err_0; + } +#if 0 + ret = gpio_request(GPIO_IRQ, "bq2589x irq pin"); + if (ret) { + dev_err(bq->dev, "%s: %d gpio request failed\n", __func__, GPIO_IRQ); + goto err_0; + } + gpio_direction_input(GPIO_IRQ); + + irqn = gpio_to_irq(GPIO_IRQ); + if (irqn < 0) { + dev_err(bq->dev, "%s:%d gpio_to_irq failed\n", __func__, irqn); + ret = irqn; + goto err_1; + } + client->irq = irqn; +#endif + ret = bq2589x_psy_register(bq); + if (ret) + goto err_0; + + INIT_WORK(&bq->irq_work, bq2589x_charger_irq_workfunc); + INIT_WORK(&bq->adapter_in_work, bq2589x_adapter_in_workfunc); + INIT_WORK(&bq->adapter_out_work, bq2589x_adapter_out_workfunc); + INIT_DELAYED_WORK(&bq->monitor_work, bq2589x_monitor_workfunc); + INIT_DELAYED_WORK(&bq->ico_work, bq2589x_ico_workfunc); + INIT_DELAYED_WORK(&bq->pe_volt_tune_work, bq2589x_tune_volt_workfunc); + INIT_DELAYED_WORK(&bq->check_pe_tuneup_work, bq2589x_check_pe_tuneup_workfunc); + + + ret = sysfs_create_group(&bq->dev->kobj, &bq2589x_attr_group); + if (ret) { + dev_err(bq->dev, "failed to register sysfs. err: %d\n", ret); + goto err_irq; + } + + ret = request_irq(client->irq, bq2589x_charger_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "bq2589x_charger1_irq", bq); + if (ret) { + dev_err(bq->dev, "%s:Request IRQ %d failed: %d\n", __func__, client->irq, ret); + goto err_irq; + } else { + dev_info(bq->dev, "%s:irq = %d\n", __func__, client->irq); + } + + pe.enable = false; + schedule_work(&bq->irq_work);/*in case of adapter has been in when power off*/ + return 0; + +err_irq: + cancel_work_sync(&bq->irq_work); + cancel_work_sync(&bq->adapter_in_work); + cancel_work_sync(&bq->adapter_out_work); + cancel_delayed_work_sync(&bq->ico_work); + cancel_delayed_work_sync(&bq->check_pe_tuneup_work); + if (pe.enable) { + cancel_delayed_work_sync(&bq->pe_volt_tune_work); + cancel_delayed_work_sync(&bq->monitor_work); + } +//err_1: +// gpio_free(GPIO_IRQ); +err_0: + g_bq = NULL; + return ret; +} + +static void bq2589x_charger_shutdown(struct i2c_client *client) +{ + struct bq2589x *bq = i2c_get_clientdata(client); + + dev_info(bq->dev, "%s: shutdown\n", __func__); + + bq2589x_psy_unregister(bq); + + sysfs_remove_group(&bq->dev->kobj, &bq2589x_attr_group); + cancel_work_sync(&bq->irq_work); + cancel_work_sync(&bq->adapter_in_work); + cancel_work_sync(&bq->adapter_out_work); + cancel_delayed_work_sync(&bq->ico_work); + cancel_delayed_work_sync(&bq->check_pe_tuneup_work); + if (pe.enable) { + cancel_delayed_work_sync(&bq->pe_volt_tune_work); + cancel_delayed_work_sync(&bq->monitor_work); + } + + free_irq(bq->client->irq, NULL); + gpio_free(GPIO_IRQ); + g_bq = NULL; +} + +static struct of_device_id bq2589x_charger_match_table[] = { + {.compatible = "ti,bq2589x-1",}, + {}, +}; + + +static const struct i2c_device_id bq2589x_charger_id[] = { + { "bq2589x-1", BQ25890 }, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, bq2589x_charger_id); + +static struct i2c_driver bq2589x_charger_driver = { + .driver = { + .name = "bq2589x-1", + .of_match_table = bq2589x_charger_match_table, + }, + .id_table = bq2589x_charger_id, + + .probe = bq2589x_charger_probe, + .shutdown = bq2589x_charger_shutdown, +}; + +module_i2c_driver(bq2589x_charger_driver); + +MODULE_DESCRIPTION("TI BQ2589x Charger Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); \ No newline at end of file diff --git a/drivers/power/bq2589x_chg_mmi/bq2589x_reg.h b/drivers/power/bq2589x_chg_mmi/bq2589x_reg.h new file mode 100644 index 000000000000..9bf40abdee97 --- /dev/null +++ b/drivers/power/bq2589x_chg_mmi/bq2589x_reg.h @@ -0,0 +1,363 @@ + +#ifndef __BQ2589X_HEADER__ +#define __BQ2589X_HEADER__ + +/* Register 00h */ +#define BQ2589X_REG_00 0x00 +#define BQ2589X_ENHIZ_MASK 0x80 +#define BQ2589X_ENHIZ_SHIFT 7 +#define BQ2589X_HIZ_ENABLE 1 +#define BQ2589X_HIZ_DISABLE 0 +#define BQ2589X_ENILIM_MASK 0x40 +#define BQ2589X_ENILIM_SHIFT 6 +#define BQ2589X_ENILIM_ENABLE 1 +#define BQ2589X_ENILIM_DISABLE 0 + +#define BQ2589X_IINLIM_MASK 0x3F +#define BQ2589X_IINLIM_SHIFT 0 +#define BQ2589X_IINLIM_BASE 100 +#define BQ2589X_IINLIM_LSB 50 + +/* Register 01h */ +#define BQ2589X_REG_01 0x01 +#define BQ2589X_BHOT_MASK 0xC0 +#define BQ2589X_BHOT_SHIFT 6 +#define BQ2589X_BCOLD_MASK 0x20 +#define BQ2589X_BCOLD_SHIFT 5 +#define BQ2589X_VINDPMOS_MASK 0x1F +#define BQ2589X_VINDPMOS_SHIFT 0 + +#define BQ2589X_VINDPMOS_BASE 0 +#define BQ2589X_VINDPMOS_LSB 100 + + +/* Register 0x02 */ +#define BQ2589X_REG_02 0x02 +#define BQ2589X_CONV_START_MASK 0x80 +#define BQ2589X_CONV_START_SHIFT 7 +#define BQ2589X_CONV_START 0 +#define BQ2589X_CONV_RATE_MASK 0x40 +#define BQ2589X_CONV_RATE_SHIFT 6 +#define BQ2589X_ADC_CONTINUE_ENABLE 1 +#define BQ2589X_ADC_CONTINUE_DISABLE 0 + +#define BQ2589X_BOOST_FREQ_MASK 0x20 +#define BQ2589X_BOOST_FREQ_SHIFT 5 +#define BQ2589X_BOOST_FREQ_1500K 0 +#define BQ2589X_BOOST_FREQ_500K 0 + +#define BQ2589X_ICOEN_MASK 0x10 +#define BQ2589X_ICOEN_SHIFT 4 +#define BQ2589X_ICO_ENABLE 1 +#define BQ2589X_ICO_DISABLE 0 +#define BQ2589X_HVDCPEN_MASK 0x08 +#define BQ2589X_HVDCPEN_SHIFT 3 +#define BQ2589X_HVDCP_ENABLE 1 +#define BQ2589X_HVDCP_DISABLE 0 +#define BQ2589X_MAXCEN_MASK 0x04 +#define BQ2589X_MAXCEN_SHIFT 2 +#define BQ2589X_MAXC_ENABLE 1 +#define BQ2589X_MAXC_DISABLE 0 + +#define BQ2589X_FORCE_DPDM_MASK 0x02 +#define BQ2589X_FORCE_DPDM_SHIFT 1 +#define BQ2589X_FORCE_DPDM 1 +#define BQ2589X_AUTO_DPDM_EN_MASK 0x01 +#define BQ2589X_AUTO_DPDM_EN_SHIFT 0 +#define BQ2589X_AUTO_DPDM_ENABLE 1 +#define BQ2589X_AUTO_DPDM_DISABLE 0 + + +/* Register 0x03 */ +#define BQ2589X_REG_03 0x03 +#define BQ2589X_BAT_LOADEN_MASK 0x80 +#define BQ2589X_BAT_LOAEN_SHIFT 7 +#define BQ2589X_WDT_RESET_MASK 0x40 +#define BQ2589X_WDT_RESET_SHIFT 6 +#define BQ2589X_WDT_RESET 1 + +#define BQ2589X_OTG_CONFIG_MASK 0x20 +#define BQ2589X_OTG_CONFIG_SHIFT 5 +#define BQ2589X_OTG_ENABLE 1 +#define BQ2589X_OTG_DISABLE 0 + +#define BQ2589X_CHG_CONFIG_MASK 0x10 +#define BQ2589X_CHG_CONFIG_SHIFT 4 +#define BQ2589X_CHG_ENABLE 1 +#define BQ2589X_CHG_DISABLE 0 + + +#define BQ2589X_SYS_MINV_MASK 0x0E +#define BQ2589X_SYS_MINV_SHIFT 1 + +#define BQ2589X_SYS_MINV_BASE 3000 +#define BQ2589X_SYS_MINV_LSB 100 + + +/* Register 0x04*/ +#define BQ2589X_REG_04 0x04 +#define BQ2589X_EN_PUMPX_MASK 0x80 +#define BQ2589X_EN_PUMPX_SHIFT 7 +#define BQ2589X_PUMPX_ENABLE 1 +#define BQ2589X_PUMPX_DISABLE 0 +#define BQ2589X_ICHG_MASK 0x7F +#define BQ2589X_ICHG_SHIFT 0 +#define BQ2589X_ICHG_BASE 0 +#define BQ2589X_ICHG_LSB 64 + +/* Register 0x05*/ +#define BQ2589X_REG_05 0x05 +#define BQ2589X_IPRECHG_MASK 0xF0 +#define BQ2589X_IPRECHG_SHIFT 4 +#define BQ2589X_ITERM_MASK 0x0F +#define BQ2589X_ITERM_SHIFT 0 +#define BQ2589X_IPRECHG_BASE 64 +#define BQ2589X_IPRECHG_LSB 64 +#define BQ2589X_ITERM_BASE 64 +#define BQ2589X_ITERM_LSB 64 + +/* Register 0x06*/ +#define BQ2589X_REG_06 0x06 +#define BQ2589X_VREG_MASK 0xFC +#define BQ2589X_VREG_SHIFT 2 +#define BQ2589X_BATLOWV_MASK 0x02 +#define BQ2589X_BATLOWV_SHIFT 1 +#define BQ2589X_BATLOWV_2800MV 0 +#define BQ2589X_BATLOWV_3000MV 1 +#define BQ2589X_VRECHG_MASK 0x01 +#define BQ2589X_VRECHG_SHIFT 0 +#define BQ2589X_VRECHG_100MV 0 +#define BQ2589X_VRECHG_200MV 1 +#define BQ2589X_VREG_BASE 3840 +#define BQ2589X_VREG_LSB 16 + +/* Register 0x07*/ +#define BQ2589X_REG_07 0x07 +#define BQ2589X_EN_TERM_MASK 0x80 +#define BQ2589X_EN_TERM_SHIFT 7 +#define BQ2589X_TERM_ENABLE 1 +#define BQ2589X_TERM_DISABLE 0 + +#define BQ2589X_WDT_MASK 0x30 +#define BQ2589X_WDT_SHIFT 4 +#define BQ2589X_WDT_DISABLE 0 +#define BQ2589X_WDT_40S 1 +#define BQ2589X_WDT_80S 2 +#define BQ2589X_WDT_160S 3 +#define BQ2589X_WDT_BASE 0 +#define BQ2589X_WDT_LSB 40 + +#define BQ2589X_EN_TIMER_MASK 0x08 +#define BQ2589X_EN_TIMER_SHIFT 3 + +#define BQ2589X_CHG_TIMER_ENABLE 1 +#define BQ2589X_CHG_TIMER_DISABLE 0 + +#define BQ2589X_CHG_TIMER_MASK 0x06 +#define BQ2589X_CHG_TIMER_SHIFT 1 +#define BQ2589X_CHG_TIMER_5HOURS 0 +#define BQ2589X_CHG_TIMER_8HOURS 1 +#define BQ2589X_CHG_TIMER_12HOURS 2 +#define BQ2589X_CHG_TIMER_20HOURS 3 + +#define BQ2589X_JEITA_ISET_MASK 0x01 +#define BQ2589X_JEITA_ISET_SHIFT 0 +#define BQ2589X_JEITA_ISET_50PCT 0 +#define BQ2589X_JEITA_ISET_20PCT 1 + + +/* Register 0x08*/ +#define BQ2589X_REG_08 0x08 +#define BQ2589X_BAT_COMP_MASK 0xE0 +#define BQ2589X_BAT_COMP_SHIFT 5 +#define BQ2589X_VCLAMP_MASK 0x1C +#define BQ2589X_VCLAMP_SHIFT 2 +#define BQ2589X_TREG_MASK 0x03 +#define BQ2589X_TREG_SHIFT 0 +#define BQ2589X_TREG_60C 0 +#define BQ2589X_TREG_80C 1 +#define BQ2589X_TREG_100C 2 +#define BQ2589X_TREG_120C 3 + +#define BQ2589X_BAT_COMP_BASE 0 +#define BQ2589X_BAT_COMP_LSB 20 +#define BQ2589X_VCLAMP_BASE 0 +#define BQ2589X_VCLAMP_LSB 32 + + +/* Register 0x09*/ +#define BQ2589X_REG_09 0x09 +#define BQ2589X_FORCE_ICO_MASK 0x80 +#define BQ2589X_FORCE_ICO_SHIFT 7 +#define BQ2589X_FORCE_ICO 1 +#define BQ2589X_TMR2X_EN_MASK 0x40 +#define BQ2589X_TMR2X_EN_SHIFT 6 +#define BQ2589X_BATFET_DIS_MASK 0x20 +#define BQ2589X_BATFET_DIS_SHIFT 5 +#define BQ2589X_BATFET_OFF 1 + +#define BQ2589X_JEITA_VSET_MASK 0x10 +#define BQ2589X_JEITA_VSET_SHIFT 4 +#define BQ2589X_JEITA_VSET_N150MV 0 +#define BQ2589X_JEITA_VSET_VREG 1 +#define BQ2589X_BATFET_RST_EN_MASK 0x04 +#define BQ2589X_BATFET_RST_EN_SHIFT 2 +#define BQ2589X_PUMPX_UP_MASK 0x02 +#define BQ2589X_PUMPX_UP_SHIFT 1 +#define BQ2589X_PUMPX_UP 1 +#define BQ2589X_PUMPX_DOWN_MASK 0x01 +#define BQ2589X_PUMPX_DOWN_SHIFT 0 +#define BQ2589X_PUMPX_DOWN 1 + + +/* Register 0x0A*/ +#define BQ2589X_REG_0A 0x0A +#define BQ2589X_BOOSTV_MASK 0xF0 +#define BQ2589X_BOOSTV_SHIFT 4 +#define BQ2589X_BOOSTV_BASE 4550 +#define BQ2589X_BOOSTV_LSB 64 + + +#define BQ2589X_BOOST_LIM_MASK 0x07 +#define BQ2589X_BOOST_LIM_SHIFT 0 +#define BQ2589X_BOOST_LIM_500MA 0x00 +#define BQ2589X_BOOST_LIM_700MA 0x01 +#define BQ2589X_BOOST_LIM_1100MA 0x02 +#define BQ2589X_BOOST_LIM_1300MA 0x03 +#define BQ2589X_BOOST_LIM_1600MA 0x04 +#define BQ2589X_BOOST_LIM_1800MA 0x05 +#define BQ2589X_BOOST_LIM_2100MA 0x06 +#define BQ2589X_BOOST_LIM_2400MA 0x07 + + +/* Register 0x0B*/ +#define BQ2589X_REG_0B 0x0B +#define BQ2589X_VBUS_STAT_MASK 0xE0 +#define BQ2589X_VBUS_STAT_SHIFT 5 +#define BQ2589X_CHRG_STAT_MASK 0x18 +#define BQ2589X_CHRG_STAT_SHIFT 3 +#define BQ2589X_CHRG_STAT_IDLE 0 +#define BQ2589X_CHRG_STAT_PRECHG 1 +#define BQ2589X_CHRG_STAT_FASTCHG 2 +#define BQ2589X_CHRG_STAT_CHGDONE 3 + +#define BQ2589X_PG_STAT_MASK 0x04 +#define BQ2589X_PG_STAT_SHIFT 2 +#define BQ2589X_SDP_STAT_MASK 0x02 +#define BQ2589X_SDP_STAT_SHIFT 1 +#define BQ2589X_VSYS_STAT_MASK 0x01 +#define BQ2589X_VSYS_STAT_SHIFT 0 + + +/* Register 0x0C*/ +#define BQ2589X_REG_0C 0x0c +#define BQ2589X_FAULT_WDT_MASK 0x80 +#define BQ2589X_FAULT_WDT_SHIFT 7 +#define BQ2589X_FAULT_BOOST_MASK 0x40 +#define BQ2589X_FAULT_BOOST_SHIFT 6 +#define BQ2589X_FAULT_CHRG_MASK 0x30 +#define BQ2589X_FAULT_CHRG_SHIFT 4 +#define BQ2589X_FAULT_CHRG_NORMAL 0 +#define BQ2589X_FAULT_CHRG_INPUT 1 +#define BQ2589X_FAULT_CHRG_THERMAL 2 +#define BQ2589X_FAULT_CHRG_TIMER 3 + +#define BQ2589X_FAULT_BAT_MASK 0x08 +#define BQ2589X_FAULT_BAT_SHIFT 3 +#define BQ2589X_FAULT_NTC_MASK 0x07 +#define BQ2589X_FAULT_NTC_SHIFT 0 +#define BQ2589X_FAULT_NTC_TSCOLD 1 +#define BQ2589X_FAULT_NTC_TSHOT 2 + +#define BQ2589X_FAULT_NTC_WARM 2 +#define BQ2589X_FAULT_NTC_COOL 3 +#define BQ2589X_FAULT_NTC_COLD 5 +#define BQ2589X_FAULT_NTC_HOT 6 + + +/* Register 0x0D*/ +#define BQ2589X_REG_0D 0x0D +#define BQ2589X_FORCE_VINDPM_MASK 0x80 +#define BQ2589X_FORCE_VINDPM_SHIFT 7 +#define BQ2589X_FORCE_VINDPM_ENABLE 1 +#define BQ2589X_FORCE_VINDPM_DISABLE 0 +#define BQ2589X_VINDPM_MASK 0x7F +#define BQ2589X_VINDPM_SHIFT 0 + +#define BQ2589X_VINDPM_BASE 2600 +#define BQ2589X_VINDPM_LSB 100 + + +/* Register 0x0E*/ +#define BQ2589X_REG_0E 0x0E +#define BQ2589X_THERM_STAT_MASK 0x80 +#define BQ2589X_THERM_STAT_SHIFT 7 +#define BQ2589X_BATV_MASK 0x7F +#define BQ2589X_BATV_SHIFT 0 +#define BQ2589X_BATV_BASE 2304 +#define BQ2589X_BATV_LSB 20 + + +/* Register 0x0F*/ +#define BQ2589X_REG_0F 0x0F +#define BQ2589X_SYSV_MASK 0x7F +#define BQ2589X_SYSV_SHIFT 0 +#define BQ2589X_SYSV_BASE 2304 +#define BQ2589X_SYSV_LSB 20 + + +/* Register 0x10*/ +#define BQ2589X_REG_10 0x10 +#define BQ2589X_TSPCT_MASK 0x7F +#define BQ2589X_TSPCT_SHIFT 0 +#define BQ2589X_TSPCT_BASE 21 +#define BQ2589X_TSPCT_LSB 465//should be 0.465,kernel does not support float + +/* Register 0x11*/ +#define BQ2589X_REG_11 0x11 +#define BQ2589X_VBUS_GD_MASK 0x80 +#define BQ2589X_VBUS_GD_SHIFT 7 +#define BQ2589X_VBUSV_MASK 0x7F +#define BQ2589X_VBUSV_SHIFT 0 +#define BQ2589X_VBUSV_BASE 2600 +#define BQ2589X_VBUSV_LSB 100 + + +/* Register 0x12*/ +#define BQ2589X_REG_12 0x12 +#define BQ2589X_ICHGR_MASK 0x7F +#define BQ2589X_ICHGR_SHIFT 0 +#define BQ2589X_ICHGR_BASE 0 +#define BQ2589X_ICHGR_LSB 50 + + +/* Register 0x13*/ +#define BQ2589X_REG_13 0x13 +#define BQ2589X_VDPM_STAT_MASK 0x80 +#define BQ2589X_VDPM_STAT_SHIFT 7 +#define BQ2589X_IDPM_STAT_MASK 0x40 +#define BQ2589X_IDPM_STAT_SHIFT 6 +#define BQ2589X_IDPM_LIM_MASK 0x3F +#define BQ2589X_IDPM_LIM_SHIFT 0 +#define BQ2589X_IDPM_LIM_BASE 100 +#define BQ2589X_IDPM_LIM_LSB 50 + + +/* Register 0x14*/ +#define BQ2589X_REG_14 0x14 +#define BQ2589X_RESET_MASK 0x80 +#define BQ2589X_RESET_SHIFT 7 +#define BQ2589X_RESET 1 +#define BQ2589X_ICO_OPTIMIZED_MASK 0x40 +#define BQ2589X_ICO_OPTIMIZED_SHIFT 6 +#define BQ2589X_PN_MASK 0x38 +#define BQ2589X_PN_SHIFT 3 +#define BQ2589X_TS_PROFILE_MASK 0x04 +#define BQ2589X_TS_PROFILE_SHIFT 2 +#define BQ2589X_DEV_REV_MASK 0x03 +#define BQ2589X_DEV_REV_SHIFT 0 + + + + +#endif