Skip to content

Commit

Permalink
max17055: Set battery parameters from device tree
Browse files Browse the repository at this point in the history
Currently the MAX17055 driver assumes that a battery matching the
default characteristics is used.

This change allows battery characteristics to be specified in device
tree and writes them to the MAX17055 on initialization.

Existing default values are maintained for backwards compatibility.

Initialization routine taken from MAX17055 Software Implementation
Guide, document UG6365.

Signed-off-by: Hayden Ball <[email protected]>
  • Loading branch information
ball-hayden authored and MaureenHelm committed Apr 5, 2021
1 parent 5ee6793 commit 64995e2
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 34 deletions.
204 changes: 181 additions & 23 deletions drivers/sensor/max17055/max17055.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <drivers/i2c.h>
#include <drivers/sensor.h>
#include <sys/byteorder.h>

#include <logging/log.h>
LOG_MODULE_REGISTER(max17055, CONFIG_SENSOR_LOG_LEVEL);
Expand All @@ -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;
Expand All @@ -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
*
Expand All @@ -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
*
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -222,31 +353,58 @@ 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 = {
.sample_fetch = max17055_sample_fetch,
.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);
45 changes: 35 additions & 10 deletions drivers/sensor/max17055/max17055.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
15 changes: 15 additions & 0 deletions dts/bindings/sensor/maxim,max17055.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
5 changes: 4 additions & 1 deletion tests/drivers/build_all/i2c.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 64995e2

Please sign in to comment.