zephyr/drivers/eeprom/eeprom_emulator.c
Maureen Helm 32b4950c61 drivers: eeprom: Refactor drivers to use shared init priority
Refactors all of the EEPROM drivers to use a shared driver class
initialization priority configuration, CONFIG_EEPROM_INIT_PRIORITY, to
allow configuring EEPROM drivers separately from other devices. This is
similar to other driver classes like I2C and SPI.

The default is set to CONFIG_KERNEL_INIT_PRIORITY_DEVICE to preserve the
existing default initialization priority for most drivers. The
exceptions are at2x and emul drivers which have dependencies on SPI,
I2C, or flash drivers and must therefore initialize later than the
default device priority.

Signed-off-by: Maureen Helm <maureen.helm@intel.com>
2021-11-04 07:33:01 -04:00

815 lines
21 KiB
C

/*
* Copyright (c) 2020 Laczen
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This driver emulates an EEPROM device in flash.
*
* The emulation represents the EEPROM in flash as a region that is a direct
* map of the EEPROM data (EEPROM data) followed by a region where changes to
* the EEPROM data (EEPROM changes) are stored. The combination of EEPROM data
* and EEPROM changes form a EEPROM page (see drawing below). Changes to EEPROM
* data are written as address-data combinations. The size of such a combination
* is determined by the flash write block size and the size of the EEPROM
* (required address space), with a minimum of 4 byte.
*
* When there is no more space to store changes a new EEPROM page is taken into
* use. This copies the existing data to the EEPROM data area of the new page.
* During this copying the write that is performed is applied at the same time.
* The old page is then invalidated.
*
* The EEPROM page needs to be a multiple of a flash page size. Multiple EEPROM
* pages are also supported and increases the number of writes that can be
* performed.
*
* The representation of the EEPROM on flash is shown in the next graph.
*
* |-----------------------------------------------------------------------|
* ||----------------------| |----------------------| |-------------------||
* || EEPROM data | | | |-Flash page--------||
* || | | | |
* || size = EEPROM size | | | |
* ||----------------------| |----------------------| ... |
* || EEPROM changes: | | | |
* || (address, new data) | | | |
* || | | | |
* || XX| | XX| |
* ||--EEPROM page 0-------| |--EEPROM page 1-------| |
* |------------------------------------------------------------Partition--|
* XX: page validity marker: all 0x00: page invalid
*
* Internally the address of an EEPROM byte is represented by a uint32_t (this
* should be sufficient in all cases). In case the EEPROM size is smaller than
* 64kB only a uint16_t is used to store changes. In this case the change stored
* for a 4 byte flash write block size are a combination of 2 byte address and
* 2 byte data.
*
* The EEPROM size, pagesize and the flash partition used for the EEPROM are
* defined in the dts. The flash partition should allow at least two EEPROM
* pages.
*
*/
#define DT_DRV_COMPAT zephyr_emu_eeprom
#define EEPROM_EMU_VERSION 0
#define EEPROM_EMU_MAGIC 0x45454d55 /* EEMU in hex */
#include <drivers/eeprom.h>
#include <drivers/flash.h>
#include <zephyr.h>
#define LOG_LEVEL CONFIG_EEPROM_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(eeprom_emulator);
#define DEV_CONFIG(dev) ((dev)->config)
#define DEV_DATA(dev) ((dev)->data)
struct eeprom_emu_config {
/* EEPROM size */
size_t size;
/* EEPROM is read-only */
bool readonly;
/* Page size used to emulate the EEPROM, contains one area of EEPROM
* size and a area to store changes.
*/
size_t page_size;
/* Offset of the flash partition used to emulate the EEPROM */
off_t flash_offset;
/* Size of the flash partition to emulate the EEPROM */
size_t flash_size;
/* Delay the erase of EEPROM pages until the complete partition is used.
*/
bool partitionerase;
/* Size of a change block */
uint8_t flash_cbs;
uint8_t *rambuf;
/* Device of the flash partition used to emulate the EEPROM */
const struct device *flash_dev;
};
struct eeprom_emu_data {
/* Offset in current (EEPROM) page where next change is written */
off_t write_offset;
/* Offset of the current (EEPROM) page */
off_t page_offset;
struct k_mutex lock;
};
/* read/write context */
struct eeprom_emu_ctx {
const void *data; /* pointer to data */
const size_t len; /* data length */
const off_t address; /* eeprom address */
size_t rlen; /* data remaining (unprocessed) length */
};
/*
* basic flash read, only used with offset aligned to flash write block size
*/
static inline int eeprom_emu_flash_read(const struct device *dev, off_t offset,
uint8_t *blk, size_t len)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
return flash_read(dev_config->flash_dev, dev_config->flash_offset +
offset, blk, len);
}
/*
* basic flash write, only used with offset aligned to flash write block size
*/
static inline int eeprom_emu_flash_write(const struct device *dev, off_t offset,
const uint8_t *blk, size_t len)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
int rc;
rc = flash_write(dev_config->flash_dev, dev_config->flash_offset +
offset, blk, len);
return rc;
}
/*
* basic flash erase, only used with offset aligned to flash page and len a
* multiple of the flash page size
*/
static inline int eeprom_emu_flash_erase(const struct device *dev, off_t offset,
size_t len)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
int rc;
rc = flash_erase(dev_config->flash_dev, dev_config->flash_offset +
offset, len);
return rc;
}
/*
* eeprom_emu_page_invalidate: invalidate a page by writing all zeros at the end
*/
static int eeprom_emu_page_invalidate(const struct device *dev, off_t offset)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
uint8_t buf[dev_config->flash_cbs];
LOG_DBG("Invalidating page at [0x%tx]", (ptrdiff_t)offset);
memset(buf, 0x00, sizeof(buf));
offset += (dev_config->page_size - sizeof(buf));
return eeprom_emu_flash_write(dev, offset, buf, sizeof(buf));
}
/*
* eeprom_emu_get_address: read the address from a change block
*/
static uint32_t eeprom_emu_get_address(const struct device *dev,
const uint8_t *blk)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
uint32_t address = 0U;
blk += dev_config->flash_cbs / 2;
for (int i = 0; i < sizeof(address); i++) {
if (2 * i == dev_config->flash_cbs) {
break;
}
address += ((uint32_t)(*blk) << (8 * i));
blk++;
}
return address;
}
/*
* eeprom_emu_set_change: create change blocks from data in blk and address
*/
static void eeprom_emu_set_change(const struct device *dev,
const uint32_t address, const uint8_t *data,
uint8_t *blk)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
for (int i = 0; i < (dev_config->flash_cbs / 2); i++) {
(*blk++) = (*data++);
}
for (int i = 0; i < (dev_config->flash_cbs / 2); i++) {
if (i < sizeof(address)) {
(*blk++) = (uint8_t)(((address >> (8 * i)) & 0xff));
} else {
(*blk++) = 0xff;
}
}
}
/*
* eeprom_emu_is_word_used: check if word is not empty
*/
static int eeprom_emu_is_word_used(const struct device *dev, const uint8_t *blk)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
for (int i = 0; i < dev_config->flash_cbs; i++) {
if ((*blk++) != 0xff) {
return 1;
}
}
return 0;
}
/*
* eeprom_emu_word_read: read basic word (cbs byte of data) item from
* address directly from flash.
*/
static int eeprom_emu_word_read(const struct device *dev, off_t address,
uint8_t *data)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
const struct eeprom_emu_data *dev_data = DEV_DATA(dev);
uint8_t buf[dev_config->flash_cbs];
off_t direct_address;
int rc;
direct_address = dev_data->page_offset + address;
/* Direct flash read */
rc = eeprom_emu_flash_read(dev, direct_address, data, sizeof(buf));
if (rc) {
return rc;
}
/* Process changes written to flash */
off_t offset, ch_address;
bool mc1 = false, mc2 = false;
offset = dev_data->write_offset;
while (((!mc1) || (!mc2)) && (offset > dev_config->size)) {
offset -= sizeof(buf);
/* read the change */
rc = eeprom_emu_flash_read(dev, dev_data->page_offset + offset,
buf, sizeof(buf));
if (rc) {
return rc;
}
/* get the address from a change block */
ch_address = eeprom_emu_get_address(dev, buf);
if ((!mc1) && (ch_address == address)) {
memcpy(data, buf, sizeof(buf)/2);
mc1 = true;
}
if ((!mc2) && (ch_address == (address + sizeof(buf)/2))) {
memcpy(data + sizeof(buf)/2, buf, sizeof(buf)/2);
mc2 = true;
}
}
return rc;
}
/* Update data specified in ctx from flash */
static int eeprom_emu_flash_get(const struct device *dev,
struct eeprom_emu_ctx *ctx)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
off_t address = ctx->address + ctx->len - ctx->rlen;
uint8_t *data8 = (uint8_t *)(ctx->data);
uint8_t buf[dev_config->flash_cbs];
const off_t addr_jmp = address & (sizeof(buf) - 1);
size_t len;
int rc;
data8 += (ctx->len - ctx->rlen);
len = MIN((sizeof(buf) - addr_jmp), ctx->rlen);
rc = eeprom_emu_word_read(dev, address - addr_jmp, buf);
if (rc) {
return rc;
}
memcpy(data8, buf + addr_jmp, len);
ctx->rlen -= len;
return rc;
}
/*
* eeprom_emu_compactor: start a new EEPROM page and copy existing data to the
* new page. During copy update the data with present write data. Invalidate
* the old page.
*/
static int eeprom_emu_compactor(const struct device *dev,
struct eeprom_emu_ctx *ctx)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
struct eeprom_emu_data *dev_data = DEV_DATA(dev);
off_t next_page_offset;
int rc = 0;
LOG_DBG("Compactor called for page at [0x%tx]",
(ptrdiff_t)dev_data->page_offset);
next_page_offset = dev_data->page_offset + dev_config->page_size;
if (next_page_offset >= dev_config->flash_size) {
next_page_offset = 0;
}
if (!dev_config->partitionerase) {
/* erase the new page */
rc = eeprom_emu_flash_erase(dev, next_page_offset,
dev_config->page_size);
} else if (next_page_offset == 0) {
/* erase the entire partition */
rc = eeprom_emu_flash_erase(dev, next_page_offset,
dev_config->flash_size);
} else {
rc = 0;
}
if (rc) {
return rc;
}
if (dev_config->rambuf && (ctx != NULL)) {
rc = eeprom_emu_flash_write(dev, next_page_offset,
dev_config->rambuf,
dev_config->size);
if (rc) {
return rc;
}
ctx->rlen = 0;
} else {
off_t rd_offset = 0;
uint8_t buf[dev_config->flash_cbs];
/* reset the context if available */
if (ctx != NULL) {
ctx->rlen = ctx->len;
}
/* copy existing data */
while (rd_offset < dev_config->size) {
rc = eeprom_emu_word_read(dev, rd_offset, buf);
if (rc) {
return rc;
}
if ((ctx != NULL) && (ctx->len) &&
(rd_offset > (ctx->address - sizeof(buf)))) {
/* overwrite buf data with context data */
uint8_t *data8 = (uint8_t *)(ctx->data);
off_t address, addr_jmp;
size_t len;
address = ctx->address + ctx->len - ctx->rlen;
addr_jmp = address & (sizeof(buf) - 1);
len = MIN((sizeof(buf) - addr_jmp), ctx->rlen);
data8 += (ctx->len - ctx->rlen);
memcpy(buf + addr_jmp, data8, len);
ctx->rlen -= len;
}
if (eeprom_emu_is_word_used(dev, buf)) {
rc = eeprom_emu_flash_write(dev,
next_page_offset +
rd_offset, buf,
sizeof(buf));
if (rc) {
return rc;
}
}
rd_offset += sizeof(buf);
}
}
if ((dev_config->partitionerase) && (next_page_offset == 0)) {
/* no need to invalidate previous page as it has been deleted */
rc = 0;
} else {
/* invalidate the old page */
rc = eeprom_emu_page_invalidate(dev, dev_data->page_offset);
}
if (!rc) {
dev_data->write_offset = dev_config->size;
dev_data->page_offset = next_page_offset;
}
return rc;
}
/*
* eeprom_emu_word_write: write basic word (cbs bytes of data) item to address,
*/
static int eeprom_emu_word_write(const struct device *dev, off_t address,
const uint8_t *data,
struct eeprom_emu_ctx *ctx)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
struct eeprom_emu_data *dev_data = DEV_DATA(dev);
uint8_t buf[dev_config->flash_cbs], tmp[dev_config->flash_cbs];
off_t direct_address, wraddr;
int rc;
direct_address = dev_data->page_offset + address;
rc = eeprom_emu_flash_read(dev, direct_address, buf, sizeof(buf));
if (rc) {
return rc;
}
if (!eeprom_emu_is_word_used(dev, buf)) {
if (eeprom_emu_is_word_used(dev, data)) {
rc = eeprom_emu_flash_write(dev, direct_address, data,
sizeof(buf));
}
return rc;
}
rc = eeprom_emu_word_read(dev, address, buf);
if (rc) {
return rc;
}
if (!memcmp(buf, data, sizeof(buf))) {
/* data has not changed */
return rc;
}
wraddr = address;
/* store change */
for (uint8_t i = 0; i < 2; i++) {
if (memcmp(&buf[i*sizeof(buf)/2], data, sizeof(buf)/2)) {
eeprom_emu_set_change(dev, wraddr, data, tmp);
rc = eeprom_emu_flash_write(dev, dev_data->page_offset +
dev_data->write_offset, tmp,
sizeof(buf));
if (rc) {
return rc;
}
dev_data->write_offset += sizeof(buf);
if ((dev_data->write_offset + sizeof(buf)) >=
dev_config->page_size) {
rc = eeprom_emu_compactor(dev, ctx);
return rc;
}
}
data += sizeof(buf)/2;
wraddr += sizeof(buf)/2;
}
return rc;
}
/* Update flash with data specified in ctx */
static int eeprom_emu_flash_set(const struct device *dev,
struct eeprom_emu_ctx *ctx)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
off_t address = ctx->address + ctx->len - ctx->rlen;
uint8_t *data8 = (uint8_t *)(ctx->data);
uint8_t buf[dev_config->flash_cbs];
const off_t addr_jmp = address & (sizeof(buf) - 1);
size_t len;
int rc;
data8 += (ctx->len - ctx->rlen);
len = MIN((sizeof(buf) - addr_jmp), ctx->rlen);
rc = eeprom_emu_word_read(dev, address - addr_jmp, buf);
if (rc) {
return rc;
}
memcpy(buf + addr_jmp, data8, len);
rc = eeprom_emu_word_write(dev, address - addr_jmp, buf, ctx);
if (rc) {
return rc;
}
if (ctx->rlen) {
ctx->rlen -= len;
}
return rc;
}
static int eeprom_emu_range_is_valid(const struct device *dev, off_t address,
size_t len)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
if ((address + len) <= dev_config->size) {
return 1;
}
return 0;
}
static int eeprom_emu_read(const struct device *dev, off_t address, void *data,
size_t len)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
struct eeprom_emu_data *dev_data = DEV_DATA(dev);
struct eeprom_emu_ctx ctx = {
.data = data,
.len = len,
.address = address,
.rlen = len,
};
int rc = 0;
/* Nothing to do */
if (!len) {
return 0;
}
/* Error checking */
if ((!data) || (!eeprom_emu_range_is_valid(dev, address, len))) {
return -EINVAL;
}
if (!device_is_ready(dev_config->flash_dev)) {
LOG_ERR("flash device is not ready");
return -EIO;
}
/* Handle normal case */
LOG_DBG("EEPROM read at [0x%tx] length[%d]", (ptrdiff_t)address, len);
k_mutex_lock(&dev_data->lock, K_FOREVER);
/* read from rambuffer if possible */
if (dev_config->rambuf) {
memcpy(data, dev_config->rambuf + address, len);
} else {
/* read from flash if no rambuffer */
while (ctx.rlen) {
rc = eeprom_emu_flash_get(dev, &ctx);
if (rc) {
break;
}
}
}
k_mutex_unlock(&dev_data->lock);
return rc;
}
static int eeprom_emu_write(const struct device *dev, off_t address,
const void *data, size_t len)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
struct eeprom_emu_data *dev_data = DEV_DATA(dev);
struct eeprom_emu_ctx ctx = {
.data = data,
.len = len,
.address = address,
.rlen = len,
};
int rc = 0;
/* Nothing to do */
if (!len) {
return 0;
}
/* Error checking */
if ((!data) || (!eeprom_emu_range_is_valid(dev, address, len))) {
return -EINVAL;
}
if (dev_config->readonly) {
LOG_ERR("attempt to write to read-only device");
return -EACCES;
}
if (!device_is_ready(dev_config->flash_dev)) {
LOG_ERR("flash device is not ready");
return -EIO;
}
/* Handle normal case */
LOG_DBG("EEPROM write at [0x%tx] length[%d]", (ptrdiff_t)address, len);
k_mutex_lock(&dev_data->lock, K_FOREVER);
/* first update the rambuffer */
if (dev_config->rambuf) {
memcpy(dev_config->rambuf + address, data, len);
}
/* second update the flash */
while (ctx.rlen) {
rc = eeprom_emu_flash_set(dev, &ctx);
if (rc) {
break;
}
}
k_mutex_unlock(&dev_data->lock);
return rc;
}
static size_t eeprom_emu_size(const struct device *dev)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
return dev_config->size;
}
static int eeprom_emu_init(const struct device *dev)
{
const struct eeprom_emu_config *dev_config = DEV_CONFIG(dev);
struct eeprom_emu_data *dev_data = DEV_DATA(dev);
off_t offset;
uint8_t buf[dev_config->flash_cbs];
int rc;
k_mutex_init(&dev_data->lock);
if (!device_is_ready(dev_config->flash_dev)) {
__ASSERT(0, "Could not get flash device binding");
return -ENODEV;
}
/* Find the page offset */
dev_data->page_offset = 0U;
dev_data->write_offset = dev_config->page_size - sizeof(buf);
while (dev_data->page_offset < dev_config->flash_size) {
offset = dev_data->page_offset + dev_data->write_offset;
rc = eeprom_emu_flash_read(dev, offset, buf, sizeof(buf));
if (rc) {
return rc;
}
if (!eeprom_emu_is_word_used(dev, buf)) {
break;
}
dev_data->page_offset += dev_config->page_size;
}
if (dev_data->page_offset == dev_config->flash_size) {
__ASSERT(0, "All pages are invalid, is this a EEPROM area?");
return -EINVAL;
}
dev_data->write_offset = dev_config->size;
/* Update the write offset */
while ((dev_data->write_offset + sizeof(buf)) < dev_config->page_size) {
offset = dev_data->page_offset + dev_data->write_offset;
rc = eeprom_emu_flash_read(dev, offset, buf, sizeof(buf));
if (rc) {
return rc;
}
if (!eeprom_emu_is_word_used(dev, buf)) {
break;
}
dev_data->write_offset += sizeof(buf);
}
/* dev_data->write_offset reaches last possible location, compaction
* might have been interrupted: call eeprom_emu_compactor again, but
* only in case we are using a write-enabled eeprom
*/
if ((!dev_config->readonly) &&
((dev_data->write_offset + sizeof(buf)) >= dev_config->page_size)) {
rc = eeprom_emu_compactor(dev, NULL);
if (rc) {
return rc;
}
}
/* Fill the ram buffer if enabled */
if (dev_config->rambuf) {
offset = 0;
while (offset < dev_config->size) {
rc = eeprom_emu_word_read(dev, offset, buf);
if (rc) {
return rc;
}
memcpy(dev_config->rambuf + offset, buf, sizeof(buf));
offset += sizeof(buf);
}
}
return rc;
}
static const struct eeprom_driver_api eeprom_emu_api = {
.read = eeprom_emu_read,
.write = eeprom_emu_write,
.size = eeprom_emu_size,
};
#define EEPROM_PARTITION(n) DT_PHANDLE_BY_IDX(DT_DRV_INST(n), partition, 0)
#define PART_WBS(part) \
DT_PROP(COND_CODE_1(DT_NODE_HAS_COMPAT(DT_GPARENT(part), soc_nv_flash),\
(DT_GPARENT(part)), (DT_PARENT(part))), write_block_size)
#define PART_CBS(part, size) (PART_WBS(part) < 4) ? \
((size > (2^16)) ? 8 : 4) : PART_WBS(part)
#define PART_DEV_ID(part) \
COND_CODE_1(DT_NODE_HAS_COMPAT(DT_GPARENT(part), soc_nv_flash), \
(DT_PARENT(DT_GPARENT(part))), (DT_GPARENT(part)))
#define PART_DEV(part) \
DEVICE_DT_GET(PART_DEV_ID(part))
#define PART_DEV_NAME(part) \
DT_PROP(PART_DEV_ID(part), label)
#define RECALC_SIZE(size, cbs) \
(size % cbs) ? ((size + cbs - 1) & ~(cbs - 1)) : size
#define ASSERT_SIZE_PAGESIZE_VALID(size, pagesize, readonly) \
BUILD_ASSERT(readonly ? (size <= pagesize) : (4*size <= 3*pagesize), \
"EEPROM size to big for pagesize")
#define ASSERT_PAGESIZE_PARTSIZE_VALID(pagesize, partsize) \
BUILD_ASSERT(partsize % pagesize == 0U, \
"Partition size not a multiple of pagesize")
#define ASSERT_PAGESIZE_SIZE(pagesize, partsize, onepage) \
BUILD_ASSERT(onepage ? (partsize >= pagesize) : (partsize > pagesize),\
"Partition size to small")
#define EEPROM_EMU_READ_ONLY(n) \
DT_INST_PROP(n, read_only) || \
DT_PROP(EEPROM_PARTITION(n), read_only)
#define EEPROM_EMU_ONEPAGE(n) \
EEPROM_EMU_READ_ONLY(n) || DT_INST_PROP(n, partition_erase)
#define EEPROM_EMU_ENABLE_RAMBUF(n) \
COND_CODE_1(DT_INST_PROP(n, rambuf), (1), \
(COND_CODE_1(DT_INST_PROP(n, partition_erase), (1), (0))))
#define EEPROM_EMU_RAMBUF(n) \
COND_CODE_0(EEPROM_EMU_ENABLE_RAMBUF(n), (), \
(static uint8_t eeprom_emu_##n##_rambuf[DT_INST_PROP(n, size)];))
#define EEPROM_EMU_RAMBUF_LINK(n) \
COND_CODE_0(EEPROM_EMU_ENABLE_RAMBUF(n), (NULL), \
(eeprom_emu_##n##_rambuf))
#define EEPROM_EMU_INIT(n) \
ASSERT_SIZE_PAGESIZE_VALID(DT_INST_PROP(n, size), \
DT_INST_PROP(n, pagesize), EEPROM_EMU_ONEPAGE(n)); \
ASSERT_PAGESIZE_PARTSIZE_VALID(DT_INST_PROP(n, pagesize), \
DT_REG_SIZE(EEPROM_PARTITION(n))); \
ASSERT_PAGESIZE_SIZE(DT_INST_PROP(n, pagesize), \
DT_REG_SIZE(EEPROM_PARTITION(n)), EEPROM_EMU_ONEPAGE(n)); \
EEPROM_EMU_RAMBUF(n) \
static const struct eeprom_emu_config eeprom_emu_##n##_config = { \
.size = RECALC_SIZE( \
DT_INST_PROP(n, size), \
(PART_CBS(EEPROM_PARTITION(n), DT_INST_PROP(n, size))) \
), \
.readonly = EEPROM_EMU_READ_ONLY(n), \
.page_size = DT_INST_PROP(n, pagesize), \
.flash_offset = DT_REG_ADDR(EEPROM_PARTITION(n)), \
.flash_size = DT_REG_SIZE(EEPROM_PARTITION(n)), \
.partitionerase = DT_INST_PROP(n, partition_erase), \
.flash_cbs = PART_CBS(EEPROM_PARTITION(n), \
DT_INST_PROP(n, size)), \
.flash_dev = PART_DEV(EEPROM_PARTITION(n)),\
.rambuf = EEPROM_EMU_RAMBUF_LINK(n), \
}; \
static struct eeprom_emu_data eeprom_emu_##n##_data; \
DEVICE_DT_INST_DEFINE(n, &eeprom_emu_init, \
NULL, &eeprom_emu_##n##_data, \
&eeprom_emu_##n##_config, POST_KERNEL, \
CONFIG_EEPROM_INIT_PRIORITY, &eeprom_emu_api); \
DT_INST_FOREACH_STATUS_OKAY(EEPROM_EMU_INIT)