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>
This commit is contained in:
parent
f754e09dcd
commit
cfdaa91ff6
6 changed files with 354 additions and 0 deletions
|
@ -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_AT2X_EMUL eeprom_at2x_emul.c)
|
||||||
|
|
||||||
zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RCXX eeprom_mb85rcxx.c)
|
zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RCXX eeprom_mb85rcxx.c)
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RSXX eeprom_mb85rsxx.c)
|
||||||
|
|
|
@ -99,6 +99,7 @@ source "drivers/eeprom/Kconfig.eeprom_emu"
|
||||||
source "drivers/eeprom/Kconfig.tmp116"
|
source "drivers/eeprom/Kconfig.tmp116"
|
||||||
source "drivers/eeprom/Kconfig.xec"
|
source "drivers/eeprom/Kconfig.xec"
|
||||||
source "drivers/eeprom/Kconfig.mb85rcxx"
|
source "drivers/eeprom/Kconfig.mb85rcxx"
|
||||||
|
source "drivers/eeprom/Kconfig.mb85rsxx"
|
||||||
|
|
||||||
config EEPROM_SIMULATOR
|
config EEPROM_SIMULATOR
|
||||||
bool "Simulated EEPROM driver"
|
bool "Simulated EEPROM driver"
|
||||||
|
|
10
drivers/eeprom/Kconfig.mb85rsxx
Normal file
10
drivers/eeprom/Kconfig.mb85rsxx
Normal file
|
@ -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
|
318
drivers/eeprom/eeprom_mb85rsxx.c
Normal file
318
drivers/eeprom/eeprom_mb85rsxx.c
Normal file
|
@ -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)
|
13
dts/bindings/mtd/fujitsu,mb85rsxx.yaml
Normal file
13
dts/bindings/mtd/fujitsu,mb85rsxx.yaml
Normal file
|
@ -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.
|
|
@ -10,6 +10,9 @@
|
||||||
* (and be extended to test) real hardware.
|
* (and be extended to test) real hardware.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <freq.h>
|
||||||
|
#include <mem.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
test {
|
test {
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
|
@ -89,6 +92,13 @@
|
||||||
wp-gpios = <&test_gpio 0 0>;
|
wp-gpios = <&test_gpio 0 0>;
|
||||||
/* read-only; */
|
/* read-only; */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
test_spi_mb85rsxx: mb85rsxx@0 {
|
||||||
|
compatible = "fujitsu,mb85rsxx";
|
||||||
|
reg = <0x0>;
|
||||||
|
spi-max-frequency = <DT_FREQ_M(25)>;
|
||||||
|
size = <DT_SIZE_K(128)>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue