summaryrefslogtreecommitdiff
path: root/driver
diff options
context:
space:
mode:
authorScott Collyer <scollyer@google.com>2018-07-08 12:52:33 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-07-29 17:10:16 -0700
commitd290053f9544a1938b1a0a578fa551855d4738d2 (patch)
treeebc47ab7f4195f305ef715aaaf42bb001ba0cc90 /driver
parent768f0d90c5291b355eceed9620d148388000d7f4 (diff)
downloadchrome-ec-d290053f9544a1938b1a0a578fa551855d4738d2.tar.gz
bq25710: Add bq25710 charger driver
This CL adds a driver for the bq25710 charger chip. This is mostly copied from the bq25703 driver which is very similar. But, the bq25710 uses smbus registers instead of i2c. BUG=b:111410058 BRANCH=none TEST=make buildall Change-Id: Ic9068fdcfe91f3dd3840244ca21d2210b64c099f Signed-off-by: Scott Collyer <scollyer@google.com> Reviewed-on: https://chromium-review.googlesource.com/1135927 Commit-Ready: Scott Collyer <scollyer@chromium.org> Tested-by: Scott Collyer <scollyer@chromium.org> Reviewed-by: Furquan Shaikh <furquan@chromium.org> Reviewed-by: Jett Rink <jettrink@chromium.org>
Diffstat (limited to 'driver')
-rw-r--r--driver/build.mk1
-rw-r--r--driver/charger/bq25710.c383
-rw-r--r--driver/charger/bq25710.h67
3 files changed, 451 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg))
+ 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, &reg);
+ } 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, &reg))
+ 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 */