-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
1 parent
2befc2c
commit e667c0a
Showing
20 changed files
with
635 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.