Skip to content

Commit

Permalink
drivers: sensor: bq274xx: Configure or confirm chemistry profile
Browse files Browse the repository at this point in the history
Both the BQ27421 and BQ27427 have a few preset Chemistry profiles.
For the BQ27421 there exists three variants of the IC, and for the BQ27427,
it can be configured. The chemistry profile among other things includes the
taper voltage, which is used to detect charge termination.

This adds an optional `chemistry-id` config option to the driver. On the
BQ27421, it will confirm that the correct variant of the IC is mounted,
and on the BQ27427, it will configure it with the correct value.

Side note: The reference manual for the BQ27427
(https://www.ti.com/lit/ug/sluucd5/sluucd5.pdf) currently contains some
errors and inconsistencies regarding these registers. The table on page 7
appears to be correct.

Signed-off-by: Tobias Pisani <[email protected]>
  • Loading branch information
topisani committed Oct 27, 2023
1 parent 1fac5ed commit 79a10d3
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 85 deletions.
183 changes: 111 additions & 72 deletions drivers/sensor/bq274xx/bq274xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ LOG_MODULE_REGISTER(bq274xx, CONFIG_SENSOR_LOG_LEVEL);
/* Time to wait for CFGUP bit to be set, up to 1 second according to the
* technical reference manual, keep some headroom like the Linux driver.
*/
#define BQ274XX_CFGUP_DELAY K_MSEC(25)
#define BQ274XX_CFGUP_DELAY K_MSEC(25)
#define BQ274XX_CFGUP_MAX_TRIES 100

/* Time to set pin in order to exit shutdown mode */
Expand All @@ -55,11 +55,11 @@ LOG_MODULE_REGISTER(bq274xx, CONFIG_SENSOR_LOG_LEVEL);
#define BQ27XXX_FLAG_CFGUP BIT(4)

/* BQ27427 CC Gain */
#define BQ27427_CC_GAIN BQ274XX_EXT_BLKDAT(5)
#define BQ27427_CC_GAIN BQ274XX_EXT_BLKDAT(5)
#define BQ27427_CC_GAIN_SIGN_BIT BIT(7)

/* Subclasses */
#define BQ274XX_SUBCLASS_82 82
#define BQ274XX_SUBCLASS_82 82
#define BQ274XX_SUBCLASS_105 105

static const struct bq274xx_regs bq27421_regs = {
Expand All @@ -76,8 +76,7 @@ static const struct bq274xx_regs bq27427_regs = {
.dm_taper_rate = 21,
};

static int bq274xx_cmd_reg_read(const struct device *dev, uint8_t reg_addr,
int16_t *val)
static int bq274xx_cmd_reg_read(const struct device *dev, uint8_t reg_addr, int16_t *val)
{
const struct bq274xx_config *config = dev->config;
uint8_t i2c_data[2];
Expand Down Expand Up @@ -132,9 +131,8 @@ static int bq274xx_get_device_type(const struct device *dev, uint16_t *val)
return 0;
}

static int bq274xx_read_block(const struct device *dev,
uint8_t subclass,
uint8_t *block, uint8_t num_bytes)
static int bq274xx_read_block(const struct device *dev, uint8_t subclass, uint8_t *block,
uint8_t num_bytes)
{
const struct bq274xx_config *const config = dev->config;
int ret;
Expand Down Expand Up @@ -163,8 +161,7 @@ static int bq274xx_read_block(const struct device *dev,
return 0;
}

static int bq274xx_write_block(const struct device *dev,
uint8_t *block, uint8_t num_bytes)
static int bq274xx_write_block(const struct device *dev, uint8_t *block, uint8_t num_bytes)
{
const struct bq274xx_config *const config = dev->config;
uint8_t checksum = 0;
Expand Down Expand Up @@ -198,9 +195,7 @@ static int bq274xx_write_block(const struct device *dev,
return 0;
}

static void bq274xx_update_block(uint8_t *block,
uint8_t offset, uint16_t val,
bool *block_modified)
static void bq274xx_update_block(uint8_t *block, uint8_t offset, uint16_t val, bool *block_modified)
{
uint16_t old_val;

Expand Down Expand Up @@ -238,7 +233,7 @@ static int bq274xx_mode_cfgupdate(const struct device *dev, bool enabled)
return -EIO;
}

enabled_flag = !!(flags & BQ27XXX_FLAG_CFGUP);
enabled_flag = !!(flags & BQ27XXX_FLAG_CFGUP);
if (enabled_flag == enabled) {
LOG_DBG("CFGUP ready, try %u", try);
break;
Expand Down Expand Up @@ -266,8 +261,7 @@ static int bq27427_ccgain_quirk(const struct device *dev)
int ret;
uint8_t val, checksum;

ret = i2c_reg_write_byte_dt(&config->i2c, BQ274XX_EXT_DATA_CLASS,
BQ274XX_SUBCLASS_105);
ret = i2c_reg_write_byte_dt(&config->i2c, BQ274XX_EXT_DATA_CLASS, BQ274XX_SUBCLASS_105);
if (ret < 0) {
LOG_ERR("Failed to update state subclass");
return -EIO;
Expand Down Expand Up @@ -321,6 +315,62 @@ static int bq27427_ccgain_quirk(const struct device *dev)
return 0;
}

static int bq274xx_ensure_chemistry(const struct device *dev, uint16_t chem_id)
{
struct bq274xx_data *data = dev->data;
int ret;
uint16_t val;

ret = bq274xx_ctrl_reg_write(dev, BQ274XX_CTRL_CHEM_ID);
if (ret < 0) {
LOG_ERR("Unable to write control register");
return -EIO;
}

ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_CONTROL, &val);
if (ret < 0) {
LOG_ERR("Unable to read register");
return -EIO;
}

LOG_DBG("Chem ID: %04x", val);

if (val != chem_id) {
/* Only the bq27427 has a configurable Chemistry ID. On bq27421, it depends on the
* variant of the chip, so just error out if the chemistry ID is wrong.
*/

Check failure on line 341 in drivers/sensor/bq274xx/bq274xx.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

CODE_INDENT

drivers/sensor/bq274xx/bq274xx.c:341 code indent should use tabs where possible
if (data->regs != &bq27427_regs) {
LOG_ERR("Unable to confirm chemistry ID 0x%04x. Device reported 0x%04x",
chem_id, val);
return -EIO;
}

uint16_t cmd;

switch (val) {
case 0x3230:
cmd = 0x0030;
break;
case 0x1202:
cmd = 0x0031;
break;
case 0x3142:
cmd = 0x0032;
break;
default:
LOG_ERR("Unsupported chemistry ID 0x%04x", val);
return -EIO;
}

ret = bq274xx_ctrl_reg_write(dev, cmd);
if (ret < 0) {
LOG_ERR("Unable to configure chemistry");
return -EIO;
}
}
return ret;
}

static int bq274xx_gauge_configure(const struct device *dev)
{
const struct bq274xx_config *const config = dev->config;
Expand Down Expand Up @@ -362,18 +412,12 @@ static int bq274xx_gauge_configure(const struct device *dev)
return ret;
}

bq274xx_update_block(block,
regs->dm_design_capacity, config->design_capacity,
&block_modified);
bq274xx_update_block(block,
regs->dm_design_energy, designenergy_mwh,
bq274xx_update_block(block, regs->dm_design_capacity, config->design_capacity,
&block_modified);
bq274xx_update_block(block,
regs->dm_terminate_voltage, config->terminate_voltage,
&block_modified);
bq274xx_update_block(block,
regs->dm_taper_rate, taperrate,
bq274xx_update_block(block, regs->dm_design_energy, designenergy_mwh, &block_modified);
bq274xx_update_block(block, regs->dm_terminate_voltage, config->terminate_voltage,
&block_modified);
bq274xx_update_block(block, regs->dm_taper_rate, taperrate, &block_modified);

if (block_modified) {
LOG_INF("bq274xx: updating fuel gauge parameters");
Expand All @@ -390,6 +434,13 @@ static int bq274xx_gauge_configure(const struct device *dev)
}
}

if (config->chemistry_id != 0) {
ret = bq274xx_ensure_chemistry(dev, config->chemistry_id);
if (ret < 0) {
return ret;
}
}

ret = bq274xx_mode_cfgupdate(dev, false);
if (ret < 0) {
return ret;
Expand Down Expand Up @@ -502,53 +553,47 @@ static int bq274xx_sample_fetch(const struct device *dev, enum sensor_channel ch
}

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_VOLTAGE) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_VOLTAGE,
&data->voltage);
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_VOLTAGE, &data->voltage);
if (ret < 0) {
LOG_ERR("Failed to read voltage");
return -EIO;
}
}

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_AVG_CURRENT) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_AVG_CURRENT,
&data->avg_current);
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_AVG_CURRENT, &data->avg_current);
if (ret < 0) {
LOG_ERR("Failed to read average current ");
return -EIO;
}
}

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_TEMP) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_INT_TEMP,
&data->internal_temperature);
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_INT_TEMP, &data->internal_temperature);
if (ret < 0) {
LOG_ERR("Failed to read internal temperature");
return -EIO;
}
}

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_STDBY_CURRENT) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_STDBY_CURRENT,
&data->stdby_current);
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_STDBY_CURRENT, &data->stdby_current);
if (ret < 0) {
LOG_ERR("Failed to read standby current");
return -EIO;
}
}

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_MAX_CURRENT,
&data->max_load_current);
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_MAX_CURRENT, &data->max_load_current);
if (ret < 0) {
LOG_ERR("Failed to read maximum current");
return -EIO;
}
}

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_STATE_OF_CHARGE) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_SOC,
&data->state_of_charge);
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_SOC, &data->state_of_charge);
if (ret < 0) {
LOG_ERR("Failed to read state of charge");
return -EIO;
Expand All @@ -557,7 +602,7 @@ static int bq274xx_sample_fetch(const struct device *dev, enum sensor_channel ch

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_FULL_CAPACITY,
&data->full_charge_capacity);
&data->full_charge_capacity);
if (ret < 0) {
LOG_ERR("Failed to read full charge capacity");
return -EIO;
Expand All @@ -566,7 +611,7 @@ static int bq274xx_sample_fetch(const struct device *dev, enum sensor_channel ch

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_REM_CAPACITY,
&data->remaining_charge_capacity);
&data->remaining_charge_capacity);
if (ret < 0) {
LOG_ERR("Failed to read remaining charge capacity");
return -EIO;
Expand All @@ -575,7 +620,7 @@ static int bq274xx_sample_fetch(const struct device *dev, enum sensor_channel ch

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_NOM_CAPACITY,
&data->nom_avail_capacity);
&data->nom_avail_capacity);
if (ret < 0) {
LOG_ERR("Failed to read nominal available capacity");
return -EIO;
Expand All @@ -584,25 +629,23 @@ static int bq274xx_sample_fetch(const struct device *dev, enum sensor_channel ch

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_AVAIL_CAPACITY,
&data->full_avail_capacity);
&data->full_avail_capacity);
if (ret < 0) {
LOG_ERR("Failed to read full available capacity");
return -EIO;
}
}

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_AVG_POWER) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_AVG_POWER,
&data->avg_power);
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_AVG_POWER, &data->avg_power);
if (ret < 0) {
LOG_ERR("Failed to read battery average power");
return -EIO;
}
}

if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_STATE_OF_HEALTH) {
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_SOH,
&data->state_of_health);
ret = bq274xx_cmd_reg_read(dev, BQ274XX_CMD_SOH, &data->state_of_health);

data->state_of_health = (data->state_of_health) & 0x00FF;

Expand Down Expand Up @@ -747,8 +790,7 @@ static int bq274xx_exit_shutdown_mode(const struct device *dev)
return 0;
}

static int bq274xx_pm_action(const struct device *dev,
enum pm_device_action action)
static int bq274xx_pm_action(const struct device *dev, enum pm_device_action action)
{
int ret;

Expand Down Expand Up @@ -777,9 +819,8 @@ static const struct sensor_driver_api bq274xx_battery_driver_api = {
};

#if defined(CONFIG_BQ274XX_PM) || defined(CONFIG_BQ274XX_TRIGGER)
#define BQ274XX_INT_CFG(index) \
.int_gpios = GPIO_DT_SPEC_INST_GET(index, int_gpios),
#define PM_BQ274XX_DT_INST_DEFINE(index, bq274xx_pm_action) \
#define BQ274XX_INT_CFG(index) .int_gpios = GPIO_DT_SPEC_INST_GET(index, int_gpios),
#define PM_BQ274XX_DT_INST_DEFINE(index, bq274xx_pm_action) \
PM_DEVICE_DT_INST_DEFINE(index, bq274xx_pm_action)
#define PM_BQ274XX_DT_INST_GET(index) PM_DEVICE_DT_INST_GET(index)
#else
Expand All @@ -788,26 +829,24 @@ static const struct sensor_driver_api bq274xx_battery_driver_api = {
#define PM_BQ274XX_DT_INST_GET(index) NULL
#endif

#define BQ274XX_INIT(index) \
static struct bq274xx_data bq274xx_driver_##index; \
\
static const struct bq274xx_config bq274xx_config_##index = { \
.i2c = I2C_DT_SPEC_INST_GET(index), \
BQ274XX_INT_CFG(index) \
.design_voltage = DT_INST_PROP(index, design_voltage), \
.design_capacity = DT_INST_PROP(index, design_capacity), \
.taper_current = DT_INST_PROP(index, taper_current), \
.terminate_voltage = DT_INST_PROP(index, terminate_voltage), \
.lazy_loading = DT_INST_PROP(index, zephyr_lazy_load), \
}; \
\
PM_BQ274XX_DT_INST_DEFINE(index, bq274xx_pm_action); \
\
SENSOR_DEVICE_DT_INST_DEFINE(index, &bq274xx_gauge_init, \
PM_BQ274XX_DT_INST_GET(index), \
&bq274xx_driver_##index, \
&bq274xx_config_##index, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \
&bq274xx_battery_driver_api);
#define BQ274XX_INIT(index) \
static struct bq274xx_data bq274xx_driver_##index; \
\
static const struct bq274xx_config bq274xx_config_##index = { \
.i2c = I2C_DT_SPEC_INST_GET(index), \
BQ274XX_INT_CFG(index).design_voltage = DT_INST_PROP(index, design_voltage), \
.design_capacity = DT_INST_PROP(index, design_capacity), \
.taper_current = DT_INST_PROP(index, taper_current), \
.terminate_voltage = DT_INST_PROP(index, terminate_voltage), \
.chemistry_id = DT_INST_PROP(index, chemistry_id), \
.lazy_loading = DT_INST_PROP(index, zephyr_lazy_load), \
}; \
\
PM_BQ274XX_DT_INST_DEFINE(index, bq274xx_pm_action); \
\
SENSOR_DEVICE_DT_INST_DEFINE(index, &bq274xx_gauge_init, PM_BQ274XX_DT_INST_GET(index), \
&bq274xx_driver_##index, &bq274xx_config_##index, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
&bq274xx_battery_driver_api);

DT_INST_FOREACH_STATUS_OKAY(BQ274XX_INIT)
Loading

0 comments on commit 79a10d3

Please sign in to comment.