Skip to content

Commit ce36c11

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 3d5e9cd commit ce36c11

File tree

5 files changed

+289
-0
lines changed

5 files changed

+289
-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: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
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+
75+
if (offset + len > config->size) {
76+
LOG_ERR("Can not read more data than the device size");
77+
return -EINVAL;
78+
}
79+
80+
if (len == 0) {
81+
return 0;
82+
}
83+
84+
uint8_t read_op[4] = {FM25XXX_READ};
85+
86+
size_t addr_bytes = eeprom_fm25xxx_size_to_addr_bytes(config->size);
87+
size_t op_len = 1 + addr_bytes;
88+
89+
switch (addr_bytes) {
90+
case 1:
91+
read_op[1] = offset & 0xff;
92+
break;
93+
case 2:
94+
sys_put_be16(offset, &read_op[1]);
95+
break;
96+
case 3:
97+
sys_put_be24(offset, &read_op[1]);
98+
break;
99+
default:
100+
LOG_ERR("Invalid number of address bytes %u", addr_bytes);
101+
return -EINVAL;
102+
}
103+
104+
LOG_HEXDUMP_WRN(read_op, 4, "Read op");
105+
106+
const struct spi_buf tx_bufs[] = {{
107+
.buf = &read_op,
108+
.len = op_len,
109+
}};
110+
const struct spi_buf_set tx_buf_set = {
111+
.buffers = tx_bufs,
112+
.count = ARRAY_SIZE(tx_bufs),
113+
};
114+
115+
const struct spi_buf rx_bufs[2] = {
116+
{
117+
.buf = NULL,
118+
.len = op_len,
119+
},
120+
{
121+
.buf = data,
122+
.len = len,
123+
},
124+
};
125+
const struct spi_buf_set rx_buf_set = {
126+
.buffers = rx_bufs,
127+
.count = ARRAY_SIZE(rx_bufs),
128+
};
129+
130+
ret = spi_transceive_dt(&config->spi, &tx_buf_set, &rx_buf_set);
131+
132+
if (ret != 0) {
133+
LOG_ERR("Failed to read from FRAM");
134+
return ret;
135+
}
136+
137+
return 0;
138+
}
139+
140+
int eeprom_fm25xxx_write(const struct device *dev, off_t offset, const void *data, size_t len)
141+
{
142+
int ret;
143+
const struct fm25xxx_config *config = dev->config;
144+
struct fm25xxx_data *dev_data = dev->data;
145+
146+
if (config->readonly) {
147+
LOG_ERR("Can not write to a readonly device");
148+
return -EACCES;
149+
}
150+
151+
if (offset + len > config->size) {
152+
LOG_ERR("Can not write more data than the device size");
153+
return -EINVAL;
154+
}
155+
156+
if (len == 0) {
157+
return 0;
158+
}
159+
160+
uint8_t write_op[4] = {FM25XXX_WRITE};
161+
162+
size_t addr_bytes = eeprom_fm25xxx_size_to_addr_bytes(config->size);
163+
size_t op_len = 1 + addr_bytes;
164+
165+
switch (addr_bytes) {
166+
case 1:
167+
write_op[1] = offset & 0xff;
168+
break;
169+
case 2:
170+
sys_put_be16(offset, &write_op[1]);
171+
break;
172+
case 3:
173+
sys_put_be24(offset, &write_op[1]);
174+
break;
175+
default:
176+
LOG_ERR("Invalid number of address bytes %u", addr_bytes);
177+
return -EINVAL;
178+
}
179+
180+
const struct spi_buf tx_bufs[] = {
181+
{
182+
.buf = &write_op,
183+
.len = op_len,
184+
},
185+
{
186+
.buf = (void *)data,
187+
.len = len,
188+
},
189+
};
190+
const struct spi_buf_set tx_buf_set = {
191+
.buffers = tx_bufs,
192+
.count = ARRAY_SIZE(tx_bufs),
193+
};
194+
195+
k_mutex_lock(&dev_data->lock, K_FOREVER);
196+
197+
ret = eeprom_fm25xxx_set_enable_write(dev, true);
198+
if (ret != 0) {
199+
k_mutex_unlock(&dev_data->lock);
200+
LOG_ERR("Could not enable writes");
201+
return ret;
202+
}
203+
204+
ret = spi_write_dt(&config->spi, &tx_buf_set);
205+
if (ret != 0) {
206+
k_mutex_unlock(&dev_data->lock);
207+
LOG_ERR("Failed to write to FRAM");
208+
return ret;
209+
}
210+
211+
ret = eeprom_fm25xxx_set_enable_write(dev, false);
212+
if (ret != 0) {
213+
k_mutex_unlock(&dev_data->lock);
214+
LOG_ERR("Could not disable writes");
215+
return ret;
216+
}
217+
218+
k_mutex_unlock(&dev_data->lock);
219+
220+
return 0;
221+
}
222+
223+
size_t eeprom_fm25xxx_get_size(const struct device *dev)
224+
{
225+
const struct fm25xxx_config *config = dev->config;
226+
227+
return config->size;
228+
}
229+
230+
static int eeprom_fm25xxx_init(const struct device *dev)
231+
{
232+
const struct fm25xxx_config *config = dev->config;
233+
struct fm25xxx_data *data = dev->data;
234+
235+
k_mutex_init(&data->lock);
236+
237+
if (!spi_is_ready_dt(&config->spi)) {
238+
LOG_ERR("SPI bus not ready");
239+
return -ENODEV;
240+
}
241+
242+
return 0;
243+
}
244+
245+
static DEVICE_API(eeprom, eeprom_fm25xxx_api) = {
246+
.read = &eeprom_fm25xxx_read,
247+
.write = &eeprom_fm25xxx_write,
248+
.size = &eeprom_fm25xxx_get_size,
249+
};
250+
251+
#define FM25XXX_INIT(inst) \
252+
static struct fm25xxx_data fm25xxx_data_##inst; \
253+
static const struct fm25xxx_config fm25xxx_config_##inst = { \
254+
.spi = SPI_DT_SPEC_INST_GET(inst, SPI_OP_MODE_MASTER | SPI_WORD_SET(8), 0), \
255+
.size = DT_INST_PROP(inst, size), \
256+
.readonly = DT_INST_PROP(inst, read_only), \
257+
}; \
258+
\
259+
DEVICE_DT_INST_DEFINE(inst, eeprom_fm25xxx_init, NULL, &fm25xxx_data_##inst, \
260+
&fm25xxx_config_##inst, POST_KERNEL, CONFIG_EEPROM_INIT_PRIORITY, \
261+
&eeprom_fm25xxx_api);
262+
263+
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)