The PM callback is no longer referenced as "pm_control" but "pm_action_cb", so reflect this new naming on the callbacks. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
782 lines
19 KiB
C
782 lines
19 KiB
C
/*
|
|
* Copyright (c) 2020 Linumiz
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT ti_bq274xx
|
|
|
|
#include <drivers/i2c.h>
|
|
#include <init.h>
|
|
#include <drivers/sensor.h>
|
|
#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);
|
|
|
|
static int bq274xx_command_reg_read(struct bq274xx_data *bq274xx, uint8_t reg_addr,
|
|
int16_t *val)
|
|
{
|
|
uint8_t i2c_data[2];
|
|
int status;
|
|
|
|
status = i2c_burst_read(bq274xx->i2c, DT_INST_REG_ADDR(0), reg_addr,
|
|
i2c_data, 2);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to read register");
|
|
return -EIO;
|
|
}
|
|
|
|
*val = (i2c_data[1] << 8) | i2c_data[0];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bq274xx_control_reg_write(struct bq274xx_data *bq274xx,
|
|
uint16_t subcommand)
|
|
{
|
|
uint8_t i2c_data, reg_addr;
|
|
int status = 0;
|
|
|
|
reg_addr = BQ274XX_COMMAND_CONTROL_LOW;
|
|
i2c_data = (uint8_t)((subcommand)&0x00FF);
|
|
|
|
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0), reg_addr,
|
|
i2c_data);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to write into control low register");
|
|
return -EIO;
|
|
}
|
|
|
|
k_msleep(BQ274XX_SUBCLASS_DELAY);
|
|
|
|
reg_addr = BQ274XX_COMMAND_CONTROL_HIGH;
|
|
i2c_data = (uint8_t)((subcommand >> 8) & 0x00FF);
|
|
|
|
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0), reg_addr,
|
|
i2c_data);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to write into control high register");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bq274xx_command_reg_write(struct bq274xx_data *bq274xx, uint8_t command,
|
|
uint8_t data)
|
|
{
|
|
uint8_t i2c_data, reg_addr;
|
|
int status = 0;
|
|
|
|
reg_addr = command;
|
|
i2c_data = data;
|
|
|
|
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0), reg_addr,
|
|
i2c_data);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to write into control register");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bq274xx_read_data_block(struct bq274xx_data *bq274xx, uint8_t offset,
|
|
uint8_t *data, uint8_t bytes)
|
|
{
|
|
uint8_t i2c_data;
|
|
int status = 0;
|
|
|
|
i2c_data = BQ274XX_EXTENDED_BLOCKDATA_START + offset;
|
|
|
|
status = i2c_burst_read(bq274xx->i2c, DT_INST_REG_ADDR(0), i2c_data,
|
|
data, bytes);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read block");
|
|
return -EIO;
|
|
}
|
|
|
|
k_msleep(BQ274XX_SUBCLASS_DELAY);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bq274xx_get_device_type(struct bq274xx_data *bq274xx, uint16_t *val)
|
|
{
|
|
int status;
|
|
|
|
status =
|
|
bq274xx_control_reg_write(bq274xx, BQ274XX_CONTROL_DEVICE_TYPE);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to write control register");
|
|
return -EIO;
|
|
}
|
|
|
|
status = bq274xx_command_reg_read(bq274xx, BQ274XX_COMMAND_CONTROL_LOW,
|
|
val);
|
|
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to read register");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief sensor value get
|
|
*
|
|
* @return -ENOTSUP for unsupported channels
|
|
*/
|
|
static int bq274xx_channel_get(const struct device *dev,
|
|
enum sensor_channel chan,
|
|
struct sensor_value *val)
|
|
{
|
|
struct bq274xx_data *bq274xx = dev->data;
|
|
float int_temp;
|
|
|
|
switch (chan) {
|
|
case SENSOR_CHAN_GAUGE_VOLTAGE:
|
|
val->val1 = ((bq274xx->voltage / 1000));
|
|
val->val2 = ((bq274xx->voltage % 1000) * 1000U);
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_AVG_CURRENT:
|
|
val->val1 = ((bq274xx->avg_current / 1000));
|
|
val->val2 = ((bq274xx->avg_current % 1000) * 1000U);
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_STDBY_CURRENT:
|
|
val->val1 = ((bq274xx->stdby_current / 1000));
|
|
val->val2 = ((bq274xx->stdby_current % 1000) * 1000U);
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT:
|
|
val->val1 = ((bq274xx->max_load_current / 1000));
|
|
val->val2 = ((bq274xx->max_load_current % 1000) * 1000U);
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_TEMP:
|
|
int_temp = (bq274xx->internal_temperature * 0.1);
|
|
int_temp = int_temp - 273.15;
|
|
val->val1 = (int32_t)int_temp;
|
|
val->val2 = (int_temp - (int32_t)int_temp) * 1000000;
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
|
|
val->val1 = bq274xx->state_of_charge;
|
|
val->val2 = 0;
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_STATE_OF_HEALTH:
|
|
val->val1 = bq274xx->state_of_health;
|
|
val->val2 = 0;
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY:
|
|
val->val1 = (bq274xx->full_charge_capacity / 1000);
|
|
val->val2 = ((bq274xx->full_charge_capacity % 1000) * 1000U);
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY:
|
|
val->val1 = (bq274xx->remaining_charge_capacity / 1000);
|
|
val->val2 =
|
|
((bq274xx->remaining_charge_capacity % 1000) * 1000U);
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY:
|
|
val->val1 = (bq274xx->nom_avail_capacity / 1000);
|
|
val->val2 = ((bq274xx->nom_avail_capacity % 1000) * 1000U);
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY:
|
|
val->val1 = (bq274xx->full_avail_capacity / 1000);
|
|
val->val2 = ((bq274xx->full_avail_capacity % 1000) * 1000U);
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_AVG_POWER:
|
|
val->val1 = (bq274xx->avg_power / 1000);
|
|
val->val2 = ((bq274xx->avg_power % 1000) * 1000U);
|
|
break;
|
|
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bq274xx_sample_fetch(const struct device *dev,
|
|
enum sensor_channel chan)
|
|
{
|
|
struct bq274xx_data *bq274xx = dev->data;
|
|
int status = 0;
|
|
|
|
#ifdef CONFIG_BQ274XX_LAZY_CONFIGURE
|
|
if (!bq274xx->lazy_loaded) {
|
|
status = bq274xx_gauge_configure(dev);
|
|
|
|
if (status < 0) {
|
|
return status;
|
|
}
|
|
bq274xx->lazy_loaded = true;
|
|
}
|
|
#endif
|
|
|
|
switch (chan) {
|
|
case SENSOR_CHAN_GAUGE_VOLTAGE:
|
|
status = bq274xx_command_reg_read(
|
|
bq274xx, BQ274XX_COMMAND_VOLTAGE, &bq274xx->voltage);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read voltage");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_AVG_CURRENT:
|
|
status = bq274xx_command_reg_read(bq274xx,
|
|
BQ274XX_COMMAND_AVG_CURRENT,
|
|
&bq274xx->avg_current);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read average current ");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_TEMP:
|
|
status = bq274xx_command_reg_read(
|
|
bq274xx, BQ274XX_COMMAND_INT_TEMP,
|
|
&bq274xx->internal_temperature);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read internal temperature");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_STDBY_CURRENT:
|
|
status = bq274xx_command_reg_read(bq274xx,
|
|
BQ274XX_COMMAND_STDBY_CURRENT,
|
|
&bq274xx->stdby_current);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read standby current");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT:
|
|
status = bq274xx_command_reg_read(bq274xx,
|
|
BQ274XX_COMMAND_MAX_CURRENT,
|
|
&bq274xx->max_load_current);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read maximum current");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
|
|
status = bq274xx_command_reg_read(bq274xx, BQ274XX_COMMAND_SOC,
|
|
&bq274xx->state_of_charge);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read state of charge");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY:
|
|
status = bq274xx_command_reg_read(
|
|
bq274xx, BQ274XX_COMMAND_FULL_CAPACITY,
|
|
&bq274xx->full_charge_capacity);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read full charge capacity");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY:
|
|
status = bq274xx_command_reg_read(
|
|
bq274xx, BQ274XX_COMMAND_REM_CAPACITY,
|
|
&bq274xx->remaining_charge_capacity);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read remaining charge capacity");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY:
|
|
status = bq274xx_command_reg_read(bq274xx,
|
|
BQ274XX_COMMAND_NOM_CAPACITY,
|
|
&bq274xx->nom_avail_capacity);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read nominal available capacity");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY:
|
|
status =
|
|
bq274xx_command_reg_read(bq274xx,
|
|
BQ274XX_COMMAND_AVAIL_CAPACITY,
|
|
&bq274xx->full_avail_capacity);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read full available capacity");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_AVG_POWER:
|
|
status = bq274xx_command_reg_read(bq274xx,
|
|
BQ274XX_COMMAND_AVG_POWER,
|
|
&bq274xx->avg_power);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read battery average power");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_CHAN_GAUGE_STATE_OF_HEALTH:
|
|
status = bq274xx_command_reg_read(bq274xx, BQ274XX_COMMAND_SOH,
|
|
&bq274xx->state_of_health);
|
|
|
|
bq274xx->state_of_health = (bq274xx->state_of_health) & 0x00FF;
|
|
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read state of health");
|
|
return -EIO;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief initialise the fuel gauge
|
|
*
|
|
* @return 0 for success
|
|
*/
|
|
static int bq274xx_gauge_init(const struct device *dev)
|
|
{
|
|
struct bq274xx_data *bq274xx = dev->data;
|
|
const struct bq274xx_config *const config = dev->config;
|
|
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.",
|
|
config->bus_name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
status = bq274xx_get_device_type(bq274xx, &id);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to get device ID");
|
|
return -EIO;
|
|
}
|
|
|
|
if (id != BQ274XX_DEVICE_ID) {
|
|
LOG_ERR("Invalid Device");
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef CONFIG_BQ274XX_LAZY_CONFIGURE
|
|
bq274xx->lazy_loaded = false;
|
|
#else
|
|
status = bq274xx_gauge_configure(dev);
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
static int bq274xx_gauge_configure(const struct device *dev)
|
|
{
|
|
struct bq274xx_data *bq274xx = dev->data;
|
|
const struct bq274xx_config *const config = dev->config;
|
|
int status = 0;
|
|
uint8_t tmp_checksum = 0, checksum_old = 0, checksum_new = 0;
|
|
uint16_t flags = 0, designenergy_mwh = 0, taperrate = 0;
|
|
uint8_t designcap_msb, designcap_lsb, designenergy_msb, designenergy_lsb,
|
|
terminatevolt_msb, terminatevolt_lsb, taperrate_msb,
|
|
taperrate_lsb;
|
|
uint8_t block[32];
|
|
|
|
designenergy_mwh = (uint16_t)3.7 * config->design_capacity;
|
|
taperrate =
|
|
(uint16_t)config->design_capacity / (0.1 * config->taper_current);
|
|
|
|
/** Unseal the battery control register **/
|
|
status = bq274xx_control_reg_write(bq274xx, BQ274XX_UNSEAL_KEY);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to unseal the battery");
|
|
return -EIO;
|
|
}
|
|
|
|
status = bq274xx_control_reg_write(bq274xx, BQ274XX_UNSEAL_KEY);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to unseal the battery");
|
|
return -EIO;
|
|
}
|
|
|
|
/* Send CFG_UPDATE */
|
|
status = bq274xx_control_reg_write(bq274xx,
|
|
BQ274XX_CONTROL_SET_CFGUPDATE);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to set CFGUpdate");
|
|
return -EIO;
|
|
}
|
|
|
|
/** Step to place the Gauge into CONFIG UPDATE Mode **/
|
|
do {
|
|
status = bq274xx_command_reg_read(
|
|
bq274xx, BQ274XX_COMMAND_FLAGS, &flags);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to read flags");
|
|
return -EIO;
|
|
}
|
|
|
|
if (!(flags & 0x0010)) {
|
|
k_msleep(BQ274XX_SUBCLASS_DELAY * 10);
|
|
}
|
|
|
|
} while (!(flags & 0x0010));
|
|
|
|
status = bq274xx_command_reg_write(bq274xx,
|
|
BQ274XX_EXTENDED_DATA_CONTROL, 0x00);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to enable block data memory");
|
|
return -EIO;
|
|
}
|
|
|
|
/* Access State subclass */
|
|
status = bq274xx_command_reg_write(bq274xx, BQ274XX_EXTENDED_DATA_CLASS,
|
|
0x52);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to update state subclass");
|
|
return -EIO;
|
|
}
|
|
|
|
/* Write the block offset */
|
|
status = bq274xx_command_reg_write(bq274xx, BQ274XX_EXTENDED_DATA_BLOCK,
|
|
0x00);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to update block offset");
|
|
return -EIO;
|
|
}
|
|
|
|
for (uint8_t i = 0; i < 32; i++) {
|
|
block[i] = 0;
|
|
}
|
|
|
|
status = bq274xx_read_data_block(bq274xx, 0x00, block, 32);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to read block data");
|
|
return -EIO;
|
|
}
|
|
|
|
tmp_checksum = 0;
|
|
for (uint8_t i = 0; i < 32; i++) {
|
|
tmp_checksum += block[i];
|
|
}
|
|
tmp_checksum = 255 - tmp_checksum;
|
|
|
|
/* Read the block checksum */
|
|
status = i2c_reg_read_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
|
BQ274XX_EXTENDED_CHECKSUM, &checksum_old);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to read block checksum");
|
|
return -EIO;
|
|
}
|
|
|
|
designcap_msb = config->design_capacity >> 8;
|
|
designcap_lsb = config->design_capacity & 0x00FF;
|
|
designenergy_msb = designenergy_mwh >> 8;
|
|
designenergy_lsb = designenergy_mwh & 0x00FF;
|
|
terminatevolt_msb = config->terminate_voltage >> 8;
|
|
terminatevolt_lsb = config->terminate_voltage & 0x00FF;
|
|
taperrate_msb = taperrate >> 8;
|
|
taperrate_lsb = taperrate & 0x00FF;
|
|
|
|
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
|
BQ274XX_EXTENDED_BLOCKDATA_DESIGN_CAP_HIGH,
|
|
designcap_msb);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to write designCAP MSB");
|
|
return -EIO;
|
|
}
|
|
|
|
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
|
BQ274XX_EXTENDED_BLOCKDATA_DESIGN_CAP_LOW,
|
|
designcap_lsb);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to erite designCAP LSB");
|
|
return -EIO;
|
|
}
|
|
|
|
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
|
BQ274XX_EXTENDED_BLOCKDATA_DESIGN_ENR_HIGH,
|
|
designenergy_msb);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to write designEnergy MSB");
|
|
return -EIO;
|
|
}
|
|
|
|
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
|
BQ274XX_EXTENDED_BLOCKDATA_DESIGN_ENR_LOW,
|
|
designenergy_lsb);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to erite designEnergy LSB");
|
|
return -EIO;
|
|
}
|
|
|
|
status = i2c_reg_write_byte(
|
|
bq274xx->i2c, DT_INST_REG_ADDR(0),
|
|
BQ274XX_EXTENDED_BLOCKDATA_TERMINATE_VOLT_HIGH,
|
|
terminatevolt_msb);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to write terminateVolt MSB");
|
|
return -EIO;
|
|
}
|
|
|
|
status = i2c_reg_write_byte(
|
|
bq274xx->i2c, DT_INST_REG_ADDR(0),
|
|
BQ274XX_EXTENDED_BLOCKDATA_TERMINATE_VOLT_LOW,
|
|
terminatevolt_lsb);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to write terminateVolt LSB");
|
|
return -EIO;
|
|
}
|
|
|
|
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
|
BQ274XX_EXTENDED_BLOCKDATA_TAPERRATE_HIGH,
|
|
taperrate_msb);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to write taperRate MSB");
|
|
return -EIO;
|
|
}
|
|
|
|
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
|
BQ274XX_EXTENDED_BLOCKDATA_TAPERRATE_LOW,
|
|
taperrate_lsb);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to erite taperRate LSB");
|
|
return -EIO;
|
|
}
|
|
|
|
for (uint8_t i = 0; i < 32; i++) {
|
|
block[i] = 0;
|
|
}
|
|
|
|
status = bq274xx_read_data_block(bq274xx, 0x00, block, 32);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to read block data");
|
|
return -EIO;
|
|
}
|
|
|
|
checksum_new = 0;
|
|
for (uint8_t i = 0; i < 32; i++) {
|
|
checksum_new += block[i];
|
|
}
|
|
checksum_new = 255 - checksum_new;
|
|
|
|
status = bq274xx_command_reg_write(bq274xx, BQ274XX_EXTENDED_CHECKSUM,
|
|
checksum_new);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to update new checksum");
|
|
return -EIO;
|
|
}
|
|
|
|
tmp_checksum = 0;
|
|
status = i2c_reg_read_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
|
BQ274XX_EXTENDED_CHECKSUM, &tmp_checksum);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to read checksum");
|
|
return -EIO;
|
|
}
|
|
|
|
status = bq274xx_control_reg_write(bq274xx, BQ274XX_CONTROL_BAT_INSERT);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to configure BAT Detect");
|
|
return -EIO;
|
|
}
|
|
|
|
status = bq274xx_control_reg_write(bq274xx, BQ274XX_CONTROL_SOFT_RESET);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to soft reset the gauge");
|
|
return -EIO;
|
|
}
|
|
|
|
flags = 0;
|
|
/* Poll Flags */
|
|
do {
|
|
status = bq274xx_command_reg_read(
|
|
bq274xx, BQ274XX_COMMAND_FLAGS, &flags);
|
|
if (status < 0) {
|
|
LOG_ERR("Unable to read flags");
|
|
return -EIO;
|
|
}
|
|
|
|
if (flags & 0x0010) {
|
|
k_msleep(BQ274XX_SUBCLASS_DELAY * 10);
|
|
}
|
|
} while (flags & 0x0010);
|
|
|
|
/* Seal the gauge */
|
|
status = bq274xx_control_reg_write(bq274xx, BQ274XX_CONTROL_SEALED);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to seal the gauge");
|
|
return -EIO;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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_action(const struct device *dev,
|
|
enum pm_device_action action)
|
|
{
|
|
int ret;
|
|
struct bq274xx_data *data = dev->data;
|
|
|
|
switch (action) {
|
|
case PM_DEVICE_ACTION_TURN_OFF:
|
|
ret = bq274xx_enter_shutdown_mode(data);
|
|
break;
|
|
case PM_DEVICE_ACTION_RESUME:
|
|
ret = bq274xx_exit_shutdown_mode(dev);
|
|
break;
|
|
default:
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
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), \
|
|
.taper_current = DT_INST_PROP(index, taper_current), \
|
|
.terminate_voltage = DT_INST_PROP(index, terminate_voltage), \
|
|
}; \
|
|
\
|
|
DEVICE_DT_INST_DEFINE(index, &bq274xx_gauge_init, bq274xx_pm_action, \
|
|
&bq274xx_driver_##index, \
|
|
&bq274xx_config_##index, POST_KERNEL, \
|
|
CONFIG_SENSOR_INIT_PRIORITY, \
|
|
&bq274xx_battery_driver_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(BQ274XX_INIT)
|