aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/flash
diff options
context:
space:
mode:
authorWojciech Macek <wma@FreeBSD.org>2021-11-25 09:36:55 +0000
committerWojciech Macek <wma@FreeBSD.org>2021-11-30 05:41:34 +0000
commit94e25b7acba7f929606a1a57f8d193995d8f8920 (patch)
tree2dd2cf8d692adad13f9638b32d210ca7d6e8b576 /sys/dev/flash
parentf16ec9c6e36f1d481468e7301d6f3b857db17561 (diff)
Diffstat (limited to 'sys/dev/flash')
-rw-r--r--sys/dev/flash/flexspi/flex_spi.c991
-rw-r--r--sys/dev/flash/flexspi/flex_spi.h336
2 files changed, 1327 insertions, 0 deletions
diff --git a/sys/dev/flash/flexspi/flex_spi.c b/sys/dev/flash/flexspi/flex_spi.c
new file mode 100644
index 000000000000..fc85919fc2f8
--- /dev/null
+++ b/sys/dev/flash/flexspi/flex_spi.c
@@ -0,0 +1,991 @@
+/*-
+ * Copyright (c) 2021 Alstom Group.
+ * Copyright (c) 2021 Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <geom/geom_disk.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <vm/pmap.h>
+
+#include "flex_spi.h"
+
+static MALLOC_DEFINE(SECTOR_BUFFER, "flex_spi", "FSL QSPI sector buffer memory");
+
+#define AHB_LUT_ID 31
+#define MHZ(x) ((x)*1000*1000)
+#define SPI_DEFAULT_CLK_RATE (MHZ(10))
+
+static int driver_flags = 0;
+SYSCTL_NODE(_hw, OID_AUTO, flex_spi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "FlexSPI driver parameters");
+SYSCTL_INT(_hw_flex_spi, OID_AUTO, driver_flags, CTLFLAG_RDTUN, &driver_flags, 0,
+ "Configuration flags and quirks");
+
+static struct ofw_compat_data flex_spi_compat_data[] = {
+ {"nxp,lx2160a-fspi", true},
+ {NULL, false}
+};
+
+struct flex_spi_flash_info {
+ char* name;
+ uint32_t jedecid;
+ uint32_t sectorsize;
+ uint32_t sectorcount;
+ uint32_t erasesize;
+ uint32_t maxclk;
+};
+
+/* Add information about supported Flashes. TODO: use SFDP instead */
+static struct flex_spi_flash_info flex_spi_flash_info[] = {
+ {"W25Q128JW", 0x001860ef, 64*1024, 256, 4096, MHZ(100)},
+ {NULL, 0, 0, 0, 0, 0}
+};
+
+struct flex_spi_softc
+{
+ device_t dev;
+ unsigned int flags;
+
+ struct bio_queue_head bio_queue;
+ struct mtx disk_mtx;
+ struct disk *disk;
+ struct proc *p;
+ unsigned int taskstate;
+ uint8_t *buf;
+
+ struct resource *ahb_mem_res;
+ struct resource *mem_res;
+
+ clk_t fspi_clk_en;
+ clk_t fspi_clk;
+ uint64_t fspi_clk_en_hz;
+ uint64_t fspi_clk_hz;
+
+ /* TODO: support more than one Flash per bus */
+ uint64_t fspi_max_clk;
+ uint32_t quirks;
+
+ /* Flash parameters */
+ uint32_t sectorsize;
+ uint32_t sectorcount;
+ uint32_t erasesize;
+};
+
+static int flex_spi_read(struct flex_spi_softc *sc, off_t offset, caddr_t data,
+ size_t count);
+static int flex_spi_write(struct flex_spi_softc *sc, off_t offset,
+ uint8_t *data, size_t size);
+
+static int flex_spi_attach(device_t dev);
+static int flex_spi_probe(device_t dev);
+static int flex_spi_detach(device_t dev);
+
+/* disk routines */
+static int flex_spi_open(struct disk *dp);
+static int flex_spi_close(struct disk *dp);
+static int flex_spi_ioctl(struct disk *, u_long, void *, int, struct thread *);
+static void flex_spi_strategy(struct bio *bp);
+static int flex_spi_getattr(struct bio *bp);
+static void flex_spi_task(void *arg);
+
+static uint32_t
+read_reg(struct flex_spi_softc *sc, uint32_t offset)
+{
+
+ return ((bus_read_4(sc->mem_res, offset)));
+}
+
+static void
+write_reg(struct flex_spi_softc *sc, uint32_t offset, uint32_t value)
+{
+
+ bus_write_4(sc->mem_res, offset, (value));
+}
+
+static int
+reg_read_poll_tout(struct flex_spi_softc *sc, uint32_t offset, uint32_t mask,
+ uint32_t delay_us, uint32_t iterations, bool positive)
+{
+ uint32_t reg;
+ uint32_t condition = 0;
+
+ do {
+ reg = read_reg(sc, offset);
+ if (positive)
+ condition = ((reg & mask) == 0);
+ else
+ condition = ((reg & mask) != 0);
+
+ if (condition == 0)
+ break;
+
+ DELAY(delay_us);
+ } while (condition && (--iterations > 0));
+
+ return (condition != 0);
+}
+
+static int
+flex_spi_clk_setup(struct flex_spi_softc *sc, uint32_t rate)
+{
+ int ret = 0;
+
+ /* disable to avoid glitching */
+ ret |= clk_disable(sc->fspi_clk_en);
+ ret |= clk_disable(sc->fspi_clk);
+
+ ret |= clk_set_freq(sc->fspi_clk, rate, 0);
+ sc->fspi_clk_hz = rate;
+
+ /* enable clocks back */
+ ret |= clk_enable(sc->fspi_clk_en);
+ ret |= clk_enable(sc->fspi_clk);
+
+ if (ret)
+ return (EINVAL);
+
+ return (0);
+}
+
+static void
+flex_spi_prepare_lut(struct flex_spi_softc *sc, uint8_t op)
+{
+ uint32_t lut_id;
+ uint32_t lut;
+
+ /* unlock LUT */
+ write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE);
+ write_reg(sc, FSPI_LCKCR, FSPI_LCKER_UNLOCK);
+
+ /* Read JEDEC ID */
+ lut_id = 0;
+
+ switch (op) {
+ case LUT_FLASH_CMD_JEDECID:
+ lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_READ_IDENT);
+ lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0);
+ write_reg(sc, FSPI_LUT_REG(lut_id), lut);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0);
+ break;
+ case LUT_FLASH_CMD_READ:
+ lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_FAST_READ);
+ lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8);
+ write_reg(sc, FSPI_LUT_REG(lut_id), lut);
+ lut = LUT_DEF(0, LUT_DUMMY, LUT_PAD(1), 1*8);
+ lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 4, lut);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 8, 0);
+ break;
+ case LUT_FLASH_CMD_STATUS_READ:
+ lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_READ_STATUS);
+ lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0);
+ write_reg(sc, FSPI_LUT_REG(lut_id), lut);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0);
+ break;
+ case LUT_FLASH_CMD_PAGE_PROGRAM:
+ lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_PAGE_PROGRAM);
+ lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8);
+ write_reg(sc, FSPI_LUT_REG(lut_id), lut);
+ lut = LUT_DEF(0, LUT_NXP_WRITE, LUT_PAD(1), 0);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 4, lut);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 8, 0);
+ break;
+ case LUT_FLASH_CMD_WRITE_ENABLE:
+ lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_WRITE_ENABLE);
+ write_reg(sc, FSPI_LUT_REG(lut_id), lut);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0);
+ break;
+ case LUT_FLASH_CMD_WRITE_DISABLE:
+ lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_WRITE_DISABLE);
+ write_reg(sc, FSPI_LUT_REG(lut_id), lut);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0);
+ break;
+ case LUT_FLASH_CMD_SECTOR_ERASE:
+ lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_SECTOR_ERASE);
+ lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8);
+ write_reg(sc, FSPI_LUT_REG(lut_id), lut);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0);
+ break;
+ default:
+ write_reg(sc, FSPI_LUT_REG(lut_id), 0);
+ }
+
+ /* lock LUT */
+ write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE);
+ write_reg(sc, FSPI_LCKCR, FSPI_LCKER_LOCK);
+}
+
+static void
+flex_spi_prepare_ahb_lut(struct flex_spi_softc *sc)
+{
+ uint32_t lut_id;
+ uint32_t lut;
+
+ /* unlock LUT */
+ write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE);
+ write_reg(sc, FSPI_LCKCR, FSPI_LCKER_UNLOCK);
+
+ lut_id = AHB_LUT_ID;
+ lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_FAST_READ);
+ lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8);
+ write_reg(sc, FSPI_LUT_REG(lut_id), lut);
+ lut = LUT_DEF(0, LUT_DUMMY, LUT_PAD(1), 1*8);
+ lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 4, lut);
+ write_reg(sc, FSPI_LUT_REG(lut_id) + 8, 0);
+
+ /* lock LUT */
+ write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE);
+ write_reg(sc, FSPI_LCKCR, FSPI_LCKER_LOCK);
+}
+
+#define DIR_READ 0
+#define DIR_WRITE 1
+
+static void
+flex_spi_read_rxfifo(struct flex_spi_softc *sc, uint8_t *buf, uint8_t size)
+{
+ int i, ret, reg;
+
+ /*
+ * Default value of water mark level is 8 bytes, hence in single
+ * read request controller can read max 8 bytes of data.
+ */
+ for (i = 0; i < size; i += 4) {
+ /* Wait for RXFIFO available */
+ if (i % 8 == 0) {
+ ret = reg_read_poll_tout(sc, FSPI_INTR, FSPI_INTR_IPRXWA,
+ 1, 50000, 1);
+ if (ret)
+ device_printf(sc->dev,
+ "timed out waiting for FSPI_INTR_IPRXWA\n");
+ }
+
+ if (i % 8 == 0)
+ reg = read_reg(sc, FSPI_RFDR);
+ else
+ reg = read_reg(sc, FSPI_RFDR + 4);
+
+ if (size >= (i + 4))
+ *(uint32_t *)(buf + i) = reg;
+ else
+ memcpy(buf + i, &reg, size - i);
+
+ /* move the FIFO pointer */
+ if (i % 8 != 0)
+ write_reg(sc, FSPI_INTR, FSPI_INTR_IPRXWA);
+ }
+
+ /* invalid the RXFIFO */
+ write_reg(sc, FSPI_IPRXFCR, FSPI_IPRXFCR_CLR);
+ /* move the FIFO pointer */
+ write_reg(sc, FSPI_INTR, FSPI_INTR_IPRXWA);
+}
+
+static void
+flex_spi_write_txfifo(struct flex_spi_softc *sc, uint8_t *buf, uint8_t size)
+{
+ int i, ret, reg;
+
+ /* invalid the TXFIFO */
+ write_reg(sc, FSPI_IPRXFCR, FSPI_IPTXFCR_CLR);
+
+ /*
+ * Default value of water mark level is 8 bytes, hence in single
+ * read request controller can read max 8 bytes of data.
+ */
+ for (i = 0; i < size; i += 4) {
+ /* Wait for RXFIFO available */
+ if (i % 8 == 0) {
+ ret = reg_read_poll_tout(sc, FSPI_INTR, FSPI_INTR_IPTXWE,
+ 1, 50000, 1);
+ if (ret)
+ device_printf(sc->dev,
+ "timed out waiting for FSPI_INTR_IPRXWA\n");
+ }
+
+ if (size >= (i + 4))
+ reg = *(uint32_t *)(buf + i);
+ else {
+ reg = 0;
+ memcpy(&reg, buf + i, size - i);
+ }
+
+ if (i % 8 == 0)
+ write_reg(sc, FSPI_TFDR, reg);
+ else
+ write_reg(sc, FSPI_TFDR + 4, reg);
+
+ /* move the FIFO pointer */
+ if (i % 8 != 0)
+ write_reg(sc, FSPI_INTR, FSPI_INTR_IPTXWE);
+ }
+
+ /* move the FIFO pointer */
+ write_reg(sc, FSPI_INTR, FSPI_INTR_IPTXWE);
+}
+
+static int
+flex_spi_do_op(struct flex_spi_softc *sc, uint32_t op, uint32_t addr,
+ uint8_t *buf, uint8_t size, uint8_t dir)
+{
+
+ uint32_t cnt = 1000, reg;
+
+ reg = read_reg(sc, FSPI_IPRXFCR);
+ /* invalidate RXFIFO first */
+ reg &= ~FSPI_IPRXFCR_DMA_EN;
+ reg |= FSPI_IPRXFCR_CLR;
+ write_reg(sc, FSPI_IPRXFCR, reg);
+
+ /* Prepare LUT */
+ flex_spi_prepare_lut(sc, op);
+
+ write_reg(sc, FSPI_IPCR0, addr);
+ /*
+ * Always start the sequence at the same index since we update
+ * the LUT at each BIO operation. And also specify the DATA
+ * length, since it's has not been specified in the LUT.
+ */
+ write_reg(sc, FSPI_IPCR1, size |
+ (0 << FSPI_IPCR1_SEQID_SHIFT) | (0 << FSPI_IPCR1_SEQNUM_SHIFT));
+
+ if ((size != 0) && (dir == DIR_WRITE))
+ flex_spi_write_txfifo(sc, buf, size);
+
+ /* Trigger the LUT now. */
+ write_reg(sc, FSPI_IPCMD, FSPI_IPCMD_TRG);
+
+
+ /* Wait for completion. */
+ do {
+ reg = read_reg(sc, FSPI_INTR);
+ if (reg & FSPI_INTR_IPCMDDONE) {
+ write_reg(sc, FSPI_INTR, FSPI_INTR_IPCMDDONE);
+ break;
+ }
+ DELAY(1);
+ } while (--cnt);
+ if (cnt == 0) {
+ device_printf(sc->dev, "timed out waiting for command completion\n");
+ return (ETIMEDOUT);
+ }
+
+ /* Invoke IP data read, if request is of data read. */
+ if ((size != 0) && (dir == DIR_READ))
+ flex_spi_read_rxfifo(sc, buf, size);
+
+ return (0);
+}
+
+static int
+flex_spi_wait_for_controller(struct flex_spi_softc *sc)
+{
+ int err;
+
+ /* Wait for controller being ready. */
+ err = reg_read_poll_tout(sc, FSPI_STS0,
+ FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, 1);
+
+ return (err);
+}
+
+static int
+flex_spi_wait_for_flash(struct flex_spi_softc *sc)
+{
+ int ret;
+ uint32_t status = 0;
+
+ ret = flex_spi_wait_for_controller(sc);
+ if (ret != 0) {
+ device_printf(sc->dev, "%s: timed out waiting for controller", __func__);
+ return (ret);
+ }
+
+ do {
+ ret = flex_spi_do_op(sc, LUT_FLASH_CMD_STATUS_READ, 0, (void*)&status,
+ 1, DIR_READ);
+ if (ret != 0) {
+ device_printf(sc->dev, "ERROR: failed to get flash status\n");
+ return (ret);
+ }
+
+ } while (status & STATUS_WIP);
+
+ return (0);
+}
+
+static int
+flex_spi_identify(struct flex_spi_softc *sc)
+{
+ int ret;
+ uint32_t id = 0;
+ struct flex_spi_flash_info *finfo = flex_spi_flash_info;
+
+ ret = flex_spi_do_op(sc, LUT_FLASH_CMD_JEDECID, 0, (void*)&id, sizeof(id), DIR_READ);
+ if (ret != 0) {
+ device_printf(sc->dev, "ERROR: failed to identify device\n");
+ return (ret);
+ }
+
+ /* XXX TODO: SFDP to be implemented */
+ while (finfo->jedecid != 0) {
+ if (id == finfo->jedecid) {
+ device_printf(sc->dev, "found %s Flash\n", finfo->name);
+ sc->sectorsize = finfo->sectorsize;
+ sc->sectorcount = finfo->sectorcount;
+ sc->erasesize = finfo->erasesize;
+ sc->fspi_max_clk = finfo->maxclk;
+ return (0);
+ }
+ finfo++;
+ }
+
+ return (EINVAL);
+}
+
+static inline int
+flex_spi_force_ip_mode(struct flex_spi_softc *sc)
+{
+
+ if (sc->quirks & FSPI_QUIRK_USE_IP_ONLY)
+ return (1);
+ if (driver_flags & FSPI_QUIRK_USE_IP_ONLY)
+ return (1);
+
+ return (0);
+}
+
+static int
+flex_spi_read(struct flex_spi_softc *sc, off_t offset, caddr_t data,
+ size_t count)
+{
+ int err;
+ size_t len;
+
+ /* Wait for controller being ready. */
+ err = flex_spi_wait_for_controller(sc);
+ if (err)
+ device_printf(sc->dev,
+ "warning: spi_read, timed out waiting for controller");
+
+ /* Use AHB access whenever we can */
+ if (flex_spi_force_ip_mode(sc) != 0) {
+ do {
+ if (((offset % 4) != 0) || (count < 4)) {
+ *(uint8_t*)data = bus_read_1(sc->ahb_mem_res, offset);
+ data++;
+ count--;
+ offset++;
+ } else {
+ *(uint32_t*)data = bus_read_4(sc->ahb_mem_res, offset);
+ data += 4;
+ count -= 4;
+ offset += 4;
+ }
+ } while (count);
+
+ return (0);
+ }
+
+ do {
+ len = min(64, count);
+ err = flex_spi_do_op(sc, LUT_FLASH_CMD_READ, offset, (void*)data,
+ len, DIR_READ);
+ if (err)
+ return (err);
+ offset += len;
+ data += len;
+ count -= len;
+ } while (count);
+
+ return (0);
+}
+
+static int
+flex_spi_write(struct flex_spi_softc *sc, off_t offset, uint8_t *data,
+ size_t size)
+{
+ int ret = 0;
+ size_t ptr;
+
+ flex_spi_wait_for_flash(sc);
+ ret = flex_spi_do_op(sc, LUT_FLASH_CMD_WRITE_ENABLE, offset, NULL,
+ 0, DIR_READ);
+ if (ret != 0) {
+ device_printf(sc->dev, "ERROR: failed to enable writes\n");
+ return (ret);
+ }
+ flex_spi_wait_for_flash(sc);
+
+ /* per-sector write */
+ while (size > 0) {
+ uint32_t sector_base = rounddown2(offset, sc->erasesize);
+ size_t size_in_sector = size;
+
+ if (size_in_sector + offset > sector_base + sc->erasesize)
+ size_in_sector = sector_base + sc->erasesize - offset;
+
+ /* Read sector */
+ ret = flex_spi_read(sc, sector_base, sc->buf, sc->erasesize);
+ if (ret != 0) {
+ device_printf(sc->dev, "ERROR: failed to read sector %d\n",
+ sector_base);
+ goto exit;
+ }
+
+ /* Erase sector */
+ flex_spi_wait_for_flash(sc);
+ ret = flex_spi_do_op(sc, LUT_FLASH_CMD_SECTOR_ERASE, offset, NULL,
+ 0, DIR_READ);
+ if (ret != 0) {
+ device_printf(sc->dev, "ERROR: failed to erase sector %d\n",
+ sector_base);
+ goto exit;
+ }
+
+ /* Update buffer with input data */
+ memcpy(sc->buf + (offset - sector_base), data, size_in_sector);
+
+ /* Write buffer back to the flash
+ * Up to 32 bytes per single request, request cannot spread
+ * across 256-byte page boundary
+ */
+ for (ptr = 0; ptr < sc->erasesize; ptr += 32) {
+ flex_spi_wait_for_flash(sc);
+ ret = flex_spi_do_op(sc, LUT_FLASH_CMD_PAGE_PROGRAM,
+ sector_base + ptr, (void*)(sc->buf + ptr), 32, DIR_WRITE);
+ if (ret != 0) {
+ device_printf(sc->dev, "ERROR: failed to write address %ld\n",
+ sector_base + ptr);
+ goto exit;
+ }
+ }
+
+ /* update pointers */
+ size = size - size_in_sector;
+ offset = offset + size;
+ }
+
+ flex_spi_wait_for_flash(sc);
+ ret = flex_spi_do_op(sc, LUT_FLASH_CMD_WRITE_DISABLE, offset, (void*)sc->buf,
+ 0, DIR_READ);
+ if (ret != 0) {
+ device_printf(sc->dev, "ERROR: failed to disable writes\n");
+ goto exit;
+ }
+ flex_spi_wait_for_flash(sc);
+
+exit:
+
+ return (ret);
+}
+
+static int
+flex_spi_default_setup(struct flex_spi_softc *sc)
+{
+ int ret, i;
+ uint32_t reg;
+
+ /* Default clock speed */
+ ret = flex_spi_clk_setup(sc, SPI_DEFAULT_CLK_RATE);
+ if (ret)
+ return (ret);
+
+ /* Reset the module */
+ /* w1c register, wait unit clear */
+ reg = read_reg(sc, FSPI_MCR0);
+ reg |= FSPI_MCR0_SWRST;
+ write_reg(sc, FSPI_MCR0, reg);
+ ret = reg_read_poll_tout(sc, FSPI_MCR0, FSPI_MCR0_SWRST, 1000, POLL_TOUT, 0);
+ if (ret != 0) {
+ device_printf(sc->dev, "time out waiting for reset");
+ return (ret);
+ }
+
+ /* Disable the module */
+ write_reg(sc, FSPI_MCR0, FSPI_MCR0_MDIS);
+
+ /* Reset the DLL register to default value */
+ write_reg(sc, FSPI_DLLACR, FSPI_DLLACR_OVRDEN);
+ write_reg(sc, FSPI_DLLBCR, FSPI_DLLBCR_OVRDEN);
+
+ /* enable module */
+ write_reg(sc, FSPI_MCR0, FSPI_MCR0_AHB_TIMEOUT(0xFF) |
+ FSPI_MCR0_IP_TIMEOUT(0xFF) | (uint32_t) FSPI_MCR0_OCTCOMB_EN);
+
+ /*
+ * Disable same device enable bit and configure all slave devices
+ * independently.
+ */
+ reg = read_reg(sc, FSPI_MCR2);
+ reg = reg & ~(FSPI_MCR2_SAMEDEVICEEN);
+ write_reg(sc, FSPI_MCR2, reg);
+
+ /* AHB configuration for access buffer 0~7. */
+ for (i = 0; i < 7; i++)
+ write_reg(sc, FSPI_AHBRX_BUF0CR0 + 4 * i, 0);
+
+ /*
+ * Set ADATSZ with the maximum AHB buffer size to improve the read
+ * performance.
+ */
+ write_reg(sc, FSPI_AHBRX_BUF7CR0, (2048 / 8 |
+ FSPI_AHBRXBUF0CR7_PREF));
+
+ /* prefetch and no start address alignment limitation */
+ write_reg(sc, FSPI_AHBCR, FSPI_AHBCR_PREF_EN | FSPI_AHBCR_RDADDROPT);
+
+ /* AHB Read - Set lut sequence ID for all CS. */
+ flex_spi_prepare_ahb_lut(sc);
+ write_reg(sc, FSPI_FLSHA1CR2, AHB_LUT_ID);
+ write_reg(sc, FSPI_FLSHA2CR2, AHB_LUT_ID);
+ write_reg(sc, FSPI_FLSHB1CR2, AHB_LUT_ID);
+ write_reg(sc, FSPI_FLSHB2CR2, AHB_LUT_ID);
+
+ /* disable interrupts */
+ write_reg(sc, FSPI_INTEN, 0);
+
+ return (0);
+}
+
+static int
+flex_spi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, flex_spi_compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "NXP FlexSPI Flash");
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+flex_spi_attach(device_t dev)
+{
+ struct flex_spi_softc *sc;
+ phandle_t node;
+ int rid;
+ uint32_t reg;
+
+ node = ofw_bus_get_node(dev);
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ mtx_init(&sc->disk_mtx, "flex_spi_DISK", "QSPI disk mtx", MTX_DEF);
+
+ /* Get memory resources. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+
+ rid = 1;
+ sc->ahb_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ if (sc->mem_res == NULL || sc->ahb_mem_res == NULL) {
+ device_printf(dev, "could not allocate resources\n");
+ flex_spi_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Get clocks */
+ if ((clk_get_by_ofw_name(dev, node, "fspi_en", &sc->fspi_clk_en) != 0)
+ || (clk_get_freq(sc->fspi_clk_en, &sc->fspi_clk_en_hz) != 0)) {
+ device_printf(dev, "could not get fspi_en clock\n");
+ flex_spi_detach(dev);
+ return (EINVAL);
+ }
+ if ((clk_get_by_ofw_name(dev, node, "fspi", &sc->fspi_clk) != 0)
+ || (clk_get_freq(sc->fspi_clk, &sc->fspi_clk_hz) != 0)) {
+ device_printf(dev, "could not get fspi clock\n");
+ flex_spi_detach(dev);
+ return (EINVAL);
+ }
+
+ /* Enable clocks */
+ if (clk_enable(sc->fspi_clk_en) != 0 ||
+ clk_enable(sc->fspi_clk) != 0) {
+ device_printf(dev, "could not enable clocks\n");
+ flex_spi_detach(dev);
+ return (EINVAL);
+ }
+
+ /* Clear potential interrupts */
+ reg = read_reg(sc, FSPI_INTR);
+ if (reg)
+ write_reg(sc, FSPI_INTR, reg);
+
+ /* Default setup */
+ if (flex_spi_default_setup(sc) != 0) {
+ device_printf(sc->dev, "Unable to initialize defaults\n");
+ flex_spi_detach(dev);
+ return (ENXIO);
+ }
+
+ /* Identify attached Flash */
+ if(flex_spi_identify(sc) != 0) {
+ device_printf(sc->dev, "Unable to identify Flash\n");
+ flex_spi_detach(dev);
+ return (ENXIO);
+ }
+
+ if (flex_spi_clk_setup(sc, sc->fspi_max_clk) != 0) {
+ device_printf(sc->dev, "Unable to set up SPI max clock\n");
+ flex_spi_detach(dev);
+ return (ENXIO);
+ }
+
+ sc->buf = malloc(sc->erasesize, SECTOR_BUFFER, M_WAITOK);
+ if (sc->buf == NULL) {
+ device_printf(sc->dev, "Unable to set up allocate internal buffer\n");
+ flex_spi_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Move it to per-flash */
+ sc->disk = disk_alloc();
+ sc->disk->d_open = flex_spi_open;
+ sc->disk->d_close = flex_spi_close;
+ sc->disk->d_strategy = flex_spi_strategy;
+ sc->disk->d_getattr = flex_spi_getattr;
+ sc->disk->d_ioctl = flex_spi_ioctl;
+ sc->disk->d_name = "flash/qspi";
+ sc->disk->d_drv1 = sc;
+ /* the most that can fit in a single spi transaction */
+ sc->disk->d_maxsize = DFLTPHYS;
+ sc->disk->d_sectorsize = FLASH_SECTORSIZE;
+ sc->disk->d_unit = device_get_unit(sc->dev);
+ sc->disk->d_dump = NULL;
+
+ sc->disk->d_mediasize = sc->sectorsize * sc->sectorcount;
+ sc->disk->d_stripesize = sc->erasesize;
+
+ bioq_init(&sc->bio_queue);
+ sc->taskstate = TSTATE_RUNNING;
+ kproc_create(&flex_spi_task, sc, &sc->p, 0, 0, "task: qspi flash");
+ disk_create(sc->disk, DISK_VERSION);
+
+ return (0);
+}
+
+static int
+flex_spi_detach(device_t dev)
+{
+ struct flex_spi_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ err = 0;
+
+ mtx_lock(&sc->disk_mtx);
+ if (sc->taskstate == TSTATE_RUNNING) {
+ sc->taskstate = TSTATE_STOPPING;
+ wakeup(sc->disk);
+ while (err == 0 && sc->taskstate != TSTATE_STOPPED) {
+ err = mtx_sleep(sc->disk, &sc->disk_mtx, 0, "flex_spi",
+ hz * 3);
+ if (err != 0) {
+ sc->taskstate = TSTATE_RUNNING;
+ device_printf(sc->dev,
+ "Failed to stop queue task\n");
+ }
+ }
+ }
+
+ mtx_unlock(&sc->disk_mtx);
+ mtx_destroy(&sc->disk_mtx);
+
+ if (err == 0 && sc->taskstate == TSTATE_STOPPED) {
+ disk_destroy(sc->disk);
+ bioq_flush(&sc->bio_queue, NULL, ENXIO);
+ }
+
+ /* Disable hardware. */
+
+ /* Release memory resource. */
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->mem_res), sc->mem_res);
+
+ if (sc->ahb_mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->ahb_mem_res), sc->ahb_mem_res);
+
+ /* Disable clocks */
+ if (sc->fspi_clk_en_hz)
+ clk_disable(sc->fspi_clk_en);
+ if (sc->fspi_clk_hz)
+ clk_disable(sc->fspi_clk);
+
+ free(sc->buf, SECTOR_BUFFER);
+
+ return (err);
+}
+
+static int
+flex_spi_open(struct disk *dp)
+{
+
+ return (0);
+}
+
+static int
+flex_spi_close(struct disk *dp)
+{
+
+ return (0);
+}
+
+static int
+flex_spi_ioctl(struct disk *dp, u_long cmd, void *data, int fflag,
+ struct thread *td)
+{
+
+ return (ENOTSUP);
+}
+
+static void
+flex_spi_strategy(struct bio *bp)
+{
+ struct flex_spi_softc *sc;
+
+ sc = (struct flex_spi_softc *)bp->bio_disk->d_drv1;
+ mtx_lock(&sc->disk_mtx);
+ bioq_disksort(&sc->bio_queue, bp);
+ mtx_unlock(&sc->disk_mtx);
+ wakeup(sc->disk);
+}
+
+static int
+flex_spi_getattr(struct bio *bp)
+{
+ struct flex_spi_softc *sc;
+ device_t dev;
+
+ if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) {
+ return (ENXIO);
+ }
+
+ sc = bp->bio_disk->d_drv1;
+ dev = sc->dev;
+
+ if (strcmp(bp->bio_attribute, "SPI::device") != 0) {
+ return (-1);
+ }
+
+ if (bp->bio_length != sizeof(dev)) {
+ return (EFAULT);
+ }
+
+ bcopy(&dev, bp->bio_data, sizeof(dev));
+
+ return (0);
+}
+
+static void
+flex_spi_task(void *arg)
+{
+ struct flex_spi_softc *sc;
+ struct bio *bp;
+ device_t dev;
+
+ sc = (struct flex_spi_softc *)arg;
+ for (;;) {
+ dev = sc->dev;
+ mtx_lock(&sc->disk_mtx);
+ do {
+ if (sc->taskstate == TSTATE_STOPPING) {
+ sc->taskstate = TSTATE_STOPPED;
+ mtx_unlock(&sc->disk_mtx);
+ wakeup(sc->disk);
+ kproc_exit(0);
+ }
+ bp = bioq_first(&sc->bio_queue);
+ if (bp == NULL)
+ mtx_sleep(sc->disk, &sc->disk_mtx, PRIBIO,
+ "flex_spi", 0);
+ } while (bp == NULL);
+ bioq_remove(&sc->bio_queue, bp);
+ mtx_unlock(&sc->disk_mtx);
+
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ bp->bio_error = flex_spi_read(sc, bp->bio_offset,
+ bp->bio_data, bp->bio_bcount);
+ break;
+ case BIO_WRITE:
+ bp->bio_error = flex_spi_write(sc, bp->bio_offset,
+ bp->bio_data, bp->bio_bcount);
+ break;
+ default:
+ bp->bio_error = EINVAL;
+ }
+ biodone(bp);
+ }
+}
+
+static devclass_t flex_spi_devclass;
+static device_method_t flex_spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, flex_spi_probe),
+ DEVMETHOD(device_attach, flex_spi_attach),
+ DEVMETHOD(device_detach, flex_spi_detach),
+
+ { 0, 0 }
+};
+
+static driver_t flex_spi_driver = {
+ "flex_spi",
+ flex_spi_methods,
+ sizeof(struct flex_spi_softc),
+};
+
+DRIVER_MODULE(flex_spi, simplebus, flex_spi_driver, flex_spi_devclass, 0, 0);
+SIMPLEBUS_PNP_INFO(flex_spi_compat_data);
diff --git a/sys/dev/flash/flexspi/flex_spi.h b/sys/dev/flash/flexspi/flex_spi.h
new file mode 100644
index 000000000000..265f947f650d
--- /dev/null
+++ b/sys/dev/flash/flexspi/flex_spi.h
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (c) 2021 Alstom Group.
+ * Copyright (c) 2021 Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DEV_FLASH_FLEX_SPI_H_
+#define DEV_FLASH_FLEX_SPI_H_
+
+#define BIT(x) (1 << (x))
+
+/* Registers used by the driver */
+#define FSPI_MCR0 0x00
+#define FSPI_MCR0_AHB_TIMEOUT(x) ((x) << 24)
+#define FSPI_MCR0_IP_TIMEOUT(x) ((x) << 16)
+#define FSPI_MCR0_LEARN_EN BIT(15)
+#define FSPI_MCR0_SCRFRUN_EN BIT(14)
+#define FSPI_MCR0_OCTCOMB_EN BIT(13)
+#define FSPI_MCR0_DOZE_EN BIT(12)
+#define FSPI_MCR0_HSEN BIT(11)
+#define FSPI_MCR0_SERCLKDIV BIT(8)
+#define FSPI_MCR0_ATDF_EN BIT(7)
+#define FSPI_MCR0_ARDF_EN BIT(6)
+#define FSPI_MCR0_RXCLKSRC(x) ((x) << 4)
+#define FSPI_MCR0_END_CFG(x) ((x) << 2)
+#define FSPI_MCR0_MDIS BIT(1)
+#define FSPI_MCR0_SWRST BIT(0)
+
+#define FSPI_MCR1 0x04
+#define FSPI_MCR1_SEQ_TIMEOUT(x) ((x) << 16)
+#define FSPI_MCR1_AHB_TIMEOUT(x) (x)
+
+#define FSPI_MCR2 0x08
+#define FSPI_MCR2_IDLE_WAIT(x) ((x) << 24)
+#define FSPI_MCR2_SAMEDEVICEEN BIT(15)
+#define FSPI_MCR2_CLRLRPHS BIT(14)
+#define FSPI_MCR2_ABRDATSZ BIT(8)
+#define FSPI_MCR2_ABRLEARN BIT(7)
+#define FSPI_MCR2_ABR_READ BIT(6)
+#define FSPI_MCR2_ABRWRITE BIT(5)
+#define FSPI_MCR2_ABRDUMMY BIT(4)
+#define FSPI_MCR2_ABR_MODE BIT(3)
+#define FSPI_MCR2_ABRCADDR BIT(2)
+#define FSPI_MCR2_ABRRADDR BIT(1)
+#define FSPI_MCR2_ABR_CMD BIT(0)
+
+#define FSPI_AHBCR 0x0c
+#define FSPI_AHBCR_RDADDROPT BIT(6)
+#define FSPI_AHBCR_PREF_EN BIT(5)
+#define FSPI_AHBCR_BUFF_EN BIT(4)
+#define FSPI_AHBCR_CACH_EN BIT(3)
+#define FSPI_AHBCR_CLRTXBUF BIT(2)
+#define FSPI_AHBCR_CLRRXBUF BIT(1)
+#define FSPI_AHBCR_PAR_EN BIT(0)
+
+#define FSPI_INTEN 0x10
+#define FSPI_INTEN_SCLKSBWR BIT(9)
+#define FSPI_INTEN_SCLKSBRD BIT(8)
+#define FSPI_INTEN_DATALRNFL BIT(7)
+#define FSPI_INTEN_IPTXWE BIT(6)
+#define FSPI_INTEN_IPRXWA BIT(5)
+#define FSPI_INTEN_AHBCMDERR BIT(4)
+#define FSPI_INTEN_IPCMDERR BIT(3)
+#define FSPI_INTEN_AHBCMDGE BIT(2)
+#define FSPI_INTEN_IPCMDGE BIT(1)
+#define FSPI_INTEN_IPCMDDONE BIT(0)
+
+#define FSPI_INTR 0x14
+#define FSPI_INTR_SCLKSBWR BIT(9)
+#define FSPI_INTR_SCLKSBRD BIT(8)
+#define FSPI_INTR_DATALRNFL BIT(7)
+#define FSPI_INTR_IPTXWE BIT(6)
+#define FSPI_INTR_IPRXWA BIT(5)
+#define FSPI_INTR_AHBCMDERR BIT(4)
+#define FSPI_INTR_IPCMDERR BIT(3)
+#define FSPI_INTR_AHBCMDGE BIT(2)
+#define FSPI_INTR_IPCMDGE BIT(1)
+#define FSPI_INTR_IPCMDDONE BIT(0)
+
+#define FSPI_LUTKEY 0x18
+#define FSPI_LUTKEY_VALUE 0x5AF05AF0
+
+#define FSPI_LCKCR 0x1C
+
+#define FSPI_LCKER_LOCK 0x1
+#define FSPI_LCKER_UNLOCK 0x2
+
+#define FSPI_BUFXCR_INVALID_MSTRID 0xE
+#define FSPI_AHBRX_BUF0CR0 0x20
+#define FSPI_AHBRX_BUF1CR0 0x24
+#define FSPI_AHBRX_BUF2CR0 0x28
+#define FSPI_AHBRX_BUF3CR0 0x2C
+#define FSPI_AHBRX_BUF4CR0 0x30
+#define FSPI_AHBRX_BUF5CR0 0x34
+#define FSPI_AHBRX_BUF6CR0 0x38
+#define FSPI_AHBRX_BUF7CR0 0x3C
+#define FSPI_AHBRXBUF0CR7_PREF BIT(31)
+
+#define FSPI_AHBRX_BUF0CR1 0x40
+#define FSPI_AHBRX_BUF1CR1 0x44
+#define FSPI_AHBRX_BUF2CR1 0x48
+#define FSPI_AHBRX_BUF3CR1 0x4C
+#define FSPI_AHBRX_BUF4CR1 0x50
+#define FSPI_AHBRX_BUF5CR1 0x54
+#define FSPI_AHBRX_BUF6CR1 0x58
+#define FSPI_AHBRX_BUF7CR1 0x5C
+
+#define FSPI_FLSHA1CR0 0x60
+#define FSPI_FLSHA2CR0 0x64
+#define FSPI_FLSHB1CR0 0x68
+#define FSPI_FLSHB2CR0 0x6C
+#define FSPI_FLSHXCR0_SZ_KB 10
+#define FSPI_FLSHXCR0_SZ(x) ((x) >> FSPI_FLSHXCR0_SZ_KB)
+
+#define FSPI_FLSHA1CR1 0x70
+#define FSPI_FLSHA2CR1 0x74
+#define FSPI_FLSHB1CR1 0x78
+#define FSPI_FLSHB2CR1 0x7C
+#define FSPI_FLSHXCR1_CSINTR(x) ((x) << 16)
+#define FSPI_FLSHXCR1_CAS(x) ((x) << 11)
+#define FSPI_FLSHXCR1_WA BIT(10)
+#define FSPI_FLSHXCR1_TCSH(x) ((x) << 5)
+#define FSPI_FLSHXCR1_TCSS(x) (x)
+
+#define FSPI_FLSHA1CR2 0x80
+#define FSPI_FLSHA2CR2 0x84
+#define FSPI_FLSHB1CR2 0x88
+#define FSPI_FLSHB2CR2 0x8C
+#define FSPI_FLSHXCR2_CLRINSP BIT(24)
+#define FSPI_FLSHXCR2_AWRWAIT BIT(16)
+#define FSPI_FLSHXCR2_AWRSEQN_SHIFT 13
+#define FSPI_FLSHXCR2_AWRSEQI_SHIFT 8
+#define FSPI_FLSHXCR2_ARDSEQN_SHIFT 5
+#define FSPI_FLSHXCR2_ARDSEQI_SHIFT 0
+
+#define FSPI_IPCR0 0xA0
+
+#define FSPI_IPCR1 0xA4
+#define FSPI_IPCR1_IPAREN BIT(31)
+#define FSPI_IPCR1_SEQNUM_SHIFT 24
+#define FSPI_IPCR1_SEQID_SHIFT 16
+#define FSPI_IPCR1_IDATSZ(x) (x)
+
+#define FSPI_IPCMD 0xB0
+#define FSPI_IPCMD_TRG BIT(0)
+
+#define FSPI_DLPR 0xB4
+
+#define FSPI_IPRXFCR 0xB8
+#define FSPI_IPRXFCR_CLR BIT(0)
+#define FSPI_IPRXFCR_DMA_EN BIT(1)
+#define FSPI_IPRXFCR_WMRK(x) ((x) << 2)
+
+#define FSPI_IPTXFCR 0xBC
+#define FSPI_IPTXFCR_CLR BIT(0)
+#define FSPI_IPTXFCR_DMA_EN BIT(1)
+#define FSPI_IPTXFCR_WMRK(x) ((x) << 2)
+
+#define FSPI_DLLACR 0xC0
+#define FSPI_DLLACR_OVRDEN BIT(8)
+
+#define FSPI_DLLBCR 0xC4
+#define FSPI_DLLBCR_OVRDEN BIT(8)
+
+#define FSPI_STS0 0xE0
+#define FSPI_STS0_DLPHB(x) ((x) << 8)
+#define FSPI_STS0_DLPHA(x) ((x) << 4)
+#define FSPI_STS0_CMD_SRC(x) ((x) << 2)
+#define FSPI_STS0_ARB_IDLE BIT(1)
+#define FSPI_STS0_SEQ_IDLE BIT(0)
+
+#define FSPI_STS1 0xE4
+#define FSPI_STS1_IP_ERRCD(x) ((x) << 24)
+#define FSPI_STS1_IP_ERRID(x) ((x) << 16)
+#define FSPI_STS1_AHB_ERRCD(x) ((x) << 8)
+#define FSPI_STS1_AHB_ERRID(x) (x)
+
+#define FSPI_AHBSPNST 0xEC
+#define FSPI_AHBSPNST_DATLFT(x) ((x) << 16)
+#define FSPI_AHBSPNST_BUFID(x) ((x) << 1)
+#define FSPI_AHBSPNST_ACTIVE BIT(0)
+
+#define FSPI_IPRXFSTS 0xF0
+#define FSPI_IPRXFSTS_RDCNTR(x) ((x) << 16)
+#define FSPI_IPRXFSTS_FILL(x) (x)
+
+#define FSPI_IPTXFSTS 0xF4
+#define FSPI_IPTXFSTS_WRCNTR(x) ((x) << 16)
+#define FSPI_IPTXFSTS_FILL(x) (x)
+
+#define FSPI_RFDR 0x100
+#define FSPI_TFDR 0x180
+
+#define FSPI_LUT_BASE 0x200
+#define FSPI_LUT_REG(idx) \
+ (FSPI_LUT_BASE + (idx) * 0x10)
+
+/*
+ * Commands
+ */
+#define FSPI_CMD_WRITE_ENABLE 0x06
+#define FSPI_CMD_WRITE_DISABLE 0x04
+#define FSPI_CMD_READ_IDENT 0x9F
+#define FSPI_CMD_READ_STATUS 0x05
+#define FSPI_CMD_WRITE_STATUS 0x01
+#define FSPI_CMD_READ 0x03
+#define FSPI_CMD_FAST_READ 0x0B
+#define FSPI_CMD_PAGE_PROGRAM 0x02
+#define FSPI_CMD_SECTOR_ERASE 0xD8
+#define FSPI_CMD_BULK_ERASE 0xC7
+#define FSPI_CMD_BLOCK_4K_ERASE 0x20
+#define FSPI_CMD_BLOCK_32K_ERASE 0x52
+#define FSPI_CMD_ENTER_4B_MODE 0xB7
+#define FSPI_CMD_EXIT_4B_MODE 0xE9
+#define FSPI_CMD_READ_CTRL_REG 0x35
+#define FSPI_CMD_BANK_REG_WRITE 0x17 /* (spansion) */
+#define FSPI_CMD_SECTOR_ERASE_4B 0xDC
+#define FSPI_CMD_BLOCK_4K_ERASE_4B 0x21
+#define FSPI_CMD_BLOCK_32K_ERASE_4B 0x5C
+#define FSPI_CMD_PAGE_PROGRAM_4B 0x12
+#define FSPI_CMD_FAST_READ_4B 0x0C
+
+
+
+/* register map end */
+
+/* Instruction set for the LUT register. */
+#define LUT_STOP 0x00
+#define LUT_CMD 0x01
+#define LUT_ADDR 0x02
+#define LUT_CADDR_SDR 0x03
+#define LUT_MODE 0x04
+#define LUT_MODE2 0x05
+#define LUT_MODE4 0x06
+#define LUT_MODE8 0x07
+#define LUT_NXP_WRITE 0x08
+#define LUT_NXP_READ 0x09
+#define LUT_LEARN_SDR 0x0A
+#define LUT_DATSZ_SDR 0x0B
+#define LUT_DUMMY 0x0C
+#define LUT_DUMMY_RWDS_SDR 0x0D
+#define LUT_JMP_ON_CS 0x1F
+#define LUT_CMD_DDR 0x21
+#define LUT_ADDR_DDR 0x22
+#define LUT_CADDR_DDR 0x23
+#define LUT_MODE_DDR 0x24
+#define LUT_MODE2_DDR 0x25
+#define LUT_MODE4_DDR 0x26
+#define LUT_MODE8_DDR 0x27
+#define LUT_WRITE_DDR 0x28
+#define LUT_READ_DDR 0x29
+#define LUT_LEARN_DDR 0x2A
+#define LUT_DATSZ_DDR 0x2B
+#define LUT_DUMMY_DDR 0x2C
+#define LUT_DUMMY_RWDS_DDR 0x2D
+
+/* LUT to operation mapping */
+#define LUT_FLASH_CMD_READ 0
+#define LUT_FLASH_CMD_JEDECID 1
+#define LUT_FLASH_CMD_STATUS_READ 2
+#define LUT_FLASH_CMD_PAGE_PROGRAM 3
+#define LUT_FLASH_CMD_WRITE_ENABLE 4
+#define LUT_FLASH_CMD_WRITE_DISABLE 5
+#define LUT_FLASH_CMD_SECTOR_ERASE 6
+
+
+/*
+ * Calculate number of required PAD bits for LUT register.
+ *
+ * The pad stands for the number of IO lines [0:7].
+ * For example, the octal read needs eight IO lines,
+ * so you should use LUT_PAD(8). This macro
+ * returns 3 i.e. use eight (2^3) IP lines for read.
+ */
+#define LUT_PAD(x) (fls(x) - 1)
+
+/*
+ * Macro for constructing the LUT entries with the following
+ * register layout:
+ *
+ * ---------------------------------------------------
+ * | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
+ * ---------------------------------------------------
+ */
+#define PAD_SHIFT 8
+#define INSTR_SHIFT 10
+#define OPRND_SHIFT 16
+
+/* Macros for constructing the LUT register. */
+#define LUT_DEF(idx, ins, pad, opr) \
+ ((((ins) << INSTR_SHIFT) | ((pad) << PAD_SHIFT) | \
+ (opr)) << (((idx) % 2) * OPRND_SHIFT))
+
+#define POLL_TOUT 5000
+#define NXP_FSPI_MAX_CHIPSELECT 4
+#define NXP_FSPI_MIN_IOMAP SZ_4M
+
+#define DCFG_RCWSR1 0x100
+
+/* Access flash memory using IP bus only */
+#define FSPI_QUIRK_USE_IP_ONLY BIT(0)
+
+#define FLASH_SECTORSIZE 512
+
+#define TSTATE_STOPPED 0
+#define TSTATE_STOPPING 1
+#define TSTATE_RUNNING 2
+
+#define STATUS_SRWD BIT(7)
+#define STATUS_BP2 BIT(4)
+#define STATUS_BP1 BIT(3)
+#define STATUS_BP0 BIT(2)
+#define STATUS_WEL BIT(1)
+#define STATUS_WIP BIT(0)
+
+
+#endif /* DEV_FLASH_FLEX_SPI_H_ */