From 69dfb2fee3c20129e0afa597c6d91a5fc7dba24c Mon Sep 17 00:00:00 2001 From: Yuval Peress Date: Fri, 5 Jan 2024 12:18:42 -0700 Subject: [PATCH] bbram: mcp7940c: Add emulator Add an emulator for the mcp7940c BBRAM that supports reading and writing. Signed-off-by: Yuval Peress --- drivers/bbram/CMakeLists.txt | 1 + drivers/bbram/Kconfig.microchip | 9 ++ drivers/bbram/bbram_microchip_mcp7940n.c | 4 +- drivers/bbram/bbram_microchip_mcp7940n_emul.c | 150 ++++++++++++++++++ 4 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 drivers/bbram/bbram_microchip_mcp7940n_emul.c diff --git a/drivers/bbram/CMakeLists.txt b/drivers/bbram/CMakeLists.txt index 2365cf4c8c5..14ed29b9b79 100644 --- a/drivers/bbram/CMakeLists.txt +++ b/drivers/bbram/CMakeLists.txt @@ -12,5 +12,6 @@ zephyr_library_sources_ifdef(CONFIG_BBRAM_IT8XXX2_EMUL bbram_it8xxx2_emul.c) zephyr_library_include_directories_ifdef(CONFIG_BBRAM_IT8XXX2 .) zephyr_library_sources_ifdef(CONFIG_BBRAM_EMUL bbram_emul.c) zephyr_library_sources_ifdef(CONFIG_BBRAM_MICROCHIP_MCP7940N bbram_microchip_mcp7940n.c) +zephyr_library_sources_ifdef(CONFIG_BBRAM_MICROCHIP_MCP7940N_EMUL bbram_microchip_mcp7940n_emul.c) zephyr_library_sources_ifdef(CONFIG_BBRAM_XEC bbram_xec.c) zephyr_library_sources_ifdef(CONFIG_BBRAM_STM32 bbram_stm32.c) diff --git a/drivers/bbram/Kconfig.microchip b/drivers/bbram/Kconfig.microchip index 015b2bcb124..35fd828e6a5 100644 --- a/drivers/bbram/Kconfig.microchip +++ b/drivers/bbram/Kconfig.microchip @@ -8,3 +8,12 @@ config BBRAM_MICROCHIP_MCP7940N select I2C help Enable driver for Microchip MCP7940N SRAM based battery-backed RAM. + +config BBRAM_MICROCHIP_MCP7940N_EMUL + bool "Emulator for the Microchip MCP7940N SRAM BBRAM driver" + default y + depends on BBRAM_MICROCHIP_MCP7940N + depends on EMUL + help + Enable the emulator for the Microchip MCP7940N SRAM based + battery-backed RAM. diff --git a/drivers/bbram/bbram_microchip_mcp7940n.c b/drivers/bbram/bbram_microchip_mcp7940n.c index c3b538c842f..ff5b7850576 100644 --- a/drivers/bbram/bbram_microchip_mcp7940n.c +++ b/drivers/bbram/bbram_microchip_mcp7940n.c @@ -153,7 +153,7 @@ static int microchip_mcp7940n_bbram_read(const struct device *dev, size_t offset size_t i = 0; int32_t rc = 0; - if ((offset + size) > MICROCHIP_MCP7940N_SRAM_SIZE) { + if (size == 0 || (offset + size) > MICROCHIP_MCP7940N_SRAM_SIZE) { return -EINVAL; } @@ -186,7 +186,7 @@ static int microchip_mcp7940n_bbram_write(const struct device *dev, size_t offse size_t i = 0; int32_t rc = 0; - if ((offset + size) > MICROCHIP_MCP7940N_SRAM_SIZE) { + if (size == 0 || (offset + size) > MICROCHIP_MCP7940N_SRAM_SIZE) { return -EINVAL; } diff --git a/drivers/bbram/bbram_microchip_mcp7940n_emul.c b/drivers/bbram/bbram_microchip_mcp7940n_emul.c new file mode 100644 index 00000000000..56a84825948 --- /dev/null +++ b/drivers/bbram/bbram_microchip_mcp7940n_emul.c @@ -0,0 +1,150 @@ +/* + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT microchip_mcp7940n + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(bbram_microchip_mcp7940n, CONFIG_BBRAM_LOG_LEVEL); + +#define MICROCHIP_MCP7940N_SRAM_OFFSET 0x20 +#define MICROCHIP_MCP7940N_SRAM_SIZE 64 +#define MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS 0x03 +#define MICROCHIP_MCP7940N_RTCWKDAY_VBATEN_BIT BIT(3) +#define MICROCHIP_MCP7940N_RTCWKDAY_PWRFAIL_BIT BIT(4) + +struct mcp7940n_emul_cfg { +}; + +struct mcp7940n_emul_data { + uint8_t rtcwkday; + uint8_t data[MICROCHIP_MCP7940N_SRAM_SIZE]; +}; + +static int mcp7940n_emul_init(const struct emul *target, const struct device *parent) +{ + ARG_UNUSED(target); + ARG_UNUSED(parent); + return 0; +} + +static int mcp7940n_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs, int num_msgs, + int addr) +{ + struct mcp7940n_emul_data *data = target->data; + + i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false); + + if (num_msgs < 1) { + LOG_ERR("Invalid number of messages: %d", num_msgs); + return -EIO; + } + if (FIELD_GET(I2C_MSG_READ, msgs->flags)) { + LOG_ERR("Unexpected read"); + return -EIO; + } + if (msgs->len < 1) { + LOG_ERR("Unexpected msg0 length %d", msgs->len); + return -EIO; + } + + uint8_t regn = msgs->buf[0]; + bool is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1; + bool is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1; + + if (!is_stop && !is_read) { + /* First message was a write with the register number, check next message */ + msgs++; + is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1; + is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1; + } + + if (is_read) { + /* Read data */ + if (regn == MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS) { + msgs->buf[0] = data->rtcwkday; + return 0; + } + if (regn >= MICROCHIP_MCP7940N_SRAM_OFFSET && + regn + msgs->len <= + MICROCHIP_MCP7940N_SRAM_OFFSET + MICROCHIP_MCP7940N_SRAM_SIZE) { + for (int i = 0; i < msgs->len; ++i) { + msgs->buf[i] = + data->data[regn + i - MICROCHIP_MCP7940N_SRAM_OFFSET]; + } + return 0; + } + } else { + /* Write data */ + if (regn == MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS) { + data->rtcwkday = msgs->buf[1]; + return 0; + } + if (regn >= MICROCHIP_MCP7940N_SRAM_OFFSET && + regn + msgs->len - 1 <= + MICROCHIP_MCP7940N_SRAM_OFFSET + MICROCHIP_MCP7940N_SRAM_SIZE) { + for (int i = 0; i < msgs->len; ++i) { + data->data[regn + i - MICROCHIP_MCP7940N_SRAM_OFFSET] = + msgs->buf[1 + i]; + } + return 0; + } + } + + return -EIO; +} + +static const struct i2c_emul_api mcp7940n_emul_api_i2c = { + .transfer = mcp7940n_emul_transfer_i2c, +}; + +static int mcp7940n_emul_backend_set_data(const struct emul *target, size_t offset, size_t count, + const uint8_t *buffer) +{ + struct mcp7940n_emul_data *data = target->data; + + if (offset + count > MICROCHIP_MCP7940N_SRAM_SIZE) { + return -ERANGE; + } + + for (size_t i = 0; i < count; ++i) { + data->data[offset + i] = buffer[i]; + } + return 0; +} + +static int mcp7940n_emul_backend_get_data(const struct emul *target, size_t offset, size_t count, + uint8_t *buffer) +{ + struct mcp7940n_emul_data *data = target->data; + + if (offset + count > MICROCHIP_MCP7940N_SRAM_SIZE) { + return -ERANGE; + } + + for (size_t i = 0; i < count; ++i) { + buffer[i] = data->data[offset + i]; + } + return 0; +} + +static const struct emul_bbram_backend_api mcp7940n_emul_backend_api = { + .set_data = mcp7940n_emul_backend_set_data, + .get_data = mcp7940n_emul_backend_get_data, +}; + +#define MCP7940N_EMUL(inst) \ + static const struct mcp7940n_emul_cfg mcp7940n_emul_cfg_##inst; \ + static struct mcp7940n_emul_data mcp7940n_emul_data_##inst; \ + EMUL_DT_INST_DEFINE(inst, mcp7940n_emul_init, &mcp7940n_emul_data_##inst, \ + &mcp7940n_emul_cfg_##inst, &mcp7940n_emul_api_i2c, \ + &mcp7940n_emul_backend_api) + +DT_INST_FOREACH_STATUS_OKAY(MCP7940N_EMUL)