drivers: eeprom: add mb85rsm1t fram support
Add a driver for the MB85RSM1T FRAM chip.
Signed-off-by: Jakub Wasilewski <jwasilewski@internships.antmicro.com>
Signed-off-by: Filip Kokosinski <fkokosinski@antmicro.com>
diff --git a/drivers/eeprom/CMakeLists.txt b/drivers/eeprom/CMakeLists.txt
index 4b148e9..c7b5f65 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)
diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig
index 87733d2..cf1cefb 100644
--- a/drivers/eeprom/Kconfig
+++ b/drivers/eeprom/Kconfig
@@ -99,6 +99,7 @@
source "drivers/eeprom/Kconfig.tmp116"
source "drivers/eeprom/Kconfig.xec"
source "drivers/eeprom/Kconfig.mb85rcxx"
+source "drivers/eeprom/Kconfig.mb85rsxx"
config EEPROM_SIMULATOR
bool "Simulated EEPROM driver"
diff --git a/drivers/eeprom/Kconfig.mb85rsxx b/drivers/eeprom/Kconfig.mb85rsxx
new file mode 100644
index 0000000..53bef99
--- /dev/null
+++ b/drivers/eeprom/Kconfig.mb85rsxx
@@ -0,0 +1,10 @@
+# Copyright (c) 2024 Antmicro <www.antmicro.com>
+# SPDX-License-Identifier: Apache-2.0
+
+config EEPROM_MB85RSXX
+ bool "FUJITSU MB85RSXX SPI FRAM"
+ default y
+ depends on DT_HAS_FUJITSU_MB85RSXX_ENABLED
+ select SPI
+ help
+ Enable FUJITSU mb85rsxx SPI FRAM
diff --git a/drivers/eeprom/eeprom_mb85rsxx.c b/drivers/eeprom/eeprom_mb85rsxx.c
new file mode 100644
index 0000000..70891a2
--- /dev/null
+++ b/drivers/eeprom/eeprom_mb85rsxx.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2024 Antmicro <www.antmicro.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief Driver for Fujitsu MB85RSXX FRAM over SPI.
+ */
+
+#define DT_DRV_COMPAT fujitsu_mb85rsxx
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/eeprom.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/kernel.h>
+#include <zephyr/sys/byteorder.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(mb85rsxx, CONFIG_EEPROM_LOG_LEVEL);
+
+/* MB85RSXX instruction set */
+#define EEPROM_MB85RSXX_WREN 0x06U /* Set Write Enable Latch */
+#define EEPROM_MB85RSXX_WRDI 0x04U /* Reset Write Enable Latch */
+#define EEPROM_MB85RSXX_RDSR 0x05U /* Read Status Register */
+#define EEPROM_MB85RSXX_WRSR 0x01U /* Write Status Register */
+#define EEPROM_MB85RSXX_READ 0x03U /* Read Memory Code */
+#define EEPROM_MB85RSXX_WRITE 0x02U /* Write Memory Code */
+#define EEPROM_MB85RSXX_RDID 0x9FU /* Read Device ID */
+#define EEPROM_MB85RSXX_FSTRD 0x0BU /* Fast Read Memory Code */
+#define EEPROM_MB85RSXX_SLEEP 0xB9U /* Sleep Mode */
+
+/* MB85RSXX status register bits */
+#define EEPROM_MB85RSXX_STATUS_WPEN BIT(7) /* Status Register Write Protect (RW) */
+#define EEPROM_MB85RSXX_STATUS_BP1 BIT(3) /* Block protection 1 (RW) */
+#define EEPROM_MB85RSXX_STATUS_BP0 BIT(2) /* Block protection 2 (RW) */
+#define EEPROM_MB85RSXX_STATUS_WEL BIT(1) /* Write Enable Latch (RO) */
+
+/* Fujitsu manufacturer ID (2 bytes) */
+#define EEPROM_MB85RSXX_MAN_ID 0x04U
+#define EEPROM_MB85RSXX_CON_CODE 0x7FU
+
+/*
+ * MB85RSXX product ID (2 bytes); first byte provides memory size, so let's use a mask later when
+ * checking it
+ */
+#define EEPROM_MB85RSXX_PROD_ID 0x20U
+#define EEPROM_MB85RSXX_PROD_ID2 0x03U
+#define EEPROM_MB85RSXX_PROD_MASK GENMASK(7, 5)
+
+struct eeprom_mb85rsxx_config {
+ struct spi_dt_spec spi;
+ size_t size;
+ bool readonly;
+};
+
+struct eeprom_mb85rsxx_data {
+ struct k_mutex lock;
+};
+
+static int eeprom_mb85rsxx_read(const struct device *dev, off_t offset, void *buf, size_t len)
+{
+ const struct eeprom_mb85rsxx_config *config = dev->config;
+ struct eeprom_mb85rsxx_data *data = dev->data;
+ uint8_t cmd[4] = {EEPROM_MB85RSXX_READ, 0, 0, 0};
+ uint8_t *paddr = &cmd[1];
+ int err;
+
+ if (offset + len > config->size) {
+ LOG_ERR("attempt to read past device boundary");
+ return -EINVAL;
+ }
+
+ if (!len) {
+ return 0;
+ }
+
+ /* Populate address in command */
+ *paddr++ = (offset >> 16);
+ *paddr++ = (offset >> 8);
+ *paddr++ = offset;
+
+ const struct spi_buf tx_buf = {
+ .buf = cmd,
+ .len = sizeof(cmd),
+ };
+ const struct spi_buf_set tx = {
+ .buffers = &tx_buf,
+ .count = 1,
+ };
+ const struct spi_buf rx_bufs[2] = {
+ {
+ .buf = NULL,
+ .len = sizeof(cmd),
+ },
+ {
+ .buf = buf,
+ .len = len,
+ },
+ };
+ const struct spi_buf_set rx = {
+ .buffers = rx_bufs,
+ .count = ARRAY_SIZE(rx_bufs),
+ };
+
+ k_mutex_lock(&data->lock, K_FOREVER);
+
+ err = spi_transceive_dt(&config->spi, &tx, &rx);
+
+ k_mutex_unlock(&data->lock);
+
+ if (err < 0) {
+ LOG_ERR("failed to read FRAM (err %d)", err);
+ }
+
+ return err;
+}
+
+static int eeprom_mb85rsxx_wren(const struct device *dev)
+{
+ const struct eeprom_mb85rsxx_config *config = dev->config;
+ uint8_t cmd = EEPROM_MB85RSXX_WREN;
+ const struct spi_buf tx_buf = {
+ .buf = &cmd,
+ .len = sizeof(cmd),
+ };
+ const struct spi_buf_set tx = {
+ .buffers = &tx_buf,
+ .count = 1,
+ };
+
+ return spi_write_dt(&config->spi, &tx);
+}
+
+static int eeprom_mb85rsxx_wrdi(const struct device *dev)
+{
+ const struct eeprom_mb85rsxx_config *config = dev->config;
+ uint8_t cmd = EEPROM_MB85RSXX_WRDI;
+ const struct spi_buf tx_buf = {
+ .buf = &cmd,
+ .len = sizeof(cmd),
+ };
+ const struct spi_buf_set tx = {
+ .buffers = &tx_buf,
+ .count = 1,
+ };
+
+ return spi_write_dt(&config->spi, &tx);
+}
+
+static int eeprom_mb85rsxx_write(const struct device *dev, off_t offset, const void *buf,
+ size_t len)
+{
+ const struct eeprom_mb85rsxx_config *config = dev->config;
+ struct eeprom_mb85rsxx_data *data = dev->data;
+ uint8_t cmd[4] = {EEPROM_MB85RSXX_WRITE, 0, 0, 0};
+ uint8_t *paddr = &cmd[1];
+ int err;
+
+ if (config->readonly) {
+ LOG_ERR("attempt to write to read-only device");
+ return -EACCES;
+ }
+
+ if (offset + len > config->size) {
+ LOG_ERR("attempt to write past device boundary");
+ return -EINVAL;
+ }
+
+ /* Populate address in command */
+ *paddr++ = (offset >> 16) & 0xFF;
+ *paddr++ = (offset >> 8) & 0xFF;
+ *paddr++ = offset & 0xFF;
+
+ const struct spi_buf tx_bufs[2] = {
+ {
+ .buf = cmd,
+ .len = sizeof(cmd),
+ },
+ {
+ .buf = (void *)buf,
+ .len = len,
+ },
+ };
+ const struct spi_buf_set tx = {
+ .buffers = tx_bufs,
+ .count = ARRAY_SIZE(tx_bufs),
+ };
+
+ k_mutex_lock(&data->lock, K_FOREVER);
+
+ err = eeprom_mb85rsxx_wren(dev);
+ if (err < 0) {
+ LOG_ERR("failed to disable write protection (err %d)", err);
+ k_mutex_unlock(&data->lock);
+ return err;
+ }
+
+ err = spi_write_dt(&config->spi, &tx);
+ if (err < 0) {
+ LOG_ERR("failed to write to FRAM (err %d)", err);
+ k_mutex_unlock(&data->lock);
+ return err;
+ }
+
+ err = eeprom_mb85rsxx_wrdi(dev);
+ if (err < 0) {
+ LOG_ERR("failed to disable write (err %d)", err);
+ }
+
+ k_mutex_unlock(&data->lock);
+
+ return err;
+}
+
+static size_t eeprom_mb85rsxx_size(const struct device *dev)
+{
+ const struct eeprom_mb85rsxx_config *config = dev->config;
+
+ return config->size;
+}
+
+static int eeprom_mb85rsxx_rdid(const struct device *dev)
+{
+ const struct eeprom_mb85rsxx_config *config = dev->config;
+ struct eeprom_mb85rsxx_data *data = dev->data;
+ uint8_t id[4];
+ uint8_t cmd = EEPROM_MB85RSXX_RDID;
+ int err;
+
+ const struct spi_buf tx_buf = {
+ .buf = &cmd,
+ .len = sizeof(cmd),
+ };
+ const struct spi_buf_set tx = {
+ .buffers = &tx_buf,
+ .count = 1,
+ };
+ const struct spi_buf rx_bufs[2] = {
+ {
+ .buf = NULL,
+ .len = sizeof(cmd),
+ },
+ {
+ .buf = id,
+ .len = sizeof(id),
+ },
+ };
+ const struct spi_buf_set rx = {
+ .buffers = rx_bufs,
+ .count = ARRAY_SIZE(rx_bufs),
+ };
+ k_mutex_lock(&data->lock, K_FOREVER);
+ err = spi_transceive_dt(&config->spi, &tx, &rx);
+ k_mutex_unlock(&data->lock);
+
+ if (err < 0) {
+ LOG_ERR("failed to read RDID (err %d)", err);
+ return err;
+ }
+
+ /* Validate Manufacturer ID and Product ID */
+ if (id[0] != EEPROM_MB85RSXX_MAN_ID
+ || id[1] != EEPROM_MB85RSXX_CON_CODE
+ || (id[2] & EEPROM_MB85RSXX_PROD_MASK) != EEPROM_MB85RSXX_PROD_ID
+ || id[3] != EEPROM_MB85RSXX_PROD_ID2) {
+ LOG_ERR("invalid device ID: %02X %02X %02X %02X", id[0], id[1], id[2], id[3]);
+ return -EIO;
+ }
+
+ LOG_INF("device ID read successfully: %02X %02X %02X %02X", id[0], id[1], id[2], id[3]);
+ return 0;
+}
+
+static int eeprom_mb85rsxx_init(const struct device *dev)
+{
+ const struct eeprom_mb85rsxx_config *config = dev->config;
+ struct eeprom_mb85rsxx_data *data = dev->data;
+ int err;
+
+ k_mutex_init(&data->lock);
+
+ if (!spi_is_ready_dt(&config->spi)) {
+ LOG_ERR("SPI bus not ready");
+ return -EINVAL;
+ }
+
+ err = eeprom_mb85rsxx_rdid(dev);
+ if (err < 0) {
+ LOG_ERR("Failed to initialize device, RDID check failed (err %d)", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct eeprom_driver_api mb85rsxx_driver_api = {
+ .read = &eeprom_mb85rsxx_read,
+ .write = &eeprom_mb85rsxx_write,
+ .size = &eeprom_mb85rsxx_size,
+};
+
+#define MB85RSXX_INIT(inst) \
+ static struct eeprom_mb85rsxx_data eeprom_mb85rsxx_data_##inst; \
+ \
+ static const struct eeprom_mb85rsxx_config eeprom_mb85rsxx_config_##inst = { \
+ .spi = SPI_DT_SPEC_INST_GET( \
+ inst, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0), \
+ .size = DT_INST_PROP(inst, size), \
+ .readonly = DT_INST_PROP(inst, read_only), \
+ }; \
+ \
+ DEVICE_DT_INST_DEFINE(inst, eeprom_mb85rsxx_init, NULL, &eeprom_mb85rsxx_data_##inst, \
+ &eeprom_mb85rsxx_config_##inst, POST_KERNEL, \
+ CONFIG_EEPROM_INIT_PRIORITY, &mb85rsxx_driver_api);
+
+DT_INST_FOREACH_STATUS_OKAY(MB85RSXX_INIT)
diff --git a/dts/bindings/mtd/fujitsu,mb85rsxx.yaml b/dts/bindings/mtd/fujitsu,mb85rsxx.yaml
new file mode 100644
index 0000000..0c41ce1
--- /dev/null
+++ b/dts/bindings/mtd/fujitsu,mb85rsxx.yaml
@@ -0,0 +1,13 @@
+# Copyright (c) 2024 Antmicro <www.antmicro.com>
+# SPDX-License-Identifier: Apache-2.0
+
+description: Fujitsu MB85RSXX SPI FRAM
+
+compatible: "fujitsu,mb85rsxx"
+
+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 b861c38..97b39a6 100644
--- a/tests/drivers/build_all/eeprom/app.overlay
+++ b/tests/drivers/build_all/eeprom/app.overlay
@@ -10,6 +10,9 @@
* (and be extended to test) real hardware.
*/
+ #include <freq.h>
+ #include <mem.h>
+
/ {
test {
#address-cells = <1>;
@@ -89,6 +92,13 @@
wp-gpios = <&test_gpio 0 0>;
/* read-only; */
};
+
+ test_spi_mb85rsxx: mb85rsxx@0 {
+ compatible = "fujitsu,mb85rsxx";
+ reg = <0x0>;
+ spi-max-frequency = <DT_FREQ_M(25)>;
+ size = <DT_SIZE_K(128)>;
+ };
};
};
};