From a15ad7f92e8532360dce656f3e952041746a10c4 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Tue, 7 Oct 2025 19:55:17 +1000 Subject: [PATCH 1/5] charger: bq25180: rename to `bq2518x` Rename the bq25180 implementation and files to the more generic bq2518x. This charger family contains the bq25180, bq25186, bq25188 and the standalone (non-I2C) bq25185. The register maps are practically identical, so the driver should be re-used. Signed-off-by: Jordan Yates --- drivers/charger/CMakeLists.txt | 2 +- drivers/charger/Kconfig | 2 +- .../{Kconfig.bq25180 => Kconfig.bq2518x} | 6 +- drivers/charger/charger_bq25180.c | 398 ------------------ drivers/charger/charger_bq2518x.c | 392 +++++++++++++++++ dts/bindings/charger/ti,bq25180.yaml | 29 +- dts/bindings/charger/ti,bq2518x-common.yaml | 28 ++ 7 files changed, 427 insertions(+), 430 deletions(-) rename drivers/charger/{Kconfig.bq25180 => Kconfig.bq2518x} (58%) delete mode 100644 drivers/charger/charger_bq25180.c create mode 100644 drivers/charger/charger_bq2518x.c create mode 100644 dts/bindings/charger/ti,bq2518x-common.yaml diff --git a/drivers/charger/CMakeLists.txt b/drivers/charger/CMakeLists.txt index d1c835b9581a2..51611a02d60a7 100644 --- a/drivers/charger/CMakeLists.txt +++ b/drivers/charger/CMakeLists.txt @@ -5,7 +5,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/charger.h) zephyr_library_sources_ifdef(CONFIG_CHARGER_AXP2101 charger_axp2101.c) zephyr_library_sources_ifdef(CONFIG_CHARGER_BQ24190 charger_bq24190.c) -zephyr_library_sources_ifdef(CONFIG_CHARGER_BQ25180 charger_bq25180.c) +zephyr_library_sources_ifdef(CONFIG_CHARGER_BQ2518X charger_bq2518x.c) zephyr_library_sources_ifdef(CONFIG_CHARGER_BQ25713 charger_bq25713.c) zephyr_library_sources_ifdef(CONFIG_CHARGER_MAX20335 charger_max20335.c) zephyr_library_sources_ifdef(CONFIG_CHARGER_PF1550 charger_pf1550.c) diff --git a/drivers/charger/Kconfig b/drivers/charger/Kconfig index d1291088d475d..9f22cb2fdc4a0 100644 --- a/drivers/charger/Kconfig +++ b/drivers/charger/Kconfig @@ -54,7 +54,7 @@ endmenu source "drivers/charger/Kconfig.axp2101" source "drivers/charger/Kconfig.sbs_charger" source "drivers/charger/Kconfig.bq24190" -source "drivers/charger/Kconfig.bq25180" +source "drivers/charger/Kconfig.bq2518x" source "drivers/charger/Kconfig.bq25713" source "drivers/charger/Kconfig.max20335" source "drivers/charger/Kconfig.pf1550" diff --git a/drivers/charger/Kconfig.bq25180 b/drivers/charger/Kconfig.bq2518x similarity index 58% rename from drivers/charger/Kconfig.bq25180 rename to drivers/charger/Kconfig.bq2518x index 44bdc9b32ee49..c1a6aeb0434b4 100644 --- a/drivers/charger/Kconfig.bq25180 +++ b/drivers/charger/Kconfig.bq2518x @@ -2,10 +2,10 @@ # # SPDX-License-Identifier: Apache-2.0 -config CHARGER_BQ25180 - bool "BQ25180 Battery Charger" +config CHARGER_BQ2518X + bool "BQ2518x Battery Charger" default y depends on DT_HAS_TI_BQ25180_ENABLED select I2C help - Enable BQ25180 battery charger driver. + Enable BQ2518x battery charger driver. diff --git a/drivers/charger/charger_bq25180.c b/drivers/charger/charger_bq25180.c deleted file mode 100644 index c140d748415ee..0000000000000 --- a/drivers/charger/charger_bq25180.c +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * SPDX-License-Identifier: Apache-2.0 - * - * BQ25180 Datasheet: https://www.ti.com/lit/gpn/bq25180 - */ - -#define DT_DRV_COMPAT ti_bq25180 - -#include -#include -#include -#include -#include -#include - -LOG_MODULE_REGISTER(bq25180, CONFIG_CHARGER_LOG_LEVEL); - -#define BQ25180_STAT0 0x00 -#define BQ25180_STAT1 0x01 -#define BQ25180_FLAG0 0x02 -#define BQ25180_VBAT_CTRL 0x03 -#define BQ25180_ICHG_CTRL 0x04 -#define BQ25180_IC_CTRL 0x07 -#define BQ25180_SHIP_RST 0x09 -#define BQ25180_MASK_ID 0x0c - -#define BQ25180_STAT0_CHG_STAT_MASK GENMASK(6, 5) -#define BQ25180_STAT0_CHG_STAT_NOT_CHARGING 0x00 -#define BQ25180_STAT0_CHG_STAT_CONSTANT_CURRENT 0x01 -#define BQ25180_STAT0_CHG_STAT_CONSTANT_VOLTAGE 0x02 -#define BQ25180_STAT0_CHG_STAT_DONE 0x03 -#define BQ25180_STAT0_VIN_PGOOD_STAT BIT(0) -#define BQ25180_VBAT_MSK GENMASK(6, 0) -#define BQ25180_ICHG_CHG_DIS BIT(7) -#define BQ25180_ICHG_MSK GENMASK(6, 0) -#define BQ25180_IC_CTRL_VRCH_100 0x00 -#define BQ25180_IC_CTRL_VRCH_200 BIT(5) -#define BQ25180_IC_CTRL_VRCH_MSK BIT(5) -#define BQ25180_VLOWV_SEL_2_8 BIT(6) -#define BQ25180_VLOWV_SEL_3_0 0x00 -#define BQ25180_VLOWV_SEL_MSK BIT(6) -#define BQ25180_WATCHDOG_SEL_1_MSK GENMASK(1, 0) -#define BQ25180_WATCHDOG_DISABLE 0x03 -#define BQ25180_DEVICE_ID_MSK GENMASK(3, 0) -#define BQ25180_DEVICE_ID 0x00 -#define BQ25180_SHIP_RST_EN_RST_SHIP_MSK GENMASK(6, 5) -#define BQ25180_SHIP_RST_EN_RST_SHIP_ADAPTER 0x20 -#define BQ25180_SHIP_RST_EN_RST_SHIP_BUTTON 0x40 - -/* Charging current limits */ -#define BQ25180_CURRENT_MIN_MA 5 -#define BQ25180_CURRENT_MAX_MA 1000 -#define BQ25180_VOLTAGE_MIN_MV 3500 -#define BQ25180_VOLTAGE_MAX_MV 4650 - -#define BQ25180_FACTOR_VBAT_TO_MV 10 - -struct bq25180_config { - struct i2c_dt_spec i2c; - uint32_t initial_current_microamp; - uint32_t max_voltage_microvolt; - uint32_t recharge_voltage_microvolt; - uint32_t precharge_threshold_voltage_microvolt; -}; - -/* - * For ICHG <= 35mA = ICHGCODE + 5mA - * For ICHG > 35mA = 40 + ((ICHGCODE-31)*10)mA. - * Maximum programmable current = 1000mA - * - * Return: value between 0 and 127, negative on error. - */ -static int bq25180_ma_to_ichg(uint32_t current_ma, uint8_t *ichg) -{ - if (!IN_RANGE(current_ma, BQ25180_CURRENT_MIN_MA, BQ25180_CURRENT_MAX_MA)) { - LOG_WRN("charging current out of range: %dmA, " - "clamping to the nearest limit", current_ma); - } - current_ma = CLAMP(current_ma, BQ25180_CURRENT_MIN_MA, BQ25180_CURRENT_MAX_MA); - - if (current_ma <= 35) { - *ichg = current_ma - 5; - return 0; - } - - *ichg = (current_ma - 40) / 10 + 31; - - return 0; -} - -static uint32_t bq25180_ichg_to_ma(uint8_t ichg) -{ - ichg &= BQ25180_ICHG_MSK; - - if (ichg <= 30) { - return (ichg + 5); - } - - return (ichg - 31) * 10 + 40; -} - -static int bq25180_mv_to_vbatreg(const struct bq25180_config *cfg, uint32_t voltage_mv, - uint8_t *vbat) -{ - if (!IN_RANGE(voltage_mv, BQ25180_VOLTAGE_MIN_MV, cfg->max_voltage_microvolt)) { - LOG_WRN("charging voltage out of range: %dmV, " - "clamping to the nearest limit", - voltage_mv); - } - voltage_mv = CLAMP(voltage_mv, BQ25180_VOLTAGE_MIN_MV, cfg->max_voltage_microvolt); - - *vbat = (voltage_mv - BQ25180_VOLTAGE_MIN_MV) / BQ25180_FACTOR_VBAT_TO_MV; - - return 0; -} - -static uint32_t bq25180_vbatreg_to_mv(uint8_t vbat) -{ - vbat &= BQ25180_VBAT_MSK; - - return (vbat * BQ25180_FACTOR_VBAT_TO_MV) + BQ25180_VOLTAGE_MIN_MV; -} - -static int bq25183_charge_enable(const struct device *dev, const bool enable) -{ - const struct bq25180_config *cfg = dev->config; - uint8_t value = enable ? 0 : BQ25180_ICHG_CHG_DIS; - int ret; - - ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL, - BQ25180_ICHG_CHG_DIS, value); - if (ret < 0) { - return ret; - } - - return 0; -} - -static int bq25180_set_charge_current(const struct device *dev, - uint32_t const_charge_current_ua) -{ - const struct bq25180_config *cfg = dev->config; - uint8_t val; - int ret; - - ret = bq25180_ma_to_ichg(const_charge_current_ua / 1000, &val); - if (ret < 0) { - return ret; - } - - ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL, - BQ25180_ICHG_MSK, val); - if (ret < 0) { - return ret; - } - - return 0; -} - -static int bq25180_get_charge_current(const struct device *dev, - uint32_t *const_charge_current_ua) -{ - const struct bq25180_config *cfg = dev->config; - uint8_t val; - int ret; - - ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL, &val); - if (ret < 0) { - return ret; - } - - *const_charge_current_ua = bq25180_ichg_to_ma(val) * 1000; - - return 0; -} - -static int bq25180_set_charge_voltage(const struct device *dev, uint32_t const_charge_voltage_uv) -{ - const struct bq25180_config *cfg = dev->config; - uint8_t val; - int ret; - - ret = bq25180_mv_to_vbatreg(cfg, const_charge_voltage_uv / 1000, &val); - if (ret < 0) { - return ret; - } - - ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_VBAT_CTRL, BQ25180_VBAT_MSK, val); - if (ret < 0) { - return ret; - } - - return 0; -} - -static int bq25180_get_charge_voltage(const struct device *dev, uint32_t *const_charge_voltage_uv) -{ - const struct bq25180_config *cfg = dev->config; - uint8_t val; - int ret; - - ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_VBAT_CTRL, &val); - if (ret < 0) { - return ret; - } - - *const_charge_voltage_uv = bq25180_vbatreg_to_mv(val) * 1000; - - return 0; -} - -static int bq25180_get_online(const struct device *dev, - enum charger_online *online) -{ - const struct bq25180_config *cfg = dev->config; - uint8_t val; - int ret; - - ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_STAT0, &val); - if (ret < 0) { - return ret; - } - - if ((val & BQ25180_STAT0_VIN_PGOOD_STAT) != 0x00) { - *online = CHARGER_ONLINE_FIXED; - } else { - *online = CHARGER_ONLINE_OFFLINE; - } - - return 0; -} - -static int bq25180_get_status(const struct device *dev, - enum charger_status *status) -{ - const struct bq25180_config *cfg = dev->config; - uint8_t stat0; - uint8_t ichg_ctrl; - int ret; - - ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_STAT0, &stat0); - if (ret < 0) { - return ret; - } - - if ((stat0 & BQ25180_STAT0_VIN_PGOOD_STAT) == 0x00) { - *status = CHARGER_STATUS_DISCHARGING; - return 0; - } - - ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL, &ichg_ctrl); - if (ret < 0) { - return ret; - } - - if ((ichg_ctrl & BQ25180_ICHG_CHG_DIS) != 0x00) { - *status = CHARGER_STATUS_NOT_CHARGING; - return 0; - } - - switch (FIELD_GET(BQ25180_STAT0_CHG_STAT_MASK, stat0)) { - case BQ25180_STAT0_CHG_STAT_NOT_CHARGING: - *status = CHARGER_STATUS_NOT_CHARGING; - break; - case BQ25180_STAT0_CHG_STAT_CONSTANT_CURRENT: - case BQ25180_STAT0_CHG_STAT_CONSTANT_VOLTAGE: - *status = CHARGER_STATUS_CHARGING; - break; - case BQ25180_STAT0_CHG_STAT_DONE: - *status = CHARGER_STATUS_FULL; - break; - } - - return 0; -} - -static int bq25180_get_prop(const struct device *dev, charger_prop_t prop, - union charger_propval *val) -{ - switch (prop) { - case CHARGER_PROP_ONLINE: - return bq25180_get_online(dev, &val->online); - case CHARGER_PROP_STATUS: - return bq25180_get_status(dev, &val->status); - case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA: - return bq25180_get_charge_current(dev, &val->const_charge_current_ua); - case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV: - return bq25180_get_charge_voltage(dev, &val->const_charge_voltage_uv); - default: - return -ENOTSUP; - } -} - -static int bq25180_set_prop(const struct device *dev, charger_prop_t prop, - const union charger_propval *val) -{ - switch (prop) { - case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA: - return bq25180_set_charge_current(dev, val->const_charge_current_ua); - case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV: - return bq25180_set_charge_voltage(dev, val->const_charge_voltage_uv); - default: - return -ENOTSUP; - } -} - -static DEVICE_API(charger, bq25180_api) = { - .get_property = bq25180_get_prop, - .set_property = bq25180_set_prop, - .charge_enable = bq25183_charge_enable, -}; - -static int bq25180_init(const struct device *dev) -{ - const struct bq25180_config *cfg = dev->config; - uint8_t val; - int ret; - - ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_MASK_ID, &val); - if (ret < 0) { - return ret; - } - - val &= BQ25180_DEVICE_ID_MSK; - if (val != BQ25180_DEVICE_ID) { - LOG_ERR("Invalid device id: %02x", val); - return -EINVAL; - } - - /* Disable the watchdog */ - ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_IC_CTRL, - BQ25180_WATCHDOG_SEL_1_MSK, - BQ25180_WATCHDOG_DISABLE); - if (ret < 0) { - return ret; - } - - ret = bq25180_set_charge_voltage(dev, cfg->max_voltage_microvolt); - if (ret < 0) { - LOG_ERR("Could not set the target voltage. (rc: %d)", ret); - return ret; - } - - if (cfg->recharge_voltage_microvolt > 0) { - if ((cfg->max_voltage_microvolt - cfg->recharge_voltage_microvolt) > 100000) { - val = BQ25180_IC_CTRL_VRCH_200; - } else { - val = BQ25180_IC_CTRL_VRCH_100; - } - - ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_IC_CTRL, BQ25180_IC_CTRL_VRCH_MSK, - val); - if (ret < 0) { - return ret; - } - } - - /* Precharge threshold voltage */ - if (cfg->precharge_threshold_voltage_microvolt <= 2800000) { - val = BQ25180_VLOWV_SEL_2_8; - } else { - val = BQ25180_VLOWV_SEL_3_0; - } - - ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_IC_CTRL, BQ25180_VLOWV_SEL_MSK, val); - if (ret < 0) { - return ret; - } - - if (cfg->initial_current_microamp > 0) { - ret = bq25180_set_charge_current(dev, cfg->initial_current_microamp); - if (ret < 0) { - return ret; - } - } - - return 0; -} - -#define CHARGER_BQ25180_INIT(inst) \ - static const struct bq25180_config bq25180_config_##inst = { \ - .i2c = I2C_DT_SPEC_INST_GET(inst), \ - .initial_current_microamp = \ - DT_INST_PROP(inst, constant_charge_current_max_microamp), \ - .max_voltage_microvolt = \ - DT_INST_PROP(inst, constant_charge_voltage_max_microvolt), \ - .recharge_voltage_microvolt = \ - DT_INST_PROP_OR(inst, re_charge_voltage_microvolt, 0), \ - .precharge_threshold_voltage_microvolt = \ - DT_INST_PROP(inst, precharge_voltage_threshold_microvolt), \ - }; \ - \ - DEVICE_DT_INST_DEFINE(inst, bq25180_init, NULL, NULL, &bq25180_config_##inst, POST_KERNEL, \ - CONFIG_CHARGER_INIT_PRIORITY, &bq25180_api); - -DT_INST_FOREACH_STATUS_OKAY(CHARGER_BQ25180_INIT) diff --git a/drivers/charger/charger_bq2518x.c b/drivers/charger/charger_bq2518x.c new file mode 100644 index 0000000000000..c9a9261c7119f --- /dev/null +++ b/drivers/charger/charger_bq2518x.c @@ -0,0 +1,392 @@ +/* + * Copyright 2024 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + * + * BQ25180 Datasheet: https://www.ti.com/lit/gpn/bq25180 + */ + +#define DT_DRV_COMPAT ti_bq25180 + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(bq2518x, CONFIG_CHARGER_LOG_LEVEL); + +#define BQ2518X_STAT0 0x00 +#define BQ2518X_STAT1 0x01 +#define BQ2518X_FLAG0 0x02 +#define BQ2518X_VBAT_CTRL 0x03 +#define BQ2518X_ICHG_CTRL 0x04 +#define BQ2518X_IC_CTRL 0x07 +#define BQ2518X_SHIP_RST 0x09 +#define BQ2518X_MASK_ID 0x0c + +#define BQ2518X_STAT0_CHG_STAT_MASK GENMASK(6, 5) +#define BQ2518X_STAT0_CHG_STAT_NOT_CHARGING 0x00 +#define BQ2518X_STAT0_CHG_STAT_CONSTANT_CURRENT 0x01 +#define BQ2518X_STAT0_CHG_STAT_CONSTANT_VOLTAGE 0x02 +#define BQ2518X_STAT0_CHG_STAT_DONE 0x03 +#define BQ2518X_STAT0_VIN_PGOOD_STAT BIT(0) +#define BQ2518X_VBAT_MSK GENMASK(6, 0) +#define BQ2518X_ICHG_CHG_DIS BIT(7) +#define BQ2518X_ICHG_MSK GENMASK(6, 0) +#define BQ2518X_IC_CTRL_VRCH_100 0x00 +#define BQ2518X_IC_CTRL_VRCH_200 BIT(5) +#define BQ2518X_IC_CTRL_VRCH_MSK BIT(5) +#define BQ2518X_VLOWV_SEL_2_8 BIT(6) +#define BQ2518X_VLOWV_SEL_3_0 0x00 +#define BQ2518X_VLOWV_SEL_MSK BIT(6) +#define BQ2518X_WATCHDOG_SEL_1_MSK GENMASK(1, 0) +#define BQ2518X_WATCHDOG_DISABLE 0x03 +#define BQ2518X_DEVICE_ID_MSK GENMASK(3, 0) +#define BQ2518X_DEVICE_ID 0x00 +#define BQ2518X_SHIP_RST_EN_RST_SHIP_MSK GENMASK(6, 5) +#define BQ2518X_SHIP_RST_EN_RST_SHIP_ADAPTER 0x20 +#define BQ2518X_SHIP_RST_EN_RST_SHIP_BUTTON 0x40 + +/* Charging current limits */ +#define BQ2518X_CURRENT_MIN_MA 5 +#define BQ2518X_CURRENT_MAX_MA 1000 +#define BQ2518X_VOLTAGE_MIN_MV 3500 +#define BQ2518X_VOLTAGE_MAX_MV 4650 + +#define BQ2518X_FACTOR_VBAT_TO_MV 10 + +struct bq2518x_config { + struct i2c_dt_spec i2c; + uint32_t initial_current_microamp; + uint32_t max_voltage_microvolt; + uint32_t recharge_voltage_microvolt; + uint32_t precharge_threshold_voltage_microvolt; +}; + +/* + * For ICHG <= 35mA = ICHGCODE + 5mA + * For ICHG > 35mA = 40 + ((ICHGCODE-31)*10)mA. + * Maximum programmable current = 1000mA + * + * Return: value between 0 and 127, negative on error. + */ +static int bq2518x_ma_to_ichg(uint32_t current_ma, uint8_t *ichg) +{ + if (!IN_RANGE(current_ma, BQ2518X_CURRENT_MIN_MA, BQ2518X_CURRENT_MAX_MA)) { + LOG_WRN("charging current out of range: %dmA, " + "clamping to the nearest limit", + current_ma); + } + current_ma = CLAMP(current_ma, BQ2518X_CURRENT_MIN_MA, BQ2518X_CURRENT_MAX_MA); + + if (current_ma <= 35) { + *ichg = current_ma - 5; + return 0; + } + + *ichg = (current_ma - 40) / 10 + 31; + + return 0; +} + +static uint32_t bq2518x_ichg_to_ma(uint8_t ichg) +{ + ichg &= BQ2518X_ICHG_MSK; + + if (ichg <= 30) { + return (ichg + 5); + } + + return (ichg - 31) * 10 + 40; +} + +static int bq2518x_mv_to_vbatreg(const struct bq2518x_config *cfg, uint32_t voltage_mv, + uint8_t *vbat) +{ + if (!IN_RANGE(voltage_mv, BQ2518X_VOLTAGE_MIN_MV, cfg->max_voltage_microvolt)) { + LOG_WRN("charging voltage out of range: %dmV, " + "clamping to the nearest limit", + voltage_mv); + } + voltage_mv = CLAMP(voltage_mv, BQ2518X_VOLTAGE_MIN_MV, cfg->max_voltage_microvolt); + + *vbat = (voltage_mv - BQ2518X_VOLTAGE_MIN_MV) / BQ2518X_FACTOR_VBAT_TO_MV; + + return 0; +} + +static uint32_t bq2518x_vbatreg_to_mv(uint8_t vbat) +{ + vbat &= BQ2518X_VBAT_MSK; + + return (vbat * BQ2518X_FACTOR_VBAT_TO_MV) + BQ2518X_VOLTAGE_MIN_MV; +} + +static int bq2518x_charge_enable(const struct device *dev, const bool enable) +{ + const struct bq2518x_config *cfg = dev->config; + uint8_t value = enable ? 0 : BQ2518X_ICHG_CHG_DIS; + int ret; + + ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ2518X_ICHG_CTRL, BQ2518X_ICHG_CHG_DIS, value); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int bq2518x_set_charge_current(const struct device *dev, uint32_t const_charge_current_ua) +{ + const struct bq2518x_config *cfg = dev->config; + uint8_t val; + int ret; + + ret = bq2518x_ma_to_ichg(const_charge_current_ua / 1000, &val); + if (ret < 0) { + return ret; + } + + ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ2518X_ICHG_CTRL, BQ2518X_ICHG_MSK, val); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int bq2518x_get_charge_current(const struct device *dev, uint32_t *const_charge_current_ua) +{ + const struct bq2518x_config *cfg = dev->config; + uint8_t val; + int ret; + + ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ2518X_ICHG_CTRL, &val); + if (ret < 0) { + return ret; + } + + *const_charge_current_ua = bq2518x_ichg_to_ma(val) * 1000; + + return 0; +} + +static int bq2518x_set_charge_voltage(const struct device *dev, uint32_t const_charge_voltage_uv) +{ + const struct bq2518x_config *cfg = dev->config; + uint8_t val; + int ret; + + ret = bq2518x_mv_to_vbatreg(cfg, const_charge_voltage_uv / 1000, &val); + if (ret < 0) { + return ret; + } + + ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ2518X_VBAT_CTRL, BQ2518X_VBAT_MSK, val); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int bq2518x_get_charge_voltage(const struct device *dev, uint32_t *const_charge_voltage_uv) +{ + const struct bq2518x_config *cfg = dev->config; + uint8_t val; + int ret; + + ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ2518X_VBAT_CTRL, &val); + if (ret < 0) { + return ret; + } + + *const_charge_voltage_uv = bq2518x_vbatreg_to_mv(val) * 1000; + + return 0; +} + +static int bq2518x_get_online(const struct device *dev, enum charger_online *online) +{ + const struct bq2518x_config *cfg = dev->config; + uint8_t val; + int ret; + + ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ2518X_STAT0, &val); + if (ret < 0) { + return ret; + } + + if ((val & BQ2518X_STAT0_VIN_PGOOD_STAT) != 0x00) { + *online = CHARGER_ONLINE_FIXED; + } else { + *online = CHARGER_ONLINE_OFFLINE; + } + + return 0; +} + +static int bq2518x_get_status(const struct device *dev, enum charger_status *status) +{ + const struct bq2518x_config *cfg = dev->config; + uint8_t stat0; + uint8_t ichg_ctrl; + int ret; + + ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ2518X_STAT0, &stat0); + if (ret < 0) { + return ret; + } + + if ((stat0 & BQ2518X_STAT0_VIN_PGOOD_STAT) == 0x00) { + *status = CHARGER_STATUS_DISCHARGING; + return 0; + } + + ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ2518X_ICHG_CTRL, &ichg_ctrl); + if (ret < 0) { + return ret; + } + + if ((ichg_ctrl & BQ2518X_ICHG_CHG_DIS) != 0x00) { + *status = CHARGER_STATUS_NOT_CHARGING; + return 0; + } + + switch (FIELD_GET(BQ2518X_STAT0_CHG_STAT_MASK, stat0)) { + case BQ2518X_STAT0_CHG_STAT_NOT_CHARGING: + *status = CHARGER_STATUS_NOT_CHARGING; + break; + case BQ2518X_STAT0_CHG_STAT_CONSTANT_CURRENT: + case BQ2518X_STAT0_CHG_STAT_CONSTANT_VOLTAGE: + *status = CHARGER_STATUS_CHARGING; + break; + case BQ2518X_STAT0_CHG_STAT_DONE: + *status = CHARGER_STATUS_FULL; + break; + } + + return 0; +} + +static int bq2518x_get_prop(const struct device *dev, charger_prop_t prop, + union charger_propval *val) +{ + switch (prop) { + case CHARGER_PROP_ONLINE: + return bq2518x_get_online(dev, &val->online); + case CHARGER_PROP_STATUS: + return bq2518x_get_status(dev, &val->status); + case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA: + return bq2518x_get_charge_current(dev, &val->const_charge_current_ua); + case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV: + return bq2518x_get_charge_voltage(dev, &val->const_charge_voltage_uv); + default: + return -ENOTSUP; + } +} + +static int bq2518x_set_prop(const struct device *dev, charger_prop_t prop, + const union charger_propval *val) +{ + switch (prop) { + case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA: + return bq2518x_set_charge_current(dev, val->const_charge_current_ua); + case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV: + return bq2518x_set_charge_voltage(dev, val->const_charge_voltage_uv); + default: + return -ENOTSUP; + } +} + +static DEVICE_API(charger, bq2518x_api) = { + .get_property = bq2518x_get_prop, + .set_property = bq2518x_set_prop, + .charge_enable = bq2518x_charge_enable, +}; + +static int bq2518x_init(const struct device *dev) +{ + const struct bq2518x_config *cfg = dev->config; + uint8_t val; + int ret; + + ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ2518X_MASK_ID, &val); + if (ret < 0) { + return ret; + } + + val &= BQ2518X_DEVICE_ID_MSK; + if (val != BQ2518X_DEVICE_ID) { + LOG_ERR("Invalid device id: %02x", val); + return -EINVAL; + } + + /* Disable the watchdog */ + ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ2518X_IC_CTRL, BQ2518X_WATCHDOG_SEL_1_MSK, + BQ2518X_WATCHDOG_DISABLE); + if (ret < 0) { + return ret; + } + + ret = bq2518x_set_charge_voltage(dev, cfg->max_voltage_microvolt); + if (ret < 0) { + LOG_ERR("Could not set the target voltage. (rc: %d)", ret); + return ret; + } + + if (cfg->recharge_voltage_microvolt > 0) { + if ((cfg->max_voltage_microvolt - cfg->recharge_voltage_microvolt) > 100000) { + val = BQ2518X_IC_CTRL_VRCH_200; + } else { + val = BQ2518X_IC_CTRL_VRCH_100; + } + + ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ2518X_IC_CTRL, BQ2518X_IC_CTRL_VRCH_MSK, + val); + if (ret < 0) { + return ret; + } + } + + /* Precharge threshold voltage */ + if (cfg->precharge_threshold_voltage_microvolt <= 2800000) { + val = BQ2518X_VLOWV_SEL_2_8; + } else { + val = BQ2518X_VLOWV_SEL_3_0; + } + + ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ2518X_IC_CTRL, BQ2518X_VLOWV_SEL_MSK, val); + if (ret < 0) { + return ret; + } + + if (cfg->initial_current_microamp > 0) { + ret = bq2518x_set_charge_current(dev, cfg->initial_current_microamp); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +#define CHARGER_BQ2518X_INIT(inst) \ + static const struct bq2518x_config bq2518x_config_##inst = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + .initial_current_microamp = \ + DT_INST_PROP(inst, constant_charge_current_max_microamp), \ + .max_voltage_microvolt = \ + DT_INST_PROP(inst, constant_charge_voltage_max_microvolt), \ + .recharge_voltage_microvolt = \ + DT_INST_PROP_OR(inst, re_charge_voltage_microvolt, 0), \ + .precharge_threshold_voltage_microvolt = \ + DT_INST_PROP(inst, precharge_voltage_threshold_microvolt), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, bq2518x_init, NULL, NULL, &bq2518x_config_##inst, POST_KERNEL, \ + CONFIG_CHARGER_INIT_PRIORITY, &bq2518x_api); + +DT_INST_FOREACH_STATUS_OKAY(CHARGER_BQ2518X_INIT) diff --git a/dts/bindings/charger/ti,bq25180.yaml b/dts/bindings/charger/ti,bq25180.yaml index 4a88af3717b86..200add399b63b 100644 --- a/dts/bindings/charger/ti,bq25180.yaml +++ b/dts/bindings/charger/ti,bq25180.yaml @@ -5,7 +5,7 @@ description: | BQ25180 I2C Controlled, 1-Cell, 1-A Linear Battery Charger with Power Path and Ship Mode. - The device has a single child node for the charger. For example: + The device has a single node for the charger. For example: bq25180@6a { compatible = "ti,bq25180"; @@ -16,29 +16,4 @@ description: | compatible: "ti,bq25180" -include: [battery.yaml, i2c-device.yaml] - - -properties: - constant-charge-current-max-microamp: - type: int - default: 0 - description: | - Charge current set at init time in uA, available range is 5 mA to 800 mA. - The value specified will be rounded down to the closest implemented - value. If set to 0 (default) skip setting the charge current value at - driver initialization. - - constant-charge-voltage-max-microvolt: - type: int - default: 4200000 - description: | - The maximum voltage that the battery will be charged at, defaults to - 4.2V, matching the device default reset configuration. - - precharge-voltage-threshold-microvolt: - type: int - default: 3000000 - description: | - Threshold at which voltage to switch to constant current charge. - Must be either 3.0V or 2.8V +include: ti,bq2518x-common.yaml diff --git a/dts/bindings/charger/ti,bq2518x-common.yaml b/dts/bindings/charger/ti,bq2518x-common.yaml new file mode 100644 index 0000000000000..b1bac9e92b1fb --- /dev/null +++ b/dts/bindings/charger/ti,bq2518x-common.yaml @@ -0,0 +1,28 @@ +# Copyright 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +include: [battery.yaml, i2c-device.yaml] + +properties: + constant-charge-current-max-microamp: + type: int + default: 0 + description: | + Charge current set at init time in uA, available range is 5 mA to 800 mA. + The value specified will be rounded down to the closest implemented + value. If set to 0 (default) skip setting the charge current value at + driver initialization. + + constant-charge-voltage-max-microvolt: + type: int + default: 4200000 + description: | + The maximum voltage that the battery will be charged at, defaults to + 4.2V, matching the device default reset configuration. + + precharge-voltage-threshold-microvolt: + type: int + default: 3000000 + description: | + Threshold at which voltage to switch to constant current charge. + Must be either 3.0V or 2.8V From 93a72a385670c5b28800aceae72f6581bcbff2f2 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Tue, 7 Oct 2025 21:15:20 +1000 Subject: [PATCH 2/5] charger: bq2518x variants Add support for the bq25186 and bq25188 chip variants. Signed-off-by: Jordan Yates --- drivers/charger/Kconfig.bq2518x | 3 +- drivers/charger/charger_bq2518x.c | 62 +++++++++++++++++++++------- dts/bindings/charger/ti,bq25186.yaml | 19 +++++++++ dts/bindings/charger/ti,bq25188.yaml | 19 +++++++++ 4 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 dts/bindings/charger/ti,bq25186.yaml create mode 100644 dts/bindings/charger/ti,bq25188.yaml diff --git a/drivers/charger/Kconfig.bq2518x b/drivers/charger/Kconfig.bq2518x index c1a6aeb0434b4..d579933669e06 100644 --- a/drivers/charger/Kconfig.bq2518x +++ b/drivers/charger/Kconfig.bq2518x @@ -5,7 +5,8 @@ config CHARGER_BQ2518X bool "BQ2518x Battery Charger" default y - depends on DT_HAS_TI_BQ25180_ENABLED + depends on DT_HAS_TI_BQ25180_ENABLED || DT_HAS_TI_BQ25186_ENABLED || \ + DT_HAS_TI_BQ25188_ENABLED select I2C help Enable BQ2518x battery charger driver. diff --git a/drivers/charger/charger_bq2518x.c b/drivers/charger/charger_bq2518x.c index c9a9261c7119f..f01dc85afb924 100644 --- a/drivers/charger/charger_bq2518x.c +++ b/drivers/charger/charger_bq2518x.c @@ -4,10 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 * * BQ25180 Datasheet: https://www.ti.com/lit/gpn/bq25180 + * BQ25186 Datasheet: https://www.ti.com/lit/gpn/bq25186 + * BQ25187 Datasheet: https://www.ti.com/lit/gpn/bq25188 + * + * Notable Differences: + * BQ25180 CHARGE_CTRL0: VINDPM lowest value is 4.2V, + * compared to VBAT + 300 mV for + * other two. */ -#define DT_DRV_COMPAT ti_bq25180 - #include #include #include @@ -17,14 +22,27 @@ LOG_MODULE_REGISTER(bq2518x, CONFIG_CHARGER_LOG_LEVEL); -#define BQ2518X_STAT0 0x00 -#define BQ2518X_STAT1 0x01 -#define BQ2518X_FLAG0 0x02 -#define BQ2518X_VBAT_CTRL 0x03 -#define BQ2518X_ICHG_CTRL 0x04 -#define BQ2518X_IC_CTRL 0x07 -#define BQ2518X_SHIP_RST 0x09 -#define BQ2518X_MASK_ID 0x0c +enum bq2518x_reg { + BQ2518X_STAT0 = 0x00, + BQ2518X_STAT1 = 0x01, + BQ2518X_FLAG0 = 0x02, + BQ2518X_VBAT_CTRL = 0x03, + BQ2518X_ICHG_CTRL = 0x04, + BQ2518X_CHARGE_CTRL0 = 0x05, + BQ2518X_CHARGE_CTRL1 = 0x06, + BQ2518X_IC_CTRL = 0x07, + BQ2518X_TMR_ILIM = 0x08, + BQ2518X_SHIP_RST = 0x09, + BQ2518X_SYS_REG = 0x0A, + BQ2518X_TS_CONTROL = 0x0B, + BQ2518X_MASK_ID = 0x0c, +}; + +enum bq2518x_device_id { + BQ25180_DEVICE_ID = 0x00, + BQ25186_DEVICE_ID = 0x01, + BQ25188_DEVICE_ID = 0x04, +}; #define BQ2518X_STAT0_CHG_STAT_MASK GENMASK(6, 5) #define BQ2518X_STAT0_CHG_STAT_NOT_CHARGING 0x00 @@ -63,6 +81,7 @@ struct bq2518x_config { uint32_t max_voltage_microvolt; uint32_t recharge_voltage_microvolt; uint32_t precharge_threshold_voltage_microvolt; + enum bq2518x_device_id device_id; }; /* @@ -319,7 +338,7 @@ static int bq2518x_init(const struct device *dev) } val &= BQ2518X_DEVICE_ID_MSK; - if (val != BQ2518X_DEVICE_ID) { + if (val != cfg->device_id) { LOG_ERR("Invalid device id: %02x", val); return -EINVAL; } @@ -373,8 +392,8 @@ static int bq2518x_init(const struct device *dev) return 0; } -#define CHARGER_BQ2518X_INIT(inst) \ - static const struct bq2518x_config bq2518x_config_##inst = { \ +#define CHARGER_BQ2518X_INIT(inst, _device_id) \ + static const struct bq2518x_config _device_id##_config_##inst = { \ .i2c = I2C_DT_SPEC_INST_GET(inst), \ .initial_current_microamp = \ DT_INST_PROP(inst, constant_charge_current_max_microamp), \ @@ -384,9 +403,20 @@ static int bq2518x_init(const struct device *dev) DT_INST_PROP_OR(inst, re_charge_voltage_microvolt, 0), \ .precharge_threshold_voltage_microvolt = \ DT_INST_PROP(inst, precharge_voltage_threshold_microvolt), \ + .device_id = _device_id##_DEVICE_ID, \ }; \ \ - DEVICE_DT_INST_DEFINE(inst, bq2518x_init, NULL, NULL, &bq2518x_config_##inst, POST_KERNEL, \ - CONFIG_CHARGER_INIT_PRIORITY, &bq2518x_api); + DEVICE_DT_INST_DEFINE(inst, bq2518x_init, NULL, NULL, &_device_id##_config_##inst, \ + POST_KERNEL, CONFIG_CHARGER_INIT_PRIORITY, &bq2518x_api); + +#define DT_DRV_COMPAT ti_bq25180 +DT_INST_FOREACH_STATUS_OKAY_VARGS(CHARGER_BQ2518X_INIT, BQ25180) +#undef DT_DRV_COMPAT + +#define DT_DRV_COMPAT ti_bq25186 +DT_INST_FOREACH_STATUS_OKAY_VARGS(CHARGER_BQ2518X_INIT, BQ25186) +#undef DT_DRV_COMPAT -DT_INST_FOREACH_STATUS_OKAY(CHARGER_BQ2518X_INIT) +#define DT_DRV_COMPAT ti_bq25188 +DT_INST_FOREACH_STATUS_OKAY_VARGS(CHARGER_BQ2518X_INIT, BQ25188) +#undef DT_DRV_COMPAT diff --git a/dts/bindings/charger/ti,bq25186.yaml b/dts/bindings/charger/ti,bq25186.yaml new file mode 100644 index 0000000000000..0dd8a54b0c07c --- /dev/null +++ b/dts/bindings/charger/ti,bq25186.yaml @@ -0,0 +1,19 @@ +# Copyright 2025 Embeint Pty Ltd +# SPDX-License-Identifier: Apache-2.0 + +description: | + BQ25186 I2C Controlled, 1-Cell, 1-A Linear Battery Charger with Power Path, + Ship Mode, Shutdown Mode and Battery Tracking VINDPM. + + The device has a single node for the charger. For example: + + bq25186@6a { + compatible = "ti,bq25186"; + reg = <0x6a>; + + constant-charge-current-max-microamp = <500000>; + }; + +compatible: "ti,bq25186" + +include: ti,bq2518x-common.yaml diff --git a/dts/bindings/charger/ti,bq25188.yaml b/dts/bindings/charger/ti,bq25188.yaml new file mode 100644 index 0000000000000..c3838a46b76ac --- /dev/null +++ b/dts/bindings/charger/ti,bq25188.yaml @@ -0,0 +1,19 @@ +# Copyright 2025 Embeint Pty Ltd +# SPDX-License-Identifier: Apache-2.0 + +description: | + BQ25188 I2C Controlled, 1-Cell, 1-A Linear Battery Charger with Power Path, + Ship Mode, Shutdown Mode, Battery Tracking VINDPM, and Wide WIN Support. + + The device has a single node for the charger. For example: + + bq25188@6a { + compatible = "ti,bq25188"; + reg = <0x6a>; + + constant-charge-current-max-microamp = <500000>; + }; + +compatible: "ti,bq25188" + +include: ti,bq2518x-common.yaml From 27b3c04533d49f66627038c0b2bb12f3b0c16eb2 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Tue, 7 Oct 2025 21:16:25 +1000 Subject: [PATCH 3/5] tests: drivers: build_all: charger: bq2518x variants Add the `ti,bq25186` and `ti,bq25188`` to the build all test. Signed-off-by: Jordan Yates --- tests/drivers/build_all/charger/i2c.dtsi | 26 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/drivers/build_all/charger/i2c.dtsi b/tests/drivers/build_all/charger/i2c.dtsi index cdd65b1764693..4279066fd739f 100644 --- a/tests/drivers/build_all/charger/i2c.dtsi +++ b/tests/drivers/build_all/charger/i2c.dtsi @@ -43,22 +43,36 @@ bq25180@2 { constant-charge-voltage-max-microvolt = <4200000>; }; -sbs-charger@3 { - compatible = "sbs,sbs-charger"; +bq25186@3 { + compatible = "ti,bq25186"; reg = <0x3>; + constant-charge-current-max-microamp = <500000>; + constant-charge-voltage-max-microvolt = <4200000>; }; -bq257130@4 { - compatible = "ti,bq25713"; +bq25188@4 { + compatible = "ti,bq25187"; reg = <0x4>; + constant-charge-current-max-microamp = <500000>; + constant-charge-voltage-max-microvolt = <4200000>; +}; + +sbs-charger@5 { + compatible = "sbs,sbs-charger"; + reg = <0x5>; +}; + +bq257130@6 { + compatible = "ti,bq25713"; + reg = <0x6>; constant-charge-current-max-microamp = <1000000>; constant-charge-voltage-max-microvolt = <3800000>; system-voltage-min-threshold-microvolt = <3328000>; }; -axp2101@5 { +axp2101@7 { compatible = "x-powers,axp2101"; - reg = <0x5>; + reg = <0x7>; charger { compatible = "x-powers,axp2101-charger"; From 890f069e30e565084aef6f5c07b4d3a6ab93cb4a Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Wed, 8 Oct 2025 09:32:41 +1000 Subject: [PATCH 4/5] charger: bq2518x: restrict precharge values in devicetree Instead of text in the description that specifies the valid values, add the `enum` property so values are validated at compile time. Signed-off-by: Jordan Yates --- dts/bindings/charger/ti,bq2518x-common.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dts/bindings/charger/ti,bq2518x-common.yaml b/dts/bindings/charger/ti,bq2518x-common.yaml index b1bac9e92b1fb..631d8c69b8847 100644 --- a/dts/bindings/charger/ti,bq2518x-common.yaml +++ b/dts/bindings/charger/ti,bq2518x-common.yaml @@ -22,7 +22,9 @@ properties: precharge-voltage-threshold-microvolt: type: int + enum: + - 2800000 + - 3000000 default: 3000000 description: | Threshold at which voltage to switch to constant current charge. - Must be either 3.0V or 2.8V From 048e694ed286faa2b37719f714ef12a2ddcd1a6b Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Wed, 8 Oct 2025 09:36:15 +1000 Subject: [PATCH 5/5] charger: bq2518x: more configuration options Add configuration for the VSYS target regulation voltage. Explicitly specify the threshold at which charging will resume, instead of deriving it from the combination of two devicetree properties which have no compile-time validation. Simplify the process of initialising the chip by precomputing the register values and explicitly writing the whole register, instead of updating multiple fields individually. Signed-off-by: Jordan Yates --- drivers/charger/charger_bq2518x.c | 66 +++++++++------------ dts/bindings/charger/ti,bq2518x-common.yaml | 27 +++++++++ tests/drivers/build_all/charger/i2c.dtsi | 3 + 3 files changed, 57 insertions(+), 39 deletions(-) diff --git a/drivers/charger/charger_bq2518x.c b/drivers/charger/charger_bq2518x.c index f01dc85afb924..734b363e565d9 100644 --- a/drivers/charger/charger_bq2518x.c +++ b/drivers/charger/charger_bq2518x.c @@ -53,14 +53,14 @@ enum bq2518x_device_id { #define BQ2518X_VBAT_MSK GENMASK(6, 0) #define BQ2518X_ICHG_CHG_DIS BIT(7) #define BQ2518X_ICHG_MSK GENMASK(6, 0) +#define BQ2518X_IC_CTRL_WDOG_DISABLE (BIT(0) | BIT(1)) +#define BQ2518X_IC_CTRL_SAFETY_6_HOUR BIT(2) #define BQ2518X_IC_CTRL_VRCH_100 0x00 #define BQ2518X_IC_CTRL_VRCH_200 BIT(5) -#define BQ2518X_IC_CTRL_VRCH_MSK BIT(5) -#define BQ2518X_VLOWV_SEL_2_8 BIT(6) -#define BQ2518X_VLOWV_SEL_3_0 0x00 -#define BQ2518X_VLOWV_SEL_MSK BIT(6) -#define BQ2518X_WATCHDOG_SEL_1_MSK GENMASK(1, 0) -#define BQ2518X_WATCHDOG_DISABLE 0x03 +#define BQ2518X_IC_CTRL_VLOWV_SEL_2_8 BIT(6) +#define BQ2518X_IC_CTRL_VLOWV_SEL_3_0 0x00 +#define BQ2518X_IC_CTRL_TS_AUTO_EN BIT(7) +#define BQ2518X_SYS_REG_CTRL_OFFSET 5 #define BQ2518X_DEVICE_ID_MSK GENMASK(3, 0) #define BQ2518X_DEVICE_ID 0x00 #define BQ2518X_SHIP_RST_EN_RST_SHIP_MSK GENMASK(6, 5) @@ -79,9 +79,9 @@ struct bq2518x_config { struct i2c_dt_spec i2c; uint32_t initial_current_microamp; uint32_t max_voltage_microvolt; - uint32_t recharge_voltage_microvolt; - uint32_t precharge_threshold_voltage_microvolt; enum bq2518x_device_id device_id; + uint8_t reg_ic_ctrl; + uint8_t reg_sys_regulation; }; /* @@ -343,42 +343,25 @@ static int bq2518x_init(const struct device *dev) return -EINVAL; } - /* Disable the watchdog */ - ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ2518X_IC_CTRL, BQ2518X_WATCHDOG_SEL_1_MSK, - BQ2518X_WATCHDOG_DISABLE); + /* Setup register IC_CTRL. + * Values from devicetree + device defaults + */ + val = BQ2518X_IC_CTRL_WDOG_DISABLE | BQ2518X_IC_CTRL_SAFETY_6_HOUR | + BQ2518X_IC_CTRL_TS_AUTO_EN | cfg->reg_ic_ctrl; + ret = i2c_reg_write_byte_dt(&cfg->i2c, BQ2518X_IC_CTRL, val); if (ret < 0) { return ret; } - ret = bq2518x_set_charge_voltage(dev, cfg->max_voltage_microvolt); + /* Setup VSYS regulation */ + ret = i2c_reg_write_byte_dt(&cfg->i2c, BQ2518X_SYS_REG, cfg->reg_sys_regulation); if (ret < 0) { - LOG_ERR("Could not set the target voltage. (rc: %d)", ret); return ret; } - if (cfg->recharge_voltage_microvolt > 0) { - if ((cfg->max_voltage_microvolt - cfg->recharge_voltage_microvolt) > 100000) { - val = BQ2518X_IC_CTRL_VRCH_200; - } else { - val = BQ2518X_IC_CTRL_VRCH_100; - } - - ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ2518X_IC_CTRL, BQ2518X_IC_CTRL_VRCH_MSK, - val); - if (ret < 0) { - return ret; - } - } - - /* Precharge threshold voltage */ - if (cfg->precharge_threshold_voltage_microvolt <= 2800000) { - val = BQ2518X_VLOWV_SEL_2_8; - } else { - val = BQ2518X_VLOWV_SEL_3_0; - } - - ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ2518X_IC_CTRL, BQ2518X_VLOWV_SEL_MSK, val); + ret = bq2518x_set_charge_voltage(dev, cfg->max_voltage_microvolt); if (ret < 0) { + LOG_ERR("Could not set the target voltage. (rc: %d)", ret); return ret; } @@ -399,11 +382,16 @@ static int bq2518x_init(const struct device *dev) DT_INST_PROP(inst, constant_charge_current_max_microamp), \ .max_voltage_microvolt = \ DT_INST_PROP(inst, constant_charge_voltage_max_microvolt), \ - .recharge_voltage_microvolt = \ - DT_INST_PROP_OR(inst, re_charge_voltage_microvolt, 0), \ - .precharge_threshold_voltage_microvolt = \ - DT_INST_PROP(inst, precharge_voltage_threshold_microvolt), \ .device_id = _device_id##_DEVICE_ID, \ + .reg_ic_ctrl = \ + (DT_INST_PROP(inst, re_charge_threshold_millivolt) == 100 \ + ? BQ2518X_IC_CTRL_VRCH_100 \ + : BQ2518X_IC_CTRL_VRCH_200) | \ + (DT_INST_PROP(inst, precharge_voltage_threshold_microvolt) == 2800000 \ + ? BQ2518X_IC_CTRL_VLOWV_SEL_2_8 \ + : BQ2518X_IC_CTRL_VLOWV_SEL_3_0), \ + .reg_sys_regulation = DT_INST_ENUM_IDX(inst, vsys_target_regulation) \ + << BQ2518X_SYS_REG_CTRL_OFFSET, \ }; \ \ DEVICE_DT_INST_DEFINE(inst, bq2518x_init, NULL, NULL, &_device_id##_config_##inst, \ diff --git a/dts/bindings/charger/ti,bq2518x-common.yaml b/dts/bindings/charger/ti,bq2518x-common.yaml index 631d8c69b8847..5219850730954 100644 --- a/dts/bindings/charger/ti,bq2518x-common.yaml +++ b/dts/bindings/charger/ti,bq2518x-common.yaml @@ -20,6 +20,17 @@ properties: The maximum voltage that the battery will be charged at, defaults to 4.2V, matching the device default reset configuration. + re-charge-threshold-millivolt: + type: int + enum: + - 100 + - 200 + default: 100 + description: | + The voltage below the maximum charge voltage at which the battery will + resume charging after falling from the max. Defaults to 100 mV, matching + the device default reset configuration. + precharge-voltage-threshold-microvolt: type: int enum: @@ -28,3 +39,19 @@ properties: default: 3000000 description: | Threshold at which voltage to switch to constant current charge. + + vsys-target-regulation: + type: string + enum: + - "Battery Tracking" + - "4V4" + - "4V5" + - "4V6" + - "4V7" + - "4V8" + - "4V9" + - "Vin Passthrough" + default: "4V5" + description: | + Target voltage for the SYS regulation loop. Defaults to 4.5V, matching + the device default reset configuration. diff --git a/tests/drivers/build_all/charger/i2c.dtsi b/tests/drivers/build_all/charger/i2c.dtsi index 4279066fd739f..502019c75d145 100644 --- a/tests/drivers/build_all/charger/i2c.dtsi +++ b/tests/drivers/build_all/charger/i2c.dtsi @@ -48,6 +48,8 @@ bq25186@3 { reg = <0x3>; constant-charge-current-max-microamp = <500000>; constant-charge-voltage-max-microvolt = <4200000>; + re-charge-threshold-millivolt = <200>; + vsys-target-regulation = "Vin Passthrough"; }; bq25188@4 { @@ -55,6 +57,7 @@ bq25188@4 { reg = <0x4>; constant-charge-current-max-microamp = <500000>; constant-charge-voltage-max-microvolt = <4200000>; + vsys-target-regulation = "Battery Tracking"; }; sbs-charger@5 {