diff --git a/drivers/sensor/max17055/max17055.c b/drivers/sensor/max17055/max17055.c index fe73f8980c7..f82ec941e82 100644 --- a/drivers/sensor/max17055/max17055.c +++ b/drivers/sensor/max17055/max17055.c @@ -6,6 +6,7 @@ #include #include +#include #include LOG_MODULE_REGISTER(max17055, CONFIG_SENSOR_LOG_LEVEL); @@ -30,8 +31,7 @@ static int max17055_reg_read(struct max17055_data *priv, int reg_addr, uint8_t i2c_data[2]; int rc; - rc = i2c_burst_read(priv->i2c, DT_INST_REG_ADDR(0), reg_addr, - i2c_data, 2); + rc = i2c_burst_read(priv->i2c, DT_INST_REG_ADDR(0), reg_addr, i2c_data, 2); if (rc < 0) { LOG_ERR("Unable to read register"); return rc; @@ -41,6 +41,16 @@ static int max17055_reg_read(struct max17055_data *priv, int reg_addr, return 0; } +static int max17055_reg_write(struct max17055_data *priv, int reg_addr, + int16_t val) +{ + uint8_t i2c_data[2]; + + sys_put_le16(val, i2c_data); + + return i2c_burst_write(priv->i2c, DT_INST_REG_ADDR(0), reg_addr, i2c_data, 2); +} + /** * @brief Convert current in MAX17055 units to milliamps * @@ -53,6 +63,18 @@ static int current_to_ma(unsigned int rsense_mohms, int16_t val) return (val * 1.5625) / rsense_mohms; } +/** + * @brief Convert current in milliamps to MAX17055 units + * + * @param rsense_mohms Value of Rsense in milliohms + * @param val Value in mA to convert + * @return corresponding value in MAX17055 units, ready to write to a register + */ +static int current_ma_to_max17055(unsigned int rsense_mohms, uint16_t val) +{ + return val * rsense_mohms / 1.5625; +} + /** * @brief Convert capacity in MAX17055 units to milliamps * @@ -72,6 +94,29 @@ static int capacity_to_ma(unsigned int rsense_mohms, int16_t val) return rem; } +/** + * @brief Convert capacity in milliamphours to MAX17055 units + * + * @param rsense_mohms Value of Rsense in milliohms + * @param val_mha Value in milliamphours to convert + * @return corresponding value in MAX17055 units, ready to write to a register + */ +static int capacity_to_max17055(unsigned int rsense_mohms, uint16_t val_mha) +{ + return val_mha * rsense_mohms / 5; +} + +/** + * @brief Convert voltage in millivolts to MAX17055 units + * + * @param val_mv Value in millivolts to convert + * @return corresponding value in MAX17055 units, ready to write to a register + */ +static int voltage_mV_to_max17055(uint16_t val_mv) +{ + return val_mv * 16 / 1.25; +} + static void set_millis(struct sensor_value *val, int val_millis) { val->val1 = val_millis / 1000; @@ -105,8 +150,7 @@ static int max17055_channel_get(const struct device *dev, case SENSOR_CHAN_GAUGE_AVG_CURRENT: { int current_ma; - current_ma = current_to_ma(config->rsense_mohms, - priv->avg_current); + current_ma = current_to_ma(config->rsense_mohms, priv->avg_current); set_millis(valp, current_ma); break; } @@ -175,6 +219,7 @@ static int max17055_sample_fetch(const struct device *dev, enum sensor_channel chan) { struct max17055_data *priv = dev->data; + struct { int reg_addr; int16_t *dest; @@ -205,14 +250,100 @@ static int max17055_sample_fetch(const struct device *dev, return 0; } +static int max17055_exit_hibernate(struct max17055_data *priv) +{ + LOG_DBG("Exit hibernate"); + + if (max17055_reg_write(priv, SOFT_WAKEUP, SOFT_WAKEUP_WAKEUP)) { + return -EIO; + } + if (max17055_reg_write(priv, HIB_CFG, HIB_CFG_CLEAR)) { + return -EIO; + } + if (max17055_reg_write(priv, SOFT_WAKEUP, SOFT_WAKEUP_CLEAR)) { + return -EIO; + } + + return 0; +} + +static int max17055_write_config(struct max17055_data *priv, + const struct max17055_config *const config) +{ + uint16_t design_capacity = capacity_to_max17055(config->rsense_mohms, + config->design_capacity); + uint16_t d_qacc = design_capacity / 32; + uint16_t d_pacc = d_qacc * 44138 / design_capacity; + uint16_t i_chg_term = current_ma_to_max17055(config->rsense_mohms, config->i_chg_term); + uint16_t v_empty = voltage_mV_to_max17055(config->v_empty); + + LOG_DBG("Writing configuration parameters"); + LOG_DBG("DesignCap: %u, dQAcc: %u, IChgTerm: %u, VEmpty: %u, dPAcc: %u", + design_capacity, d_qacc, i_chg_term, v_empty, d_pacc); + + if (max17055_reg_write(priv, DESIGN_CAP, design_capacity)) { + return -EIO; + } + if (max17055_reg_write(priv, D_QACC, d_qacc)) { + return -EIO; + } + if (max17055_reg_write(priv, ICHG_TERM, i_chg_term)) { + return -EIO; + } + if (max17055_reg_write(priv, V_EMPTY, v_empty)) { + return -EIO; + } + if (max17055_reg_write(priv, D_PACC, d_pacc)) { + return -EIO; + } + if (max17055_reg_write(priv, MODEL_CFG, MODELCFG_REFRESH)) { + return -EIO; + } + + uint16_t model_cfg = MODELCFG_REFRESH; + + while (model_cfg & MODELCFG_REFRESH) { + max17055_reg_read(priv, MODEL_CFG, &model_cfg); + k_sleep(K_MSEC(10)); + } + + return 0; +} + +static int max17055_init_config(struct max17055_data *priv, + const struct max17055_config *const config) +{ + int16_t hib_cfg; + + if (max17055_reg_read(priv, HIB_CFG, &hib_cfg)) { + return -EIO; + } + + if (max17055_exit_hibernate(priv)) { + return -EIO; + } + + if (max17055_write_config(priv, config)) { + return -EIO; + } + + if (max17055_reg_write(priv, HIB_CFG, hib_cfg)) { + return -EIO; + } + + return 0; +} + /** * @brief initialise the fuel gauge * * @return 0 for success + * @return -EIO on I2C communication error * @return -EINVAL if the I2C controller could not be found */ static int max17055_gauge_init(const struct device *dev) { + int16_t tmp; struct max17055_data *priv = dev->data; const struct max17055_config *const config = dev->config; @@ -222,7 +353,32 @@ static int max17055_gauge_init(const struct device *dev) return -EINVAL; } - return 0; + if (max17055_reg_read(priv, STATUS, &tmp)) { + return -EIO; + } + + if (!(tmp & STATUS_POR)) { + LOG_DBG("No POR event detected - skip device configuration"); + return 0; + } + + /* Wait for FSTAT_DNR to be cleared */ + tmp = FSTAT_DNR; + while (tmp & FSTAT_DNR) { + max17055_reg_read(priv, FSTAT, &tmp); + } + + if (max17055_init_config(priv, config)) { + return -EIO; + } + + /* Clear PowerOnReset bit */ + if (max17055_reg_read(priv, STATUS, &tmp)) { + return -EIO; + } + + tmp &= ~STATUS_POR; + return max17055_reg_write(priv, STATUS, tmp); } static const struct sensor_driver_api max17055_battery_driver_api = { @@ -230,23 +386,25 @@ static const struct sensor_driver_api max17055_battery_driver_api = { .channel_get = max17055_channel_get, }; -#define MAX17055_INIT(index) \ - static struct max17055_data max17055_driver_##index; \ - \ - static const struct max17055_config max17055_config_##index = { \ - .bus_name = DT_INST_BUS_LABEL(index), \ - .rsense_mohms = DT_INST_PROP(index, rsense_mohms), \ - .design_voltage = DT_INST_PROP(index, design_voltage), \ - .desired_voltage = DT_INST_PROP(index, desired_voltage), \ - .desired_charging_current = \ - DT_INST_PROP(index, desired_charging_current), \ - }; \ - \ - DEVICE_DT_INST_DEFINE(index, &max17055_gauge_init, \ - device_pm_control_nop, \ - &max17055_driver_##index, \ - &max17055_config_##index, POST_KERNEL, \ - CONFIG_SENSOR_INIT_PRIORITY, \ - &max17055_battery_driver_api) +#define MAX17055_INIT(index) \ + static struct max17055_data max17055_driver_##index; \ + \ + static const struct max17055_config max17055_config_##index = { \ + .bus_name = DT_INST_BUS_LABEL(index), \ + .design_capacity = DT_INST_PROP(index, design_capacity), \ + .design_voltage = DT_INST_PROP(index, design_voltage), \ + .desired_charging_current = DT_INST_PROP(index, desired_charging_current), \ + .desired_voltage = DT_INST_PROP(index, desired_voltage), \ + .i_chg_term = DT_INST_PROP(index, i_chg_term), \ + .rsense_mohms = DT_INST_PROP(index, rsense_mohms), \ + .v_empty = DT_INST_PROP(index, v_empty), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(index, &max17055_gauge_init, \ + device_pm_control_nop, \ + &max17055_driver_##index, \ + &max17055_config_##index, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, \ + &max17055_battery_driver_api) DT_INST_FOREACH_STATUS_OKAY(MAX17055_INIT); diff --git a/drivers/sensor/max17055/max17055.h b/drivers/sensor/max17055/max17055.h index 4f3a7a49b5c..7d717cd72ed 100644 --- a/drivers/sensor/max17055/max17055.h +++ b/drivers/sensor/max17055/max17055.h @@ -9,16 +9,35 @@ /* Register addresses */ enum { - REP_CAP = 0x5, - REP_SOC = 0x6, - INT_TEMP = 0x8, - VCELL = 0x9, - AVG_CURRENT = 0xb, - FULL_CAP_REP = 0x10, - TTE = 0x11, - CYCLES = 0x17, - DESIGN_CAP = 0x18, - TTF = 0x20, + STATUS = 0x0, + REP_CAP = 0x5, + REP_SOC = 0x6, + INT_TEMP = 0x8, + VCELL = 0x9, + AVG_CURRENT = 0xb, + FULL_CAP_REP = 0x10, + TTE = 0x11, + ICHG_TERM = 0x1e, + CYCLES = 0x17, + DESIGN_CAP = 0x18, + TTF = 0x20, + V_EMPTY = 0x3a, + FSTAT = 0x3d, + D_QACC = 0x45, + D_PACC = 0x46, + SOFT_WAKEUP = 0x60, + HIB_CFG = 0xba, + MODEL_CFG = 0xdb, +}; + +/* Masks */ +enum { + FSTAT_DNR = 0x0001, + HIB_CFG_CLEAR = 0x0000, + MODELCFG_REFRESH = 0x8000, + SOFT_WAKEUP_CLEAR = 0x0000, + SOFT_WAKEUP_WAKEUP = 0x0090, + STATUS_POR = 0x0002, }; struct max17055_data { @@ -49,12 +68,18 @@ struct max17055_config { char *bus_name; /* Value of Rsense resistor in milliohms (typicallly 5 or 10) */ uint16_t rsense_mohms; + /* The design capacity (aka label capacity) of the cell in mAh */ + uint16_t design_capacity; /* Design voltage of cell in mV */ uint16_t design_voltage; /* Desired voltage of cell in mV */ uint16_t desired_voltage; /* Desired charging current in mA */ uint16_t desired_charging_current; + /* The charge termination current in uA */ + uint16_t i_chg_term; + /* The empty voltage of the cell in mV */ + uint16_t v_empty; }; #endif diff --git a/dts/bindings/sensor/maxim,max17055.yaml b/dts/bindings/sensor/maxim,max17055.yaml index 2296cd677ae..3fb40daa843 100644 --- a/dts/bindings/sensor/maxim,max17055.yaml +++ b/dts/bindings/sensor/maxim,max17055.yaml @@ -11,6 +11,11 @@ compatible: "maxim,max17055" include: i2c-device.yaml properties: + design-capacity: + type: int + required: true + description: The design capacity (aka label capacity) of the cell in mAh + design-voltage: type: int required: true @@ -26,8 +31,18 @@ properties: required: true description: Battery Design Charging Current in mA (e.g. 2000) + i-chg-term: + type: int + required: true + description: The charge termination current in mA + rsense-mohms: type: int required: true description: > Value of Rsense resistor in milliohms (e.g. 5). It cannot be 0. + + v-empty: + type: int + required: true + description: The empty voltage of the cell in mV diff --git a/tests/drivers/build_all/i2c.dtsi b/tests/drivers/build_all/i2c.dtsi index 904354adbec..13fc1f65485 100644 --- a/tests/drivers/build_all/i2c.dtsi +++ b/tests/drivers/build_all/i2c.dtsi @@ -564,10 +564,13 @@ test_i2c_max17055: max17055@49 { compatible = "maxim,max17055"; label = "max17055"; reg = <0x49>; + design-capacity = <1500>; design-voltage = <3860>; - desired-voltage = <4400>; desired-charging-current = <2000>; + desired-voltage = <4400>; + i-chg-term = <100>; rsense-mohms = <5>; + v-empty = <3300>; }; test_i2c_vcnl4040: vcnl4040@60 {