From 79a8d657c62728bc1844626f1f872dd5cf0e6ad9 Mon Sep 17 00:00:00 2001 From: Luna Pes Date: Fri, 15 Aug 2025 18:34:04 +0200 Subject: [PATCH] drivers: eeprom: fm25xxx: add support for infineon fm25xxx FRAM This driver adds support for the Infineon FM25XXX series of chips. Has been tested on Infineon FM25CL64B-G. Signed-off-by: Luna Pes --- drivers/eeprom/CMakeLists.txt | 2 + drivers/eeprom/Kconfig | 1 + drivers/eeprom/Kconfig.fm25xxx | 10 + drivers/eeprom/eeprom_fm25xxx.c | 263 +++++++++++++++++++++ dts/bindings/mtd/infineon,fm25xxx.yaml | 13 + tests/drivers/build_all/eeprom/app.overlay | 7 + 6 files changed, 296 insertions(+) create mode 100644 drivers/eeprom/Kconfig.fm25xxx create mode 100644 drivers/eeprom/eeprom_fm25xxx.c create mode 100644 dts/bindings/mtd/infineon,fm25xxx.yaml diff --git a/drivers/eeprom/CMakeLists.txt b/drivers/eeprom/CMakeLists.txt index dced9ecb29afa..5f667fa2434ac 100644 --- a/drivers/eeprom/CMakeLists.txt +++ b/drivers/eeprom/CMakeLists.txt @@ -26,3 +26,5 @@ zephyr_library_sources_ifdef(CONFIG_EEPROM_AT2X_EMUL eeprom_at2x_emul.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RCXX eeprom_mb85rcxx.c) zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RSXX eeprom_mb85rsxx.c) + +zephyr_library_sources_ifdef(CONFIG_EEPROM_FM25XXX eeprom_fm25xxx.c) diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig index ca8d3c4e4726e..ec90b1e731011 100644 --- a/drivers/eeprom/Kconfig +++ b/drivers/eeprom/Kconfig @@ -100,6 +100,7 @@ source "drivers/eeprom/Kconfig.tmp11x" source "drivers/eeprom/Kconfig.xec" source "drivers/eeprom/Kconfig.mb85rcxx" source "drivers/eeprom/Kconfig.mb85rsxx" +source "drivers/eeprom/Kconfig.fm25xxx" config EEPROM_SIMULATOR bool "Simulated EEPROM driver" diff --git a/drivers/eeprom/Kconfig.fm25xxx b/drivers/eeprom/Kconfig.fm25xxx new file mode 100644 index 0000000000000..db0b7ff62394d --- /dev/null +++ b/drivers/eeprom/Kconfig.fm25xxx @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Luna Pes +# SPDX-License-Identifier: Apache-2.0 + +config EEPROM_FM25XXX + bool "Infineon FM25XXX SPI FRAM" + default y + depends on DT_HAS_INFINEON_FM25XXX_ENABLED + select SPI + help + Enable Infineon FM25XXX SPI FRAM diff --git a/drivers/eeprom/eeprom_fm25xxx.c b/drivers/eeprom/eeprom_fm25xxx.c new file mode 100644 index 0000000000000..8cab5f5c45712 --- /dev/null +++ b/drivers/eeprom/eeprom_fm25xxx.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2025 Luna Pes + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* This file implements the Infineon AN304 SPI Guide for F-RAM */ + +#include "zephyr/devicetree.h" +#include "zephyr/kernel.h" +#include "zephyr/sys/byteorder.h" +#include "zephyr/sys/util.h" +#include +#include +#include +#include + +#define DT_DRV_COMPAT infineon_fm25xxx + +LOG_MODULE_REGISTER(fm25xxx, CONFIG_EEPROM_LOG_LEVEL); + +/* Registers */ +#define FM25XXX_WREN 0x06 +#define FM25XXX_WRDI 0x04 +#define FM25XXX_RDSR 0x05 +#define FM25XXX_WRSR 0x01 +#define FM25XXX_READ 0x03 +#define FM25XXX_WRITE 0x02 + +struct fm25xxx_config { + struct spi_dt_spec spi; + size_t size; + bool readonly; +}; + +struct fm25xxx_data { + struct k_mutex lock; +}; + +static uint8_t eeprom_fm25xxx_size_to_addr_bytes(size_t size) +{ + if (size <= 500) { + return 1; + } else if (size <= 64000) { + return 2; + } else { + return 3; + } +} + +static int eeprom_fm25xxx_set_enable_write(const struct device *dev, bool enable_writes) +{ + const struct fm25xxx_config *config = dev->config; + uint8_t op = enable_writes ? FM25XXX_WREN : FM25XXX_WRDI; + + const struct spi_buf tx_bufs[] = {{ + .buf = &op, + .len = sizeof(op), + }}; + const struct spi_buf_set tx_buf_set = { + .buffers = tx_bufs, + .count = ARRAY_SIZE(tx_bufs), + }; + + spi_write_dt(&config->spi, &tx_buf_set); + + return 0; +} + +int eeprom_fm25xxx_read(const struct device *dev, off_t offset, void *data, size_t len) +{ + int ret; + const struct fm25xxx_config *config = dev->config; + + if (offset + len > config->size) { + LOG_ERR("Can not read more data than the device size"); + return -EINVAL; + } + + if (len == 0) { + return 0; + } + + uint8_t read_op[4] = {FM25XXX_READ}; + + size_t addr_bytes = eeprom_fm25xxx_size_to_addr_bytes(config->size); + size_t op_len = 1 + addr_bytes; + + switch (addr_bytes) { + case 1: + read_op[1] = offset & 0xff; + break; + case 2: + sys_put_be16(offset, &read_op[1]); + break; + case 3: + sys_put_be24(offset, &read_op[1]); + break; + default: + LOG_ERR("Invalid number of address bytes %u", addr_bytes); + return -EINVAL; + } + + LOG_HEXDUMP_WRN(read_op, 4, "Read op"); + + const struct spi_buf tx_bufs[] = {{ + .buf = &read_op, + .len = op_len, + }}; + const struct spi_buf_set tx_buf_set = { + .buffers = tx_bufs, + .count = ARRAY_SIZE(tx_bufs), + }; + + const struct spi_buf rx_bufs[2] = { + { + .buf = NULL, + .len = op_len, + }, + { + .buf = data, + .len = len, + }, + }; + const struct spi_buf_set rx_buf_set = { + .buffers = rx_bufs, + .count = ARRAY_SIZE(rx_bufs), + }; + + ret = spi_transceive_dt(&config->spi, &tx_buf_set, &rx_buf_set); + + if (ret != 0) { + LOG_ERR("Failed to read from FRAM"); + return ret; + } + + return 0; +} + +int eeprom_fm25xxx_write(const struct device *dev, off_t offset, const void *data, size_t len) +{ + int ret; + const struct fm25xxx_config *config = dev->config; + struct fm25xxx_data *dev_data = dev->data; + + if (config->readonly) { + LOG_ERR("Can not write to a readonly device"); + return -EACCES; + } + + if (offset + len > config->size) { + LOG_ERR("Can not write more data than the device size"); + return -EINVAL; + } + + if (len == 0) { + return 0; + } + + uint8_t write_op[4] = {FM25XXX_WRITE}; + + size_t addr_bytes = eeprom_fm25xxx_size_to_addr_bytes(config->size); + size_t op_len = 1 + addr_bytes; + + switch (addr_bytes) { + case 1: + write_op[1] = offset & 0xff; + break; + case 2: + sys_put_be16(offset, &write_op[1]); + break; + case 3: + sys_put_be24(offset, &write_op[1]); + break; + default: + LOG_ERR("Invalid number of address bytes %u", addr_bytes); + return -EINVAL; + } + + const struct spi_buf tx_bufs[] = { + { + .buf = &write_op, + .len = op_len, + }, + { + .buf = (void *)data, + .len = len, + }, + }; + const struct spi_buf_set tx_buf_set = { + .buffers = tx_bufs, + .count = ARRAY_SIZE(tx_bufs), + }; + + k_mutex_lock(&dev_data->lock, K_FOREVER); + + ret = eeprom_fm25xxx_set_enable_write(dev, true); + if (ret != 0) { + k_mutex_unlock(&dev_data->lock); + LOG_ERR("Could not enable writes"); + return ret; + } + + ret = spi_write_dt(&config->spi, &tx_buf_set); + if (ret != 0) { + k_mutex_unlock(&dev_data->lock); + LOG_ERR("Failed to write to FRAM"); + return ret; + } + + ret = eeprom_fm25xxx_set_enable_write(dev, false); + if (ret != 0) { + k_mutex_unlock(&dev_data->lock); + LOG_ERR("Could not disable writes"); + return ret; + } + + k_mutex_unlock(&dev_data->lock); + + return 0; +} + +size_t eeprom_fm25xxx_get_size(const struct device *dev) +{ + const struct fm25xxx_config *config = dev->config; + + return config->size; +} + +static int eeprom_fm25xxx_init(const struct device *dev) +{ + const struct fm25xxx_config *config = dev->config; + struct fm25xxx_data *data = dev->data; + + k_mutex_init(&data->lock); + + if (!spi_is_ready_dt(&config->spi)) { + LOG_ERR("SPI bus not ready"); + return -ENODEV; + } + + return 0; +} + +static DEVICE_API(eeprom, eeprom_fm25xxx_api) = { + .read = &eeprom_fm25xxx_read, + .write = &eeprom_fm25xxx_write, + .size = &eeprom_fm25xxx_get_size, +}; + +#define FM25XXX_INIT(inst) \ + static struct fm25xxx_data fm25xxx_data_##inst; \ + static const struct fm25xxx_config fm25xxx_config_##inst = { \ + .spi = SPI_DT_SPEC_INST_GET(inst, SPI_OP_MODE_MASTER | SPI_WORD_SET(8)), \ + .size = DT_INST_PROP(inst, size), \ + .readonly = DT_INST_PROP(inst, read_only), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, eeprom_fm25xxx_init, NULL, &fm25xxx_data_##inst, \ + &fm25xxx_config_##inst, POST_KERNEL, CONFIG_EEPROM_INIT_PRIORITY, \ + &eeprom_fm25xxx_api); + +DT_INST_FOREACH_STATUS_OKAY(FM25XXX_INIT) diff --git a/dts/bindings/mtd/infineon,fm25xxx.yaml b/dts/bindings/mtd/infineon,fm25xxx.yaml new file mode 100644 index 0000000000000..d2aa89cbcb056 --- /dev/null +++ b/dts/bindings/mtd/infineon,fm25xxx.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2025 Luna Pes +# SPDX-License-Identifier: Apache-2.0 + +description: Infineon FM25XXX SPI FRAM + +compatible: "infineon,fm25xxx" + +include: ["eeprom-base.yaml", spi-device.yaml] + +properties: + size: + required: true + description: Total FRAM size in bytes. diff --git a/tests/drivers/build_all/eeprom/app.overlay b/tests/drivers/build_all/eeprom/app.overlay index fcc4bbe9e0852..ff20fd2c83bc5 100644 --- a/tests/drivers/build_all/eeprom/app.overlay +++ b/tests/drivers/build_all/eeprom/app.overlay @@ -99,6 +99,13 @@ spi-max-frequency = ; size = ; }; + + test_spi_fm25xxx: fm25xxx@2 { + compatible = "infineon,fm25xxx"; + reg = <0x2>; + spi-max-frequency = ; + size = <8000>; + }; }; }; };