Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions drivers/eeprom/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ 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)

zephyr_library_sources_ifdef(CONFIG_EEPROM_FM25XXX eeprom_fm25xxx.c)
1 change: 1 addition & 0 deletions drivers/eeprom/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ source "drivers/eeprom/Kconfig.tmp11x"
source "drivers/eeprom/Kconfig.xec"
source "drivers/eeprom/Kconfig.mb85rcxx"
source "drivers/eeprom/Kconfig.mb85rsxx"
source "drivers/eeprom/Kconfig.fm25xxx"

config EEPROM_SIMULATOR
bool "Simulated EEPROM driver"
Expand Down
10 changes: 10 additions & 0 deletions drivers/eeprom/Kconfig.fm25xxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2025 Luna Pes <[email protected]>
# SPDX-License-Identifier: Apache-2.0

config EEPROM_FM25XXX
bool "Infineon FM25XXX SPI FRAM"
default y
depends on DT_HAS_INFINEON_FM25XXX_ENABLED
select SPI
help
Enable Infineon FM25XXX SPI FRAM
263 changes: 263 additions & 0 deletions drivers/eeprom/eeprom_fm25xxx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
/*
* Copyright (c) 2025 Luna Pes <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

/* This file implements the Infineon AN304 SPI Guide for F-RAM */

#include "zephyr/devicetree.h"
#include "zephyr/kernel.h"
#include "zephyr/sys/byteorder.h"
#include "zephyr/sys/util.h"
#include <zephyr/device.h>
#include <zephyr/drivers/eeprom.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/logging/log.h>

#define DT_DRV_COMPAT infineon_fm25xxx

LOG_MODULE_REGISTER(fm25xxx, CONFIG_EEPROM_LOG_LEVEL);

/* Registers */
#define FM25XXX_WREN 0x06
#define FM25XXX_WRDI 0x04
#define FM25XXX_RDSR 0x05
#define FM25XXX_WRSR 0x01
#define FM25XXX_READ 0x03
#define FM25XXX_WRITE 0x02

struct fm25xxx_config {
struct spi_dt_spec spi;
size_t size;
bool readonly;
};

struct fm25xxx_data {
struct k_mutex lock;
};

static uint8_t eeprom_fm25xxx_size_to_addr_bytes(size_t size)
{
if (size <= 500) {
return 1;
} else if (size <= 64000) {
return 2;
} else {
return 3;
}
}

static int eeprom_fm25xxx_set_enable_write(const struct device *dev, bool enable_writes)
{
const struct fm25xxx_config *config = dev->config;
uint8_t op = enable_writes ? FM25XXX_WREN : FM25XXX_WRDI;

const struct spi_buf tx_bufs[] = {{
.buf = &op,
.len = sizeof(op),
}};
const struct spi_buf_set tx_buf_set = {
.buffers = tx_bufs,
.count = ARRAY_SIZE(tx_bufs),
};

spi_write_dt(&config->spi, &tx_buf_set);

return 0;
}

int eeprom_fm25xxx_read(const struct device *dev, off_t offset, void *data, size_t len)
{
int ret;
const struct fm25xxx_config *config = dev->config;

if (offset + len > config->size) {
LOG_ERR("Can not read more data than the device size");
return -EINVAL;
}

if (len == 0) {
return 0;
}

uint8_t read_op[4] = {FM25XXX_READ};

size_t addr_bytes = eeprom_fm25xxx_size_to_addr_bytes(config->size);
size_t op_len = 1 + addr_bytes;

switch (addr_bytes) {
case 1:
read_op[1] = offset & 0xff;
break;
case 2:
sys_put_be16(offset, &read_op[1]);
break;
case 3:
sys_put_be24(offset, &read_op[1]);
break;
default:
LOG_ERR("Invalid number of address bytes %u", addr_bytes);
return -EINVAL;
}

LOG_HEXDUMP_WRN(read_op, 4, "Read op");

const struct spi_buf tx_bufs[] = {{
.buf = &read_op,
.len = op_len,
}};
const struct spi_buf_set tx_buf_set = {
.buffers = tx_bufs,
.count = ARRAY_SIZE(tx_bufs),
};

const struct spi_buf rx_bufs[2] = {
{
.buf = NULL,
.len = op_len,
},
{
.buf = data,
.len = len,
},
};
const struct spi_buf_set rx_buf_set = {
.buffers = rx_bufs,
.count = ARRAY_SIZE(rx_bufs),
};

ret = spi_transceive_dt(&config->spi, &tx_buf_set, &rx_buf_set);

if (ret != 0) {
LOG_ERR("Failed to read from FRAM");
return ret;
}

return 0;
}

int eeprom_fm25xxx_write(const struct device *dev, off_t offset, const void *data, size_t len)
{
int ret;
const struct fm25xxx_config *config = dev->config;
struct fm25xxx_data *dev_data = dev->data;

if (config->readonly) {
LOG_ERR("Can not write to a readonly device");
return -EACCES;
}

if (offset + len > config->size) {
LOG_ERR("Can not write more data than the device size");
return -EINVAL;
}

if (len == 0) {
return 0;
}

uint8_t write_op[4] = {FM25XXX_WRITE};

size_t addr_bytes = eeprom_fm25xxx_size_to_addr_bytes(config->size);
size_t op_len = 1 + addr_bytes;

switch (addr_bytes) {
case 1:
write_op[1] = offset & 0xff;
break;
case 2:
sys_put_be16(offset, &write_op[1]);
break;
case 3:
sys_put_be24(offset, &write_op[1]);
break;
default:
LOG_ERR("Invalid number of address bytes %u", addr_bytes);
return -EINVAL;
}

const struct spi_buf tx_bufs[] = {
{
.buf = &write_op,
.len = op_len,
},
{
.buf = (void *)data,
.len = len,
},
};
const struct spi_buf_set tx_buf_set = {
.buffers = tx_bufs,
.count = ARRAY_SIZE(tx_bufs),
};

k_mutex_lock(&dev_data->lock, K_FOREVER);

ret = eeprom_fm25xxx_set_enable_write(dev, true);
if (ret != 0) {
k_mutex_unlock(&dev_data->lock);
LOG_ERR("Could not enable writes");
return ret;
}

ret = spi_write_dt(&config->spi, &tx_buf_set);
if (ret != 0) {
k_mutex_unlock(&dev_data->lock);
LOG_ERR("Failed to write to FRAM");
return ret;
}

ret = eeprom_fm25xxx_set_enable_write(dev, false);
if (ret != 0) {
k_mutex_unlock(&dev_data->lock);
LOG_ERR("Could not disable writes");
return ret;
}

k_mutex_unlock(&dev_data->lock);

return 0;
}

size_t eeprom_fm25xxx_get_size(const struct device *dev)
{
const struct fm25xxx_config *config = dev->config;

return config->size;
}

static int eeprom_fm25xxx_init(const struct device *dev)
{
const struct fm25xxx_config *config = dev->config;
struct fm25xxx_data *data = dev->data;

k_mutex_init(&data->lock);

if (!spi_is_ready_dt(&config->spi)) {
LOG_ERR("SPI bus not ready");
return -ENODEV;
}

return 0;
}

static DEVICE_API(eeprom, eeprom_fm25xxx_api) = {
.read = &eeprom_fm25xxx_read,
.write = &eeprom_fm25xxx_write,
.size = &eeprom_fm25xxx_get_size,
};

#define FM25XXX_INIT(inst) \
static struct fm25xxx_data fm25xxx_data_##inst; \
static const struct fm25xxx_config fm25xxx_config_##inst = { \
.spi = SPI_DT_SPEC_INST_GET(inst, SPI_OP_MODE_MASTER | SPI_WORD_SET(8)), \
.size = DT_INST_PROP(inst, size), \
.readonly = DT_INST_PROP(inst, read_only), \
}; \
\
DEVICE_DT_INST_DEFINE(inst, eeprom_fm25xxx_init, NULL, &fm25xxx_data_##inst, \
&fm25xxx_config_##inst, POST_KERNEL, CONFIG_EEPROM_INIT_PRIORITY, \
&eeprom_fm25xxx_api);

DT_INST_FOREACH_STATUS_OKAY(FM25XXX_INIT)
13 changes: 13 additions & 0 deletions dts/bindings/mtd/infineon,fm25xxx.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright (c) 2025 Luna Pes <[email protected]>
# SPDX-License-Identifier: Apache-2.0

description: Infineon FM25XXX SPI FRAM

compatible: "infineon,fm25xxx"

include: ["eeprom-base.yaml", spi-device.yaml]

properties:
size:
required: true
description: Total FRAM size in bytes.
7 changes: 7 additions & 0 deletions tests/drivers/build_all/eeprom/app.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@
spi-max-frequency = <DT_FREQ_M(25)>;
size = <DT_SIZE_K(128)>;
};

test_spi_fm25xxx: fm25xxx@2 {
compatible = "infineon,fm25xxx";
reg = <0x2>;
spi-max-frequency = <DT_FREQ_M(20)>;
size = <8000>;
};
};
};
};