This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new bac870123f2 drivers/mtd: add MX66UW1G45G support
bac870123f2 is described below
commit bac870123f20226140602a3fa5ff0ddfe811f2a5
Author: Peter Barada <[email protected]>
AuthorDate: Fri Oct 9 14:49:49 2026 -0400
drivers/mtd: add MX66UW1G45G support
add an MTD driver for the Macronix MX66UW1G45G Octal SPI NOR flash.
Signed-off-by: Peter Barada <[email protected]>
---
.../os/drivers/special/mtd/devices/mx66uw1g45g.rst | 25 +
drivers/mtd/CMakeLists.txt | 4 +
drivers/mtd/Kconfig | 23 +
drivers/mtd/Make.defs | 4 +
drivers/mtd/mx66uw1g45g.c | 834 +++++++++++++++++++++
include/nuttx/mtd/mtd.h | 12 +
6 files changed, 902 insertions(+)
diff --git a/Documentation/os/drivers/special/mtd/devices/mx66uw1g45g.rst
b/Documentation/os/drivers/special/mtd/devices/mx66uw1g45g.rst
new file mode 100644
index 00000000000..294ce95815e
--- /dev/null
+++ b/Documentation/os/drivers/special/mtd/devices/mx66uw1g45g.rst
@@ -0,0 +1,25 @@
+=====================
+MX66UW1G45G NOR Flash
+=====================
+
+NuttX provides support for the Octal SPI NOR flash MX66UW1G45G using QSPI
+and OPI transactions.
+
+Supported capacity is up to 1 Gbit (128 MB).
+
+The driver can be enabled by ``CONFIG_MTD_MX65UW1G45G`` option. It is possible
+to select the SPI mode with ``CONFIG_MX66UW1G45G_SPIMODE`` option and
+communication frequency with ``CONFIG_MMX66UW1G45G_QSPI_FREQUENCY`` option.
+
+As this is the first supported chip in the MX66UW family the size of
+flash is fixed at 1 Gbit.
+
+The flash memory has to be initialized before used. This is typically done
+from board support package layer during the board's bringup phase. This
+operation is performed by following function.
+
+.. code-block:: C
+
+ #include <nuttx/mtd/mtd.h>
+
+ FAR struct mtd_dev_s *mx66uw1g456_initialize_spi(FAR struct qspi_dev_s *dev)
diff --git a/drivers/mtd/CMakeLists.txt b/drivers/mtd/CMakeLists.txt
index 9c78ce21f91..116c53e3b5d 100644
--- a/drivers/mtd/CMakeLists.txt
+++ b/drivers/mtd/CMakeLists.txt
@@ -162,6 +162,10 @@ if(CONFIG_MTD)
list(APPEND SRCS mx35.c)
endif()
+ if(CONFIG_MTD_MX66UW1G45G)
+ list(APPEND SRCS mx66uw1g45g.c)
+ endif()
+
if(CONFIG_MTD_S25FL1)
list(APPEND SRCS s25fl1.c)
endif()
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index dd74be9d005..bd45c7ef9b5 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -936,6 +936,29 @@ config MX25RXX_LXX
endif # MTD_MX25RXX
+config MTD_MX66UW1G45G
+ bool "Macronix MX66UW1G45G XSPI flash"
+ default n
+ ---help---
+ Enable the MTD driver for the 1-Gbit Macronix MX66UW1G45G
+ OctaBus flash memory. The driver uses the QSPI interface.
+
+if MTD_MX66UW1G45G
+
+config MX66UW1G45G_QSPIMODE
+ int "MX66UW1G45G QSPI mode"
+ default 0
+ ---help---
+ The MX66UW1G45G supports QSPI mode 0 or 3.
+
+config MX66UW1G45G_QSPI_FREQUENCY
+ int "MX66UW1G45G QSPI frequency"
+ default 75000000
+ ---help---
+ Clock frequency used for MX66UW1G45G transfers.
+
+endif # MTD_MX66UW1G45G
+
config MTD_GD55
bool "QuadSPI-based GigaDevices GD55 family FLASH"
default n
diff --git a/drivers/mtd/Make.defs b/drivers/mtd/Make.defs
index ef41ed84c94..528210aa6eb 100644
--- a/drivers/mtd/Make.defs
+++ b/drivers/mtd/Make.defs
@@ -165,6 +165,10 @@ ifeq ($(CONFIG_MTD_MX35),y)
CSRCS += mx35.c
endif
+ifeq ($(CONFIG_MTD_MX66UW1G45G),y)
+CSRCS += mx66uw1g45g.c
+endif
+
ifeq ($(CONFIG_MTD_S25FL1),y)
CSRCS += s25fl1.c
endif
diff --git a/drivers/mtd/mx66uw1g45g.c b/drivers/mtd/mx66uw1g45g.c
new file mode 100644
index 00000000000..d916bdb63d5
--- /dev/null
+++ b/drivers/mtd/mx66uw1g45g.c
@@ -0,0 +1,834 @@
+/****************************************************************************
+ * drivers/mtd/mx66uw1g45g.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/debug.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/spi/qspi.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* SPI commands *************************************************************/
+
+#define MX66UW_RDID 0x9f
+#define MX66UW_RDSR 0x05
+#define MX66UW_WREN 0x06
+#define MX66UW_RDCR2 0x71
+#define MX66UW_WRCR2 0x72
+#define MX66UW_RSTEN 0x66
+#define MX66UW_RST 0x99
+
+/* Octal commands ***********************************************************/
+
+#define MX66UW_OCTA_READ 0xec13
+#define MX66UW_OCTA_PP 0x12ed
+#define MX66UW_OCTA_SE 0x21de
+#define MX66UW_OCTA_CE 0x609f
+#define MX66UW_OCTA_RDSR 0x05fa
+#define MX66UW_OCTA_WREN 0x06f9
+#define MX66UW_OCTA_RDCR2 0x718e
+#define MX66UW_OCTA_RSTEN 0x6699
+#define MX66UW_OCTA_RST 0x9966
+
+/* Registers ****************************************************************/
+
+#define MX66UW_SR_WIP (1 << 0)
+#define MX66UW_SR_WEL (1 << 1)
+
+#define MX66UW_CR2_MODE 0x00000000
+#define MX66UW_CR2_DUMMY 0x00000300
+#define MX66UW_CR2_SOPI 0x01
+#define MX66UW_CR2_DUMMY_6 0x07
+
+/* Geometry *****************************************************************/
+
+#define MX66UW_PAGE_SHIFT 8
+#define MX66UW_PAGE_SIZE (1 << MX66UW_PAGE_SHIFT)
+#define MX66UW_SECTOR_SHIFT 12
+#define MX66UW_SECTOR_SIZE (1 << MX66UW_SECTOR_SHIFT)
+#define MX66UW_NSECTORS 32768
+#define MX66UW_FLASH_SIZE (MX66UW_SECTOR_SIZE * MX66UW_NSECTORS)
+#define MX66UW_ERASED_STATE 0xff
+
+/* Timing *******************************************************************/
+
+#define MX66UW_RESET_TIME_MS 100
+#define MX66UW_PAGE_TIME_MS 1000
+#define MX66UW_SUBSECTOR_TIME_MS 400
+#define MX66UW_BULK_TIME_MS 460000
+#define MX66UW_REGISTER_TIME_MS 40
+#define MX66UW_REG_DUMMY 4
+#define MX66UW_READ_DUMMY 6
+
+/* Identification ***********************************************************/
+
+#define MX66UW_MANUFACTURER 0xc2
+#define MX66UW_MEMORY_TYPE 0x81
+#define MX66UW_MEMORY_DENSITY 0x3b
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct mx66uw_dev_s
+{
+ struct mtd_dev_s mtd;
+ FAR struct qspi_dev_s *qspi;
+ bool sopi;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int mx66uw_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks);
+static ssize_t mx66uw_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR uint8_t *buffer);
+static ssize_t mx66uw_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buffer);
+static ssize_t mx66uw_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 mx66uw_write(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes, FAR const uint8_t *buffer);
+#endif
+static int mx66uw_ioctl(FAR struct mtd_dev_s *dev, int cmd,
+ unsigned long arg);
+
+static int mx66uw_lock(FAR struct mx66uw_dev_s *priv);
+static void mx66uw_unlock(FAR struct mx66uw_dev_s *priv);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mx66uw_command
+ ****************************************************************************/
+
+static int mx66uw_command(FAR struct mx66uw_dev_s *priv,
+ uint32_t instruction, bool octal, bool dtr)
+{
+ struct qspi_cmdinfo_s cmd;
+
+ cmd.flags = (octal ? QSPICMD_IOCTAL : 0) |
+ (dtr ? QSPICMD_DTR : 0);
+ cmd.addrlen = 0;
+ cmd.cmd = instruction;
+ cmd.buflen = 0;
+ cmd.addr = 0;
+ cmd.buffer = NULL;
+
+ return QSPI_COMMAND(priv->qspi, &cmd);
+}
+
+/****************************************************************************
+ * Name: mx66uw_read_status
+ ****************************************************************************/
+
+static int mx66uw_read_status(FAR struct mx66uw_dev_s *priv,
+ FAR uint8_t *status)
+{
+ struct qspi_meminfo_s mem;
+
+ mem.flags = priv->sopi ? QSPIMEM_IOCTAL | QSPIMEM_OCTALIO : 0;
+ mem.addrlen = priv->sopi ? 4 : 0;
+ mem.dummies = priv->sopi ? MX66UW_REG_DUMMY : 0;
+ mem.cmd = priv->sopi ? MX66UW_OCTA_RDSR : MX66UW_RDSR;
+ mem.buflen = 1;
+ mem.addr = 0;
+ mem.buffer = status;
+
+ return QSPI_MEMORY(priv->qspi, &mem);
+}
+
+/****************************************************************************
+ * Name: mx66uw_wait_ready
+ ****************************************************************************/
+
+static int mx66uw_wait_ready(FAR struct mx66uw_dev_s *priv,
+ unsigned int timeout)
+{
+ uint8_t status;
+ int retry;
+ int ret;
+
+ for (retry = 0; retry < timeout; retry++)
+ {
+ ret = mx66uw_read_status(priv, &status);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if ((status & MX66UW_SR_WIP) == 0)
+ {
+ return OK;
+ }
+
+ up_mdelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Name: mx66uw_write_enable
+ ****************************************************************************/
+
+static int mx66uw_write_enable(FAR struct mx66uw_dev_s *priv)
+{
+ uint8_t status;
+ int ret;
+
+ ret = mx66uw_wait_ready(priv, MX66UW_PAGE_TIME_MS);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = mx66uw_command(priv,
+ priv->sopi ? MX66UW_OCTA_WREN : MX66UW_WREN,
+ priv->sopi, false);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = mx66uw_read_status(priv, &status);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ return (status & MX66UW_SR_WEL) != 0 ? OK : -EACCES;
+}
+
+/****************************************************************************
+ * Name: mx66uw_reset
+ ****************************************************************************/
+
+static int mx66uw_reset(FAR struct mx66uw_dev_s *priv)
+{
+ int ret;
+
+ priv->sopi = false;
+ ret = mx66uw_command(priv, MX66UW_RSTEN, false, false);
+ if (ret == OK)
+ {
+ ret = mx66uw_command(priv, MX66UW_RST, false, false);
+ }
+
+ if (ret == OK)
+ {
+ ret = mx66uw_command(priv, MX66UW_OCTA_RSTEN, true, false);
+ }
+
+ if (ret == OK)
+ {
+ ret = mx66uw_command(priv, MX66UW_OCTA_RST, true, false);
+ }
+
+ if (ret == OK)
+ {
+ ret = mx66uw_command(priv, MX66UW_OCTA_RSTEN, true, true);
+ }
+
+ if (ret == OK)
+ {
+ ret = mx66uw_command(priv, MX66UW_OCTA_RST, true, true);
+ }
+
+ if (ret == OK)
+ {
+ up_mdelay(MX66UW_RESET_TIME_MS);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mx66uw_read_id
+ ****************************************************************************/
+
+static int mx66uw_read_id(FAR struct mx66uw_dev_s *priv)
+{
+ uint8_t id[3];
+ struct qspi_cmdinfo_s cmd;
+ int ret;
+
+ cmd.flags = QSPICMD_READDATA;
+ cmd.addrlen = 0;
+ cmd.cmd = MX66UW_RDID;
+ cmd.buflen = sizeof(id);
+ cmd.addr = 0;
+ cmd.buffer = id;
+
+ ret = QSPI_COMMAND(priv->qspi, &cmd);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (id[0] != MX66UW_MANUFACTURER || id[1] != MX66UW_MEMORY_TYPE ||
+ id[2] != MX66UW_MEMORY_DENSITY)
+ {
+ return -ENODEV;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mx66uw_write_cr2
+ ****************************************************************************/
+
+static int mx66uw_write_cr2(FAR struct mx66uw_dev_s *priv,
+ uint32_t address, uint8_t value)
+{
+ struct qspi_cmdinfo_s cmd;
+ int ret;
+
+ cmd.flags = QSPICMD_ADDRESS | QSPICMD_WRITEDATA;
+ cmd.addrlen = 4;
+ cmd.cmd = MX66UW_WRCR2;
+ cmd.buflen = 1;
+ cmd.addr = address;
+ cmd.buffer = &value;
+
+ ret = mx66uw_write_enable(priv);
+ return ret < 0 ? ret : QSPI_COMMAND(priv->qspi, &cmd);
+}
+
+/****************************************************************************
+ * Name: mx66uw_read_cr2
+ ****************************************************************************/
+
+static int mx66uw_read_cr2(FAR struct mx66uw_dev_s *priv,
+ uint32_t address, FAR uint8_t *value)
+{
+ struct qspi_meminfo_s mem;
+
+ mem.flags = priv->sopi ? QSPIMEM_IOCTAL | QSPIMEM_OCTALIO : 0;
+ mem.addrlen = 4;
+ mem.dummies = priv->sopi ? MX66UW_REG_DUMMY : 0;
+ mem.cmd = priv->sopi ? MX66UW_OCTA_RDCR2 : MX66UW_RDCR2;
+ mem.buflen = 1;
+ mem.addr = address;
+ mem.buffer = value;
+
+ return QSPI_MEMORY(priv->qspi, &mem);
+}
+
+/****************************************************************************
+ * Name: mx66uw_enter_sopi
+ ****************************************************************************/
+
+static int mx66uw_enter_sopi(FAR struct mx66uw_dev_s *priv)
+{
+ uint8_t value;
+ int ret;
+
+ ret = mx66uw_write_cr2(priv, MX66UW_CR2_DUMMY,
+ MX66UW_CR2_DUMMY_6);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = mx66uw_wait_ready(priv, MX66UW_REGISTER_TIME_MS);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = mx66uw_write_cr2(priv, MX66UW_CR2_MODE, MX66UW_CR2_SOPI);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ up_mdelay(MX66UW_REGISTER_TIME_MS);
+ priv->sopi = true;
+
+ ret = mx66uw_wait_ready(priv, MX66UW_REGISTER_TIME_MS);
+ if (ret == OK)
+ {
+ ret = mx66uw_read_cr2(priv, MX66UW_CR2_MODE, &value);
+ }
+
+ if (ret == OK && value != MX66UW_CR2_SOPI)
+ {
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mx66uw_lock
+ ****************************************************************************/
+
+static int mx66uw_lock(FAR struct mx66uw_dev_s *priv)
+{
+ int ret;
+
+ ret = QSPI_LOCK(priv->qspi, true);
+ if (ret >= 0)
+ {
+ QSPI_SETMODE(priv->qspi, CONFIG_MX66UW1G45G_QSPIMODE);
+ QSPI_SETBITS(priv->qspi, 8);
+ QSPI_SETFREQUENCY(priv->qspi,
+ CONFIG_MX66UW1G45G_QSPI_FREQUENCY);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mx66uw_unlock
+ ****************************************************************************/
+
+static void mx66uw_unlock(FAR struct mx66uw_dev_s *priv)
+{
+ QSPI_LOCK(priv->qspi, false);
+}
+
+/****************************************************************************
+ * Name: mx66uw_read_data
+ ****************************************************************************/
+
+static int mx66uw_read_data(FAR struct mx66uw_dev_s *priv,
+ uint32_t address, FAR uint8_t *buffer,
+ size_t buflen)
+{
+ struct qspi_meminfo_s mem;
+
+ mem.flags = QSPIMEM_IOCTAL | QSPIMEM_OCTALIO;
+ mem.addrlen = 4;
+ mem.dummies = MX66UW_READ_DUMMY;
+ mem.cmd = MX66UW_OCTA_READ;
+ mem.buflen = buflen;
+ mem.addr = address;
+ mem.buffer = buffer;
+
+ return QSPI_MEMORY(priv->qspi, &mem);
+}
+
+/****************************************************************************
+ * Name: mx66uw_page_program
+ ****************************************************************************/
+
+static int mx66uw_page_program(FAR struct mx66uw_dev_s *priv,
+ uint32_t address,
+ FAR const uint8_t *buffer, size_t buflen)
+{
+ struct qspi_meminfo_s mem;
+ int ret;
+
+ mem.flags = QSPIMEM_WRITE | QSPIMEM_IOCTAL | QSPIMEM_OCTALIO;
+ mem.addrlen = 4;
+ mem.dummies = 0;
+ mem.cmd = MX66UW_OCTA_PP;
+ mem.buflen = buflen;
+ mem.addr = address;
+ mem.buffer = (FAR void *)buffer;
+
+ ret = mx66uw_write_enable(priv);
+ if (ret == OK)
+ {
+ ret = QSPI_MEMORY(priv->qspi, &mem);
+ }
+
+ return ret < 0 ? ret : mx66uw_wait_ready(priv, MX66UW_PAGE_TIME_MS);
+}
+
+/****************************************************************************
+ * Name: mx66uw_sector_erase
+ ****************************************************************************/
+
+static int mx66uw_sector_erase(FAR struct mx66uw_dev_s *priv,
+ uint32_t address)
+{
+ struct qspi_cmdinfo_s cmd;
+ int ret;
+
+ cmd.flags = QSPICMD_ADDRESS | QSPICMD_IOCTAL | QSPICMD_OCTALIO;
+ cmd.addrlen = 4;
+ cmd.cmd = MX66UW_OCTA_SE;
+ cmd.buflen = 0;
+ cmd.addr = address;
+ cmd.buffer = NULL;
+
+ ret = mx66uw_write_enable(priv);
+ if (ret == OK)
+ {
+ ret = QSPI_COMMAND(priv->qspi, &cmd);
+ }
+
+ return ret < 0 ? ret :
+ mx66uw_wait_ready(priv, MX66UW_SUBSECTOR_TIME_MS);
+}
+
+/****************************************************************************
+ * Name: mx66uw_chip_erase
+ ****************************************************************************/
+
+static int mx66uw_chip_erase(FAR struct mx66uw_dev_s *priv)
+{
+ int ret;
+
+ ret = mx66uw_write_enable(priv);
+ if (ret == OK)
+ {
+ ret = mx66uw_command(priv, MX66UW_OCTA_CE, true, false);
+ }
+
+ return ret < 0 ? ret : mx66uw_wait_ready(priv, MX66UW_BULK_TIME_MS);
+}
+
+/****************************************************************************
+ * Name: mx66uw_erase
+ ****************************************************************************/
+
+static int mx66uw_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks)
+{
+ FAR struct mx66uw_dev_s *priv = (FAR struct mx66uw_dev_s *)dev;
+ size_t erased;
+ int ret;
+
+ if (startblock < 0 || startblock >= MX66UW_NSECTORS ||
+ nblocks > MX66UW_NSECTORS - startblock)
+ {
+ return -EINVAL;
+ }
+
+ ret = mx66uw_lock(priv);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ for (erased = 0; ret == OK && erased < nblocks; erased++)
+ {
+ ret = mx66uw_sector_erase(priv,
+ (startblock + erased) <<
+ MX66UW_SECTOR_SHIFT);
+ }
+
+ mx66uw_unlock(priv);
+ return ret < 0 ? ret : (int)erased;
+}
+
+/****************************************************************************
+ * Name: mx66uw_bread
+ ****************************************************************************/
+
+static ssize_t mx66uw_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR uint8_t *buffer)
+{
+ ssize_t ret;
+
+ ret = mx66uw_read(dev, startblock << MX66UW_PAGE_SHIFT,
+ nblocks << MX66UW_PAGE_SHIFT, buffer);
+ return ret < 0 ? ret : (ssize_t)nblocks;
+}
+
+/****************************************************************************
+ * Name: mx66uw_bwrite
+ ****************************************************************************/
+
+static ssize_t mx66uw_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+ size_t nblocks, FAR const uint8_t *buffer)
+{
+ FAR struct mx66uw_dev_s *priv = (FAR struct mx66uw_dev_s *)dev;
+ size_t written;
+ int ret;
+
+ if (startblock < 0 || startblock >=
+ (MX66UW_FLASH_SIZE >> MX66UW_PAGE_SHIFT) ||
+ nblocks > (MX66UW_FLASH_SIZE >> MX66UW_PAGE_SHIFT) - startblock)
+ {
+ return -EINVAL;
+ }
+
+ ret = mx66uw_lock(priv);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ for (written = 0; ret == OK && written < nblocks; written++)
+ {
+ ret = mx66uw_page_program(priv,
+ (startblock + written) <<
+ MX66UW_PAGE_SHIFT,
+ buffer + (written << MX66UW_PAGE_SHIFT),
+ MX66UW_PAGE_SIZE);
+ }
+
+ mx66uw_unlock(priv);
+ return ret < 0 ? ret : (ssize_t)written;
+}
+
+/****************************************************************************
+ * Name: mx66uw_read
+ ****************************************************************************/
+
+static ssize_t mx66uw_read(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes, FAR uint8_t *buffer)
+{
+ FAR struct mx66uw_dev_s *priv = (FAR struct mx66uw_dev_s *)dev;
+ int ret;
+
+ if (offset < 0 || offset >= MX66UW_FLASH_SIZE ||
+ nbytes > MX66UW_FLASH_SIZE - offset)
+ {
+ return -EINVAL;
+ }
+
+ ret = mx66uw_lock(priv);
+ if (ret == OK)
+ {
+ ret = mx66uw_read_data(priv, offset, buffer, nbytes);
+ mx66uw_unlock(priv);
+ }
+
+ return ret < 0 ? ret : (ssize_t)nbytes;
+}
+
+#ifdef CONFIG_MTD_BYTE_WRITE
+/****************************************************************************
+ * Name: mx66uw_write
+ ****************************************************************************/
+
+static ssize_t mx66uw_write(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes, FAR const uint8_t *buffer)
+{
+ FAR struct mx66uw_dev_s *priv = (FAR struct mx66uw_dev_s *)dev;
+ size_t pagesize;
+ size_t written;
+ int ret;
+
+ if (offset < 0 || offset >= MX66UW_FLASH_SIZE ||
+ nbytes > MX66UW_FLASH_SIZE - offset)
+ {
+ return -EINVAL;
+ }
+
+ ret = mx66uw_lock(priv);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ for (written = 0; ret == OK && written < nbytes; written += pagesize)
+ {
+ pagesize = MX66UW_PAGE_SIZE - ((offset + written) &
+ (MX66UW_PAGE_SIZE - 1));
+ if (pagesize > nbytes - written)
+ {
+ pagesize = nbytes - written;
+ }
+
+ ret = mx66uw_page_program(priv, offset + written,
+ buffer + written, pagesize);
+ }
+
+ mx66uw_unlock(priv);
+ return ret < 0 ? ret : (ssize_t)written;
+}
+#endif
+
+/****************************************************************************
+ * Name: mx66uw_ioctl
+ ****************************************************************************/
+
+static int mx66uw_ioctl(FAR struct mtd_dev_s *dev, int cmd,
+ unsigned long arg)
+{
+ FAR struct mx66uw_dev_s *priv = (FAR struct mx66uw_dev_s *)dev;
+ int ret = -ENOTTY;
+
+ switch (cmd)
+ {
+ case MTDIOC_GEOMETRY:
+ {
+ FAR struct mtd_geometry_s *geo =
+ (FAR struct mtd_geometry_s *)(uintptr_t)arg;
+
+ if (geo == NULL)
+ {
+ ret = -EINVAL;
+ }
+ else
+ {
+ geo->blocksize = MX66UW_PAGE_SIZE;
+ geo->erasesize = MX66UW_SECTOR_SIZE;
+ geo->neraseblocks = MX66UW_NSECTORS;
+ strlcpy(geo->model, "MX66UW1G45G", sizeof(geo->model));
+ ret = OK;
+ }
+ }
+ break;
+
+ case BIOC_PARTINFO:
+ {
+ FAR struct partition_info_s *info =
+ (FAR struct partition_info_s *)(uintptr_t)arg;
+
+ if (info == NULL)
+ {
+ ret = -EINVAL;
+ }
+ else
+ {
+ info->numsectors = MX66UW_FLASH_SIZE / MX66UW_PAGE_SIZE;
+ info->sectorsize = MX66UW_PAGE_SIZE;
+ info->startsector = 0;
+ info->parent[0] = '\0';
+ ret = OK;
+ }
+ }
+ break;
+
+ case MTDIOC_BULKERASE:
+ ret = mx66uw_lock(priv);
+ if (ret == OK)
+ {
+ ret = mx66uw_chip_erase(priv);
+ mx66uw_unlock(priv);
+ }
+ break;
+
+ case MTDIOC_ERASESTATE:
+ {
+ FAR uint8_t *state = (FAR uint8_t *)(uintptr_t)arg;
+
+ if (state == NULL)
+ {
+ ret = -EINVAL;
+ }
+ else
+ {
+ *state = MX66UW_ERASED_STATE;
+ ret = OK;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mx66uw1g45g_initialize
+ *
+ * Description:
+ * Bind an MX66UW1G45G flash memory to an XSPI interface.
+ *
+ * Input Parameters:
+ * qspi - QSPI interface
+ *
+ * Returned Value:
+ * An MTD device on success; NULL on failure.
+ *
+ ****************************************************************************/
+
+FAR struct mtd_dev_s *
+mx66uw1g45g_initialize(FAR struct qspi_dev_s *qspi)
+{
+ FAR struct mx66uw_dev_s *priv;
+ int ret;
+
+ if (qspi == NULL)
+ {
+ return NULL;
+ }
+
+ priv = kmm_zalloc(sizeof(*priv));
+ if (priv == NULL)
+ {
+ return NULL;
+ }
+
+ priv->mtd.erase = mx66uw_erase;
+ priv->mtd.bread = mx66uw_bread;
+ priv->mtd.bwrite = mx66uw_bwrite;
+ priv->mtd.read = mx66uw_read;
+#ifdef CONFIG_MTD_BYTE_WRITE
+ priv->mtd.write = mx66uw_write;
+#endif
+ priv->mtd.ioctl = mx66uw_ioctl;
+ priv->mtd.name = "mx66uw1g45g";
+ priv->qspi = qspi;
+
+ ret = mx66uw_lock(priv);
+ if (ret < 0)
+ {
+ kmm_free(priv);
+ return NULL;
+ }
+
+ ret = mx66uw_reset(priv);
+ if (ret == OK)
+ {
+ ret = mx66uw_read_id(priv);
+ }
+
+ if (ret == OK)
+ {
+ ret = mx66uw_enter_sopi(priv);
+ }
+
+ mx66uw_unlock(priv);
+
+ if (ret < 0)
+ {
+ kmm_free(priv);
+ return NULL;
+ }
+
+ return &priv->mtd;
+}
diff --git a/include/nuttx/mtd/mtd.h b/include/nuttx/mtd/mtd.h
index e597f4c402d..e67b00a36ac 100644
--- a/include/nuttx/mtd/mtd.h
+++ b/include/nuttx/mtd/mtd.h
@@ -749,6 +749,18 @@ mx25uw25645g_initialize(FAR struct qspi_dev_s *qspi);
FAR struct mtd_dev_s *n25qxxx_initialize(FAR struct qspi_dev_s *qspi,
bool unprotect);
+/****************************************************************************
+ * Name: mx66uw1g45g_initialize
+ *
+ * Description:
+ * Create an initialized MTD device instance for the QSPI-based
+ * MX66U21G45G FLASH part.
+ *
+ ****************************************************************************/
+
+FAR struct mtd_dev_s *
+mx66uw1g45g_initialize(FAR struct qspi_dev_s *qspi);
+
/****************************************************************************
* Name: w25qxxxjv_initialize
*