From 8e6b925200b25819d0265f8b5b3bdb4f2fd6f693 Mon Sep 17 00:00:00 2001 From: jsanchez-2g Date: Thu, 8 Oct 2026 11:02:00 -0500 Subject: [PATCH] mtd/ramtron: Add optional byte-write support. Implement the MTD write callback when CONFIG_MTD_BYTE_WRITE is enabled, allowing unaligned byte writes without changing existing block operations. Reject ranges outside the physical device and return zero for valid empty writes without SPI traffic. Use the initialized SPI device ID, hold the bus lock across the request, and issue write-enable for each transfer. With CONFIG_RAMTRON_CHUNKING, split writes at the write-buffer boundaries of chunk-limited parts. Document the optional callback and its bounds and chunk behavior. Verified with mocked-SPI host tests, driver compilation with byte writes and chunking enabled/disabled, and a Conductor STM32H743BI hardware test covering single-byte and unaligned writes, surrounding-byte preservation, invalid ranges, and restoration of the original FRAM contents. Hardware coverage is limited to the installed 32 KiB part. Assisted-by: Codex:GPT-6 Signed-off-by: jsanchez-2g --- .../os/drivers/special/mtd/index.rst | 15 ++++ drivers/mtd/ramtron.c | 74 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/Documentation/os/drivers/special/mtd/index.rst b/Documentation/os/drivers/special/mtd/index.rst index 138a97e8634..e08beac8d78 100644 --- a/Documentation/os/drivers/special/mtd/index.rst +++ b/Documentation/os/drivers/special/mtd/index.rst @@ -312,3 +312,18 @@ NuttX provides support for the following MTD devices. :glob: devices/* + +RAMTRON SPI FRAM byte writes +=========================== + +With ``CONFIG_MTD_RAMTRON`` and ``CONFIG_MTD_BYTE_WRITE`` enabled, the +RAMTRON driver implements ``MTD_WRITE()`` for arbitrary byte offsets and +lengths. Writes need not align to the emulated block or erase geometry. +The callback returns the number of bytes written; requests outside the +physical device return ``-EINVAL``. A zero-length request within the +device, including its end offset, returns zero without SPI traffic. + +When ``CONFIG_RAMTRON_CHUNKING`` is enabled, writes to parts marked as +chunk-limited are split at their write-buffer boundaries. Each transfer +has its own write-enable and write-command sequence. Existing block-write +operations are unchanged. diff --git a/drivers/mtd/ramtron.c b/drivers/mtd/ramtron.c index 7045c561fab..4d26fd7ead9 100644 --- a/drivers/mtd/ramtron.c +++ b/drivers/mtd/ramtron.c @@ -431,6 +431,10 @@ static ssize_t ramtron_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR uint8_t *buffer); +#ifdef CONFIG_MTD_BYTE_WRITE +static ssize_t ramtron_write(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buffer); +#endif static int ramtron_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg); @@ -844,6 +848,71 @@ static ssize_t ramtron_read(FAR struct mtd_dev_s *dev, return nbytes; } +/**************************************************************************** + * Name: ramtron_write + ****************************************************************************/ + +#ifdef CONFIG_MTD_BYTE_WRITE +static ssize_t ramtron_write(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buffer) +{ + FAR struct ramtron_dev_s *priv = (FAR struct ramtron_dev_s *)dev; + size_t remaining = nbytes; + size_t count; +#ifdef CONFIG_RAMTRON_CHUNKING + size_t available; +#endif + + /* Reject requests that could wrap around the physical device. */ + + if (offset < 0 || offset > priv->part->size || + nbytes > priv->part->size - (uint32_t)offset) + { + return -EINVAL; + } + + if (nbytes == 0) + { + return 0; + } + + DEBUGASSERT(buffer != NULL); + ramtron_lock(priv); + + while (remaining > 0) + { + count = remaining; +#ifdef CONFIG_RAMTRON_CHUNKING + if (priv->part->chunked) + { + /* Do not cross a write-buffer boundary on chunk-limited parts. */ + + available = priv->part->chunksize - + offset % priv->part->chunksize; + if (count > available) + { + count = available; + } + } +#endif + + ramtron_writeenable(priv); + SPI_SELECT(priv->dev, SPIDEV_FLASH(priv->devid), true); + SPI_SEND(priv->dev, RAMTRON_WRITE); + ramtron_sendaddr(priv, offset); + SPI_SNDBLOCK(priv->dev, buffer, count); + SPI_SELECT(priv->dev, SPIDEV_FLASH(priv->devid), false); + + offset += count; + buffer += count; + remaining -= count; + } + + ramtron_unlock(priv->dev); + return nbytes; +} +#endif + /**************************************************************************** * Name: ramtron_ioctl ****************************************************************************/ @@ -863,6 +932,7 @@ static int ramtron_ioctl(FAR struct mtd_dev_s *dev, { FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg); + if (geo) { memset(geo, 0, sizeof(*geo)); @@ -892,6 +962,7 @@ static int ramtron_ioctl(FAR struct mtd_dev_s *dev, { FAR struct partition_info_s *info = (FAR struct partition_info_s *)arg; + if (info != NULL) { info->numsectors = priv->nsectors * @@ -975,6 +1046,9 @@ FAR struct mtd_dev_s *ramtron_initialize(FAR struct spi_dev_s *dev, priv->mtd.bread = ramtron_bread; priv->mtd.bwrite = ramtron_bwrite; priv->mtd.read = ramtron_read; +#ifdef CONFIG_MTD_BYTE_WRITE + priv->mtd.write = ramtron_write; +#endif priv->mtd.ioctl = ramtron_ioctl; priv->mtd.name = "ramtron"; priv->dev = dev;