From 30ccfafa37121b6c6ca481d1f639e5a9823f68d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 22:52:13 +0000 Subject: [PATCH] [documentation][device_driver_model] Add NVMEM framework documentation (EN) - module 5/26 Co-authored-by: BernardXiong <1241087+BernardXiong@users.noreply.github.com> --- .../device_driver_model/nvmem/README.md | 859 ++++++++++++++++++ 1 file changed, 859 insertions(+) create mode 100644 documentation/6.components/device-driver/device_driver_model/nvmem/README.md diff --git a/documentation/6.components/device-driver/device_driver_model/nvmem/README.md b/documentation/6.components/device-driver/device_driver_model/nvmem/README.md new file mode 100644 index 0000000000..a9a0cd0d3b --- /dev/null +++ b/documentation/6.components/device-driver/device_driver_model/nvmem/README.md @@ -0,0 +1,859 @@ +# NVMEM (Non-Volatile Memory) Framework + +## Overview + +### What is NVMEM? + +Non-Volatile Memory (NVMEM) refers to computer memory that can retain stored information even when power is removed. Common examples include: + +- **EEPROM** (Electrically Erasable Programmable Read-Only Memory) +- **OTP** (One-Time Programmable memory) +- **eFuse** (Electronic fuse) +- **Battery-backed SRAM** +- **Flash memory regions** (for configuration storage) + +NVMEM devices are commonly used to store: +- Device configuration and calibration data +- MAC addresses and serial numbers +- Hardware identifiers and unique IDs +- Manufacturing test data +- Secure key storage + +### NVMEM Framework in RT-Thread + +RT-Thread's NVMEM framework provides a unified abstraction layer for accessing various types of non-volatile memory devices. The framework supports: + +- **Unified Access Interface**: Common APIs for reading/writing NVMEM regardless of underlying hardware +- **Cell-Based Organization**: NVMEM data organized into named cells (regions) +- **Device Tree Integration**: Hardware description through device tree +- **Write Protection**: Hardware write-protect pin support +- **Bit-Level Access**: Support for bit-addressable memory regions +- **Reference Counting**: Automatic resource management + +The framework is located in: +- Header: `components/drivers/include/drivers/nvmem.h` +- Implementation: `components/drivers/nvmem/nvmem.c` + +## Kconfig Configuration + +### Enable NVMEM Framework + +``` +RT-Thread Components + → Device Drivers + → Using Device Driver Model (RT_USING_DM) + → Using Open Firmware (OF) API (RT_USING_OFW) + → Using Non Volatile Memory (NVMEM) device drivers (RT_USING_NVMEM) +``` + +### Configuration Options + +#### RT_USING_NVMEM + +```kconfig +menuconfig RT_USING_NVMEM + bool "Using Non Volatile Memory (NVMEM) device drivers" + depends on RT_USING_DM + depends on RT_USING_OFW + depends on RT_USING_PIN + select RT_USING_ADT + select RT_USING_ADT_REF + default n +``` + +**Dependencies**: +- `RT_USING_DM`: Device Driver Model must be enabled +- `RT_USING_OFW`: Open Firmware (Device Tree) support required +- `RT_USING_PIN`: PIN driver framework for write-protect pins + +**Description**: Enables the NVMEM framework providing unified access to non-volatile memory devices like EEPROM, OTP, eFuse, etc. + +#### SOC-Specific NVMEM Drivers + +```kconfig +if RT_USING_NVMEM + osource "$(SOC_DM_NVMEM_DIR)/Kconfig" +endif +``` + +SoC vendors can provide their specific NVMEM driver configurations through the `SOC_DM_NVMEM_DIR` directory. + +## Device Tree Bindings + +### NVMEM Provider Node + +```dts +eeprom: eeprom@50 { + compatible = "atmel,24c256"; + reg = <0x50>; + + #address-cells = <1>; + #size-cells = <1>; + + /* Optional properties */ + read-only; /* Memory is read-only */ + wp-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>; /* Write protect pin */ + + /* Define memory cells */ + mac_address: mac@0 { + reg = <0x00 6>; /* Offset 0x00, 6 bytes */ + }; + + serial_number: serial@10 { + reg = <0x10 16>; /* Offset 0x10, 16 bytes */ + }; + + calibration: calib@100 { + reg = <0x100 32>; /* Offset 0x100, 32 bytes */ + bits = <4 12>; /* Bit offset 4, 12 bits wide */ + }; +}; +``` + +### NVMEM Consumer Node + +```dts +ethernet@40000000 { + compatible = "vendor,ethernet"; + reg = <0x40000000 0x10000>; + + /* Reference NVMEM cells */ + nvmem-cells = <&mac_address>; + nvmem-cell-names = "mac-address"; +}; + +device@50000000 { + compatible = "vendor,device"; + reg = <0x50000000 0x1000>; + + /* Multiple NVMEM references */ + nvmem-cells = <&serial_number>, <&calibration>; + nvmem-cell-names = "serial", "calibration-data"; +}; +``` + +### Device Tree Properties + +#### NVMEM Provider Properties + +| Property | Type | Description | +|----------|------|-------------| +| `#address-cells` | u32 | Number of cells for cell address (usually 1) | +| `#size-cells` | u32 | Number of cells for cell size (usually 1) | +| `read-only` | bool | Memory is read-only, writes not allowed | +| `wp-gpios` | phandle | GPIO for hardware write protection | + +#### NVMEM Cell Properties + +| Property | Type | Description | +|----------|------|-------------| +| `reg` | u32 array | `` - Memory region for this cell | +| `bits` | u32 array | `` - Bit-level addressing | + +#### NVMEM Consumer Properties + +| Property | Type | Description | +|----------|------|-------------| +| `nvmem-cells` | phandle | Reference to NVMEM cells | +| `nvmem-cell-names` | string array | Names for referenced cells | + +## Application Layer API + +### Data Structures + +#### struct rt_nvmem_device + +```c +struct rt_nvmem_device { + struct rt_device parent; /* Parent device */ + + int cells_nr; /* Number of cells */ + rt_list_t cell_nodes; /* List of cells */ + + /* Read/Write callbacks */ + rt_ssize_t (*reg_read)(struct rt_nvmem_device *, int offset, + void *val, rt_size_t bytes); + rt_ssize_t (*reg_write)(struct rt_nvmem_device *, int offset, + void *val, rt_size_t bytes); + + rt_ssize_t size; /* Total size in bytes */ + int word_size; /* Word size (1, 2, 4 bytes) */ + int stride; /* Minimum access stride */ + + rt_bool_t read_only; /* Read-only flag */ + rt_bool_t ignore_wp; /* Ignore WP pin */ + rt_base_t wp_pin; /* Write protect GPIO */ + rt_uint8_t wp_pin_active; /* WP active level */ + + struct rt_ref ref; /* Reference counter */ + struct rt_spinlock spinlock; /* Spinlock for protection */ + + void *priv; /* Private data */ +}; +``` + +#### struct rt_nvmem_cell + +```c +struct rt_nvmem_cell { + rt_list_t list; /* List node */ + + int index; /* Cell index */ + const char *id; /* Cell identifier */ + const rt_bool_t free_able; /* Can be freed */ + + rt_uint32_t offset; /* Offset in bytes */ + rt_uint32_t bytes; /* Size in bytes */ + rt_uint32_t bit_offset; /* Bit offset within byte */ + rt_uint32_t nbits; /* Number of bits */ + + struct rt_ref ref; /* Reference counter */ + + struct rt_ofw_node *np; /* Device tree node */ + struct rt_nvmem_device *nvmem; /* Parent NVMEM device */ +}; +``` + +### Consumer APIs + +#### Get NVMEM Cell + +```c +struct rt_nvmem_cell *rt_nvmem_get_cell_by_name(struct rt_device *dev, + const char *id); +struct rt_nvmem_cell *rt_nvmem_get_cell_by_index(struct rt_device *dev, + int index); +``` + +**Parameters**: +- `dev`: Consumer device +- `id`: Cell name from device tree `nvmem-cell-names` +- `index`: Cell index (0-based) + +**Returns**: Pointer to `rt_nvmem_cell` on success, NULL on failure + +**Description**: Obtain a reference to an NVMEM cell for reading/writing. Must be released with `rt_nvmem_put_cell()`. + +**Example**: +```c +struct rt_nvmem_cell *cell; + +/* Get by name */ +cell = rt_nvmem_get_cell_by_name(dev, "mac-address"); +if (!cell) { + rt_kprintf("Failed to get MAC address cell\n"); + return -RT_ERROR; +} + +/* Get by index */ +cell = rt_nvmem_get_cell_by_index(dev, 0); +``` + +#### Release NVMEM Cell + +```c +void rt_nvmem_put_cell(struct rt_nvmem_cell *cell); +``` + +**Parameters**: +- `cell`: NVMEM cell to release + +**Description**: Release a reference to an NVMEM cell obtained with `rt_nvmem_get_cell_*()`. + +#### Read NVMEM Cell + +```c +rt_ssize_t rt_nvmem_cell_read(struct rt_nvmem_cell *cell, + void *buffer, rt_size_t len); +``` + +**Parameters**: +- `cell`: NVMEM cell to read +- `buffer`: Buffer to store read data +- `len`: Number of bytes to read + +**Returns**: Number of bytes read on success, negative error code on failure + +**Description**: Read data from an NVMEM cell. Supports bit-level access if configured. + +#### Write NVMEM Cell + +```c +rt_ssize_t rt_nvmem_cell_write(struct rt_nvmem_cell *cell, + void *buffer, rt_size_t len); +``` + +**Parameters**: +- `cell`: NVMEM cell to write +- `buffer`: Data to write +- `len`: Number of bytes to write + +**Returns**: Number of bytes written on success, negative error code on failure + +**Description**: Write data to an NVMEM cell. Returns error if memory is read-only. + +#### Typed Read Functions + +```c +rt_ssize_t rt_nvmem_cell_read_u8(struct rt_nvmem_cell *cell, + rt_uint8_t *out_val); +rt_ssize_t rt_nvmem_cell_read_u16(struct rt_nvmem_cell *cell, + rt_uint16_t *out_val); +rt_ssize_t rt_nvmem_cell_read_u32(struct rt_nvmem_cell *cell, + rt_uint32_t *out_val); +rt_ssize_t rt_nvmem_cell_read_u64(struct rt_nvmem_cell *cell, + rt_uint64_t *out_val); +``` + +**Parameters**: +- `cell`: NVMEM cell to read +- `out_val`: Pointer to store the value + +**Returns**: Number of bytes read on success, negative error code on failure + +**Description**: Convenience functions to read specific integer types from NVMEM cells. + +### Provider APIs + +#### Register NVMEM Device + +```c +rt_err_t rt_nvmem_device_register(struct rt_nvmem_device *ndev); +``` + +**Parameters**: +- `ndev`: NVMEM device to register + +**Returns**: `RT_EOK` on success, negative error code on failure + +**Description**: Register an NVMEM device with the framework. Initializes reference counting, parses device tree, and configures write-protect pin if present. + +#### Unregister NVMEM Device + +```c +rt_err_t rt_nvmem_device_unregister(struct rt_nvmem_device *ndev); +``` + +**Parameters**: +- `ndev`: NVMEM device to unregister + +**Returns**: `RT_EOK` on success, `-RT_EBUSY` if device is still in use + +**Description**: Unregister an NVMEM device. Fails if there are outstanding references. + +#### Append Cell to Device + +```c +rt_err_t rt_nvmem_device_append_cell(struct rt_nvmem_device *ndev, + struct rt_nvmem_cell *cell); +``` + +**Parameters**: +- `ndev`: NVMEM device +- `cell`: Cell to append + +**Returns**: `RT_EOK` on success, negative error code on failure + +**Description**: Add a cell to an NVMEM device. Used for runtime cell registration. + +## Complete Example: Ethernet Driver with MAC Address + +### Device Tree + +```dts +/* EEPROM device with MAC address storage */ +i2c0: i2c@40000000 { + compatible = "vendor,i2c"; + reg = <0x40000000 0x1000>; + + eeprom@50 { + compatible = "atmel,24c32"; + reg = <0x50>; + + #address-cells = <1>; + #size-cells = <1>; + + /* MAC address cell */ + eth_mac: mac@0 { + reg = <0x00 6>; + }; + + /* Device configuration */ + eth_config: config@10 { + reg = <0x10 16>; + }; + }; +}; + +/* Ethernet controller referencing MAC address */ +ethernet@50000000 { + compatible = "vendor,ethernet"; + reg = <0x50000000 0x10000>; + interrupts = <32 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&cru CLK_EMAC>; + clock-names = "stmmaceth"; + + /* Reference NVMEM for MAC address */ + nvmem-cells = <ð_mac>; + nvmem-cell-names = "mac-address"; +}; +``` + +### Ethernet Driver Implementation + +```c +#include +#include +#include + +struct ethernet_device { + struct rt_device parent; + void *base; + int irq; + rt_uint8_t mac_addr[6]; + /* Other fields... */ +}; + +static rt_err_t ethernet_load_mac_address(struct ethernet_device *eth_dev) +{ + struct rt_nvmem_cell *cell; + rt_ssize_t ret; + + /* Get MAC address cell from device tree */ + cell = rt_nvmem_get_cell_by_name(ð_dev->parent, "mac-address"); + if (!cell) { + rt_kprintf("[ETH] No MAC address in NVMEM, using default\n"); + /* Use default or random MAC */ + eth_dev->mac_addr[0] = 0x00; + eth_dev->mac_addr[1] = 0x11; + eth_dev->mac_addr[2] = 0x22; + eth_dev->mac_addr[3] = 0x33; + eth_dev->mac_addr[4] = 0x44; + eth_dev->mac_addr[5] = 0x55; + return RT_EOK; + } + + /* Read MAC address from NVMEM */ + ret = rt_nvmem_cell_read(cell, eth_dev->mac_addr, 6); + if (ret != 6) { + rt_kprintf("[ETH] Failed to read MAC address: %d\n", ret); + rt_nvmem_put_cell(cell); + return -RT_ERROR; + } + + /* Release cell */ + rt_nvmem_put_cell(cell); + + rt_kprintf("[ETH] MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n", + eth_dev->mac_addr[0], eth_dev->mac_addr[1], + eth_dev->mac_addr[2], eth_dev->mac_addr[3], + eth_dev->mac_addr[4], eth_dev->mac_addr[5]); + + return RT_EOK; +} + +static rt_err_t ethernet_write_mac_address(struct ethernet_device *eth_dev, + const rt_uint8_t *mac) +{ + struct rt_nvmem_cell *cell; + rt_ssize_t ret; + + /* Get MAC address cell */ + cell = rt_nvmem_get_cell_by_name(ð_dev->parent, "mac-address"); + if (!cell) { + rt_kprintf("[ETH] No MAC address cell available\n"); + return -RT_ERROR; + } + + /* Write new MAC address */ + ret = rt_nvmem_cell_write(cell, (void *)mac, 6); + if (ret != 6) { + rt_kprintf("[ETH] Failed to write MAC address: %d\n", ret); + rt_nvmem_put_cell(cell); + return -RT_ERROR; + } + + rt_nvmem_put_cell(cell); + + /* Update local copy */ + rt_memcpy(eth_dev->mac_addr, mac, 6); + + rt_kprintf("[ETH] MAC Address updated successfully\n"); + + return RT_EOK; +} + +static rt_err_t ethernet_probe(struct rt_platform_device *pdev) +{ + struct ethernet_device *eth_dev; + rt_err_t ret; + + eth_dev = rt_calloc(1, sizeof(*eth_dev)); + if (!eth_dev) { + return -RT_ENOMEM; + } + + /* Initialize device */ + eth_dev->parent.ofw_node = pdev->parent.ofw_node; + + /* Get hardware resources */ + eth_dev->base = rt_dm_dev_get_address(&pdev->parent); + if (!eth_dev->base) { + rt_free(eth_dev); + return -RT_ERROR; + } + + /* Load MAC address from NVMEM */ + ret = ethernet_load_mac_address(eth_dev); + if (ret != RT_EOK) { + rt_kprintf("[ETH] Warning: MAC address load failed\n"); + } + + /* Configure hardware with MAC address */ + /* ... hardware initialization ... */ + + rt_kprintf("[ETH] Ethernet initialized\n"); + + return RT_EOK; +} + +static const struct rt_ofw_node_id ethernet_ofw_match[] = { + { .compatible = "vendor,ethernet" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver ethernet_driver = { + .name = "vendor-ethernet", + .ids = ethernet_ofw_match, + .probe = ethernet_probe, +}; +RT_PLATFORM_DRIVER_EXPORT(ethernet_driver); +``` + +## NVMEM Provider Driver Example + +### I2C EEPROM Driver + +```c +#include +#include +#include +#include + +struct eeprom_device { + struct rt_nvmem_device nvmem; + struct rt_i2c_bus_device *i2c_bus; + rt_uint16_t i2c_addr; + rt_size_t size; + rt_uint32_t page_size; +}; + +static rt_ssize_t eeprom_reg_read(struct rt_nvmem_device *ndev, + int offset, void *val, rt_size_t bytes) +{ + struct eeprom_device *eeprom; + struct rt_i2c_msg msgs[2]; + rt_uint8_t addr_buf[2]; + rt_ssize_t ret; + + eeprom = rt_container_of(ndev, struct eeprom_device, nvmem); + + /* Check bounds */ + if (offset + bytes > eeprom->size) { + return -RT_EINVAL; + } + + /* Prepare address (big-endian) */ + addr_buf[0] = (offset >> 8) & 0xFF; + addr_buf[1] = offset & 0xFF; + + /* Write address */ + msgs[0].addr = eeprom->i2c_addr; + msgs[0].flags = RT_I2C_WR; + msgs[0].buf = addr_buf; + msgs[0].len = 2; + + /* Read data */ + msgs[1].addr = eeprom->i2c_addr; + msgs[1].flags = RT_I2C_RD; + msgs[1].buf = val; + msgs[1].len = bytes; + + ret = rt_i2c_transfer(eeprom->i2c_bus, msgs, 2); + if (ret != 2) { + return -RT_ERROR; + } + + return bytes; +} + +static rt_ssize_t eeprom_reg_write(struct rt_nvmem_device *ndev, + int offset, void *val, rt_size_t bytes) +{ + struct eeprom_device *eeprom; + struct rt_i2c_msg msg; + rt_uint8_t *write_buf; + rt_size_t written = 0; + rt_ssize_t ret; + + eeprom = rt_container_of(ndev, struct eeprom_device, nvmem); + + /* Check bounds */ + if (offset + bytes > eeprom->size) { + return -RT_EINVAL; + } + + write_buf = rt_malloc(eeprom->page_size + 2); + if (!write_buf) { + return -RT_ENOMEM; + } + + /* Write page by page */ + while (written < bytes) { + rt_uint32_t page_offset = offset % eeprom->page_size; + rt_size_t chunk = RT_MIN(bytes - written, + eeprom->page_size - page_offset); + + /* Prepare write buffer: address + data */ + write_buf[0] = (offset >> 8) & 0xFF; + write_buf[1] = offset & 0xFF; + rt_memcpy(&write_buf[2], (rt_uint8_t *)val + written, chunk); + + msg.addr = eeprom->i2c_addr; + msg.flags = RT_I2C_WR; + msg.buf = write_buf; + msg.len = chunk + 2; + + ret = rt_i2c_transfer(eeprom->i2c_bus, &msg, 1); + if (ret != 1) { + rt_free(write_buf); + return -RT_ERROR; + } + + /* Wait for write cycle to complete (typ. 5ms for EEPROM) */ + rt_thread_mdelay(5); + + written += chunk; + offset += chunk; + } + + rt_free(write_buf); + + return written; +} + +static rt_err_t eeprom_probe(struct rt_platform_device *pdev) +{ + struct eeprom_device *eeprom; + struct rt_device *dev = &pdev->parent; + rt_err_t ret; + rt_uint32_t size; + + eeprom = rt_calloc(1, sizeof(*eeprom)); + if (!eeprom) { + return -RT_ENOMEM; + } + + /* Get I2C bus */ + eeprom->i2c_bus = rt_i2c_bus_device_find( + rt_dm_dev_get_name_id(dev, NULL, 0, "i2c-bus")); + if (!eeprom->i2c_bus) { + rt_kprintf("[EEPROM] I2C bus not found\n"); + rt_free(eeprom); + return -RT_ERROR; + } + + /* Get I2C address */ + if (rt_dm_dev_prop_read_u32(dev, "reg", &eeprom->i2c_addr) != RT_EOK) { + rt_kprintf("[EEPROM] No I2C address specified\n"); + rt_free(eeprom); + return -RT_ERROR; + } + + /* Get EEPROM size (default 4KB for AT24C32) */ + if (rt_dm_dev_prop_read_u32(dev, "size", &size) != RT_EOK) { + size = 4096; /* Default 4KB */ + } + eeprom->size = size; + + /* Get page size (default 32 bytes) */ + if (rt_dm_dev_prop_read_u32(dev, "pagesize", + &eeprom->page_size) != RT_EOK) { + eeprom->page_size = 32; + } + + /* Initialize NVMEM device */ + eeprom->nvmem.parent.ofw_node = dev->ofw_node; + eeprom->nvmem.reg_read = eeprom_reg_read; + eeprom->nvmem.reg_write = eeprom_reg_write; + eeprom->nvmem.size = eeprom->size; + eeprom->nvmem.word_size = 1; + eeprom->nvmem.stride = 1; + eeprom->nvmem.priv = eeprom; + + /* Register NVMEM device */ + ret = rt_nvmem_device_register(&eeprom->nvmem); + if (ret != RT_EOK) { + rt_kprintf("[EEPROM] Failed to register NVMEM device\n"); + rt_free(eeprom); + return ret; + } + + rt_kprintf("[EEPROM] Registered %d bytes at I2C address 0x%02X\n", + eeprom->size, eeprom->i2c_addr); + + return RT_EOK; +} + +static const struct rt_ofw_node_id eeprom_ofw_match[] = { + { .compatible = "atmel,24c32" }, + { .compatible = "atmel,24c64" }, + { .compatible = "atmel,24c256" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver eeprom_driver = { + .name = "atmel-eeprom", + .ids = eeprom_ofw_match, + .probe = eeprom_probe, +}; +RT_PLATFORM_DRIVER_EXPORT(eeprom_driver); +``` + +## Best Practices + +### For Consumers + +1. **Always Check Return Values** + ```c + cell = rt_nvmem_get_cell_by_name(dev, "calibration"); + if (!cell) { + /* Handle error - use default values */ + } + ``` + +2. **Release Resources** + ```c + cell = rt_nvmem_get_cell_by_name(dev, "data"); + if (cell) { + rt_nvmem_cell_read(cell, buffer, size); + rt_nvmem_put_cell(cell); /* Always release */ + } + ``` + +3. **Handle Read-Only Memory** + ```c + ret = rt_nvmem_cell_write(cell, data, len); + if (ret < 0) { + if (ret == -RT_ENOSYS) { + rt_kprintf("Memory is read-only\n"); + } + } + ``` + +4. **Use Typed Reads for Simple Values** + ```c + rt_uint32_t serial_number; + + cell = rt_nvmem_get_cell_by_name(dev, "serial"); + if (cell) { + rt_nvmem_cell_read_u32(cell, &serial_number); + rt_nvmem_put_cell(cell); + } + ``` + +### For Providers + +1. **Implement Proper Bounds Checking** + ```c + if (offset + bytes > nvmem->size) { + return -RT_EINVAL; + } + ``` + +2. **Support Write Protection** + ```c + /* Framework handles wp-gpios automatically */ + /* Just don't set ignore_wp flag */ + nvmem->ignore_wp = RT_FALSE; + ``` + +3. **Handle Page-Based Writes** + ```c + /* For devices with page write limits */ + /* Write in page-sized chunks and wait for completion */ + rt_thread_mdelay(write_cycle_time_ms); + ``` + +4. **Initialize All Fields** + ```c + nvmem->size = total_size; + nvmem->word_size = 1; /* Byte-addressable */ + nvmem->stride = 1; /* Minimum access unit */ + nvmem->reg_read = my_read; + nvmem->reg_write = my_write; + ``` + +## Troubleshooting + +### Cell Not Found + +**Problem**: `rt_nvmem_get_cell_by_name()` returns NULL + +**Solutions**: +1. Check device tree `nvmem-cells` and `nvmem-cell-names` properties match +2. Verify NVMEM provider device is probed before consumer +3. Check `#address-cells` and `#size-cells` in provider node + +### Write Fails + +**Problem**: `rt_nvmem_cell_write()` returns error + +**Solutions**: +1. Check if memory is marked `read-only` in device tree +2. Verify write-protect pin is not active +3. Check provider's `reg_write` callback is implemented +4. Verify offset and size are within bounds + +### Data Corruption + +**Problem**: Read data doesn't match written data + +**Solutions**: +1. Check page-write boundaries for page-based devices +2. Verify write cycle delays are sufficient +3. Add proper synchronization if accessing from multiple threads +4. Check bit-offset calculations for bit-addressable cells + +## Performance Considerations + +### Read Performance + +- **Caching**: Cache frequently-read values (e.g., MAC address) after first read +- **Batch Reads**: Read multiple cells in one operation if possible +- **Async Access**: Use separate thread for slow NVMEM operations + +### Write Performance + +- **Write Caching**: Buffer writes and flush periodically +- **Wear Leveling**: Distribute writes across memory for EEPROMs +- **Write Coalescing**: Combine multiple small writes into larger operations + +### Resource Usage + +- **Cell References**: Release cells promptly with `rt_nvmem_put_cell()` +- **Memory**: Consider memory cost of buffering for page-based devices +- **I/O**: EEPROM writes can take several milliseconds per page + +## Related Modules + +- **OFW (Open Firmware)**: Device tree parsing for NVMEM configuration +- **PIN**: Write-protect GPIO handling +- **I2C/SPI**: Common buses for NVMEM devices + +## References + +- NVMEM header: `components/drivers/include/drivers/nvmem.h` +- NVMEM implementation: `components/drivers/nvmem/nvmem.c` +- Linux NVMEM framework: Documentation reference for compatible design