Skip to content

Commit 2472a8b

Browse files
author
Luna Pes
committed
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 <[email protected]>
1 parent 1cb0b6a commit 2472a8b

File tree

5 files changed

+292
-0
lines changed

5 files changed

+292
-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_AT2X_EMUL eeprom_at2x_emul.c)
2626
zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RCXX eeprom_mb85rcxx.c)
2727

2828
zephyr_library_sources_ifdef(CONFIG_EEPROM_MB85RSXX eeprom_mb85rsxx.c)
29+
30+
zephyr_library_sources_ifdef(CONFIG_EEPROM_FM25XXX eeprom_fm25xxx.c)

drivers/eeprom/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ source "drivers/eeprom/Kconfig.tmp11x"
100100
source "drivers/eeprom/Kconfig.xec"
101101
source "drivers/eeprom/Kconfig.mb85rcxx"
102102
source "drivers/eeprom/Kconfig.mb85rsxx"
103+
source "drivers/eeprom/Kconfig.fm25xxx"
103104

104105
config EEPROM_SIMULATOR
105106
bool "Simulated EEPROM driver"

drivers/eeprom/Kconfig.fm25xxx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2025 Luna Pes <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config EEPROM_FM25XXX
5+
bool "Infineon FM25XXX SPI FRAM"
6+
default y
7+
depends on DT_HAS_INFINEON_FM25XXX_ENABLED
8+
select SPI
9+
help
10+
Enable Infineon FM25XXX SPI FRAM

drivers/eeprom/eeprom_fm25xxx.c

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/*
2+
* Copyright (c) 2025 Luna Pes <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/* This file implements the Infineon AN304 SPI Guide for F-RAM */
8+
9+
#include "zephyr/devicetree.h"
10+
#include "zephyr/kernel.h"
11+
#include "zephyr/sys/byteorder.h"
12+
#include "zephyr/sys/util.h"
13+
#include <zephyr/device.h>
14+
#include <zephyr/drivers/eeprom.h>
15+
#include <zephyr/drivers/spi.h>
16+
#include <zephyr/logging/log.h>
17+
18+
#define DT_DRV_COMPAT infineon_fm25xxx
19+
20+
LOG_MODULE_REGISTER(fm25xxx, CONFIG_EEPROM_LOG_LEVEL);
21+
22+
/* Registers */
23+
#define FM25XXX_WREN 0x06
24+
#define FM25XXX_WRDI 0x04
25+
#define FM25XXX_RDSR 0x05
26+
#define FM25XXX_WRSR 0x01
27+
#define FM25XXX_READ 0x03
28+
#define FM25XXX_WRITE 0x02
29+
30+
struct fm25xxx_config {
31+
struct spi_dt_spec spi;
32+
size_t size;
33+
bool readonly;
34+
};
35+
36+
struct fm25xxx_data {
37+
struct k_mutex lock;
38+
};
39+
40+
static uint8_t eeprom_fm25xxx_size_to_addr_bytes(size_t size)
41+
{
42+
if (size <= 500) {
43+
return 1;
44+
} else if (size <= 64000) {
45+
return 2;
46+
} else {
47+
return 3;
48+
}
49+
}
50+
51+
static int eeprom_fm25xxx_set_enable_write(const struct device *dev, bool enable_writes)
52+
{
53+
const struct fm25xxx_config *config = dev->config;
54+
uint8_t op = enable_writes ? FM25XXX_WREN : FM25XXX_WRDI;
55+
56+
const struct spi_buf tx_bufs[] = {{
57+
.buf = &op,
58+
.len = sizeof(op),
59+
}};
60+
const struct spi_buf_set tx_buf_set = {
61+
.buffers = tx_bufs,
62+
.count = ARRAY_SIZE(tx_bufs),
63+
};
64+
65+
spi_write_dt(&config->spi, &tx_buf_set);
66+
67+
return 0;
68+
}
69+
70+
int eeprom_fm25xxx_read(const struct device *dev, off_t offset, void *data, size_t len)
71+
{
72+
int ret;
73+
const struct fm25xxx_config *config = dev->config;
74+
struct fm25xxx_data *dev_data = dev->data;
75+
76+
if (offset + len > config->size) {
77+
LOG_ERR("Can not read more data than the device size");
78+
return -EINVAL;
79+
}
80+
81+
if (len == 0) {
82+
return 0;
83+
}
84+
85+
uint8_t read_op[4] = {FM25XXX_READ};
86+
87+
size_t addr_bytes = eeprom_fm25xxx_size_to_addr_bytes(config->size);
88+
size_t op_len = 1 + addr_bytes;
89+
90+
switch (addr_bytes) {
91+
case 1:
92+
read_op[1] = offset & 0xff;
93+
break;
94+
case 2:
95+
sys_put_be16(offset, &read_op[1]);
96+
break;
97+
case 3:
98+
sys_put_be24(offset, &read_op[1]);
99+
break;
100+
default:
101+
LOG_ERR("Invalid number of address bytes %u", addr_bytes);
102+
break;
103+
}
104+
105+
LOG_HEXDUMP_WRN(read_op, 4, "Read op");
106+
107+
const struct spi_buf tx_bufs[] = {{
108+
.buf = &read_op,
109+
.len = op_len,
110+
}};
111+
const struct spi_buf_set tx_buf_set = {
112+
.buffers = tx_bufs,
113+
.count = ARRAY_SIZE(tx_bufs),
114+
};
115+
116+
const struct spi_buf rx_bufs[2] = {
117+
{
118+
.buf = NULL,
119+
.len = op_len,
120+
},
121+
{
122+
.buf = data,
123+
.len = len,
124+
},
125+
};
126+
const struct spi_buf_set rx_buf_set = {
127+
.buffers = rx_bufs,
128+
.count = ARRAY_SIZE(rx_bufs),
129+
};
130+
131+
k_mutex_lock(&dev_data->lock, K_FOREVER);
132+
133+
ret = spi_transceive_dt(&config->spi, &tx_buf_set, &rx_buf_set);
134+
135+
k_mutex_unlock(&dev_data->lock);
136+
137+
if (ret != 0) {
138+
LOG_ERR("Failed to read from FRAM");
139+
return ret;
140+
}
141+
142+
return 0;
143+
}
144+
145+
int eeprom_fm25xxx_write(const struct device *dev, off_t offset, const void *data, size_t len)
146+
{
147+
int ret;
148+
const struct fm25xxx_config *config = dev->config;
149+
struct fm25xxx_data *dev_data = dev->data;
150+
151+
if (config->readonly) {
152+
LOG_ERR("Can not write to a readonly device");
153+
return -EACCES;
154+
}
155+
156+
if (offset + len > config->size) {
157+
LOG_ERR("Can not write more data than the device size");
158+
return -EINVAL;
159+
}
160+
161+
if (len == 0) {
162+
return 0;
163+
}
164+
165+
uint8_t write_op[4] = {FM25XXX_WRITE};
166+
167+
size_t addr_bytes = eeprom_fm25xxx_size_to_addr_bytes(config->size);
168+
size_t op_len = 1 + addr_bytes;
169+
170+
switch (addr_bytes) {
171+
case 1:
172+
write_op[1] = offset & 0xff;
173+
break;
174+
case 2:
175+
sys_put_be16(offset, &write_op[1]);
176+
break;
177+
case 3:
178+
sys_put_be24(offset, &write_op[1]);
179+
break;
180+
default:
181+
LOG_ERR("Invalid number of address bytes %u", addr_bytes);
182+
break;
183+
}
184+
185+
const struct spi_buf tx_bufs[] = {
186+
{
187+
.buf = &write_op,
188+
.len = op_len,
189+
},
190+
{
191+
.buf = (void *)data,
192+
.len = len,
193+
},
194+
};
195+
const struct spi_buf_set tx_buf_set = {
196+
.buffers = tx_bufs,
197+
.count = ARRAY_SIZE(tx_bufs),
198+
};
199+
200+
k_mutex_lock(&dev_data->lock, K_FOREVER);
201+
202+
ret = eeprom_fm25xxx_set_enable_write(dev, true);
203+
if (ret != 0) {
204+
k_mutex_unlock(&dev_data->lock);
205+
LOG_ERR("Could not enable writes");
206+
return ret;
207+
}
208+
209+
ret = spi_write_dt(&config->spi, &tx_buf_set);
210+
if (ret != 0) {
211+
k_mutex_unlock(&dev_data->lock);
212+
LOG_ERR("Failed to write to FRAM");
213+
return ret;
214+
}
215+
216+
ret = eeprom_fm25xxx_set_enable_write(dev, false);
217+
if (ret != 0) {
218+
k_mutex_unlock(&dev_data->lock);
219+
LOG_ERR("Could not disable writes");
220+
return ret;
221+
}
222+
223+
return 0;
224+
}
225+
226+
size_t eeprom_fm25xxx_get_size(const struct device *dev)
227+
{
228+
const struct fm25xxx_config *config = dev->config;
229+
230+
return config->size;
231+
}
232+
233+
static int eeprom_fm25xxx_init(const struct device *dev)
234+
{
235+
const struct fm25xxx_config *config = dev->config;
236+
struct fm25xxx_data *data = dev->data;
237+
238+
k_mutex_init(&data->lock);
239+
240+
if (!spi_is_ready_dt(&config->spi)) {
241+
LOG_ERR("SPI bus not ready");
242+
return -EINVAL;
243+
}
244+
245+
return 0;
246+
}
247+
248+
static DEVICE_API(eeprom, eeprom_fm25xxx_api) = {
249+
.read = &eeprom_fm25xxx_read,
250+
.write = &eeprom_fm25xxx_write,
251+
.size = &eeprom_fm25xxx_get_size,
252+
};
253+
254+
#define FM25XXX_INIT(inst) \
255+
static struct fm25xxx_data fm25xxx_data_##inst; \
256+
static const struct fm25xxx_config fm25xxx_config_##inst = { \
257+
.spi = SPI_DT_SPEC_INST_GET(inst, SPI_OP_MODE_MASTER | SPI_WORD_SET(8), 0), \
258+
.size = DT_INST_PROP(inst, size), \
259+
.readonly = DT_INST_PROP(inst, read_only), \
260+
}; \
261+
\
262+
DEVICE_DT_INST_DEFINE(inst, eeprom_fm25xxx_init, NULL, &fm25xxx_data_##inst, \
263+
&fm25xxx_config_##inst, POST_KERNEL, CONFIG_EEPROM_INIT_PRIORITY, \
264+
&eeprom_fm25xxx_api);
265+
266+
DT_INST_FOREACH_STATUS_OKAY(FM25XXX_INIT)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) 2025 Luna Pes <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Infineon FM25XXX SPI FRAM
5+
6+
compatible: "infineon,fm25xxx"
7+
8+
include: ["eeprom-base.yaml", spi-device.yaml]
9+
10+
properties:
11+
size:
12+
required: true
13+
description: Total FRAM size in bytes.

0 commit comments

Comments
 (0)