Merge branch 'master' of https://gitlab.denx.de/u-boot/custodians/u-boot-spi

- fix for fsl_qspi read timeout (Thomas)
- spi-mem read data size fix (Ye Li)
- SiFive SPI driver, mmc_spi flags (Bhargav, Anup)
- Micron spi-nor parts (Ashish)
- MT7629 spi-mem driver(Weijie)
diff --git a/MAINTAINERS b/MAINTAINERS
index a72ccd0..ffa8e7b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -205,7 +205,7 @@
 F:	drivers/pinctrl/mediatek/
 F:	drivers/power/domain/mtk-power-domain.c
 F:	drivers/ram/mediatek/
-F:	drivers/spi/mtk_qspi.c
+F:	drivers/spi/mtk_snfi_spi.c
 F:	drivers/timer/mtk_timer.c
 F:	drivers/watchdog/mtk_wdt.c
 F:	drivers/net/mtk_eth.c
diff --git a/configs/mt7629_rfb_defconfig b/configs/mt7629_rfb_defconfig
index d6a7c84..c0a586f 100644
--- a/configs/mt7629_rfb_defconfig
+++ b/configs/mt7629_rfb_defconfig
@@ -64,7 +64,7 @@
 CONFIG_MTK_SERIAL=y
 CONFIG_SPI=y
 CONFIG_DM_SPI=y
-CONFIG_MTK_QSPI=y
+CONFIG_MTK_SNFI_SPI=y
 CONFIG_SYSRESET=y
 CONFIG_SPL_SYSRESET=y
 CONFIG_SYSRESET_WATCHDOG=y
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
index f3d687a..350812a 100644
--- a/drivers/mmc/mmc_spi.c
+++ b/drivers/mmc/mmc_spi.c
@@ -84,7 +84,7 @@
 	cmdo[4] = cmdarg >> 8;
 	cmdo[5] = cmdarg;
 	cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01;
-	ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, 0);
+	ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, SPI_XFER_BEGIN);
 	if (ret)
 		return ret;
 
@@ -360,6 +360,8 @@
 	}
 
 done:
+	dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END);
+
 	dm_spi_release_bus(dev);
 
 	return ret;
diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index ec92976..a3920ba 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -163,11 +163,15 @@
 	{ INFO("n25q128a13",  0x20ba18, 0, 64 * 1024,  256, SECT_4K | SPI_NOR_QUAD_READ) },
 	{ INFO("n25q256a",    0x20ba19, 0, 64 * 1024,  512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
 	{ INFO("n25q256ax1",  0x20bb19, 0, 64 * 1024,  512, SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+	{ INFO6("mt25qu512a",  0x20bb20, 0x104400, 64 * 1024, 1024,
+		 SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
 	{ INFO("n25q512a",    0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
 	{ INFO("n25q512ax3",  0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
 	{ INFO("n25q00",      0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
 	{ INFO("n25q00a",     0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
 	{ INFO("mt25qu02g",   0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
+	{ INFO("mt35xu512aba", 0x2c5b1a, 0,  128 * 1024,  512, USE_FSR | SPI_NOR_4B_OPCODES) },
+	{ INFO("mt35xu02g",  0x2c5b1c, 0, 128 * 1024,  2048, USE_FSR | SPI_NOR_4B_OPCODES) },
 #endif
 #ifdef CONFIG_SPI_FLASH_SPANSION	/* SPANSION */
 	/* Spansion/Cypress -- single (large) sector size only, at least
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index cc174dd..f459c0a 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -158,13 +158,14 @@
 	  the SPI NOR flash on platforms embedding this Ralink / MediaTek
 	  SPI core, like MT7621/7628/7688.
 
-config MTK_QSPI
-	bool "Mediatek QSPI driver"
-	imply SPI_FLASH_BAR
+config MTK_SNFI_SPI
+	bool "Mediatek SPI memory controller driver"
+	depends on SPI_MEM
 	help
-	  Enable the Mediatek QSPI driver. This driver can be
-	  used to access the SPI NOR flash on platforms embedding this
-	  Mediatek QSPI IP core.
+	  Enable the Mediatek SPI memory controller driver. This driver is
+	  originally based on the MediaTek SNFI IP core. It can only be
+	  used to access SPI memory devices like SPI-NOR or SPI-NAND on
+	  platforms embedding this IP core, like MT7622/M7629.
 
 config MVEBU_A3700_SPI
 	bool "Marvell Armada 3700 SPI driver"
@@ -232,6 +233,14 @@
 		};
 	  };
 
+config SPI_SIFIVE
+	bool "SiFive SPI driver"
+	help
+	  This driver supports the SiFive SPI IP. If unsure say N.
+	  Enable the SiFive SPI controller driver.
+
+	  The SiFive SPI controller driver is found on various SiFive SoCs.
+
 config SPI_SUNXI
 	bool "Allwinner SoC SPI controllers"
 	help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index ab84122..ae4f295 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -37,7 +37,7 @@
 obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o
 obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o
 obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
-obj-$(CONFIG_MTK_QSPI) += mtk_qspi.o
+obj-$(CONFIG_MTK_SNFI_SPI) += mtk_snfi_spi.o
 obj-$(CONFIG_MT7621_SPI) += mt7621_spi.o
 obj-$(CONFIG_MSCC_BB_SPI) += mscc_bb_spi.o
 obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o
@@ -50,6 +50,7 @@
 obj-$(CONFIG_RENESAS_RPC_SPI) += renesas_rpc_spi.o
 obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o
 obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o
+obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o
 obj-$(CONFIG_SPI_SUNXI) += spi-sunxi.o
 obj-$(CONFIG_SH_SPI) += sh_spi.o
 obj-$(CONFIG_SH_QSPI) += sh_qspi.o
diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c
index 1598c4f..41abe19 100644
--- a/drivers/spi/fsl_qspi.c
+++ b/drivers/spi/fsl_qspi.c
@@ -10,6 +10,7 @@
 #include <spi.h>
 #include <asm/io.h>
 #include <linux/sizes.h>
+#include <linux/iopoll.h>
 #include <dm.h>
 #include <errno.h>
 #include <watchdog.h>
@@ -150,20 +151,13 @@
 static inline int is_controller_busy(const struct fsl_qspi_priv *priv)
 {
 	u32 val;
-	const u32 mask = QSPI_SR_BUSY_MASK | QSPI_SR_AHB_ACC_MASK |
-			 QSPI_SR_IP_ACC_MASK;
-	unsigned int retry = 5;
+	u32 mask = QSPI_SR_BUSY_MASK | QSPI_SR_AHB_ACC_MASK |
+		   QSPI_SR_IP_ACC_MASK;
 
-	do {
-		val = qspi_read32(priv->flags, &priv->regs->sr);
-
-		if ((~val & mask) == mask)
-			return 0;
-
-		udelay(1);
-	} while (--retry);
+	if (priv->flags & QSPI_FLAG_REGMAP_ENDIAN_BIG)
+		mask = (u32)cpu_to_be32(mask);
 
-	return -ETIMEDOUT;
+	return readl_poll_timeout(&priv->regs->sr, val, !(val & mask), 1000);
 }
 
 /* QSPI support swapping the flash read/write data
diff --git a/drivers/spi/mtk_qspi.c b/drivers/spi/mtk_qspi.c
deleted file mode 100644
index b510733..0000000
--- a/drivers/spi/mtk_qspi.c
+++ /dev/null
@@ -1,359 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2018  MediaTek, Inc.
- * Author : Guochun.Mao@mediatek.com
- */
-
-#include <common.h>
-#include <dm.h>
-#include <malloc.h>
-#include <spi.h>
-#include <asm/io.h>
-#include <linux/iopoll.h>
-#include <linux/ioport.h>
-
-/* Register Offset */
-struct mtk_qspi_regs {
-	u32 cmd;
-	u32 cnt;
-	u32 rdsr;
-	u32 rdata;
-	u32 radr[3];
-	u32 wdata;
-	u32 prgdata[6];
-	u32 shreg[10];
-	u32 cfg[2];
-	u32 shreg10;
-	u32 mode_mon;
-	u32 status[4];
-	u32 flash_time;
-	u32 flash_cfg;
-	u32 reserved_0[3];
-	u32 sf_time;
-	u32 pp_dw_data;
-	u32 reserved_1;
-	u32 delsel_0[2];
-	u32 intrstus;
-	u32 intren;
-	u32 reserved_2;
-	u32 cfg3;
-	u32 reserved_3;
-	u32 chksum;
-	u32 aaicmd;
-	u32 wrprot;
-	u32 radr3;
-	u32 dual;
-	u32 delsel_1[3];
-};
-
-struct mtk_qspi_platdata {
-	fdt_addr_t reg_base;
-	fdt_addr_t mem_base;
-};
-
-struct mtk_qspi_priv {
-	struct mtk_qspi_regs *regs;
-	unsigned long *mem_base;
-	u8 op;
-	u8 tx[3]; /* only record max 3 bytes paras, when it's address. */
-	u32 txlen; /* dout buffer length  - op code length */
-	u8 *rx;
-	u32 rxlen;
-};
-
-#define MTK_QSPI_CMD_POLLINGREG_US 500000
-#define MTK_QSPI_WRBUF_SIZE        256
-#define MTK_QSPI_COMMAND_ENABLE    0x30
-
-/* NOR flash controller commands */
-#define MTK_QSPI_RD_TRIGGER        BIT(0)
-#define MTK_QSPI_READSTATUS        BIT(1)
-#define MTK_QSPI_PRG_CMD           BIT(2)
-#define MTK_QSPI_WR_TRIGGER        BIT(4)
-#define MTK_QSPI_WRITESTATUS       BIT(5)
-#define MTK_QSPI_AUTOINC           BIT(7)
-
-#define MTK_QSPI_MAX_RX_TX_SHIFT   0x6
-#define MTK_QSPI_MAX_SHIFT         0x8
-
-#define MTK_QSPI_WR_BUF_ENABLE     0x1
-#define MTK_QSPI_WR_BUF_DISABLE    0x0
-
-static int mtk_qspi_execute_cmd(struct mtk_qspi_priv *priv, u8 cmd)
-{
-	u8 tmp;
-	u8 val = cmd & ~MTK_QSPI_AUTOINC;
-
-	writeb(cmd, &priv->regs->cmd);
-
-	return readb_poll_timeout(&priv->regs->cmd, tmp, !(val & tmp),
-				  MTK_QSPI_CMD_POLLINGREG_US);
-}
-
-static int mtk_qspi_tx_rx(struct mtk_qspi_priv *priv)
-{
-	int len = 1 + priv->txlen + priv->rxlen;
-	int i, ret, idx;
-
-	if (len > MTK_QSPI_MAX_SHIFT)
-		return -ERR_INVAL;
-
-	writeb(len * 8, &priv->regs->cnt);
-
-	/* start at PRGDATA5, go down to PRGDATA0 */
-	idx = MTK_QSPI_MAX_RX_TX_SHIFT - 1;
-
-	/* opcode */
-	writeb(priv->op, &priv->regs->prgdata[idx]);
-	idx--;
-
-	/* program TX data */
-	for (i = 0; i < priv->txlen; i++, idx--)
-		writeb(priv->tx[i], &priv->regs->prgdata[idx]);
-
-	/* clear out rest of TX registers */
-	while (idx >= 0) {
-		writeb(0, &priv->regs->prgdata[idx]);
-		idx--;
-	}
-
-	ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_PRG_CMD);
-	if (ret)
-		return ret;
-
-	/* restart at first RX byte */
-	idx = priv->rxlen - 1;
-
-	/* read out RX data */
-	for (i = 0; i < priv->rxlen; i++, idx--)
-		priv->rx[i] = readb(&priv->regs->shreg[idx]);
-
-	return 0;
-}
-
-static int mtk_qspi_read(struct mtk_qspi_priv *priv,
-			 u32 addr, u8 *buf, u32 len)
-{
-	memcpy(buf, (u8 *)priv->mem_base + addr, len);
-	return 0;
-}
-
-static void mtk_qspi_set_addr(struct mtk_qspi_priv *priv, u32 addr)
-{
-	int i;
-
-	for (i = 0; i < 3; i++) {
-		writeb(addr & 0xff, &priv->regs->radr[i]);
-		addr >>= 8;
-	}
-}
-
-static int mtk_qspi_write_single_byte(struct mtk_qspi_priv *priv,
-				      u32 addr, u32 length, const u8 *data)
-{
-	int i, ret;
-
-	mtk_qspi_set_addr(priv, addr);
-
-	for (i = 0; i < length; i++) {
-		writeb(*data++, &priv->regs->wdata);
-		ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER);
-		if (ret < 0)
-			return ret;
-	}
-	return 0;
-}
-
-static int mtk_qspi_write_buffer(struct mtk_qspi_priv *priv, u32 addr,
-				 const u8 *buf)
-{
-	int i, data;
-
-	mtk_qspi_set_addr(priv, addr);
-
-	for (i = 0; i < MTK_QSPI_WRBUF_SIZE; i += 4) {
-		data = buf[i + 3] << 24 | buf[i + 2] << 16 |
-		       buf[i + 1] << 8 | buf[i];
-		writel(data, &priv->regs->pp_dw_data);
-	}
-
-	return mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER);
-}
-
-static int mtk_qspi_write(struct mtk_qspi_priv *priv,
-			  u32 addr, const u8 *buf, u32 len)
-{
-	int ret;
-
-	/* setting pre-fetch buffer for page program */
-	writel(MTK_QSPI_WR_BUF_ENABLE, &priv->regs->cfg[1]);
-	while (len >= MTK_QSPI_WRBUF_SIZE) {
-		ret = mtk_qspi_write_buffer(priv, addr, buf);
-		if (ret < 0)
-			return ret;
-
-		len -= MTK_QSPI_WRBUF_SIZE;
-		addr += MTK_QSPI_WRBUF_SIZE;
-		buf += MTK_QSPI_WRBUF_SIZE;
-	}
-	/* disable pre-fetch buffer for page program */
-	writel(MTK_QSPI_WR_BUF_DISABLE, &priv->regs->cfg[1]);
-
-	if (len)
-		return mtk_qspi_write_single_byte(priv, addr, len, buf);
-
-	return 0;
-}
-
-static int mtk_qspi_claim_bus(struct udevice *dev)
-{
-	/* nothing to do */
-	return 0;
-}
-
-static int mtk_qspi_release_bus(struct udevice *dev)
-{
-	/* nothing to do */
-	return 0;
-}
-
-static int mtk_qspi_transfer(struct mtk_qspi_priv *priv, unsigned int bitlen,
-			     const void *dout, void *din, unsigned long flags)
-{
-	u32 bytes = DIV_ROUND_UP(bitlen, 8);
-	u32 addr;
-
-	if (!bytes)
-		return -ERR_INVAL;
-
-	if (dout) {
-		if (flags & SPI_XFER_BEGIN) {
-			/* parse op code and potential paras first */
-			priv->op = *(u8 *)dout;
-			if (bytes > 1)
-				memcpy(priv->tx, (u8 *)dout + 1,
-				       bytes <= 4 ? bytes - 1 : 3);
-			priv->txlen = bytes - 1;
-		}
-
-		if (flags == SPI_XFER_ONCE) {
-			/* operations without receiving or sending data.
-			 * for example: erase, write flash register or write
-			 * enable...
-			 */
-			priv->rx = NULL;
-			priv->rxlen = 0;
-			return mtk_qspi_tx_rx(priv);
-		}
-
-		if (flags & SPI_XFER_END) {
-			/* here, dout should be data to be written.
-			 * and priv->tx should be filled 3Bytes address.
-			 */
-			addr = priv->tx[0] << 16 | priv->tx[1] << 8 |
-			       priv->tx[2];
-			return mtk_qspi_write(priv, addr, (u8 *)dout, bytes);
-		}
-	}
-
-	if (din) {
-		if (priv->txlen >= 3) {
-			/* if run to here, priv->tx[] should be the address
-			 * where read data from,
-			 * and, din is the buf to receive data.
-			 */
-			addr = priv->tx[0] << 16 | priv->tx[1] << 8 |
-			       priv->tx[2];
-			return mtk_qspi_read(priv, addr, (u8 *)din, bytes);
-		}
-
-		/* should be reading flash's register */
-		priv->rx = (u8 *)din;
-		priv->rxlen = bytes;
-		return mtk_qspi_tx_rx(priv);
-	}
-
-	return 0;
-}
-
-static int mtk_qspi_xfer(struct udevice *dev, unsigned int bitlen,
-			 const void *dout, void *din, unsigned long flags)
-{
-	struct udevice *bus = dev->parent;
-	struct mtk_qspi_priv *priv = dev_get_priv(bus);
-
-	return  mtk_qspi_transfer(priv, bitlen, dout, din, flags);
-}
-
-static int mtk_qspi_set_speed(struct udevice *bus, uint speed)
-{
-	/* nothing to do */
-	return 0;
-}
-
-static int mtk_qspi_set_mode(struct udevice *bus, uint mode)
-{
-	/* nothing to do */
-	return 0;
-}
-
-static int mtk_qspi_ofdata_to_platdata(struct udevice *bus)
-{
-	struct resource res_reg, res_mem;
-	struct mtk_qspi_platdata *plat = bus->platdata;
-	int ret;
-
-	ret = dev_read_resource_byname(bus, "reg_base", &res_reg);
-	if (ret) {
-		debug("can't get reg_base resource(ret = %d)\n", ret);
-		return -ENOMEM;
-	}
-
-	ret = dev_read_resource_byname(bus, "mem_base", &res_mem);
-	if (ret) {
-		debug("can't get map_base resource(ret = %d)\n", ret);
-		return -ENOMEM;
-	}
-
-	plat->mem_base = res_mem.start;
-	plat->reg_base = res_reg.start;
-
-	return 0;
-}
-
-static int mtk_qspi_probe(struct udevice *bus)
-{
-	struct mtk_qspi_platdata *plat = dev_get_platdata(bus);
-	struct mtk_qspi_priv *priv = dev_get_priv(bus);
-
-	priv->regs = (struct mtk_qspi_regs *)plat->reg_base;
-	priv->mem_base = (unsigned long *)plat->mem_base;
-
-	writel(MTK_QSPI_COMMAND_ENABLE, &priv->regs->wrprot);
-
-	return 0;
-}
-
-static const struct dm_spi_ops mtk_qspi_ops = {
-	.claim_bus      = mtk_qspi_claim_bus,
-	.release_bus    = mtk_qspi_release_bus,
-	.xfer           = mtk_qspi_xfer,
-	.set_speed      = mtk_qspi_set_speed,
-	.set_mode       = mtk_qspi_set_mode,
-};
-
-static const struct udevice_id mtk_qspi_ids[] = {
-	{ .compatible = "mediatek,mt7629-qspi" },
-	{ }
-};
-
-U_BOOT_DRIVER(mtk_qspi) = {
-	.name     = "mtk_qspi",
-	.id       = UCLASS_SPI,
-	.of_match = mtk_qspi_ids,
-	.ops      = &mtk_qspi_ops,
-	.ofdata_to_platdata       = mtk_qspi_ofdata_to_platdata,
-	.platdata_auto_alloc_size = sizeof(struct mtk_qspi_platdata),
-	.priv_auto_alloc_size     = sizeof(struct mtk_qspi_priv),
-	.probe    = mtk_qspi_probe,
-};
diff --git a/drivers/spi/mtk_snfi_spi.c b/drivers/spi/mtk_snfi_spi.c
new file mode 100644
index 0000000..2a89476
--- /dev/null
+++ b/drivers/spi/mtk_snfi_spi.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <spi.h>
+#include <spi-mem.h>
+#include <stdbool.h>
+#include <watchdog.h>
+#include <dm/pinctrl.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#define SNFI_MAC_CTL			0x500
+#define MAC_XIO_SEL			BIT(4)
+#define SF_MAC_EN			BIT(3)
+#define SF_TRIG				BIT(2)
+#define WIP_READY			BIT(1)
+#define WIP				BIT(0)
+
+#define SNFI_MAC_OUTL			0x504
+#define SNFI_MAC_INL			0x508
+
+#define SNFI_MISC_CTL			0x538
+#define SW_RST				BIT(28)
+#define FIFO_RD_LTC_SHIFT		25
+#define FIFO_RD_LTC			GENMASK(26, 25)
+#define LATCH_LAT_SHIFT			8
+#define LATCH_LAT			GENMASK(9, 8)
+#define CS_DESELECT_CYC_SHIFT		0
+#define CS_DESELECT_CYC			GENMASK(4, 0)
+
+#define SNF_STA_CTL1			0x550
+#define SPI_STATE			GENMASK(3, 0)
+
+#define SNFI_GPRAM_OFFSET		0x800
+#define SNFI_GPRAM_SIZE			0x80
+
+#define SNFI_POLL_INTERVAL		500000
+#define SNFI_RST_POLL_INTERVAL		1000000
+
+struct mtk_snfi_priv {
+	void __iomem *base;
+
+	struct clk nfi_clk;
+	struct clk pad_clk;
+};
+
+static int mtk_snfi_adjust_op_size(struct spi_slave *slave,
+				   struct spi_mem_op *op)
+{
+	u32 nbytes;
+
+	/*
+	 * When there is input data, it will be appended after the output
+	 * data in the GPRAM. So the total size of either pure output data
+	 * or the output+input data must not exceed the GPRAM size.
+	 */
+
+	nbytes = sizeof(op->cmd.opcode) + op->addr.nbytes +
+		op->dummy.nbytes;
+
+	if (nbytes + op->data.nbytes <= SNFI_GPRAM_SIZE)
+		return 0;
+
+	if (nbytes >= SNFI_GPRAM_SIZE)
+		return -ENOTSUPP;
+
+	op->data.nbytes = SNFI_GPRAM_SIZE - nbytes;
+
+	return 0;
+}
+
+static bool mtk_snfi_supports_op(struct spi_slave *slave,
+				 const struct spi_mem_op *op)
+{
+	if (op->cmd.buswidth > 1 || op->addr.buswidth > 1 ||
+	    op->dummy.buswidth > 1 || op->data.buswidth > 1)
+		return false;
+
+	return true;
+}
+
+static int mtk_snfi_mac_trigger(struct mtk_snfi_priv *priv,
+				struct udevice *bus, u32 outlen, u32 inlen)
+{
+	int ret;
+	u32 val;
+
+#ifdef CONFIG_PINCTRL
+	pinctrl_select_state(bus, "snfi");
+#endif
+
+	writel(SF_MAC_EN, priv->base + SNFI_MAC_CTL);
+	writel(outlen, priv->base + SNFI_MAC_OUTL);
+	writel(inlen, priv->base + SNFI_MAC_INL);
+
+	writel(SF_MAC_EN | SF_TRIG, priv->base + SNFI_MAC_CTL);
+
+	ret = readl_poll_timeout(priv->base + SNFI_MAC_CTL, val,
+				 val & WIP_READY, SNFI_POLL_INTERVAL);
+	if (ret) {
+		printf("%s: timed out waiting for WIP_READY\n", __func__);
+		goto cleanup;
+	}
+
+	ret = readl_poll_timeout(priv->base + SNFI_MAC_CTL, val,
+				 !(val & WIP), SNFI_POLL_INTERVAL);
+	if (ret)
+		printf("%s: timed out waiting for WIP cleared\n", __func__);
+
+	writel(0, priv->base + SNFI_MAC_CTL);
+
+cleanup:
+#ifdef CONFIG_PINCTRL
+	pinctrl_select_state(bus, "default");
+#endif
+
+	return ret;
+}
+
+static int mtk_snfi_mac_reset(struct mtk_snfi_priv *priv)
+{
+	int ret;
+	u32 val;
+
+	setbits_32(priv->base + SNFI_MISC_CTL, SW_RST);
+
+	ret = readl_poll_timeout(priv->base + SNF_STA_CTL1, val,
+				 !(val & SPI_STATE), SNFI_POLL_INTERVAL);
+	if (ret)
+		printf("%s: failed to reset snfi mac\n", __func__);
+
+	writel((2 << FIFO_RD_LTC_SHIFT) |
+		(10 << CS_DESELECT_CYC_SHIFT),
+		priv->base + SNFI_MISC_CTL);
+
+	return ret;
+}
+
+static void mtk_snfi_copy_to_gpram(struct mtk_snfi_priv *priv,
+				   const void *data, size_t len)
+{
+	void __iomem *gpram = priv->base + SNFI_GPRAM_OFFSET;
+	size_t i, n = (len + sizeof(u32) - 1) / sizeof(u32);
+	const u32 *buff = data;
+
+	/*
+	 * The output data will always be copied to the beginning of
+	 * the GPRAM. Uses word write for better performace.
+	 *
+	 * Trailing bytes in the last word are not cared.
+	 */
+
+	for (i = 0; i < n; i++)
+		writel(buff[i], gpram + i * sizeof(u32));
+}
+
+static void mtk_snfi_copy_from_gpram(struct mtk_snfi_priv *priv, u8 *cache,
+				     void *data, size_t pos, size_t len)
+{
+	void __iomem *gpram = priv->base + SNFI_GPRAM_OFFSET;
+	u32 *buff = (u32 *)cache;
+	size_t i, off, end;
+
+	/* Start position in the buffer */
+	off = pos & (sizeof(u32) - 1);
+
+	/* End position for copy */
+	end = (len + pos + sizeof(u32) - 1) & (~(sizeof(u32) - 1));
+
+	/* Start position for copy */
+	pos &= ~(sizeof(u32) - 1);
+
+	/*
+	 * Read aligned data from GPRAM to buffer first.
+	 * Uses word read for better performace.
+	 */
+	i = 0;
+	while (pos < end) {
+		buff[i++] = readl(gpram + pos);
+		pos += sizeof(u32);
+	}
+
+	/* Copy rx data */
+	memcpy(data, cache + off, len);
+}
+
+static int mtk_snfi_exec_op(struct spi_slave *slave,
+			    const struct spi_mem_op *op)
+{
+	struct udevice *bus = dev_get_parent(slave->dev);
+	struct mtk_snfi_priv *priv = dev_get_priv(bus);
+	u8 gpram_cache[SNFI_GPRAM_SIZE];
+	u32 i, len = 0, inlen = 0;
+	int addr_sh;
+	int ret;
+
+	WATCHDOG_RESET();
+
+	ret = mtk_snfi_mac_reset(priv);
+	if (ret)
+		return ret;
+
+	/* Put opcode */
+	gpram_cache[len++] = op->cmd.opcode;
+
+	/* Put address */
+	addr_sh = (op->addr.nbytes - 1) * 8;
+	while (addr_sh >= 0) {
+		gpram_cache[len++] = (op->addr.val >> addr_sh) & 0xff;
+		addr_sh -= 8;
+	}
+
+	/* Put dummy bytes */
+	for (i = 0; i < op->dummy.nbytes; i++)
+		gpram_cache[len++] = 0;
+
+	/* Put output data */
+	if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) {
+		memcpy(gpram_cache + len, op->data.buf.out, op->data.nbytes);
+		len += op->data.nbytes;
+	}
+
+	/* Copy final output data to GPRAM */
+	mtk_snfi_copy_to_gpram(priv, gpram_cache, len);
+
+	/* Start one SPI transaction */
+	if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN)
+		inlen = op->data.nbytes;
+
+	ret = mtk_snfi_mac_trigger(priv, bus, len, inlen);
+	if (ret)
+		return ret;
+
+	/* Copy input data from GPRAM */
+	if (inlen)
+		mtk_snfi_copy_from_gpram(priv, gpram_cache, op->data.buf.in,
+					 len, inlen);
+
+	return 0;
+}
+
+static int mtk_snfi_spi_probe(struct udevice *bus)
+{
+	struct mtk_snfi_priv *priv = dev_get_priv(bus);
+	int ret;
+
+	priv->base = (void __iomem *)devfdt_get_addr(bus);
+	if (!priv->base)
+		return -EINVAL;
+
+	ret = clk_get_by_name(bus, "nfi_clk", &priv->nfi_clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_get_by_name(bus, "pad_clk", &priv->pad_clk);
+	if (ret < 0)
+		return ret;
+
+	clk_enable(&priv->nfi_clk);
+	clk_enable(&priv->pad_clk);
+
+	return 0;
+}
+
+static int mtk_snfi_set_speed(struct udevice *bus, uint speed)
+{
+	/*
+	 * The SNFI does not have a bus clock divider.
+	 * The bus clock is set in dts (pad_clk, UNIVPLL2_D8 = 50MHz).
+	 */
+
+	return 0;
+}
+
+static int mtk_snfi_set_mode(struct udevice *bus, uint mode)
+{
+	/* The SNFI supports only mode 0 */
+
+	if (mode)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct spi_controller_mem_ops mtk_snfi_mem_ops = {
+	.adjust_op_size = mtk_snfi_adjust_op_size,
+	.supports_op = mtk_snfi_supports_op,
+	.exec_op = mtk_snfi_exec_op,
+};
+
+static const struct dm_spi_ops mtk_snfi_spi_ops = {
+	.mem_ops	= &mtk_snfi_mem_ops,
+	.set_speed	= mtk_snfi_set_speed,
+	.set_mode	= mtk_snfi_set_mode,
+};
+
+static const struct udevice_id mtk_snfi_spi_ids[] = {
+	{ .compatible = "mediatek,mtk-snfi-spi" },
+	{ }
+};
+
+U_BOOT_DRIVER(mtk_snfi_spi) = {
+	.name			= "mtk_snfi_spi",
+	.id			= UCLASS_SPI,
+	.of_match		= mtk_snfi_spi_ids,
+	.ops			= &mtk_snfi_spi_ops,
+	.priv_auto_alloc_size	= sizeof(struct mtk_snfi_priv),
+	.probe			= mtk_snfi_spi_probe,
+};
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 7aabebe..7788ab9 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -430,12 +430,14 @@
 		if (slave->max_write_size && len > slave->max_write_size)
 			return -EINVAL;
 
-		if (op->data.dir == SPI_MEM_DATA_IN && slave->max_read_size)
-			op->data.nbytes = min(op->data.nbytes,
+		if (op->data.dir == SPI_MEM_DATA_IN) {
+			if (slave->max_read_size)
+				op->data.nbytes = min(op->data.nbytes,
 					      slave->max_read_size);
-		else if (slave->max_write_size)
+		} else if (slave->max_write_size) {
 			op->data.nbytes = min(op->data.nbytes,
 					      slave->max_write_size - len);
+		}
 
 		if (!op->data.nbytes)
 			return -EINVAL;
diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c
new file mode 100644
index 0000000..969bd4b
--- /dev/null
+++ b/drivers/spi/spi-sifive.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 SiFive, Inc.
+ * Copyright 2019 Bhargav Shah <bhargavshah1988@gmail.com>
+ *
+ * SiFive SPI controller driver (master mode only)
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <spi.h>
+#include <asm/io.h>
+#include <linux/log2.h>
+#include <clk.h>
+
+#define SIFIVE_SPI_MAX_CS		32
+
+#define SIFIVE_SPI_DEFAULT_DEPTH	8
+#define SIFIVE_SPI_DEFAULT_BITS		8
+
+/* register offsets */
+#define SIFIVE_SPI_REG_SCKDIV            0x00 /* Serial clock divisor */
+#define SIFIVE_SPI_REG_SCKMODE           0x04 /* Serial clock mode */
+#define SIFIVE_SPI_REG_CSID              0x10 /* Chip select ID */
+#define SIFIVE_SPI_REG_CSDEF             0x14 /* Chip select default */
+#define SIFIVE_SPI_REG_CSMODE            0x18 /* Chip select mode */
+#define SIFIVE_SPI_REG_DELAY0            0x28 /* Delay control 0 */
+#define SIFIVE_SPI_REG_DELAY1            0x2c /* Delay control 1 */
+#define SIFIVE_SPI_REG_FMT               0x40 /* Frame format */
+#define SIFIVE_SPI_REG_TXDATA            0x48 /* Tx FIFO data */
+#define SIFIVE_SPI_REG_RXDATA            0x4c /* Rx FIFO data */
+#define SIFIVE_SPI_REG_TXMARK            0x50 /* Tx FIFO watermark */
+#define SIFIVE_SPI_REG_RXMARK            0x54 /* Rx FIFO watermark */
+#define SIFIVE_SPI_REG_FCTRL             0x60 /* SPI flash interface control */
+#define SIFIVE_SPI_REG_FFMT              0x64 /* SPI flash instruction format */
+#define SIFIVE_SPI_REG_IE                0x70 /* Interrupt Enable Register */
+#define SIFIVE_SPI_REG_IP                0x74 /* Interrupt Pendings Register */
+
+/* sckdiv bits */
+#define SIFIVE_SPI_SCKDIV_DIV_MASK       0xfffU
+
+/* sckmode bits */
+#define SIFIVE_SPI_SCKMODE_PHA           BIT(0)
+#define SIFIVE_SPI_SCKMODE_POL           BIT(1)
+#define SIFIVE_SPI_SCKMODE_MODE_MASK     (SIFIVE_SPI_SCKMODE_PHA | \
+					  SIFIVE_SPI_SCKMODE_POL)
+
+/* csmode bits */
+#define SIFIVE_SPI_CSMODE_MODE_AUTO      0U
+#define SIFIVE_SPI_CSMODE_MODE_HOLD      2U
+#define SIFIVE_SPI_CSMODE_MODE_OFF       3U
+
+/* delay0 bits */
+#define SIFIVE_SPI_DELAY0_CSSCK(x)       ((u32)(x))
+#define SIFIVE_SPI_DELAY0_CSSCK_MASK     0xffU
+#define SIFIVE_SPI_DELAY0_SCKCS(x)       ((u32)(x) << 16)
+#define SIFIVE_SPI_DELAY0_SCKCS_MASK     (0xffU << 16)
+
+/* delay1 bits */
+#define SIFIVE_SPI_DELAY1_INTERCS(x)     ((u32)(x))
+#define SIFIVE_SPI_DELAY1_INTERCS_MASK   0xffU
+#define SIFIVE_SPI_DELAY1_INTERXFR(x)    ((u32)(x) << 16)
+#define SIFIVE_SPI_DELAY1_INTERXFR_MASK  (0xffU << 16)
+
+/* fmt bits */
+#define SIFIVE_SPI_FMT_PROTO_SINGLE      0U
+#define SIFIVE_SPI_FMT_PROTO_DUAL        1U
+#define SIFIVE_SPI_FMT_PROTO_QUAD        2U
+#define SIFIVE_SPI_FMT_PROTO_MASK        3U
+#define SIFIVE_SPI_FMT_ENDIAN            BIT(2)
+#define SIFIVE_SPI_FMT_DIR               BIT(3)
+#define SIFIVE_SPI_FMT_LEN(x)            ((u32)(x) << 16)
+#define SIFIVE_SPI_FMT_LEN_MASK          (0xfU << 16)
+
+/* txdata bits */
+#define SIFIVE_SPI_TXDATA_DATA_MASK      0xffU
+#define SIFIVE_SPI_TXDATA_FULL           BIT(31)
+
+/* rxdata bits */
+#define SIFIVE_SPI_RXDATA_DATA_MASK      0xffU
+#define SIFIVE_SPI_RXDATA_EMPTY          BIT(31)
+
+/* ie and ip bits */
+#define SIFIVE_SPI_IP_TXWM               BIT(0)
+#define SIFIVE_SPI_IP_RXWM               BIT(1)
+
+struct sifive_spi {
+	void		*regs;		/* base address of the registers */
+	u32		fifo_depth;
+	u32		bits_per_word;
+	u32		cs_inactive;	/* Level of the CS pins when inactive*/
+	u32		freq;
+	u32		num_cs;
+};
+
+static void sifive_spi_prep_device(struct sifive_spi *spi,
+				   struct dm_spi_slave_platdata *slave)
+{
+	/* Update the chip select polarity */
+	if (slave->mode & SPI_CS_HIGH)
+		spi->cs_inactive &= ~BIT(slave->cs);
+	else
+		spi->cs_inactive |= BIT(slave->cs);
+	writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
+
+	/* Select the correct device */
+	writel(slave->cs, spi->regs + SIFIVE_SPI_REG_CSID);
+}
+
+static int sifive_spi_set_cs(struct sifive_spi *spi,
+			     struct dm_spi_slave_platdata *slave)
+{
+	u32 cs_mode = SIFIVE_SPI_CSMODE_MODE_HOLD;
+
+	if (slave->mode & SPI_CS_HIGH)
+		cs_mode = SIFIVE_SPI_CSMODE_MODE_AUTO;
+
+	writel(cs_mode, spi->regs + SIFIVE_SPI_REG_CSMODE);
+
+	return 0;
+}
+
+static void sifive_spi_clear_cs(struct sifive_spi *spi)
+{
+	writel(SIFIVE_SPI_CSMODE_MODE_AUTO, spi->regs + SIFIVE_SPI_REG_CSMODE);
+}
+
+static void sifive_spi_prep_transfer(struct sifive_spi *spi,
+				     bool is_rx_xfer,
+				     struct dm_spi_slave_platdata *slave)
+{
+	u32 cr;
+
+	/* Modify the SPI protocol mode */
+	cr = readl(spi->regs + SIFIVE_SPI_REG_FMT);
+
+	/* Bits per word ? */
+	cr &= ~SIFIVE_SPI_FMT_LEN_MASK;
+	cr |= SIFIVE_SPI_FMT_LEN(spi->bits_per_word);
+
+	/* LSB first? */
+	cr &= ~SIFIVE_SPI_FMT_ENDIAN;
+	if (slave->mode & SPI_LSB_FIRST)
+		cr |= SIFIVE_SPI_FMT_ENDIAN;
+
+	/* Number of wires ? */
+	cr &= ~SIFIVE_SPI_FMT_PROTO_MASK;
+	if ((slave->mode & SPI_TX_QUAD) || (slave->mode & SPI_RX_QUAD))
+		cr |= SIFIVE_SPI_FMT_PROTO_QUAD;
+	else if ((slave->mode & SPI_TX_DUAL) || (slave->mode & SPI_RX_DUAL))
+		cr |= SIFIVE_SPI_FMT_PROTO_DUAL;
+	else
+		cr |= SIFIVE_SPI_FMT_PROTO_SINGLE;
+
+	/* SPI direction in/out ? */
+	cr &= ~SIFIVE_SPI_FMT_DIR;
+	if (!is_rx_xfer)
+		cr |= SIFIVE_SPI_FMT_DIR;
+
+	writel(cr, spi->regs + SIFIVE_SPI_REG_FMT);
+}
+
+static void sifive_spi_rx(struct sifive_spi *spi, u8 *rx_ptr)
+{
+	u32 data;
+
+	do {
+		data = readl(spi->regs + SIFIVE_SPI_REG_RXDATA);
+	} while (data & SIFIVE_SPI_RXDATA_EMPTY);
+
+	if (rx_ptr)
+		*rx_ptr = data & SIFIVE_SPI_RXDATA_DATA_MASK;
+}
+
+static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr)
+{
+	u32 data;
+	u8 tx_data = (tx_ptr) ? *tx_ptr & SIFIVE_SPI_TXDATA_DATA_MASK :
+				SIFIVE_SPI_TXDATA_DATA_MASK;
+
+	do {
+		data = readl(spi->regs + SIFIVE_SPI_REG_TXDATA);
+	} while (data & SIFIVE_SPI_TXDATA_FULL);
+
+	writel(tx_data, spi->regs + SIFIVE_SPI_REG_TXDATA);
+}
+
+static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
+			   const void *dout, void *din, unsigned long flags)
+{
+	struct udevice *bus = dev->parent;
+	struct sifive_spi *spi = dev_get_priv(bus);
+	struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
+	const unsigned char *tx_ptr = dout;
+	u8 *rx_ptr = din;
+	u32 remaining_len;
+	int ret;
+
+	if (flags & SPI_XFER_BEGIN) {
+		sifive_spi_prep_device(spi, slave);
+
+		ret = sifive_spi_set_cs(spi, slave);
+		if (ret)
+			return ret;
+	}
+
+	sifive_spi_prep_transfer(spi, true, slave);
+
+	remaining_len = bitlen / 8;
+
+	while (remaining_len) {
+		int n_words, tx_words, rx_words;
+
+		n_words = min(remaining_len, spi->fifo_depth);
+
+		/* Enqueue n_words for transmission */
+		if (tx_ptr) {
+			for (tx_words = 0; tx_words < n_words; ++tx_words) {
+				sifive_spi_tx(spi, tx_ptr);
+				sifive_spi_rx(spi, NULL);
+				tx_ptr++;
+			}
+		}
+
+		/* Read out all the data from the RX FIFO */
+		if (rx_ptr) {
+			for (rx_words = 0; rx_words < n_words; ++rx_words) {
+				sifive_spi_tx(spi, NULL);
+				sifive_spi_rx(spi, rx_ptr);
+				rx_ptr++;
+			}
+		}
+
+		remaining_len -= n_words;
+	}
+
+	if (flags & SPI_XFER_END)
+		sifive_spi_clear_cs(spi);
+
+	return 0;
+}
+
+static int sifive_spi_set_speed(struct udevice *bus, uint speed)
+{
+	struct sifive_spi *spi = dev_get_priv(bus);
+	u32 scale;
+
+	if (speed > spi->freq)
+		speed = spi->freq;
+
+	/* Cofigure max speed */
+	scale = (DIV_ROUND_UP(spi->freq >> 1, speed) - 1)
+					& SIFIVE_SPI_SCKDIV_DIV_MASK;
+	writel(scale, spi->regs + SIFIVE_SPI_REG_SCKDIV);
+
+	return 0;
+}
+
+static int sifive_spi_set_mode(struct udevice *bus, uint mode)
+{
+	struct sifive_spi *spi = dev_get_priv(bus);
+	u32 cr;
+
+	/* Switch clock mode bits */
+	cr = readl(spi->regs + SIFIVE_SPI_REG_SCKMODE) &
+				~SIFIVE_SPI_SCKMODE_MODE_MASK;
+	if (mode & SPI_CPHA)
+		cr |= SIFIVE_SPI_SCKMODE_PHA;
+	if (mode & SPI_CPOL)
+		cr |= SIFIVE_SPI_SCKMODE_POL;
+
+	writel(cr, spi->regs + SIFIVE_SPI_REG_SCKMODE);
+
+	return 0;
+}
+
+static int sifive_spi_cs_info(struct udevice *bus, uint cs,
+			      struct spi_cs_info *info)
+{
+	struct sifive_spi *spi = dev_get_priv(bus);
+
+	if (cs >= spi->num_cs)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void sifive_spi_init_hw(struct sifive_spi *spi)
+{
+	u32 cs_bits;
+
+	/* probe the number of CS lines */
+	spi->cs_inactive = readl(spi->regs + SIFIVE_SPI_REG_CSDEF);
+	writel(0xffffffffU, spi->regs + SIFIVE_SPI_REG_CSDEF);
+	cs_bits = readl(spi->regs + SIFIVE_SPI_REG_CSDEF);
+	writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
+	if (!cs_bits) {
+		printf("Could not auto probe CS lines\n");
+		return;
+	}
+
+	spi->num_cs = ilog2(cs_bits) + 1;
+	if (spi->num_cs > SIFIVE_SPI_MAX_CS) {
+		printf("Invalid number of spi slaves\n");
+		return;
+	}
+
+	/* Watermark interrupts are disabled by default */
+	writel(0, spi->regs + SIFIVE_SPI_REG_IE);
+
+	/* Set CS/SCK Delays and Inactive Time to defaults */
+	writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1),
+	       spi->regs + SIFIVE_SPI_REG_DELAY0);
+	writel(SIFIVE_SPI_DELAY1_INTERCS(1) | SIFIVE_SPI_DELAY1_INTERXFR(0),
+	       spi->regs + SIFIVE_SPI_REG_DELAY1);
+
+	/* Exit specialized memory-mapped SPI flash mode */
+	writel(0, spi->regs + SIFIVE_SPI_REG_FCTRL);
+}
+
+static int sifive_spi_probe(struct udevice *bus)
+{
+	struct sifive_spi *spi = dev_get_priv(bus);
+	struct clk clkdev;
+	int ret;
+
+	spi->regs = (void *)(ulong)dev_remap_addr(bus);
+	if (!spi->regs)
+		return -ENODEV;
+
+	spi->fifo_depth = dev_read_u32_default(bus,
+					       "sifive,fifo-depth",
+					       SIFIVE_SPI_DEFAULT_DEPTH);
+
+	spi->bits_per_word = dev_read_u32_default(bus,
+						  "sifive,max-bits-per-word",
+						  SIFIVE_SPI_DEFAULT_BITS);
+
+	ret = clk_get_by_index(bus, 0, &clkdev);
+	if (ret)
+		return ret;
+	spi->freq = clk_get_rate(&clkdev);
+
+	/* init the sifive spi hw */
+	sifive_spi_init_hw(spi);
+
+	return 0;
+}
+
+static const struct dm_spi_ops sifive_spi_ops = {
+	.xfer		= sifive_spi_xfer,
+	.set_speed	= sifive_spi_set_speed,
+	.set_mode	= sifive_spi_set_mode,
+	.cs_info        = sifive_spi_cs_info,
+};
+
+static const struct udevice_id sifive_spi_ids[] = {
+	{ .compatible = "sifive,spi0" },
+	{ }
+};
+
+U_BOOT_DRIVER(sifive_spi) = {
+	.name	= "sifive_spi",
+	.id	= UCLASS_SPI,
+	.of_match = sifive_spi_ids,
+	.ops	= &sifive_spi_ops,
+	.priv_auto_alloc_size = sizeof(struct sifive_spi),
+	.probe	= sifive_spi_probe,
+};