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 <jsanchez@2g-eng.com>
This commit is contained in:
jsanchez-2g 2026-10-08 11:02:00 -05:00 • committed by Xiang Xiao
parent c341f6855b
commit 8e6b925200
2 changed files with 89 additions and 0 deletions

View file

@ -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.

View file

@ -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;