mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-03 15:16:51 +00:00
Removes an unnecessary comparison of an unsigned int against the value zero. Signed-off-by: Ricardo Rivera-Matos <ricardo.rivera-matos@cirrus.com>
463 lines
14 KiB
C
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)
|