Skip to content

Commit cfdaa91

Browse files
Jakub Wasilewskinashif
authored andcommitted
drivers: eeprom: add mb85rsm1t fram support
Add a driver for the MB85RSM1T FRAM chip. Signed-off-by: Jakub Wasilewski <[email protected]> Signed-off-by: Filip Kokosinski <[email protected]>
1 parent f754e09 commit cfdaa91

File tree

6 files changed

+354
-0
lines changed

6 files changed

+354
-0
lines changed

drivers/eeprom/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,5 @@ zephyr_library_sources_ifdef(CONFIG_EEPROM_FAKE eeprom_fake.c)
2626
zephyr_library_sources_ifdef(CONFIG_EEPROM_AT2X_EMUL eeprom_at2x_emul.c)
2727

2828
zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RCXX eeprom_mb85rcxx.c)
29+
30+
zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RSXX eeprom_mb85rsxx.c)

drivers/eeprom/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ source "drivers/eeprom/Kconfig.eeprom_emu"
9999
source "drivers/eeprom/Kconfig.tmp116"
100100
source "drivers/eeprom/Kconfig.xec"
101101
source "drivers/eeprom/Kconfig.mb85rcxx"
102+
source "drivers/eeprom/Kconfig.mb85rsxx"
102103

103104
config EEPROM_SIMULATOR
104105
bool "Simulated EEPROM driver"

drivers/eeprom/Kconfig.mb85rsxx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2024 Antmicro <www.antmicro.com>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config EEPROM_MB85RSXX
5+
bool "FUJITSU MB85RSXX SPI FRAM"
6+
default y
7+
depends on DT_HAS_FUJITSU_MB85RSXX_ENABLED
8+
select SPI
9+
help
10+
Enable FUJITSU mb85rsxx SPI FRAM

drivers/eeprom/eeprom_mb85rsxx.c

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
/*
2+
* Copyright (c) 2024 Antmicro <www.antmicro.com>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @file
9+
* @brief Driver for Fujitsu MB85RSXX FRAM over SPI.
10+
*/
11+
12+
#define DT_DRV_COMPAT fujitsu_mb85rsxx
13+
14+
#include <zephyr/device.h>
15+
#include <zephyr/drivers/eeprom.h>
16+
#include <zephyr/drivers/spi.h>
17+
#include <zephyr/kernel.h>
18+
#include <zephyr/sys/byteorder.h>
19+
20+
#include <zephyr/logging/log.h>
21+
LOG_MODULE_REGISTER(mb85rsxx, CONFIG_EEPROM_LOG_LEVEL);
22+
23+
/* MB85RSXX instruction set */
24+
#define EEPROM_MB85RSXX_WREN 0x06U /* Set Write Enable Latch */
25+
#define EEPROM_MB85RSXX_WRDI 0x04U /* Reset Write Enable Latch */
26+
#define EEPROM_MB85RSXX_RDSR 0x05U /* Read Status Register */
27+
#define EEPROM_MB85RSXX_WRSR 0x01U /* Write Status Register */
28+
#define EEPROM_MB85RSXX_READ 0x03U /* Read Memory Code */
29+
#define EEPROM_MB85RSXX_WRITE 0x02U /* Write Memory Code */
30+
#define EEPROM_MB85RSXX_RDID 0x9FU /* Read Device ID */
31+
#define EEPROM_MB85RSXX_FSTRD 0x0BU /* Fast Read Memory Code */
32+
#define EEPROM_MB85RSXX_SLEEP 0xB9U /* Sleep Mode */
33+
34+
/* MB85RSXX status register bits */
35+
#define EEPROM_MB85RSXX_STATUS_WPEN BIT(7) /* Status Register Write Protect (RW) */
36+
#define EEPROM_MB85RSXX_STATUS_BP1 BIT(3) /* Block protection 1 (RW) */
37+
#define EEPROM_MB85RSXX_STATUS_BP0 BIT(2) /* Block protection 2 (RW) */
38+
#define EEPROM_MB85RSXX_STATUS_WEL BIT(1) /* Write Enable Latch (RO) */
39+
40+
/* Fujitsu manufacturer ID (2 bytes) */
41+
#define EEPROM_MB85RSXX_MAN_ID 0x04U
42+
#define EEPROM_MB85RSXX_CON_CODE 0x7FU
43+
44+
/*
45+
* MB85RSXX product ID (2 bytes); first byte provides memory size, so let's use a mask later when
46+
* checking it
47+
*/
48+
#define EEPROM_MB85RSXX_PROD_ID 0x20U
49+
#define EEPROM_MB85RSXX_PROD_ID2 0x03U
50+
#define EEPROM_MB85RSXX_PROD_MASK GENMASK(7, 5)
51+
52+
struct eeprom_mb85rsxx_config {
53+
struct spi_dt_spec spi;
54+
size_t size;
55+
bool readonly;
56+
};
57+
58+
struct eeprom_mb85rsxx_data {
59+
struct k_mutex lock;
60+
};
61+
62+
static int eeprom_mb85rsxx_read(const struct device *dev, off_t offset, void *buf, size_t len)
63+
{
64+
const struct eeprom_mb85rsxx_config *config = dev->config;
65+
struct eeprom_mb85rsxx_data *data = dev->data;
66+
uint8_t cmd[4] = {EEPROM_MB85RSXX_READ, 0, 0, 0};
67+
uint8_t *paddr = &cmd[1];
68+
int err;
69+
70+
if (offset + len > config->size) {
71+
LOG_ERR("attempt to read past device boundary");
72+
return -EINVAL;
73+
}
74+
75+
if (!len) {
76+
return 0;
77+
}
78+
79+
/* Populate address in command */
80+
*paddr++ = (offset >> 16);
81+
*paddr++ = (offset >> 8);
82+
*paddr++ = offset;
83+
84+
const struct spi_buf tx_buf = {
85+
.buf = cmd,
86+
.len = sizeof(cmd),
87+
};
88+
const struct spi_buf_set tx = {
89+
.buffers = &tx_buf,
90+
.count = 1,
91+
};
92+
const struct spi_buf rx_bufs[2] = {
93+
{
94+
.buf = NULL,
95+
.len = sizeof(cmd),
96+
},
97+
{
98+
.buf = buf,
99+
.len = len,
100+
},
101+
};
102+
const struct spi_buf_set rx = {
103+
.buffers = rx_bufs,
104+
.count = ARRAY_SIZE(rx_bufs),
105+
};
106+
107+
k_mutex_lock(&data->lock, K_FOREVER);
108+
109+
err = spi_transceive_dt(&config->spi, &tx, &rx);
110+
111+
k_mutex_unlock(&data->lock);
112+
113+
if (err < 0) {
114+
LOG_ERR("failed to read FRAM (err %d)", err);
115+
}
116+
117+
return err;
118+
}
119+
120+
static int eeprom_mb85rsxx_wren(const struct device *dev)
121+
{
122+
const struct eeprom_mb85rsxx_config *config = dev->config;
123+
uint8_t cmd = EEPROM_MB85RSXX_WREN;
124+
const struct spi_buf tx_buf = {
125+
.buf = &cmd,
126+
.len = sizeof(cmd),
127+
};
128+
const struct spi_buf_set tx = {
129+
.buffers = &tx_buf,
130+
.count = 1,
131+
};
132+
133+
return spi_write_dt(&config->spi, &tx);
134+
}
135+
136+
static int eeprom_mb85rsxx_wrdi(const struct device *dev)
137+
{
138+
const struct eeprom_mb85rsxx_config *config = dev->config;
139+
uint8_t cmd = EEPROM_MB85RSXX_WRDI;
140+
const struct spi_buf tx_buf = {
141+
.buf = &cmd,
142+
.len = sizeof(cmd),
143+
};
144+
const struct spi_buf_set tx = {
145+
.buffers = &tx_buf,
146+
.count = 1,
147+
};
148+
149+
return spi_write_dt(&config->spi, &tx);
150+
}
151+
152+
static int eeprom_mb85rsxx_write(const struct device *dev, off_t offset, const void *buf,
153+
size_t len)
154+
{
155+
const struct eeprom_mb85rsxx_config *config = dev->config;
156+
struct eeprom_mb85rsxx_data *data = dev->data;
157+
uint8_t cmd[4] = {EEPROM_MB85RSXX_WRITE, 0, 0, 0};
158+
uint8_t *paddr = &cmd[1];
159+
int err;
160+
161+
if (config->readonly) {
162+
LOG_ERR("attempt to write to read-only device");
163+
return -EACCES;
164+
}
165+
166+
if (offset + len > config->size) {
167+
LOG_ERR("attempt to write past device boundary");
168+
return -EINVAL;
169+
}
170+
171+
/* Populate address in command */
172+
*paddr++ = (offset >> 16) & 0xFF;
173+
*paddr++ = (offset >> 8) & 0xFF;
174+
*paddr++ = offset & 0xFF;
175+
176+
const struct spi_buf tx_bufs[2] = {
177+
{
178+
.buf = cmd,
179+
.len = sizeof(cmd),
180+
},
181+
{
182+
.buf = (void *)buf,
183+
.len = len,
184+
},
185+
};
186+
const struct spi_buf_set tx = {
187+
.buffers = tx_bufs,
188+
.count = ARRAY_SIZE(tx_bufs),
189+
};
190+
191+
k_mutex_lock(&data->lock, K_FOREVER);
192+
193+
err = eeprom_mb85rsxx_wren(dev);
194+
if (err < 0) {
195+
LOG_ERR("failed to disable write protection (err %d)", err);
196+
k_mutex_unlock(&data->lock);
197+
return err;
198+
}
199+
200+
err = spi_write_dt(&config->spi, &tx);
201+
if (err < 0) {
202+
LOG_ERR("failed to write to FRAM (err %d)", err);
203+
k_mutex_unlock(&data->lock);
204+
return err;
205+
}
206+
207+
err = eeprom_mb85rsxx_wrdi(dev);
208+
if (err < 0) {
209+
LOG_ERR("failed to disable write (err %d)", err);
210+
}
211+
212+
k_mutex_unlock(&data->lock);
213+
214+
return err;
215+
}
216+
217+
static size_t eeprom_mb85rsxx_size(const struct device *dev)
218+
{
219+
const struct eeprom_mb85rsxx_config *config = dev->config;
220+
221+
return config->size;
222+
}
223+
224+
static int eeprom_mb85rsxx_rdid(const struct device *dev)
225+
{
226+
const struct eeprom_mb85rsxx_config *config = dev->config;
227+
struct eeprom_mb85rsxx_data *data = dev->data;
228+
uint8_t id[4];
229+
uint8_t cmd = EEPROM_MB85RSXX_RDID;
230+
int err;
231+
232+
const struct spi_buf tx_buf = {
233+
.buf = &cmd,
234+
.len = sizeof(cmd),
235+
};
236+
const struct spi_buf_set tx = {
237+
.buffers = &tx_buf,
238+
.count = 1,
239+
};
240+
const struct spi_buf rx_bufs[2] = {
241+
{
242+
.buf = NULL,
243+
.len = sizeof(cmd),
244+
},
245+
{
246+
.buf = id,
247+
.len = sizeof(id),
248+
},
249+
};
250+
const struct spi_buf_set rx = {
251+
.buffers = rx_bufs,
252+
.count = ARRAY_SIZE(rx_bufs),
253+
};
254+
k_mutex_lock(&data->lock, K_FOREVER);
255+
err = spi_transceive_dt(&config->spi, &tx, &rx);
256+
k_mutex_unlock(&data->lock);
257+
258+
if (err < 0) {
259+
LOG_ERR("failed to read RDID (err %d)", err);
260+
return err;
261+
}
262+
263+
/* Validate Manufacturer ID and Product ID */
264+
if (id[0] != EEPROM_MB85RSXX_MAN_ID
265+
|| id[1] != EEPROM_MB85RSXX_CON_CODE
266+
|| (id[2] & EEPROM_MB85RSXX_PROD_MASK) != EEPROM_MB85RSXX_PROD_ID
267+
|| id[3] != EEPROM_MB85RSXX_PROD_ID2) {
268+
LOG_ERR("invalid device ID: %02X %02X %02X %02X", id[0], id[1], id[2], id[3]);
269+
return -EIO;
270+
}
271+
272+
LOG_INF("device ID read successfully: %02X %02X %02X %02X", id[0], id[1], id[2], id[3]);
273+
return 0;
274+
}
275+
276+
static int eeprom_mb85rsxx_init(const struct device *dev)
277+
{
278+
const struct eeprom_mb85rsxx_config *config = dev->config;
279+
struct eeprom_mb85rsxx_data *data = dev->data;
280+
int err;
281+
282+
k_mutex_init(&data->lock);
283+
284+
if (!spi_is_ready_dt(&config->spi)) {
285+
LOG_ERR("SPI bus not ready");
286+
return -EINVAL;
287+
}
288+
289+
err = eeprom_mb85rsxx_rdid(dev);
290+
if (err < 0) {
291+
LOG_ERR("Failed to initialize device, RDID check failed (err %d)", err);
292+
return err;
293+
}
294+
295+
return 0;
296+
}
297+
298+
static const struct eeprom_driver_api mb85rsxx_driver_api = {
299+
.read = &eeprom_mb85rsxx_read,
300+
.write = &eeprom_mb85rsxx_write,
301+
.size = &eeprom_mb85rsxx_size,
302+
};
303+
304+
#define MB85RSXX_INIT(inst) \
305+
static struct eeprom_mb85rsxx_data eeprom_mb85rsxx_data_##inst; \
306+
\
307+
static const struct eeprom_mb85rsxx_config eeprom_mb85rsxx_config_##inst = { \
308+
.spi = SPI_DT_SPEC_INST_GET( \
309+
inst, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0), \
310+
.size = DT_INST_PROP(inst, size), \
311+
.readonly = DT_INST_PROP(inst, read_only), \
312+
}; \
313+
\
314+
DEVICE_DT_INST_DEFINE(inst, eeprom_mb85rsxx_init, NULL, &eeprom_mb85rsxx_data_##inst, \
315+
&eeprom_mb85rsxx_config_##inst, POST_KERNEL, \
316+
CONFIG_EEPROM_INIT_PRIORITY, &mb85rsxx_driver_api);
317+
318+
DT_INST_FOREACH_STATUS_OKAY(MB85RSXX_INIT)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) 2024 Antmicro <www.antmicro.com>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Fujitsu MB85RSXX SPI FRAM
5+
6+
compatible: "fujitsu,mb85rsxx"
7+
8+
include: ["eeprom-base.yaml", spi-device.yaml]
9+
10+
properties:
11+
size:
12+
required: true
13+
description: Total FRAM size in bytes.

tests/drivers/build_all/eeprom/app.overlay

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
* (and be extended to test) real hardware.
1111
*/
1212

13+
#include <freq.h>
14+
#include <mem.h>
15+
1316
/ {
1417
test {
1518
#address-cells = <1>;
@@ -89,6 +92,13 @@
8992
wp-gpios = <&test_gpio 0 0>;
9093
/* read-only; */
9194
};
95+
96+
test_spi_mb85rsxx: mb85rsxx@0 {
97+
compatible = "fujitsu,mb85rsxx";
98+
reg = <0x0>;
99+
spi-max-frequency = <DT_FREQ_M(25)>;
100+
size = <DT_SIZE_K(128)>;
101+
};
92102
};
93103
};
94104
};

0 commit comments

Comments
 (0)