diff options
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | driver/charger/bq25710.c | 383 | ||||
-rw-r--r-- | driver/charger/bq25710.h | 67 | ||||
-rw-r--r-- | include/config.h | 1 |
4 files changed, 452 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk index 6d0990bcec..00dbf7beb4 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -52,6 +52,7 @@ driver-$(CONFIG_CHARGER_BQ24738)+=charger/bq24738.o driver-$(CONFIG_CHARGER_BQ24770)+=charger/bq24773.o driver-$(CONFIG_CHARGER_BQ24773)+=charger/bq24773.o driver-$(CONFIG_CHARGER_BQ25703)+=charger/bq25703.o +driver-$(CONFIG_CHARGER_BQ25710)+=charger/bq25710.o driver-$(CONFIG_CHARGER_BQ25890)+=charger/bq2589x.o driver-$(CONFIG_CHARGER_BQ25892)+=charger/bq2589x.o driver-$(CONFIG_CHARGER_BQ25895)+=charger/bq2589x.o diff --git a/driver/charger/bq25710.c b/driver/charger/bq25710.c new file mode 100644 index 0000000000..a3f5d878c4 --- /dev/null +++ b/driver/charger/bq25710.c @@ -0,0 +1,383 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * TI bq25710 battery charger driver. + */ + +#include "battery_smart.h" +#include "bq25710.h" +#include "charger.h" +#include "common.h" +#include "console.h" +#include "i2c.h" +#include "timer.h" + +/* Sense resistor configurations and macros */ +#define DEFAULT_SENSE_RESISTOR 10 + +#define INPUT_RESISTOR_RATIO \ + ((CONFIG_CHARGER_SENSE_RESISTOR_AC) / DEFAULT_SENSE_RESISTOR) +#define REG_TO_INPUT_CURRENT(REG) ((REG + 1) * 50 / INPUT_RESISTOR_RATIO) +#define INPUT_CURRENT_TO_REG(CUR) (((CUR) * INPUT_RESISTOR_RATIO / 50) - 1) + +#define CHARGING_RESISTOR_RATIO \ + ((CONFIG_CHARGER_SENSE_RESISTOR) / DEFAULT_SENSE_RESISTOR) +#define REG_TO_CHARGING_CURRENT(REG) ((REG) / CHARGING_RESISTOR_RATIO) +#define CHARGING_CURRENT_TO_REG(CUR) ((CUR) * CHARGING_RESISTOR_RATIO) + +/* Console output macros */ +#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args) + +/* Charger parameters */ +static const struct charger_info bq25710_charger_info = { + .name = "bq25710", + .voltage_max = 19200, + .voltage_min = 1024, + .voltage_step = 16, + .current_max = 8128 / CHARGING_RESISTOR_RATIO, + .current_min = 64 / CHARGING_RESISTOR_RATIO, + .current_step = 64 / CHARGING_RESISTOR_RATIO, + .input_current_max = 6400 / INPUT_RESISTOR_RATIO, + .input_current_min = 50 / INPUT_RESISTOR_RATIO, + .input_current_step = 50 / INPUT_RESISTOR_RATIO, +}; + +static inline int raw_read16(int offset, int *value) +{ + return i2c_read16(I2C_PORT_CHARGER, BQ25710_SMBUS_ADDR1, offset, value); +} + +static inline int raw_write16(int offset, int value) +{ + return i2c_write16(I2C_PORT_CHARGER, BQ25710_SMBUS_ADDR1, offset, + value); +} + +#ifdef CONFIG_CHARGE_RAMP_HW +static int bq25710_get_low_power_mode(int *mode) +{ + int rv; + int reg; + + rv = raw_read16(BQ25710_REG_CHARGE_OPTION_0, ®); + if (rv) + return rv; + + *mode = !!(reg & BQ25710_CHARGE_OPTION_0_LOW_POWER_MODE); + + return EC_SUCCESS; +} + +static int bq25710_set_low_power_mode(int enable) +{ + int rv; + int reg; + + rv = raw_read16(BQ25710_REG_CHARGE_OPTION_0, ®); + if (rv) + return rv; + + if (enable) + reg |= BQ25710_CHARGE_OPTION_0_LOW_POWER_MODE; + else + reg &= ~BQ25710_CHARGE_OPTION_0_LOW_POWER_MODE; + + rv = raw_write16(BQ25710_REG_CHARGE_OPTION_0, reg); + if (rv) + return rv; + + return EC_SUCCESS; +} +#endif + +/* Charger interfaces */ + +const struct charger_info *charger_get_info(void) +{ + return &bq25710_charger_info; +} + +int charger_post_init(void) +{ + /* + * Note: bq25710 power on reset state is: + * watch dog timer = 175 sec + * input current limit = ~1/2 maximum setting + * charging voltage = 0 mV + * charging current = 0 mA + * discharge on AC = disabled + */ + + /* Set charger input current limit */ + return charger_set_input_current(CONFIG_CHARGER_INPUT_CURRENT); +} + +int charger_get_status(int *status) +{ + int rv; + int option; + + rv = charger_get_option(&option); + if (rv) + return rv; + + /* Default status */ + *status = CHARGER_LEVEL_2; + + if (option & BQ25710_CHARGE_OPTION_0_CHRG_INHIBIT) + *status |= CHARGER_CHARGE_INHIBITED; + + return EC_SUCCESS; +} + +int charger_set_mode(int mode) +{ + int rv; + int option; + + rv = charger_get_option(&option); + if (rv) + return rv; + + if (mode & CHARGER_CHARGE_INHIBITED) + option |= BQ25710_CHARGE_OPTION_0_CHRG_INHIBIT; + else + option &= ~BQ25710_CHARGE_OPTION_0_CHRG_INHIBIT; + + return charger_set_option(option); +} + +int charger_enable_otg_power(int enabled) +{ + /* This is controlled with the EN_OTG pin. Support not added yet. */ + return EC_ERROR_UNIMPLEMENTED; +} + +int charger_set_otg_current_voltage(int output_current, int output_voltage) +{ + /* Add when needed. */ + return EC_ERROR_UNIMPLEMENTED; +} + +int charger_is_sourcing_otg_power(int port) +{ + /* Add when needed. */ + return EC_ERROR_UNIMPLEMENTED; +} + +int charger_get_current(int *current) +{ + int rv, reg; + + rv = raw_read16(BQ25710_REG_CHARGE_CURRENT, ®); + if (!rv) + *current = REG_TO_CHARGING_CURRENT(reg); + + return rv; +} + +int charger_set_current(int current) +{ + return raw_write16(BQ25710_REG_CHARGE_CURRENT, + CHARGING_CURRENT_TO_REG(current)); +} + +/* Get/set charge voltage limit in mV */ +int charger_get_voltage(int *voltage) +{ + return raw_read16(BQ25710_REG_MAX_CHARGE_VOLTAGE, voltage); +} + +int charger_set_voltage(int voltage) +{ + return raw_write16(BQ25710_REG_MAX_CHARGE_VOLTAGE, voltage); +} + +/* Discharge battery when on AC power. */ +int charger_discharge_on_ac(int enable) +{ + int rv, option; + + rv = charger_get_option(&option); + if (rv) + return rv; + + if (enable) + option |= BQ25710_CHARGE_OPTION_0_EN_LEARN; + else + option &= ~BQ25710_CHARGE_OPTION_0_EN_LEARN; + + return charger_set_option(option); +} + +int charger_set_input_current(int input_current) +{ + int num_steps = INPUT_CURRENT_TO_REG(input_current); + + return raw_write16(BQ25710_REG_IIN_HOST, num_steps << + BQ25710_CHARGE_IIN_BIT_0FFSET); +} + +int charger_get_input_current(int *input_current) +{ + int rv, reg; + + /* + * IIN_DPM register reflects the actual input current limit programmed + * in the register, either from host or from ICO. After ICO, the + * current limit used by DPM regulation may differ from the IIN_HOST + * register settings. + */ + rv = raw_read16(BQ25710_REG_IIN_DPM, ®); + if (!rv) + *input_current = + REG_TO_INPUT_CURRENT((reg >> + BQ25710_CHARGE_IIN_BIT_0FFSET)); + + return rv; +} + +int charger_manufacturer_id(int *id) +{ + return raw_read16(BQ25710_REG_MANUFACTURER_ID, id); +} + +int charger_device_id(int *id) +{ + return raw_read16(BQ25710_REG_DEVICE_ADDRESS, id); +} + +int charger_get_option(int *option) +{ + /* There are 4 option registers, but we only need the first for now. */ + return raw_read16(BQ25710_REG_CHARGE_OPTION_0, option); +} + +int charger_set_option(int option) +{ + /* There are 4 option registers, but we only need the first for now. */ + return raw_write16(BQ25710_REG_CHARGE_OPTION_0, option); +} + +#ifdef CONFIG_CHARGE_RAMP_HW + +static void bq25710_chg_ramp_handle(void) +{ + int ramp_curr; + + /* + * Once the charge ramp is stable write back the stable ramp + * current to input current register. + */ + if (chg_ramp_is_stable()) { + ramp_curr = chg_ramp_get_current_limit(); + if (ramp_curr && !charger_set_input_current(ramp_curr)) + CPRINTF("stable ramp current=%d\n", ramp_curr); + } +} +DECLARE_DEFERRED(bq25710_chg_ramp_handle); + +int charger_set_hw_ramp(int enable) +{ + int option3_reg, option2_reg, rv; + + rv = raw_read16(BQ25710_REG_CHARGE_OPTION_3, &option3_reg); + if (rv) + return rv; + rv = raw_read16(BQ25710_REG_CHARGE_OPTION_2, &option2_reg); + if (rv) + return rv; + + if (enable) { + /* Set InputVoltage register to BC1.2 minimum ramp voltage */ + rv = raw_write16(BQ25710_REG_INPUT_VOLTAGE, + BQ25710_BC12_MIN_VOLTAGE_MV); + if (rv) + return rv; + + /* Enable ICO algorithm */ + option3_reg |= BQ25710_CHARGE_OPTION_3_EN_ICO_MODE; + + /* 0b: Input current limit is set by BQ25710_REG_IIN_HOST */ + option2_reg &= ~BQ25710_CHARGE_OPTION_2_EN_EXTILIM; + + /* Charge ramp may take up to 2s to settle down */ + hook_call_deferred(&bq25710_chg_ramp_handle_data, (4 * SECOND)); + } else { + /* Disable ICO algorithm */ + option3_reg &= ~BQ25710_CHARGE_OPTION_3_EN_ICO_MODE; + + /* + * 1b: Input current limit is set by the lower value of + * ILIM_HIZ pin and BQ25710_REG_IIN_HOST + */ + option2_reg |= BQ25710_CHARGE_OPTION_2_EN_EXTILIM; + } + + rv = raw_write16(BQ25710_REG_CHARGE_OPTION_2, option2_reg); + if (rv) + return rv; + return raw_write16(BQ25710_REG_CHARGE_OPTION_3, option3_reg); +} + +int chg_ramp_is_stable(void) +{ + int reg; + + if (raw_read16(BQ25710_REG_CHARGER_STATUS, ®)) + return 0; + + return reg & BQ25710_CHARGE_STATUS_ICO_DONE; +} + +int chg_ramp_get_current_limit(void) +{ + int reg; + int mode; + int tries_left = 8; + + /* Save current mode to restore same state after ADC read */ + if (bq25710_get_low_power_mode(&mode)) + goto error; + + /* Exit low power mode so ADC conversion takes typical time */ + if (bq25710_set_low_power_mode(0)) + goto error; + + /* Turn on the ADC for one reading */ + reg = BQ25710_ADC_OPTION_ADC_START | BQ25710_ADC_OPTION_EN_ADC_IIN; + if (raw_write16(BQ25710_REG_ADC_OPTION, reg)) + goto error; + + /* + * Wait until the ADC operation completes. The spec says typical + * conversion time is 10 msec. If low power mode isn't exited first, + * then the conversion time jumps to ~60 msec. + */ + do { + msleep(2); + raw_read16(BQ25710_REG_ADC_OPTION, ®); + } while (--tries_left && (reg & BQ25710_ADC_OPTION_ADC_START)); + + /* ADC reading attempt complete, go back to low power mode */ + if (bq25710_set_low_power_mode(mode)) + goto error; + + /* Could not complete read */ + if (reg & BQ25710_ADC_OPTION_ADC_START) + goto error; + + /* Read ADC value */ + if (raw_read16(BQ25710_REG_ADC_CMPIN_IIN, ®)) + goto error; + + /* LSB => 50mA */ + return (reg >> BQ25710_ADC_IIN_STEP_BIT_OFFSET) * + BQ25710_ADC_IIN_STEP_MA; + +error: + CPRINTF("Could not read input current limit ADC!\n"); + return 0; +} +#endif /* CONFIG_CHARGE_RAMP_HW */ diff --git a/driver/charger/bq25710.h b/driver/charger/bq25710.h new file mode 100644 index 0000000000..317bfee074 --- /dev/null +++ b/driver/charger/bq25710.h @@ -0,0 +1,67 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * TI bq25710 battery charger driver. + */ + +#ifndef __CROS_EC_BQ25710_H +#define __CROS_EC_BQ25710_H + +/* SMBUS Interface */ +#define BQ25710_SMBUS_ADDR1 0x12 + +#define BQ25703_BC12_MIN_VOLTAGE_MV 1408 + +/* Registers */ +#define BQ25710_REG_CHARGE_OPTION_0 0x12 +#define BQ25710_REG_CHARGE_CURRENT 0x14 +#define BQ25710_REG_MAX_CHARGE_VOLTAGE 0x15 +#define BQ25710_REG_CHARGE_OPTION_1 0x30 +#define BQ25710_REG_CHARGE_OPTION_2 0x31 +#define BQ25710_REG_CHARGE_OPTION_3 0x32 +#define BQ25710_REG_PROCHOT_OPTION_0 0x33 +#define BQ25710_REG_PROCHOT_OPTION_1 0x34 +#define BQ25710_REG_ADC_OPTION 0x35 +#define BQ25710_REG_CHARGER_STATUS 0x20 +#define BQ25710_REG_PROCHOT_STATUS 0x21 +#define BQ25710_REG_IIN_DPM 0x22 +#define BQ25710_REG_ADC_VBUS_PSYS 0x23 +#define BQ25710_REG_ADC_IBAT 0x24 +#define BQ25710_REG_ADC_CMPIN_IIN 0x25 +#define BQ25710_REG_ADC_VSYS_VBAT 0x2C +#define BQ25710_REG_OTG_VOLTAGE 0x3B +#define BQ25710_REG_OTG_CURRENT 0x3C +#define BQ25710_REG_INPUT_VOLTAGE 0x3D +#define BQ25710_REG_MIN_SYSTEM_VOLTAGE 0x3E +#define BQ25710_REG_IIN_HOST 0x3F +#define BQ25710_REG_MANUFACTURER_ID 0xFE +#define BQ25710_REG_DEVICE_ADDRESS 0xFF + +/* ChargeOption0 Register */ +#define BQ25710_CHARGE_OPTION_0_LOW_POWER_MODE (1 << 15) +#define BQ25710_CHARGE_OPTION_0_EN_LEARN (1 << 5) +#define BQ25710_CHARGE_OPTION_0_CHRG_INHIBIT (1 << 0) + +/* ChargeOption2 Register */ +#define BQ25710_CHARGE_OPTION_2_EN_EXTILIM (1 << 7) + +/* ChargeOption3 Register */ +#define BQ25710_CHARGE_OPTION_3_EN_ICO_MODE (1 << 11) + +/* ChargeStatus Register */ +#define BQ25710_CHARGE_STATUS_ICO_DONE (1 << 14) + +/* IIN_DPM Register */ +#define BQ25710_CHARGE_IIN_BIT_0FFSET 8 +#define BQ25710_CHARGE_MA_PER_STEP 50 + +/* ADCOption Register */ +#define BQ25710_ADC_OPTION_ADC_START (1 << 14) +#define BQ25710_ADC_OPTION_EN_ADC_IIN (1 << 4) + +/* ADCIIN Register */ +#define BQ25710_ADC_IIN_STEP_MA 50 +#define BQ25710_ADC_IIN_STEP_BIT_OFFSET 8 + +#endif /* __CROS_EC_BQ25710_H */ diff --git a/include/config.h b/include/config.h index bc215c6f7d..2d17c2501b 100644 --- a/include/config.h +++ b/include/config.h @@ -572,6 +572,7 @@ #undef CONFIG_CHARGER_BQ24770 #undef CONFIG_CHARGER_BQ24773 #undef CONFIG_CHARGER_BQ25703 +#undef CONFIG_CHARGER_BQ25710 #undef CONFIG_CHARGER_BQ25890 #undef CONFIG_CHARGER_BQ25892 #undef CONFIG_CHARGER_BQ25895 |