zephyr/drivers/charger/charger_bq25713.c
Ricardo Rivera-Matos 68f300dc31 charger: bq25713: Corrects unsigned int comparison
Removes an unnecessary comparison of an unsigned int against the value
zero.

Signed-off-by: Ricardo Rivera-Matos <ricardo.rivera-matos@cirrus.com>
2025-05-28 11:10:43 +02:00

463 lines
14 KiB
C

/*
* Copyright 2025 Palta Tech, S.A
*
* SPDX-License-Identifier: Apache-2.0
*
* BQ25713 Datasheet: https://www.ti.com/lit/ds/symlink/bq25713.pdf
*/
#define DT_DRV_COMPAT ti_bq25713
#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/drivers/charger.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ti_bq25713, CONFIG_CHARGER_LOG_LEVEL);
/* Charge Option 0 */
#define BQ25713_REG_CO0_LOW 0x00
#define BQ25713_REG_CO0_INHIBIT 0x01
#define BQ25713_REG_CO0_INHIBIT_MASK BIT(0)
/* Charge Current */
#define BQ25713_REG_CC_LOW 0x02
#define BQ25713_REG_CC_CHARGE_CURRENT_MASK GENMASK(12, 6)
#define BQ25713_REG_CC_CHARGE_CURRENT_STEP_UA 64000
#define BQ25713_REG_CC_CHARGE_CURRENT_MIN_UA 0
#define BQ25713_REG_CC_CHARGE_CURRENT_MAX_UA 8128000
/* Charger Status */
#define BQ25713_REG_CS_HIGH 0x21
#define BQ25713_REG_CS_AC_STAT 0x01
#define BQ25713_REG_CS_AC_STAT_MASK BIT(7)
#define BQ25713_REG_CS_PRE_FAST_CHARGE GENMASK(2, 1)
#define BQ25713_REG_CS_PRECHARGE BIT(1)
#define BQ25713_REG_CS_FASTCHARGE BIT(2)
/* Max Charge Voltage */
#define BQ25713_REG_CV_LOW 0x04
#define BQ25713_REG_CV_CHARGE_VOLTAGE_MASK GENMASK(14, 3)
#define BQ25713_REG_CV_CHARGE_VOLTAGE_STEP_UV 8000
#define BQ25713_REG_CV_CHARGE_VOLTAGE_MIN_UV 1024000
#define BQ25713_REG_CV_CHARGE_VOLTAGE_MAX_UV 19200000
/* Input current set by host IDPM */
#define BQ25713_REG_IIN_HOST_HIGH 0x0F
#define BQ25713_REG_IIN_HOST_MASK GENMASK(7, 0)
#define BQ25713_REG_IIN_HOST_STEP_UA 50000
#define BQ25713_REG_IIN_HOST_MIN_UV BQ25713_REG_IIN_HOST_STEP_UA
#define BQ25713_REG_IIN_HOST_MAX_UV 6400000
/* Input voltage IDPM */
#define BQ25713_REG_IIN_DPM_HIGH 0x25
#define BQ25713_REG_IIN_DPM_MASK BQ25713_REG_IIN_HOST_MASK
#define BQ25713_REG_IIN_DPM_STEP_UA BQ25713_REG_IIN_HOST_STEP_UA
/* Mininum system voltage */
#define BQ25713_REG_MIN_SYS_VOLTAGE_HI 0x0D
#define BQ25713_REG_MIN_SYS_VOLTAGE_MASK GENMASK(5, 0)
#define BQ25713_REG_MIN_SYS_VOLTAGE_STEP_UV 256000
#define BQ25713_REG_MIN_SYS_VOLTAGE_MIN_UV 1024000
#define BQ25713_REG_MIN_SYS_VOLTAGE_MAX_UV 16128000
/* Input voltage VDPM */
#define BQ25713_REG_VIN_LOW 0x0A
#define BQ25713_REG_VIN_DPM_MASK GENMASK(13, 6)
#define BQ25713_REG_VIN_DPM_STEP_UV 64000
#define BQ25713_REG_VIN_DPM_OFFSET_UV 3200000
#define BQ25713_REG_VIN_DPM_VOLTAGE_MIN_UV BQ25713_REG_VIN_DPM_OFFSET_UV
#define BQ25713_REG_VIN_DPM_VOLTAGE_MAX_UV 195200000
/* Manufacture ID */
#define BQ25713_REG_ID_LOW 0x2E
#define BQ25713_REG_ID_PN_25713 0x4088
#define BQ25713_REG_ID_PN_25713B 0x408A
#define BQ25713_FACTOR_U_TO_M 1000
struct bq25713_config {
struct i2c_dt_spec i2c;
uint32_t vsys_min_uv;
uint32_t ichg_ua;
uint32_t vreg_uv;
};
static inline int bq25713_write8(const struct device *dev, uint8_t reg, uint8_t value)
{
const struct bq25713_config *const config = dev->config;
return i2c_reg_write_byte_dt(&config->i2c, reg, value);
}
static inline int bq25713_read8(const struct device *dev, uint8_t reg, uint8_t *value)
{
const struct bq25713_config *const config = dev->config;
int ret;
ret = i2c_reg_read_byte_dt(&config->i2c, reg, value);
if (ret < 0) {
LOG_ERR("Unable to read register");
}
return ret;
}
static inline int bq25713_update8(const struct device *dev, uint8_t reg, uint8_t mask,
uint8_t value)
{
const struct bq25713_config *const config = dev->config;
int ret;
ret = i2c_reg_update_byte_dt(&config->i2c, reg, mask, value);
if (ret < 0) {
LOG_ERR("Unable to update register");
}
return ret;
}
static inline int bq25713_write16(const struct device *dev, uint8_t reg, uint16_t value)
{
const struct bq25713_config *const config = dev->config;
uint8_t buf[3];
buf[0] = reg;
sys_put_le16(value, &buf[1]);
return i2c_write_dt(&config->i2c, buf, sizeof(buf));
}
static inline int bq25713_read16(const struct device *dev, uint8_t reg, uint16_t *value)
{
const struct bq25713_config *config = dev->config;
uint8_t i2c_data[2];
int ret;
ret = i2c_burst_read_dt(&config->i2c, reg, i2c_data, sizeof(i2c_data));
if (ret < 0) {
LOG_ERR("Unable to read register");
return ret;
}
*value = sys_get_be16(i2c_data);
return 0;
}
static int bq25713_set_minimum_system_voltage(const struct device *dev, uint32_t voltage_uv)
{
if (!IN_RANGE(voltage_uv, BQ25713_REG_MIN_SYS_VOLTAGE_MIN_UV,
BQ25713_REG_MIN_SYS_VOLTAGE_MAX_UV)) {
LOG_WRN("minimum system voltage out of range: %umV, "
"clamping to the nearest limit",
voltage_uv / BQ25713_FACTOR_U_TO_M);
}
uint32_t v;
voltage_uv = CLAMP(voltage_uv, BQ25713_REG_MIN_SYS_VOLTAGE_MIN_UV,
BQ25713_REG_MIN_SYS_VOLTAGE_MAX_UV);
v = voltage_uv / BQ25713_REG_MIN_SYS_VOLTAGE_STEP_UV;
v = FIELD_PREP(BQ25713_REG_MIN_SYS_VOLTAGE_MASK, v);
return bq25713_write8(dev, BQ25713_REG_MIN_SYS_VOLTAGE_HI, v);
}
static int bq25713_set_constant_charge_current(const struct device *dev, uint32_t current_ua)
{
if (current_ua > BQ25713_REG_CC_CHARGE_CURRENT_MAX_UA) {
LOG_WRN("charging current out of range: %umA, "
"clamping to the nearest limit",
current_ua / BQ25713_FACTOR_U_TO_M);
}
current_ua = CLAMP(current_ua, BQ25713_REG_CC_CHARGE_CURRENT_MIN_UA,
BQ25713_REG_CC_CHARGE_CURRENT_MAX_UA);
uint32_t v;
v = current_ua / BQ25713_REG_CC_CHARGE_CURRENT_STEP_UA;
v = FIELD_PREP(BQ25713_REG_CC_CHARGE_CURRENT_MASK, v);
return bq25713_write16(dev, BQ25713_REG_CC_LOW, v);
}
static int bq25713_set_constant_charge_voltage(const struct device *dev, uint32_t voltage_uv)
{
if (!IN_RANGE(voltage_uv, BQ25713_REG_CV_CHARGE_VOLTAGE_MIN_UV,
BQ25713_REG_CV_CHARGE_VOLTAGE_MAX_UV)) {
LOG_WRN("charging voltage out of range: %umV, "
"clamping to the nearest limit",
voltage_uv / BQ25713_FACTOR_U_TO_M);
}
uint32_t v;
voltage_uv = CLAMP(voltage_uv, BQ25713_REG_CV_CHARGE_VOLTAGE_MIN_UV,
BQ25713_REG_CV_CHARGE_VOLTAGE_MAX_UV);
v = voltage_uv / BQ25713_REG_CV_CHARGE_VOLTAGE_STEP_UV;
v = FIELD_PREP(BQ25713_REG_CV_CHARGE_VOLTAGE_MASK, v);
return bq25713_write16(dev, BQ25713_REG_CV_LOW, v);
}
static int bq25713_set_iindpm(const struct device *dev, uint32_t current_ua)
{
if (!IN_RANGE(current_ua, BQ25713_REG_IIN_HOST_MIN_UV, BQ25713_REG_IIN_HOST_MAX_UV)) {
LOG_WRN("input current regulation out of range: %umA, "
"clamping to the nearest limit",
current_ua / BQ25713_FACTOR_U_TO_M);
}
uint32_t v;
current_ua = CLAMP(current_ua, BQ25713_REG_IIN_HOST_MIN_UV, BQ25713_REG_IIN_HOST_MAX_UV);
v = current_ua / BQ25713_REG_IIN_HOST_STEP_UA;
v = FIELD_PREP(BQ25713_REG_IIN_HOST_MASK, v);
return bq25713_write8(dev, BQ25713_REG_IIN_HOST_HIGH, v);
}
static int bq25713_set_vindpm(const struct device *dev, uint32_t voltage_ua)
{
if (!IN_RANGE(voltage_ua, BQ25713_REG_VIN_DPM_VOLTAGE_MIN_UV,
BQ25713_REG_VIN_DPM_VOLTAGE_MAX_UV)) {
LOG_WRN("input voltage regulation of range: %umV, "
"clamping to the nearest limit",
voltage_ua / BQ25713_FACTOR_U_TO_M);
}
uint32_t v;
voltage_ua = CLAMP(voltage_ua, BQ25713_REG_VIN_DPM_VOLTAGE_MIN_UV,
BQ25713_REG_VIN_DPM_VOLTAGE_MAX_UV);
v = (voltage_ua - BQ25713_REG_VIN_DPM_OFFSET_UV) / BQ25713_REG_VIN_DPM_STEP_UV;
v = FIELD_PREP(BQ25713_REG_VIN_DPM_MASK, v);
return bq25713_write16(dev, BQ25713_REG_VIN_LOW, v);
}
static int bq25713_get_constant_charge_current(const struct device *dev, uint32_t *current_ua)
{
uint16_t v;
int ret;
ret = bq25713_read16(dev, BQ25713_REG_CC_LOW, &v);
if (ret < 0) {
return ret;
}
v = FIELD_GET(BQ25713_REG_CC_CHARGE_CURRENT_MASK, v);
*current_ua = v * BQ25713_REG_CC_CHARGE_CURRENT_STEP_UA;
return 0;
}
static int bq25713_get_constant_charge_voltage(const struct device *dev, uint32_t *voltage_uv)
{
uint16_t value;
int ret;
ret = bq25713_read16(dev, BQ25713_REG_CV_LOW, &value);
if (ret < 0) {
return ret;
}
value = FIELD_GET(BQ25713_REG_CV_CHARGE_VOLTAGE_MASK, value);
*voltage_uv = value * BQ25713_REG_CV_CHARGE_VOLTAGE_STEP_UV;
return 0;
}
static int bq25713_get_iindpm(const struct device *dev, uint32_t *current_ua)
{
uint8_t value;
int ret;
ret = bq25713_read8(dev, BQ25713_REG_IIN_DPM_HIGH, &value);
if (ret < 0) {
return ret;
}
value = FIELD_GET(BQ25713_REG_IIN_DPM_MASK, value);
*current_ua = value * BQ25713_REG_IIN_DPM_STEP_UA;
return 0;
}
static int bq25713_get_vindpm(const struct device *dev, uint32_t *voltage_uv)
{
uint16_t value;
int ret;
ret = bq25713_read16(dev, BQ25713_REG_VIN_LOW, &value);
if (ret < 0) {
return ret;
}
value = FIELD_GET(BQ25713_REG_VIN_DPM_MASK, value);
*voltage_uv = (value * BQ25713_REG_VIN_DPM_STEP_UV) + BQ25713_REG_VIN_DPM_OFFSET_UV;
return 0;
}
static int bq25713_get_status(const struct device *dev, enum charger_status *status)
{
uint8_t charge_status;
int ret;
ret = bq25713_read8(dev, BQ25713_REG_CS_HIGH, &charge_status);
if (ret < 0) {
return ret;
}
switch (FIELD_GET(BQ25713_REG_CS_PRE_FAST_CHARGE, charge_status)) {
case BQ25713_REG_CS_FASTCHARGE:
__fallthrough;
case BQ25713_REG_CS_PRECHARGE:
*status = CHARGER_STATUS_CHARGING;
break;
default:
*status = CHARGER_STATUS_UNKNOWN;
break;
}
return 0;
}
static int bq25713_get_online(const struct device *dev, enum charger_online *online)
{
uint8_t status;
int ret;
ret = bq25713_read8(dev, BQ25713_REG_CS_HIGH, &status);
if (ret < 0) {
return ret;
}
status = FIELD_GET(BQ25713_REG_CS_AC_STAT_MASK, status);
if (status == BQ25713_REG_CS_AC_STAT) {
*online = CHARGER_ONLINE_FIXED;
} else {
*online = CHARGER_ONLINE_OFFLINE;
}
return 0;
}
static int bq25713_charger_get_charge_type(const struct device *dev,
enum charger_charge_type *charge_type)
{
*charge_type = CHARGER_CHARGE_TYPE_UNKNOWN;
return 0;
}
static int bq25713_get_prop(const struct device *dev, charger_prop_t prop,
union charger_propval *value)
{
switch (prop) {
case CHARGER_PROP_ONLINE:
return bq25713_get_online(dev, &value->online);
case CHARGER_PROP_CHARGE_TYPE:
return bq25713_charger_get_charge_type(dev, &value->charge_type);
case CHARGER_PROP_STATUS:
return bq25713_get_status(dev, &value->status);
case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA:
return bq25713_get_constant_charge_current(dev, &value->const_charge_current_ua);
case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV:
return bq25713_get_constant_charge_voltage(dev, &value->const_charge_voltage_uv);
case CHARGER_PROP_INPUT_REGULATION_CURRENT_UA:
return bq25713_get_iindpm(dev, &value->input_current_regulation_current_ua);
case CHARGER_PROP_INPUT_REGULATION_VOLTAGE_UV:
return bq25713_get_vindpm(dev, &value->input_voltage_regulation_voltage_uv);
default:
return -ENOTSUP;
}
}
static int bq25713_set_prop(const struct device *dev, charger_prop_t prop,
const union charger_propval *value)
{
switch (prop) {
case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA:
return bq25713_set_constant_charge_current(dev, value->const_charge_current_ua);
case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV:
return bq25713_set_constant_charge_voltage(dev, value->const_charge_voltage_uv);
case CHARGER_PROP_INPUT_REGULATION_CURRENT_UA:
return bq25713_set_iindpm(dev, value->input_current_regulation_current_ua);
case CHARGER_PROP_INPUT_REGULATION_VOLTAGE_UV:
return bq25713_set_vindpm(dev, value->input_voltage_regulation_voltage_uv);
default:
return -ENOTSUP;
}
}
static int bq25713_charge_enable(const struct device *dev, const bool enable)
{
uint8_t value = enable ? 0 : BQ25713_REG_CO0_INHIBIT;
return bq25713_update8(dev, BQ25713_REG_CO0_LOW, BQ25713_REG_CO0_INHIBIT_MASK, value);
}
static int bq25713_set_config(const struct device *dev)
{
const struct bq25713_config *const config = dev->config;
union charger_propval value;
int ret;
value.const_charge_current_ua = config->ichg_ua;
ret = bq25713_set_constant_charge_current(dev, value.const_charge_current_ua);
if (ret < 0) {
return ret;
}
value.const_charge_voltage_uv = config->vreg_uv;
ret = bq25713_set_constant_charge_voltage(dev, value.const_charge_voltage_uv);
if (ret < 0) {
return ret;
}
return bq25713_set_minimum_system_voltage(dev, config->vsys_min_uv);
}
static int bq25713_init(const struct device *dev)
{
uint16_t value;
int ret;
ret = bq25713_read16(dev, BQ25713_REG_ID_LOW, &value);
if (ret < 0) {
return ret;
}
switch (value) {
case BQ25713_REG_ID_PN_25713:
__fallthrough;
case BQ25713_REG_ID_PN_25713B:
break;
default:
LOG_ERR("Error unknown model: 0x%04x\n", value);
return -ENODEV;
}
return bq25713_set_config(dev);
}
static DEVICE_API(charger, bq7513_driver_api) = {
.get_property = bq25713_get_prop,
.set_property = bq25713_set_prop,
.charge_enable = bq25713_charge_enable,
};
#define BQ25713_INIT(inst) \
\
static const struct bq25713_config bq25713_config_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
.ichg_ua = DT_INST_PROP(inst, constant_charge_current_max_microamp), \
.vreg_uv = DT_INST_PROP(inst, constant_charge_voltage_max_microvolt), \
.vsys_min_uv = DT_INST_PROP(inst, system_voltage_min_threshold_microvolt), \
}; \
\
DEVICE_DT_INST_DEFINE(inst, bq25713_init, NULL, NULL, &bq25713_config_##inst, POST_KERNEL, \
CONFIG_CHARGER_INIT_PRIORITY, &bq7513_driver_api);
DT_INST_FOREACH_STATUS_OKAY(BQ25713_INIT)