charger: Sample sbs charger driver with tests
Adds a sample sbs charger driver and basics tests. Signed-off-by: Ricardo Rivera-Matos <rriveram@opensource.cirrus.com>
This commit is contained in:
parent
a7adb06ff4
commit
aee815f19d
17 changed files with 589 additions and 0 deletions
|
@ -20,6 +20,7 @@ add_subdirectory_ifdef(CONFIG_BT_DRIVERS bluetooth)
|
|||
add_subdirectory_ifdef(CONFIG_CACHE_MANAGEMENT cache)
|
||||
add_subdirectory_ifdef(CONFIG_CAN can)
|
||||
add_subdirectory_ifdef(CONFIG_CLOCK_CONTROL clock_control)
|
||||
add_subdirectory_ifdef(CONFIG_CHARGER charger)
|
||||
add_subdirectory_ifdef(CONFIG_CONSOLE console)
|
||||
add_subdirectory_ifdef(CONFIG_COREDUMP_DEVICE coredump)
|
||||
add_subdirectory_ifdef(CONFIG_COUNTER counter)
|
||||
|
|
|
@ -12,6 +12,7 @@ source "drivers/bbram/Kconfig"
|
|||
source "drivers/bluetooth/Kconfig"
|
||||
source "drivers/cache/Kconfig"
|
||||
source "drivers/can/Kconfig"
|
||||
source "drivers/charger/Kconfig"
|
||||
source "drivers/clock_control/Kconfig"
|
||||
source "drivers/console/Kconfig"
|
||||
source "drivers/coredump/Kconfig"
|
||||
|
|
8
drivers/charger/CMakeLists.txt
Normal file
8
drivers/charger/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/charger.h)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_SBS_CHARGER sbs_charger.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE charger_handlers.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_EMUL_SBS_CHARGER emul_sbs_charger.c)
|
24
drivers/charger/Kconfig
Normal file
24
drivers/charger/Kconfig
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Copyright 2023 Cirrus Logic, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig CHARGER
|
||||
bool "Battery charger drivers"
|
||||
help
|
||||
Enable battery charger driver configuration.
|
||||
|
||||
if CHARGER
|
||||
|
||||
module = CHARGER
|
||||
module-str = charger
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
config CHARGER_INIT_PRIORITY
|
||||
int "Battery charger init priority"
|
||||
default 90
|
||||
help
|
||||
Battery charger initialization priority.
|
||||
|
||||
source "drivers/charger/Kconfig.sbs_charger"
|
||||
|
||||
endif # CHARGER
|
19
drivers/charger/Kconfig.sbs_charger
Normal file
19
drivers/charger/Kconfig.sbs_charger
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Copyright 2023 Cirrus Logic, Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config SBS_CHARGER
|
||||
bool "Smart Battery Charger"
|
||||
default y
|
||||
depends on DT_HAS_SBS_SBS_CHARGER_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable I2C-based/SMBus-based driver for a Smart Battery Charger.
|
||||
|
||||
config EMUL_SBS_CHARGER
|
||||
bool "Emulate an SBS 1.1 compliant smart battery charger"
|
||||
default y
|
||||
depends on EMUL
|
||||
depends on SBS_CHARGER
|
||||
help
|
||||
It provides reading which follow a simple sequence, thus allowing
|
||||
test code to check that things are working as expected.
|
141
drivers/charger/emul_sbs_charger.c
Normal file
141
drivers/charger/emul_sbs_charger.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright 2023 Cirrus Logic, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Emulator for SBS 1.1 compliant smart battery charger.
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT sbs_sbs_charger
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/emul.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/i2c_emul.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include "sbs_charger.h"
|
||||
|
||||
LOG_MODULE_REGISTER(sbs_sbs_charger);
|
||||
|
||||
/** Static configuration for the emulator */
|
||||
struct sbs_charger_emul_cfg {
|
||||
/** I2C address of emulator */
|
||||
uint16_t addr;
|
||||
};
|
||||
|
||||
static int emul_sbs_charger_reg_write(const struct emul *target, int reg, int val)
|
||||
{
|
||||
LOG_INF("write %x = %x", reg, val);
|
||||
switch (reg) {
|
||||
default:
|
||||
LOG_ERR("Unknown write %x", reg);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emul_sbs_charger_reg_read(const struct emul *target, int reg, int *val)
|
||||
{
|
||||
switch (reg) {
|
||||
case SBS_CHARGER_REG_SPEC_INFO:
|
||||
case SBS_CHARGER_REG_CHARGER_MODE:
|
||||
case SBS_CHARGER_REG_STATUS:
|
||||
case SBS_CHARGER_REG_ALARM_WARNING:
|
||||
/* Arbitrary stub value. */
|
||||
*val = 1;
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Unknown register 0x%x read", reg);
|
||||
return -EIO;
|
||||
}
|
||||
LOG_INF("read 0x%x = 0x%x", reg, *val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbs_charger_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs,
|
||||
int num_msgs, int addr)
|
||||
{
|
||||
/* Largely copied from emul_sbs_gauge.c */
|
||||
struct sbs_charger_emul_data *data;
|
||||
unsigned int val;
|
||||
int reg;
|
||||
int rc;
|
||||
|
||||
data = target->data;
|
||||
|
||||
i2c_dump_msgs_rw("emul", msgs, num_msgs, addr, false);
|
||||
switch (num_msgs) {
|
||||
case 2:
|
||||
if (msgs->flags & I2C_MSG_READ) {
|
||||
LOG_ERR("Unexpected read");
|
||||
return -EIO;
|
||||
}
|
||||
if (msgs->len != 1) {
|
||||
LOG_ERR("Unexpected msg0 length %d", msgs->len);
|
||||
return -EIO;
|
||||
}
|
||||
reg = msgs->buf[0];
|
||||
|
||||
/* Now process the 'read' part of the message */
|
||||
msgs++;
|
||||
if (msgs->flags & I2C_MSG_READ) {
|
||||
switch (msgs->len - 1) {
|
||||
case 1:
|
||||
rc = emul_sbs_charger_reg_read(target, reg, &val);
|
||||
if (rc) {
|
||||
/* Return before writing bad value to message buffer */
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* SBS uses SMBus, which sends data in little-endian format. */
|
||||
sys_put_le16(val, msgs->buf);
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Unexpected msg1 length %d", msgs->len);
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
/* We write a word (2 bytes by the SBS spec) */
|
||||
if (msgs->len != 2) {
|
||||
LOG_ERR("Unexpected msg1 length %d", msgs->len);
|
||||
}
|
||||
uint16_t value = sys_get_le16(msgs->buf);
|
||||
|
||||
rc = emul_sbs_charger_reg_write(target, reg, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Invalid number of messages: %d", num_msgs);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct i2c_emul_api sbs_charger_emul_api_i2c = {
|
||||
.transfer = sbs_charger_emul_transfer_i2c,
|
||||
};
|
||||
|
||||
static int emul_sbs_sbs_charger_init(const struct emul *target, const struct device *parent)
|
||||
{
|
||||
ARG_UNUSED(target);
|
||||
ARG_UNUSED(parent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main instantiation macro. SBS Charger Emulator only implemented for I2C
|
||||
*/
|
||||
#define SBS_CHARGER_EMUL(n) \
|
||||
static const struct sbs_charger_emul_cfg sbs_charger_emul_cfg_##n = { \
|
||||
.addr = DT_INST_REG_ADDR(n), \
|
||||
}; \
|
||||
EMUL_DT_INST_DEFINE(n, emul_sbs_sbs_charger_init, NULL, &sbs_charger_emul_cfg_##n, \
|
||||
&sbs_charger_emul_api_i2c, NULL)
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SBS_CHARGER_EMUL)
|
171
drivers/charger/sbs_charger.c
Normal file
171
drivers/charger/sbs_charger.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright 2023 Cirrus Logic, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT sbs_sbs_charger
|
||||
|
||||
#include <zephyr/drivers/charger.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include "sbs_charger.h"
|
||||
|
||||
struct sbs_charger_config {
|
||||
struct i2c_dt_spec i2c;
|
||||
};
|
||||
|
||||
LOG_MODULE_REGISTER(sbs_charger);
|
||||
|
||||
static int sbs_cmd_reg_read(const struct device *dev, uint8_t reg_addr, uint16_t *val)
|
||||
{
|
||||
const struct sbs_charger_config *cfg;
|
||||
uint8_t i2c_data[2];
|
||||
int status;
|
||||
|
||||
cfg = dev->config;
|
||||
status = i2c_burst_read_dt(&cfg->i2c, reg_addr, i2c_data, sizeof(i2c_data));
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to read register");
|
||||
return status;
|
||||
}
|
||||
|
||||
*val = sys_get_le16(i2c_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbs_cmd_reg_write(const struct device *dev, uint8_t reg_addr, uint16_t val)
|
||||
{
|
||||
const struct sbs_charger_config *config = dev->config;
|
||||
uint8_t buf[2];
|
||||
|
||||
sys_put_le16(val, buf);
|
||||
|
||||
return i2c_burst_write_dt(&config->i2c, reg_addr, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static int sbs_cmd_reg_update(const struct device *dev, uint8_t reg_addr, uint16_t mask,
|
||||
uint16_t val)
|
||||
{
|
||||
uint16_t old_val, new_val;
|
||||
int ret;
|
||||
|
||||
ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, &old_val);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
new_val = (old_val & ~mask) | (val & mask);
|
||||
if (new_val == old_val) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sbs_cmd_reg_write(dev, reg_addr, new_val);
|
||||
}
|
||||
|
||||
static int sbs_charger_get_prop(const struct device *dev, const charger_prop_t prop,
|
||||
union charger_propval *val)
|
||||
{
|
||||
uint16_t reg_val;
|
||||
int ret;
|
||||
|
||||
switch (prop) {
|
||||
case CHARGER_PROP_ONLINE:
|
||||
ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, ®_val);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg_val & SBS_CHARGER_STATUS_AC_PRESENT) {
|
||||
val->online = CHARGER_ONLINE_FIXED;
|
||||
} else {
|
||||
val->online = CHARGER_ONLINE_OFFLINE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
case CHARGER_PROP_PRESENT:
|
||||
ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, ®_val);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg_val & SBS_CHARGER_STATUS_BATTERY_PRESENT) {
|
||||
val->present = true;
|
||||
} else {
|
||||
val->present = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
case CHARGER_PROP_STATUS:
|
||||
ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, ®_val);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!(reg_val & SBS_CHARGER_STATUS_BATTERY_PRESENT)) {
|
||||
val->status = CHARGER_STATUS_NOT_CHARGING;
|
||||
} else if (reg_val & SBS_CHARGER_STATUS_AC_PRESENT &&
|
||||
!(reg_val & SBS_CHARGER_STATUS_CHARGE_INHIBITED)) {
|
||||
val->status = CHARGER_STATUS_CHARGING;
|
||||
} else {
|
||||
val->status = CHARGER_STATUS_DISCHARGING;
|
||||
}
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
static int sbs_charger_set_prop(const struct device *dev, const charger_prop_t prop,
|
||||
const union charger_propval *val)
|
||||
{
|
||||
uint16_t reg_val = 0;
|
||||
|
||||
switch (prop) {
|
||||
case CHARGER_PROP_STATUS:
|
||||
if (val->status != CHARGER_STATUS_CHARGING) {
|
||||
reg_val = SBS_CHARGER_MODE_INHIBIT_CHARGE;
|
||||
}
|
||||
return sbs_cmd_reg_update(dev, SBS_CHARGER_REG_CHARGER_MODE,
|
||||
SBS_CHARGER_MODE_INHIBIT_CHARGE, reg_val);
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief initialize the charger
|
||||
*
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int sbs_charger_init(const struct device *dev)
|
||||
{
|
||||
const struct sbs_charger_config *cfg = dev->config;
|
||||
|
||||
if (!i2c_is_ready_dt(&cfg->i2c)) {
|
||||
LOG_ERR("Bus device is not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct charger_driver_api sbs_charger_driver_api = {
|
||||
.get_property = &sbs_charger_get_prop,
|
||||
.set_property = &sbs_charger_set_prop,
|
||||
};
|
||||
|
||||
#define SBS_CHARGER_INIT(inst) \
|
||||
\
|
||||
static const struct sbs_charger_config sbs_charger_config_##inst = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(inst), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, &sbs_charger_init, NULL, NULL, &sbs_charger_config_##inst, \
|
||||
POST_KERNEL, CONFIG_CHARGER_INIT_PRIORITY, &sbs_charger_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SBS_CHARGER_INIT)
|
20
drivers/charger/sbs_charger.h
Normal file
20
drivers/charger/sbs_charger.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2023 Cirrus Logic, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define SBS_CHARGER_REG_SPEC_INFO 0x11
|
||||
#define SBS_CHARGER_REG_CHARGER_MODE 0x12
|
||||
#define SBS_CHARGER_REG_STATUS 0x13
|
||||
#define SBS_CHARGER_REG_ALARM_WARNING 0x16
|
||||
|
||||
#define SBS_CHARGER_MODE_INHIBIT_CHARGE BIT(0)
|
||||
|
||||
#define SBS_CHARGER_STATUS_CHARGE_INHIBITED BIT(0)
|
||||
#define SBS_CHARGER_STATUS_RES_COLD BIT(9)
|
||||
#define SBS_CHARGER_STATUS_RES_HOT BIT(10)
|
||||
#define SBS_CHARGER_STATUS_BATTERY_PRESENT BIT(14)
|
||||
#define SBS_CHARGER_STATUS_AC_PRESENT BIT(15)
|
||||
|
||||
#define SBS_CHARGER_POLL_TIME 500
|
Loading…
Add table
Add a link
Reference in a new issue