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.bq25180 deleted file mode 100644 index 44bdc9b32ee49..0000000000000 --- a/drivers/charger/Kconfig.bq25180 +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -config CHARGER_BQ25180 - bool "BQ25180 Battery Charger" - default y - depends on DT_HAS_TI_BQ25180_ENABLED - select I2C - help - Enable BQ25180 battery charger driver. diff --git a/drivers/charger/Kconfig.bq2518x b/drivers/charger/Kconfig.bq2518x new file mode 100644 index 0000000000000..d579933669e06 --- /dev/null +++ b/drivers/charger/Kconfig.bq2518x @@ -0,0 +1,12 @@ +# Copyright 2024 Google LLC +# +# SPDX-License-Identifier: Apache-2.0 + +config CHARGER_BQ2518X + bool "BQ2518x Battery Charger" + default y + 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_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..734b363e565d9 --- /dev/null +++ b/drivers/charger/charger_bq2518x.c @@ -0,0 +1,410 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(bq2518x, CONFIG_CHARGER_LOG_LEVEL); + +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 +#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_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_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) +#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; + enum bq2518x_device_id device_id; + uint8_t reg_ic_ctrl; + uint8_t reg_sys_regulation; +}; + +/* + * 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 != cfg->device_id) { + LOG_ERR("Invalid device id: %02x", val); + return -EINVAL; + } + + /* 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; + } + + /* Setup VSYS regulation */ + ret = i2c_reg_write_byte_dt(&cfg->i2c, BQ2518X_SYS_REG, cfg->reg_sys_regulation); + 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->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, _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), \ + .max_voltage_microvolt = \ + DT_INST_PROP(inst, constant_charge_voltage_max_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, \ + 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 + +#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,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,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 diff --git a/dts/bindings/charger/ti,bq2518x-common.yaml b/dts/bindings/charger/ti,bq2518x-common.yaml new file mode 100644 index 0000000000000..5219850730954 --- /dev/null +++ b/dts/bindings/charger/ti,bq2518x-common.yaml @@ -0,0 +1,57 @@ +# 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. + + 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: + - 2800000 + - 3000000 + 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 cdd65b1764693..502019c75d145 100644 --- a/tests/drivers/build_all/charger/i2c.dtsi +++ b/tests/drivers/build_all/charger/i2c.dtsi @@ -43,22 +43,39 @@ 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>; + re-charge-threshold-millivolt = <200>; + vsys-target-regulation = "Vin Passthrough"; }; -bq257130@4 { - compatible = "ti,bq25713"; +bq25188@4 { + compatible = "ti,bq25187"; reg = <0x4>; + constant-charge-current-max-microamp = <500000>; + constant-charge-voltage-max-microvolt = <4200000>; + vsys-target-regulation = "Battery Tracking"; +}; + +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";