sensor: bq274xx: Add power management and shutdown mode

Add support for power management and the shutdown mode for bq274xx fuel
gauge sensor. This now allows boards that have any kind of low power
mode to turn on or off the sensor.

Tested on a Company's custom board with bq27421 sensor on it.

Signed-off-by: Luka Lopotar <luka.lopotar@greyp.com>
This commit is contained in:
Luka Lopotar 2021-03-30 07:29:39 +00:00 committed by Christopher Friedt
commit 7b67e239fe
3 changed files with 154 additions and 1 deletions

View file

@ -12,10 +12,15 @@
#include <sys/__assert.h>
#include <string.h>
#include <sys/byteorder.h>
#include <drivers/gpio.h>
#include "bq274xx.h"
#define BQ274XX_SUBCLASS_DELAY 5 /* subclass 64 & 82 needs 5ms delay */
/* Time to set pin in order to exit shutdown mode */
#define PIN_DELAY_TIME 1U
/* Time it takes device to initialize before doing any configuration */
#define INIT_TIME 100U
static int bq274xx_gauge_configure(const struct device *dev);
@ -370,6 +375,13 @@ static int bq274xx_gauge_init(const struct device *dev)
int status = 0;
uint16_t id;
#ifdef CONFIG_PM_DEVICE
if (!device_is_ready(config->int_gpios.port)) {
LOG_ERR("GPIO device pointer is not ready to be used");
return -ENODEV;
}
#endif
bq274xx->i2c = device_get_binding(config->bus_name);
if (bq274xx->i2c == NULL) {
LOG_ERR("Could not get pointer to %s device.",
@ -636,18 +648,142 @@ static int bq274xx_gauge_configure(const struct device *dev)
return -EIO;
}
#ifdef CONFIG_PM_DEVICE
bq274xx->pm_state = PM_DEVICE_STATE_ACTIVE;
#endif
return 0;
}
#ifdef CONFIG_PM_DEVICE
static int bq274xx_enter_shutdown_mode(struct bq274xx_data *data)
{
int status;
status = bq274xx_control_reg_write(data, BQ274XX_UNSEAL_KEY);
if (status < 0) {
LOG_ERR("Unable to unseal the battery");
return status;
}
status = bq274xx_control_reg_write(data, BQ274XX_UNSEAL_KEY);
if (status < 0) {
LOG_ERR("Unable to unseal the battery");
return status;
}
status = bq274xx_control_reg_write(data,
BQ274XX_CONTROL_SHUTDOWN_ENABLE);
if (status < 0) {
LOG_ERR("Unable to enable shutdown mode");
return status;
}
status = bq274xx_control_reg_write(data, BQ274XX_CONTROL_SHUTDOWN);
if (status < 0) {
LOG_ERR("Unable to enter shutdown mode");
return status;
}
status = bq274xx_control_reg_write(data, BQ274XX_CONTROL_SEALED);
if (status < 0) {
LOG_ERR("Failed to seal the gauge");
return status;
}
data->pm_state = PM_DEVICE_STATE_OFF;
return 0;
}
static int bq274xx_exit_shutdown_mode(const struct device *dev)
{
const struct bq274xx_config *const config = dev->config;
int status = 0;
status = gpio_pin_configure_dt(&config->int_gpios,
GPIO_OUTPUT | GPIO_OPEN_DRAIN);
if (status < 0) {
LOG_ERR("Unable to configure interrupt pin to output and open drain");
return status;
}
status = gpio_pin_set_dt(&config->int_gpios, 0);
if (status < 0) {
LOG_ERR("Unable to set interrupt pin to low");
return status;
}
k_msleep(PIN_DELAY_TIME);
status = gpio_pin_configure_dt(&config->int_gpios, GPIO_INPUT);
if (status < 0) {
LOG_ERR("Unable to configure interrupt pin to input");
return status;
}
k_msleep(INIT_TIME);
status = bq274xx_gauge_configure(dev);
if (status < 0) {
LOG_ERR("Unable to configure bq274xx gauge");
return status;
}
return 0;
}
static int bq274xx_pm_control(const struct device *dev, uint32_t ctrl_command,
enum pm_device_state *state)
{
int ret = 0;
struct bq274xx_data *data = dev->data;
switch (ctrl_command) {
case PM_DEVICE_STATE_SET:
if (*state == PM_DEVICE_STATE_OFF) {
ret = bq274xx_enter_shutdown_mode(data);
if (ret < 0) {
LOG_ERR("Unable to enter off state");
}
} else if (*state == PM_DEVICE_STATE_ACTIVE) {
ret = bq274xx_exit_shutdown_mode(dev);
if (ret < 0) {
LOG_ERR("Unable to enter active state");
}
} else {
LOG_ERR("State to set is not implemented");
ret = -ENOTSUP;
}
break;
case PM_DEVICE_STATE_GET:
*state = data->pm_state;
break;
default:
ret = -EINVAL;
}
return ret;
}
#endif /* CONFIG_PM_DEVICE */
static const struct sensor_driver_api bq274xx_battery_driver_api = {
.sample_fetch = bq274xx_sample_fetch,
.channel_get = bq274xx_channel_get,
};
#ifdef CONFIG_PM_DEVICE
#define BQ274XX_INT_CFG(index) \
.int_gpios = GPIO_DT_SPEC_INST_GET(index, int_gpios),
#else
#define BQ274XX_INT_CFG(index)
#endif
#define BQ274XX_INIT(index) \
static struct bq274xx_data bq274xx_driver_##index; \
\
static const struct bq274xx_config bq274xx_config_##index = { \
BQ274XX_INT_CFG(index) \
.bus_name = DT_INST_BUS_LABEL(index), \
.design_voltage = DT_INST_PROP(index, design_voltage), \
.design_capacity = DT_INST_PROP(index, design_capacity), \
@ -655,7 +791,7 @@ static const struct sensor_driver_api bq274xx_battery_driver_api = {
.terminate_voltage = DT_INST_PROP(index, terminate_voltage), \
}; \
\
DEVICE_DT_INST_DEFINE(index, &bq274xx_gauge_init, NULL, \
DEVICE_DT_INST_DEFINE(index, &bq274xx_gauge_init, bq274xx_pm_control, \
&bq274xx_driver_##index, \
&bq274xx_config_##index, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \

View file

@ -8,6 +8,7 @@
#define ZEPHYR_DRIVERS_SENSOR_BATTERY_BQ274XX_H_
#include <logging/log.h>
#include <drivers/gpio.h>
LOG_MODULE_REGISTER(bq274xx, CONFIG_SENSOR_LOG_LEVEL);
/*** General Constant ***/
@ -95,6 +96,9 @@ struct bq274xx_data {
uint16_t remaining_charge_capacity;
uint16_t nom_avail_capacity;
uint16_t full_avail_capacity;
#ifdef CONFIG_PM_DEVICE
enum pm_device_state pm_state;
#endif
};
struct bq274xx_config {
@ -103,6 +107,9 @@ struct bq274xx_config {
uint16_t design_capacity;
uint16_t taper_current;
uint16_t terminate_voltage;
#ifdef CONFIG_PM_DEVICE
struct gpio_dt_spec int_gpios;
#endif
};
#endif

View file

@ -30,3 +30,13 @@ properties:
type: int
required: true
description: Battery Terminate Voltage in mV
int-gpios:
type: phandle-array
required: false
description: |
The INT signal defaults to active low open drain, so requires a
pull-up on the board. By default it acts as an output and signals
specific events happening (e.g. change in State of Charge). While in
shutdown mode it acts as an input and toggling it will make the sensor
exit the mode.