From 39eb124c81e88a6b2da3d42c9f76a91f29fbbb28 Mon Sep 17 00:00:00 2001 From: Bartosz Bilas Date: Wed, 8 Nov 2023 15:45:30 +0100 Subject: [PATCH] drivers: add MAX20335 charger driver Add a MAX20335 MFD subdriver for the built-in battery charger. Signed-off-by: Bartosz Bilas --- drivers/charger/CMakeLists.txt | 1 + drivers/charger/Kconfig | 1 + drivers/charger/Kconfig.max20335 | 11 + drivers/charger/charger_max20335.c | 287 ++++++++++++++++++ .../charger/maxim,max20335-charger.yaml | 8 + tests/drivers/build_all/charger/i2c.dtsi | 13 + 6 files changed, 321 insertions(+) create mode 100644 drivers/charger/Kconfig.max20335 create mode 100644 drivers/charger/charger_max20335.c create mode 100644 dts/bindings/charger/maxim,max20335-charger.yaml diff --git a/drivers/charger/CMakeLists.txt b/drivers/charger/CMakeLists.txt index 894561982a3..ce70f0590c6 100644 --- a/drivers/charger/CMakeLists.txt +++ b/drivers/charger/CMakeLists.txt @@ -4,6 +4,7 @@ zephyr_library() zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/charger.h) zephyr_library_sources_ifdef(CONFIG_CHARGER_BQ24190 charger_bq24190.c) +zephyr_library_sources_ifdef(CONFIG_CHARGER_MAX20335 charger_max20335.c) zephyr_library_sources_ifdef(CONFIG_SBS_CHARGER sbs_charger.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE charger_handlers.c) zephyr_library_sources_ifdef(CONFIG_EMUL_SBS_CHARGER emul_sbs_charger.c) diff --git a/drivers/charger/Kconfig b/drivers/charger/Kconfig index 4a6071fd8f8..9c7873168a5 100644 --- a/drivers/charger/Kconfig +++ b/drivers/charger/Kconfig @@ -21,5 +21,6 @@ config CHARGER_INIT_PRIORITY source "drivers/charger/Kconfig.sbs_charger" source "drivers/charger/Kconfig.bq24190" +source "drivers/charger/Kconfig.max20335" endif # CHARGER diff --git a/drivers/charger/Kconfig.max20335 b/drivers/charger/Kconfig.max20335 new file mode 100644 index 00000000000..7104e5daab6 --- /dev/null +++ b/drivers/charger/Kconfig.max20335 @@ -0,0 +1,11 @@ +# Copyright 2023 Grinn +# SPDX-License-Identifier: Apache-2.0 + +config CHARGER_MAX20335 + bool "MAX20335 battery charger driver" + default y + depends on DT_HAS_MAXIM_MAX20335_CHARGER_ENABLED + select I2C + select MFD + help + Enable the MAX20335 battery charger driver. diff --git a/drivers/charger/charger_max20335.c b/drivers/charger/charger_max20335.c new file mode 100644 index 00000000000..75eae16f888 --- /dev/null +++ b/drivers/charger/charger_max20335.c @@ -0,0 +1,287 @@ +/* + * Copyright 2023 Grinn + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT maxim_max20335_charger + +#include +#include +#include +#include +#include +#include + +#include "zephyr/logging/log.h" +LOG_MODULE_REGISTER(max20335_charger); + +#define MAX20335_REG_STATUS_A 0x02 +#define MAX20335_REG_ILIMCNTL 0x09 +#define MAX20335_REG_CHG_CNTL_A 0x0A +#define MAX20335_CHGCNTLA_BAT_REG_CFG_MASK GENMASK(4, 1) +#define MAX20335_ILIMCNTL_MASK GENMASK(1, 0) +#define MAX20335_STATUS_A_CHG_STAT_MASK GENMASK(2, 0) +#define MAX20335_CHRG_EN_MASK BIT(0) +#define MAX20335_CHRG_EN BIT(0) +#define MAX20335_REG_CVC_VREG_MIN_UV 4050000U +#define MAX20335_REG_CVC_VREG_STEP_UV 50000U +#define MAX20335_REG_CVC_VREG_MIN_IDX 0x0U +#define MAX20335_REG_CVC_VREG_MAX_IDX 0x0CU + +struct charger_max20335_config { + struct i2c_dt_spec bus; + uint32_t max_ichg_ua; + uint32_t max_vreg_uv; +}; + +enum { + MAX20335_CHARGER_OFF, + MAX20335_CHARGING_SUSPENDED_DUE_TO_TEMPERATURE, + MAX20335_PRE_CHARGE_IN_PROGRESS, + MAX20335_FAST_CHARGE_IN_PROGRESS_1, + MAX20335_FAST_CHARGE_IN_PROGRESS_2, + MAX20335_MAINTAIN_CHARGE_IN_PROGRESS, + MAX20335_MAIN_CHARGER_TIMER_DONE, + MAX20335_CHARGER_FAULT_CONDITION, +}; + +static const struct linear_range charger_uv_range = + LINEAR_RANGE_INIT(MAX20335_REG_CVC_VREG_MIN_UV, + MAX20335_REG_CVC_VREG_STEP_UV, + MAX20335_REG_CVC_VREG_MIN_IDX, + MAX20335_REG_CVC_VREG_MAX_IDX); + +static int max20335_get_status(const struct device *dev, enum charger_status *status) +{ + const struct charger_max20335_config *const config = dev->config; + uint8_t val; + int ret; + + ret = i2c_reg_read_byte_dt(&config->bus, MAX20335_REG_STATUS_A, &val); + if (ret) { + return ret; + } + + val = FIELD_GET(MAX20335_STATUS_A_CHG_STAT_MASK, val); + + switch (val) { + case MAX20335_CHARGER_OFF: + __fallthrough; + case MAX20335_CHARGING_SUSPENDED_DUE_TO_TEMPERATURE: + __fallthrough; + case MAX20335_CHARGER_FAULT_CONDITION: + *status = CHARGER_STATUS_NOT_CHARGING; + break; + case MAX20335_PRE_CHARGE_IN_PROGRESS: + __fallthrough; + case MAX20335_FAST_CHARGE_IN_PROGRESS_1: + __fallthrough; + case MAX20335_FAST_CHARGE_IN_PROGRESS_2: + __fallthrough; + case MAX20335_MAINTAIN_CHARGE_IN_PROGRESS: + *status = CHARGER_STATUS_CHARGING; + break; + case MAX20335_MAIN_CHARGER_TIMER_DONE: + *status = CHARGER_STATUS_FULL; + break; + default: + *status = CHARGER_STATUS_UNKNOWN; + break; + }; + + return 0; +} + +static int max20335_set_constant_charge_voltage(const struct device *dev, + uint32_t voltage_uv) +{ + const struct charger_max20335_config *const config = dev->config; + uint16_t idx; + uint8_t val; + int ret; + + if (voltage_uv > config->max_vreg_uv) { + LOG_WRN("Exceeded max constant charge voltage!"); + return -EINVAL; + } + + ret = linear_range_get_index(&charger_uv_range, voltage_uv, &idx); + if (ret == -EINVAL) { + return ret; + } + + val = FIELD_PREP(MAX20335_CHGCNTLA_BAT_REG_CFG_MASK, idx); + + return i2c_reg_update_byte_dt(&config->bus, + MAX20335_REG_CHG_CNTL_A, + MAX20335_CHGCNTLA_BAT_REG_CFG_MASK, + val); +} + +static int max20335_set_constant_charge_current(const struct device *dev, + uint32_t current_ua) +{ + const struct charger_max20335_config *const config = dev->config; + uint8_t val; + + if (current_ua > config->max_ichg_ua) { + LOG_WRN("Exceeded max constant charge current!"); + return -EINVAL; + } + + switch (current_ua) { + case 0: + val = 0x00; + break; + case 100000: + val = 0x01; + break; + case 500000: + val = 0x02; + break; + case 1000000: + val = 0x03; + break; + default: + return -ENOTSUP; + }; + + val = FIELD_PREP(MAX20335_ILIMCNTL_MASK, val); + + return i2c_reg_update_byte_dt(&config->bus, + MAX20335_REG_ILIMCNTL, + MAX20335_ILIMCNTL_MASK, + val); +} + +static int max20335_get_constant_charge_current(const struct device *dev, + uint32_t *current_ua) +{ + const struct charger_max20335_config *const config = dev->config; + uint8_t val; + int ret; + + ret = i2c_reg_read_byte_dt(&config->bus, MAX20335_REG_ILIMCNTL, &val); + if (ret) { + return ret; + } + + switch (val) { + case 0x00: + *current_ua = 0; + break; + case 0x01: + *current_ua = 100000; + break; + case 0x02: + *current_ua = 500000; + break; + case 0x03: + *current_ua = 1000000; + break; + default: + return -ENOTSUP; + }; + + return 0; +} + +static int max20335_get_constant_charge_voltage(const struct device *dev, + uint32_t *current_uv) +{ + const struct charger_max20335_config *const config = dev->config; + uint8_t val; + int ret; + + ret = i2c_reg_read_byte_dt(&config->bus, MAX20335_REG_CHG_CNTL_A, &val); + if (ret) { + return ret; + } + + val = FIELD_GET(MAX20335_CHGCNTLA_BAT_REG_CFG_MASK, val); + + return linear_range_get_value(&charger_uv_range, val, current_uv); +} + +static int max20335_set_enabled(const struct device *dev, bool enable) +{ + const struct charger_max20335_config *const config = dev->config; + + return i2c_reg_update_byte_dt(&config->bus, + MAX20335_REG_CHG_CNTL_A, + MAX20335_CHRG_EN_MASK, + enable ? MAX20335_CHRG_EN : 0); +} + +static int max20335_get_prop(const struct device *dev, charger_prop_t prop, + union charger_propval *val) +{ + switch (prop) { + case CHARGER_PROP_STATUS: + return max20335_get_status(dev, &val->status); + case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA: + return max20335_get_constant_charge_current(dev, + &val->const_charge_current_ua); + case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV: + return max20335_get_constant_charge_voltage(dev, + &val->const_charge_voltage_uv); + default: + return -ENOTSUP; + } +} + +static int max20335_set_prop(const struct device *dev, charger_prop_t prop, + const union charger_propval *val) +{ + switch (prop) { + case CHARGER_PROP_STATUS: + switch (val->status) { + case CHARGER_STATUS_CHARGING: + return max20335_set_enabled(dev, true); + case CHARGER_STATUS_NOT_CHARGING: + return max20335_set_enabled(dev, false); + default: + return -ENOTSUP; + } + case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA: + return max20335_set_constant_charge_current(dev, + val->const_charge_current_ua); + case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV: + return max20335_set_constant_charge_voltage(dev, + val->const_charge_voltage_uv); + default: + return -ENOTSUP; + } + +} + +static int max20335_init(const struct device *dev) +{ + const struct charger_max20335_config *config = dev->config; + + if (!i2c_is_ready_dt(&config->bus)) { + return -ENODEV; + } + + return 0; +} + +static const struct charger_driver_api max20335_driver_api = { + .get_property = max20335_get_prop, + .set_property = max20335_set_prop, +}; + +#define MAX20335_DEFINE(inst) \ + static const struct charger_max20335_config charger_max20335_config_##inst = { \ + .bus = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)), \ + .max_ichg_ua = DT_INST_PROP(inst, constant_charge_current_max_microamp), \ + .max_vreg_uv = DT_INST_PROP(inst, constant_charge_voltage_max_microvolt), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, &max20335_init, NULL, NULL, \ + &charger_max20335_config_##inst, \ + POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, \ + &max20335_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(MAX20335_DEFINE) diff --git a/dts/bindings/charger/maxim,max20335-charger.yaml b/dts/bindings/charger/maxim,max20335-charger.yaml new file mode 100644 index 00000000000..45b7cc87997 --- /dev/null +++ b/dts/bindings/charger/maxim,max20335-charger.yaml @@ -0,0 +1,8 @@ +# Copyright (c), 2023 Grinn +# SPDX -License-Identifier: Apache-2.0 + +description: Maxim MAX20335 battery charger + +include: battery.yaml + +compatible: "maxim,max20335-charger" diff --git a/tests/drivers/build_all/charger/i2c.dtsi b/tests/drivers/build_all/charger/i2c.dtsi index 53cbcf0e59b..c30efdcfcb8 100644 --- a/tests/drivers/build_all/charger/i2c.dtsi +++ b/tests/drivers/build_all/charger/i2c.dtsi @@ -15,3 +15,16 @@ bq24190@0 { constant-charge-current-max-microamp = <1000000>; constant-charge-voltage-max-microvolt = <4208000>; }; + +max20335@1 { + compatible = "maxim,max20335"; + reg = <0x01>; + status = "okay"; + + charger: charger { + compatible = "maxim,max20335-charger"; + + constant-charge-current-max-microamp = <100000>; + constant-charge-voltage-max-microvolt = <4050000>; + }; +};