From 20623dfa4c0ac7836dcc249bd45577b4cd94a131 Mon Sep 17 00:00:00 2001 From: Laczen JMS Date: Fri, 8 Nov 2019 11:27:32 +0100 Subject: [PATCH] drivers: eeprom: Add support for eeprom simulator Add support for a eeprom simulator. The PR limits the addition to qemu_x86 but it can easily be added to other devices by defining the eeprom simulator in the dts and setting 'CONFIG_EEPROM_SIMULATOR=y' Signed-off-by: Laczen JMS Signed-off-by: Anas Nashif --- boards/x86/qemu_x86/qemu_x86.dts | 8 + drivers/eeprom/CMakeLists.txt | 1 + drivers/eeprom/Kconfig | 32 ++++ drivers/eeprom/eeprom_simulator.c | 209 ++++++++++++++++++++++++ dts/bindings/mtd/zephyr,sim-eeprom.yaml | 12 ++ tests/drivers/eeprom/prj_qemu_x86.conf | 4 + tests/drivers/eeprom/testcase.yaml | 4 +- 7 files changed, 268 insertions(+), 2 deletions(-) create mode 100644 drivers/eeprom/eeprom_simulator.c create mode 100644 dts/bindings/mtd/zephyr,sim-eeprom.yaml create mode 100644 tests/drivers/eeprom/prj_qemu_x86.conf diff --git a/boards/x86/qemu_x86/qemu_x86.dts b/boards/x86/qemu_x86/qemu_x86.dts index 1f6af0db321..46a9218d0a0 100644 --- a/boards/x86/qemu_x86/qemu_x86.dts +++ b/boards/x86/qemu_x86/qemu_x86.dts @@ -21,6 +21,7 @@ aliases { uart-0 = &uart0; uart-1 = &uart1; + eeprom-0 = &eeprom0; }; chosen { @@ -61,6 +62,13 @@ write-block-size = <4>; }; }; + + eeprom0: eeprom { + status = "okay"; + compatible = "zephyr,sim-eeprom"; + label = "EEPROM_0"; + size = ; + }; }; &uart0 { diff --git a/drivers/eeprom/CMakeLists.txt b/drivers/eeprom/CMakeLists.txt index 90e683a891b..61f684a3aaf 100644 --- a/drivers/eeprom/CMakeLists.txt +++ b/drivers/eeprom/CMakeLists.txt @@ -8,3 +8,4 @@ zephyr_library_sources_ifdef(CONFIG_EEPROM_SHELL eeprom_shell.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_NATIVE_POSIX eeprom_native_posix.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_AT2X eeprom_at2x.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_STM32 eeprom_stm32.c) +zephyr_library_sources_ifdef(CONFIG_EEPROM_SIMULATOR eeprom_simulator.c) diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig index bdfba71803b..47ca342898d 100644 --- a/drivers/eeprom/Kconfig +++ b/drivers/eeprom/Kconfig @@ -56,4 +56,36 @@ config EEPROM_AT25 source "drivers/eeprom/Kconfig.stm32" +config EEPROM_SIMULATOR + bool "Simulated EEPROM driver" + select STATS + select STATS_NAMES + help + Enable Simulated EEPROM driver. + +if EEPROM_SIMULATOR + +config EEPROM_SIMULATOR_SIMULATE_TIMING + bool "Enable hardware timing simulation" + help + Enable Simulated hardware timing. + +if EEPROM_SIMULATOR_SIMULATE_TIMING + +config EEPROM_SIMULATOR_MIN_READ_TIME_US + int + prompt "Minimum read time (µS)" + default 2 + range 1 1000000 + +config EEPROM_SIMULATOR_MIN_WRITE_TIME_US + int + prompt "Minimum write time (µS)" + default 100 + range 1 1000000 + +endif # EEPROM_SIMULATOR_SIMULATE_TIMING + +endif # EEPROM_SIMULATOR + endif # EEPROM diff --git a/drivers/eeprom/eeprom_simulator.c b/drivers/eeprom/eeprom_simulator.c new file mode 100644 index 00000000000..d44133c19a2 --- /dev/null +++ b/drivers/eeprom/eeprom_simulator.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_EEPROM_LOG_LEVEL +#include +LOG_MODULE_REGISTER(eeprom_simulator); + +struct eeprom_sim_config { + size_t size; + bool readonly; +}; + +#define DEV_NAME(dev) ((dev)->config->name) +#define DEV_CONFIG(dev) ((dev)->config->config_info) + +#define EEPROM(addr) (mock_eeprom + (addr)) + +#if defined(CONFIG_MULTITHREADING) +/* semaphore for locking flash resources (tickers) */ +static struct k_sem sem_lock; +#define SYNC_INIT() k_sem_init(&sem_lock, 1, 1) +#define SYNC_LOCK() k_sem_take(&sem_lock, K_FOREVER) +#define SYNC_UNLOCK() k_sem_give(&sem_lock) +#else +#define SYNC_INIT() +#define SYNC_LOCK() +#define SYNC_UNLOCK() +#endif + +/* simulator statistcs */ +STATS_SECT_START(eeprom_sim_stats) +STATS_SECT_ENTRY32(bytes_read) /* total bytes read */ +STATS_SECT_ENTRY32(bytes_written) /* total bytes written */ +STATS_SECT_ENTRY32(eeprom_read_calls) /* calls to eeprom_read() */ +STATS_SECT_ENTRY32(eeprom_read_time_us) /* time spent in eeprom_read() */ +STATS_SECT_ENTRY32(eeprom_write_calls) /* calls to eeprom_write() */ +STATS_SECT_ENTRY32(eeprom_write_time_us)/* time spent in eeprom_write() */ +STATS_SECT_END; + +STATS_SECT_DECL(eeprom_sim_stats) eeprom_sim_stats; +STATS_NAME_START(eeprom_sim_stats) +STATS_NAME(eeprom_sim_stats, bytes_read) +STATS_NAME(eeprom_sim_stats, bytes_written) +STATS_NAME(eeprom_sim_stats, eeprom_read_calls) +STATS_NAME(eeprom_sim_stats, eeprom_read_time_us) +STATS_NAME(eeprom_sim_stats, eeprom_write_calls) +STATS_NAME(eeprom_sim_stats, eeprom_write_time_us) +STATS_NAME_END(eeprom_sim_stats); + +/* simulator dynamic thresholds */ +STATS_SECT_START(eeprom_sim_thresholds) +STATS_SECT_ENTRY32(max_write_calls) +STATS_SECT_ENTRY32(max_len) +STATS_SECT_END; + +STATS_SECT_DECL(eeprom_sim_thresholds) eeprom_sim_thresholds; +STATS_NAME_START(eeprom_sim_thresholds) +STATS_NAME(eeprom_sim_thresholds, max_write_calls) +STATS_NAME(eeprom_sim_thresholds, max_len) +STATS_NAME_END(eeprom_sim_thresholds); + +static u8_t mock_eeprom[DT_INST_0_ZEPHYR_SIM_EEPROM_SIZE]; + +static int eeprom_range_is_valid(struct device *dev, off_t offset, size_t len) +{ + const struct eeprom_sim_config *config = DEV_CONFIG(dev); + + if ((offset + len) < config->size) { + return 1; + } + + return 0; +} + +static int eeprom_sim_read(struct device *dev, const off_t offset, void *data, + const size_t len) +{ + if (!len) { + return 0; + } + + if (!eeprom_range_is_valid(dev, offset, len)) { + LOG_WRN("attempt to read past device boundary"); + return -EINVAL; + } + + SYNC_LOCK(); + + STATS_INC(eeprom_sim_stats, eeprom_read_calls); + memcpy(data, EEPROM(offset), len); + STATS_INCN(eeprom_sim_stats, bytes_read, len); + + SYNC_UNLOCK(); + +#ifdef CONFIG_EEPROM_SIMULATOR_SIMULATE_TIMING + k_busy_wait(CONFIG_EEPROM_SIMULATOR_MIN_READ_TIME_US); + STATS_INCN(eeprom_sim_stats, eeprom_read_time_us, + CONFIG_EEPROM_SIMULATOR_MIN_READ_TIME_US); +#endif + + return 0; +} + +static int eeprom_sim_write(struct device *dev, const off_t offset, + const void *data, const size_t len) +{ + const struct eeprom_sim_config *config = DEV_CONFIG(dev); + + if (config->readonly) { + LOG_WRN("attempt to write to read-only device"); + return -EACCES; + } + + if (!len) { + return 0; + } + + if (!eeprom_range_is_valid(dev, offset, len)) { + LOG_WRN("attempt to write past device boundary"); + return -EINVAL; + } + + SYNC_LOCK(); + + STATS_INC(eeprom_sim_stats, eeprom_write_calls); + + bool data_part_ignored = false; + + if (eeprom_sim_thresholds.max_write_calls != 0) { + if (eeprom_sim_stats.eeprom_write_calls > + eeprom_sim_thresholds.max_write_calls) { + return 0; + } else if (eeprom_sim_stats.eeprom_write_calls == + eeprom_sim_thresholds.max_write_calls) { + if (eeprom_sim_thresholds.max_len == 0) { + return 0; + } + + data_part_ignored = true; + } + } + + for (u32_t i = 0; i < len; i++) { + if (data_part_ignored) { + if (i >= eeprom_sim_thresholds.max_len) { + return 0; + } + } + + *(EEPROM(offset + i)) = *((u8_t *)data + i); + } + + STATS_INCN(eeprom_sim_stats, bytes_written, len); + +#ifdef CONFIG_EEPROM_SIMULATOR_SIMULATE_TIMING + /* wait before returning */ + k_busy_wait(CONFIG_EEPROM_SIMULATOR_MIN_WRITE_TIME_US); + STATS_INCN(eeprom_sim_stats, eeprom_write_time_us, + CONFIG_EEPROM_SIMULATOR_MIN_WRITE_TIME_US); +#endif + + SYNC_UNLOCK(); + + return 0; +} + +static size_t eeprom_sim_size(struct device *dev) +{ + const struct eeprom_sim_config *config = DEV_CONFIG(dev); + + return config->size; +} + +static const struct eeprom_driver_api eeprom_sim_api = { + .read = eeprom_sim_read, + .write = eeprom_sim_write, + .size = eeprom_sim_size, +}; + +static const struct eeprom_sim_config eeprom_sim_config_0 = { + .size = DT_INST_0_ZEPHYR_SIM_EEPROM_SIZE, + .readonly = DT_INST_0_ZEPHYR_SIM_EEPROM_READ_ONLY, +}; + +static int eeprom_sim_init(struct device *dev) +{ + SYNC_INIT(); + STATS_INIT_AND_REG(eeprom_sim_stats, STATS_SIZE_32, "eeprom_sim_stats"); + STATS_INIT_AND_REG(eeprom_sim_thresholds, STATS_SIZE_32, + "eeprom_sim_thresholds"); + memset(mock_eeprom, 0xFF, ARRAY_SIZE(mock_eeprom)); + + return 0; +} + +DEVICE_AND_API_INIT(eeprom_sim_0, DT_INST_0_ZEPHYR_SIM_EEPROM_LABEL, + &eeprom_sim_init, NULL, &eeprom_sim_config_0, POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &eeprom_sim_api); diff --git a/dts/bindings/mtd/zephyr,sim-eeprom.yaml b/dts/bindings/mtd/zephyr,sim-eeprom.yaml new file mode 100644 index 00000000000..c7c7cb7b4ff --- /dev/null +++ b/dts/bindings/mtd/zephyr,sim-eeprom.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2019 Laczen +# SPDX-License-Identifier: Apache-2.0 + +description: Zephyr Simulated EEPROM device + +compatible: "zephyr,sim-eeprom" + +include: eeprom-base.yaml + +properties: + size: + required: true diff --git a/tests/drivers/eeprom/prj_qemu_x86.conf b/tests/drivers/eeprom/prj_qemu_x86.conf new file mode 100644 index 00000000000..7438b7eddba --- /dev/null +++ b/tests/drivers/eeprom/prj_qemu_x86.conf @@ -0,0 +1,4 @@ +CONFIG_ZTEST=y +CONFIG_TEST_USERSPACE=y +CONFIG_EEPROM=y +CONFIG_EEPROM_SIMULATOR=y \ No newline at end of file diff --git a/tests/drivers/eeprom/testcase.yaml b/tests/drivers/eeprom/testcase.yaml index 73f1123716b..eb71ef95f3b 100644 --- a/tests/drivers/eeprom/testcase.yaml +++ b/tests/drivers/eeprom/testcase.yaml @@ -1,6 +1,6 @@ tests: - drivers.eeprom: - platform_whitelist: native_posix native_posix_64 + peripheral.eeprom: + platform_whitelist: native_posix native_posix_64 qemu_x86 tags: drivers userspace peripheral.eeprom.generic: build_on_all: true