From cfdaa91ff6bbf6904b1f7f61eabe204acfa5612c Mon Sep 17 00:00:00 2001 From: Jakub Wasilewski Date: Wed, 21 Aug 2024 18:19:21 +0200 Subject: [PATCH] drivers: eeprom: add mb85rsm1t fram support Add a driver for the MB85RSM1T FRAM chip. Signed-off-by: Jakub Wasilewski Signed-off-by: Filip Kokosinski --- drivers/eeprom/CMakeLists.txt | 2 + drivers/eeprom/Kconfig | 1 + drivers/eeprom/Kconfig.mb85rsxx | 10 + drivers/eeprom/eeprom_mb85rsxx.c | 318 +++++++++++++++++++++ dts/bindings/mtd/fujitsu,mb85rsxx.yaml | 13 + tests/drivers/build_all/eeprom/app.overlay | 10 + 6 files changed, 354 insertions(+) create mode 100644 drivers/eeprom/Kconfig.mb85rsxx create mode 100644 drivers/eeprom/eeprom_mb85rsxx.c create mode 100644 dts/bindings/mtd/fujitsu,mb85rsxx.yaml diff --git a/drivers/eeprom/CMakeLists.txt b/drivers/eeprom/CMakeLists.txt index 4b148e9f1a2..c7b5f65c21f 100644 --- a/drivers/eeprom/CMakeLists.txt +++ b/drivers/eeprom/CMakeLists.txt @@ -26,3 +26,5 @@ zephyr_library_sources_ifdef(CONFIG_EEPROM_FAKE eeprom_fake.c) 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 87733d2c2e1..cf1cefb5505 100644 --- a/drivers/eeprom/Kconfig +++ b/drivers/eeprom/Kconfig @@ -99,6 +99,7 @@ source "drivers/eeprom/Kconfig.eeprom_emu" 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 00000000000..53bef99a1a8 --- /dev/null +++ b/drivers/eeprom/Kconfig.mb85rsxx @@ -0,0 +1,10 @@ +# Copyright (c) 2024 Antmicro +# 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 00000000000..70891a28e92 --- /dev/null +++ b/drivers/eeprom/eeprom_mb85rsxx.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2024 Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Driver for Fujitsu MB85RSXX FRAM over SPI. + */ + +#define DT_DRV_COMPAT fujitsu_mb85rsxx + +#include +#include +#include +#include +#include + +#include +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 00000000000..0c41ce174c2 --- /dev/null +++ b/dts/bindings/mtd/fujitsu,mb85rsxx.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Antmicro +# 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 b861c382ff4..97b39a66664 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 + #include + / { 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 = ; + size = ; + }; }; }; };