Skip to content

Commit

Permalink
charger: Sample sbs charger driver with tests
Browse files Browse the repository at this point in the history
Adds a sample sbs charger driver and basics tests.

Signed-off-by: Ricardo Rivera-Matos <rriveram@opensource.cirrus.com>
  • Loading branch information
rriveramcrus committed Apr 19, 2023
1 parent 2befc2c commit e667c0a
Show file tree
Hide file tree
Showing 20 changed files with 635 additions and 6 deletions.
1 change: 1 addition & 0 deletions drivers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,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)
Expand Down
1 change: 1 addition & 0 deletions drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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"
Expand Down
5 changes: 5 additions & 0 deletions drivers/charger/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: Apache-2.0

add_subdirectory_ifdef(CONFIG_SBS_CHARGER sbs_charger)

zephyr_library_sources_ifdef(CONFIG_USERSPACE charger_syscall_handlers.c)
24 changes: 24 additions & 0 deletions drivers/charger/Kconfig
Original file line number Diff line number Diff line change
@@ -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/sbs_charger/Kconfig"

endif # CHARGER
26 changes: 26 additions & 0 deletions drivers/charger/charger_syscall_handlers.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2023 Cirrus Logic, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/syscall_handler.h>
#include <zephyr/drivers/charger.h>

static inline int z_vrfy_charger_get_prop(const struct device *dev,
struct charger_get_property *props, size_t props_len)
{
struct charger_get_property k_props[props_len];

Z_OOPS(Z_SYSCALL_DRIVER_CHARGER(dev, get_property));

Z_OOPS(z_user_from_copy(k_props, props, props_len * sizeof(struct charger_get_property)));

int ret = z_impl_charger_get_prop(dev, k_props, props_len);

Z_OOPS(z_user_to_copy(props, k_props, props_len * sizeof(struct charger_get_property)));

return ret;
}

#include <syscalls/charger_get_prop_mrsh.c>
6 changes: 6 additions & 0 deletions drivers/charger/sbs_charger/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
zephyr_library()

zephyr_library_sources(sbs_charger.c)

zephyr_include_directories_ifdef(CONFIG_EMUL_SBS_CHARGER .)
zephyr_library_sources_ifdef(CONFIG_EMUL_SBS_CHARGER emul_sbs_charger.c)
17 changes: 17 additions & 0 deletions drivers/charger/sbs_charger/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# 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"
depends on EMUL
help
It provides reading which follow a simple sequence, thus allowing
test code to check that things are working as expected.
142 changes: 142 additions & 0 deletions drivers/charger/sbs_charger/emul_sbs_charger.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* 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/logging/log.h>
LOG_MODULE_REGISTER(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/sys/byteorder.h>

#include "sbs_charger.h"

/** 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_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;

__ASSERT_NO_MSG(msgs && num_msgs);

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)
122 changes: 122 additions & 0 deletions drivers/charger/sbs_charger/sbs_charger.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2023 Cirrus Logic, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT sbs_sbs_charger

#include "sbs_charger.h"

#include <zephyr/drivers/charger.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>

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, ARRAY_SIZE(i2c_data));
if (status < 0) {
LOG_ERR("Unable to read register");
return status;
}

*val = sys_get_le16(i2c_data);

return 0;
}

static int sbs_charger_get_prop(const struct device *dev, struct charger_get_property *prop)
{
uint16_t val = 0;
int ret = 0;

switch (prop->property_type) {
case CHARGER_ONLINE:
ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, &val);
if (val & SBS_CHARGER_STATUS_AC_PRESENT)
prop->value.online = CHARGER_ONLINE_FIXED;
else
prop->value.online = CHARGER_ONLINE_OFFLINE;

break;
case CHARGER_PRESENT:
ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, &val);
prop->value.present = !!(val & SBS_CHARGER_STATUS_BATTERY_PRESENT);

break;
case CHARGER_STATUS:
ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, &val);
if (!(val & SBS_CHARGER_STATUS_BATTERY_PRESENT))
prop->value.status = CHARGER_STATUS_NOT_CHARGING;
else if (val & SBS_CHARGER_STATUS_AC_PRESENT &&
!(val & SBS_CHARGER_STATUS_CHARGE_INHIBITED))
prop->value.status = CHARGER_STATUS_CHARGING;
else
prop->value.status = CHARGER_STATUS_DISCHARGING;
break;
default:
ret = -ENOTSUP;
}

prop->err = ret;

return ret;
}

static int sbs_charger_get_props(const struct device *dev, struct charger_get_property *props,
size_t len)
{
int err_count = 0;

for (int i = 0; i < len; i++) {
int ret = sbs_charger_get_prop(dev, props + i);

err_count += ret ? 1 : 0;
}

err_count = (err_count == len) ? -1 : err_count;

return err_count;
}

/**
* @brief initialize the fuel gauge
*
* @return 0 for success
*/
static int sbs_charger_init(const struct device *dev)
{
const struct sbs_charger_config *cfg;

cfg = dev->config;

if (!device_is_ready(cfg->i2c.bus)) {
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_props,
};

#define SBS_CHARGER_INIT(index) \
\
static const struct sbs_charger_config sbs_charger_config_##index = { \
.i2c = I2C_DT_SPEC_INST_GET(index), \
}; \
\
DEVICE_DT_INST_DEFINE(index, &sbs_charger_init, NULL, NULL, &sbs_charger_config_##index, \
POST_KERNEL, CONFIG_CHARGER_INIT_PRIORITY, &sbs_charger_driver_api);

DT_INST_FOREACH_STATUS_OKAY(SBS_CHARGER_INIT)
29 changes: 29 additions & 0 deletions drivers/charger/sbs_charger/sbs_charger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2023 Cirrus Logic, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_DRIVERS_SENSOR_SBS_CHARGER_H_
#define ZEPHYR_DRIVERS_SENSOR_SBS_CHARGER_H_

#include <stdint.h>
#include <zephyr/drivers/i2c.h>

#define SBS_CHARGER_REG_SPEC_INFO 0x11
#define SBS_CHARGER_REG_STATUS 0x13
#define SBS_CHARGER_REG_ALARM_WARNING 0x16

#define SBS_CHARGER_STATUS_CHARGE_INHIBITED BIT(1)
#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

struct sbs_charger_config {
struct i2c_dt_spec i2c;
};

#endif

0 comments on commit e667c0a

Please sign in to comment.