/* * 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)