diff mbox

[OpenWrt-Devel,4/7] ramips: improve rt2880/mt7620 spi flash read speed

Message ID 1442928363-6219-4-git-send-email-igvtee@gmail.com
State Superseded
Headers show

Commit Message

Mingyu Li Sept. 22, 2015, 1:26 p.m. UTC
From: michael lee <igvtee@gmail.com>

support for all
* 2 spi master
* use device tree to set spi bus number for driver use
* fast read

new features for mt7620
* hardware spi flash command/user mode support
* fast read dual out. (make sure SPI_NOR_DUAL_READ flag is
  enabled at spi-nor.c, device tree also need to modify
  spi-rx-bus-width = <2>, m25p,fast-read and adjuest
  spi-max-frequency according to flash datasheet)

need verify
* gpio pin as chip select
* second spi master
* fast read quad out

test results on mt7620. use dd read bs=512 with 15744 records
old driver : 20.88s
new driver : 11.30s
new driver + fast read + clock from 10MHz to 80MHz : 1.83s
new driver + fast read dual out + 80MHz clock : 1.18s

Signed-off-by: Michael Lee <igvtee@gmail.com>
---
 target/linux/ramips/dts/mt7620a.dtsi               |  32 +-
 target/linux/ramips/dts/mt7620n.dtsi               |  32 +-
 target/linux/ramips/dts/rt3050.dtsi                |   6 +-
 target/linux/ramips/dts/rt3352.dtsi                |  31 +-
 target/linux/ramips/dts/rt3883.dtsi                |  25 +-
 target/linux/ramips/dts/rt5350.dtsi                |  29 +-
 ...0050-SPI-ralink-add-Ralink-SoC-spi-driver.patch | 837 ++++++++++++++++-----
 .../0051-rt5350-spi-second-device.patch            | 368 ---------
 8 files changed, 796 insertions(+), 564 deletions(-)
 delete mode 100644 target/linux/ramips/patches-3.18/0051-rt5350-spi-second-device.patch
diff mbox

Patch

diff --git a/target/linux/ramips/dts/mt7620a.dtsi b/target/linux/ramips/dts/mt7620a.dtsi
index 80e8977..5b39c84 100644
--- a/target/linux/ramips/dts/mt7620a.dtsi
+++ b/target/linux/ramips/dts/mt7620a.dtsi
@@ -20,6 +20,11 @@ 
 		compatible = "mti,cpu-interrupt-controller";
 	};
 
+	aliases {
+		spi0 = &spi0;
+		spi1 = &spi1;
+	};
+
 	palmbus@10000000 {
 		compatible = "palmbus";
 		reg = <0x10000000 0x200000>;
@@ -202,9 +207,9 @@ 
 			status = "disabled";
 		};
 
-		spi@b00 {
+		spi0: spi@b00 {
 			compatible = "ralink,mt7620a-spi", "ralink,rt2880-spi";
-			reg = <0xb00 0x100>;
+			reg = <0xb00 0x40>;
 
 			resets = <&rstctrl 18>;
 			reset-names = "spi";
@@ -218,6 +223,22 @@ 
 			pinctrl-0 = <&spi_pins>;
 		};
 
+		spi1: spi@b40 {
+			compatible = "ralink,rt2880-spi";
+			reg = <0xb40 0x60>;
+
+			resets = <&rstctrl 18>;
+			reset-names = "spi";
+
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			status = "disabled";
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi_cs1>;
+		};
+
 		uartlite@c00 {
 			compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a";
 			reg = <0xc00 0x100>;
@@ -305,6 +326,13 @@ 
 			};
 		};
 
+		spi_cs1: spi1 {
+			spi1 {
+				ralink,group = "spi_cs1";
+				ralink,function = "spi_cs1";
+			};
+		};
+
 		i2c_pins: i2c {
 			i2c {
 				ralink,group = "i2c";
diff --git a/target/linux/ramips/dts/mt7620n.dtsi b/target/linux/ramips/dts/mt7620n.dtsi
index e886c6f..61fff00 100644
--- a/target/linux/ramips/dts/mt7620n.dtsi
+++ b/target/linux/ramips/dts/mt7620n.dtsi
@@ -20,6 +20,11 @@ 
 		compatible = "mti,cpu-interrupt-controller";
 	};
 
+	aliases {
+		spi0 = &spi0;
+		spi1 = &spi1;
+	};
+
 	palmbus@10000000 {
 		compatible = "palmbus";
 		reg = <0x10000000 0x200000>;
@@ -154,9 +159,9 @@ 
 			status = "disabled";
 		};
 
-		spi@b00 {
+		spi0: spi@b00 {
 			compatible = "ralink,mt7620a-spi", "ralink,rt2880-spi";
-			reg = <0xb00 0x100>;
+			reg = <0xb00 0x40>;
 
 			resets = <&rstctrl 18>;
 			reset-names = "spi";
@@ -170,6 +175,22 @@ 
 			pinctrl-0 = <&spi_pins>;
 		};
 
+		spi1: spi@b40 {
+			compatible = "ralink,rt2880-spi";
+			reg = <0xb40 0x60>;
+
+			resets = <&rstctrl 18>;
+			reset-names = "spi";
+
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			status = "disabled";
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi_cs1>;
+		};
+
 		uartlite@c00 {
 			compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a";
 			reg = <0xc00 0x100>;
@@ -213,6 +234,13 @@ 
 			};
 		};
 
+		spi_cs1: spi1 {
+			spi1 {
+				ralink,group = "spi_cs1";
+				ralink,function = "spi_cs1";
+			};
+		};
+
 		uartlite_pins: uartlite {
 			uart {
 				ralink,group = "uartlite";
diff --git a/target/linux/ramips/dts/rt3050.dtsi b/target/linux/ramips/dts/rt3050.dtsi
index 27e4179..7f0fb4a 100644
--- a/target/linux/ramips/dts/rt3050.dtsi
+++ b/target/linux/ramips/dts/rt3050.dtsi
@@ -13,6 +13,10 @@ 
 		bootargs = "console=ttyS0,57600";
 	};
 
+	aliases {
+		spi0 = &spi0;
+	};
+
 	cpuintc: cpuintc@0 {
 		#address-cells = <0>;
 		#interrupt-cells = <1>;
@@ -144,7 +148,7 @@ 
 			status = "disabled";
 		};
 
-		spi@b00 {
+		spi0: spi@b00 {
 			compatible = "ralink,rt3050-spi", "ralink,rt2880-spi";
 			reg = <0xb00 0x100>;
 
diff --git a/target/linux/ramips/dts/rt3352.dtsi b/target/linux/ramips/dts/rt3352.dtsi
index 009277f..89296f8 100644
--- a/target/linux/ramips/dts/rt3352.dtsi
+++ b/target/linux/ramips/dts/rt3352.dtsi
@@ -20,6 +20,11 @@ 
 		compatible = "mti,cpu-interrupt-controller";
 	};
 
+	aliases {
+		spi0 = &spi0;
+		spi1 = &spi1;
+	};
+
 	palmbus@10000000 {
 		compatible = "palmbus";
 		reg = <0x10000000 0x200000>;
@@ -140,9 +145,9 @@ 
 			status = "disabled";
 		};
 
-		spi@b00 {
+		spi0: spi@b00 {
 			compatible = "ralink,rt3352-spi", "ralink,rt2880-spi";
-			reg = <0xb00 0x100>;
+			reg = <0xb00 0x40>;
 			#address-cells = <1>;
 			#size-cells = <1>;
 
@@ -155,6 +160,21 @@ 
 			status = "disabled";
 		};
 
+		spi1: spi@b40 {
+			compatible = "ralink,rt3352-spi", "ralink,rt2880-spi";
+			reg = <0xb40 0x60>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			resets = <&rstctrl 18>;
+			reset-names = "spi";
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi_cs1>;
+
+			status = "disabled";
+		};
+
 		uartlite@c00 {
 			compatible = "ralink,rt3352-uart", "ralink,rt2880-uart", "ns16550a";
 			reg = <0xc00 0x100>;
@@ -188,6 +208,13 @@ 
 			};
 		};
 
+		spi_cs1: spi1 {
+			spi1 {
+				ralink,group = "spi_cs1";
+				ralink,function = "spi_cs1";
+			};
+		};
+
 		uartlite_pins: uartlite {
 			uart {
 				ralink,group = "uartlite";
diff --git a/target/linux/ramips/dts/rt3883.dtsi b/target/linux/ramips/dts/rt3883.dtsi
index dc26782..6592b3b 100644
--- a/target/linux/ramips/dts/rt3883.dtsi
+++ b/target/linux/ramips/dts/rt3883.dtsi
@@ -15,6 +15,7 @@ 
 
 	aliases {
 		spi0 = &spi0;
+		spi1 = &spi1;
 	};
 
 	cpuintc: cpuintc@0 {
@@ -166,7 +167,7 @@ 
 
 		spi0: spi@b00 {
 			compatible = "ralink,rt3883-spi", "ralink,rt2880-spi";
-			reg = <0xb00 0x100>;
+			reg = <0xb00 0x40>;
 			#address-cells = <1>;
 			#size-cells = <0>;
 
@@ -179,6 +180,21 @@ 
 			status = "disabled";
 		};
 
+		spi1: spi@b40 {
+			compatible = "ralink,rt3883-spi", "ralink,rt2880-spi";
+			reg = <0xb40 0x60>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			resets = <&rstctrl 18>;
+			reset-names = "spi";
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi_cs1>;
+
+			status = "disabled";
+		};
+
 		uartlite@c00 {
 			compatible = "ralink,rt3883-uart", "ralink,rt2880-uart", "ns16550a";
 			reg = <0xc00 0x100>;
@@ -212,6 +228,13 @@ 
 			};
 		};
 
+		spi_cs1: spi1 {
+			spi1 {
+				ralink,group = "spi_cs1";
+				ralink,function = "spi_cs1";
+			};
+		};
+
 		uartlite_pins: uartlite {
 			uart {
 				ralink,group = "uartlite";
diff --git a/target/linux/ramips/dts/rt5350.dtsi b/target/linux/ramips/dts/rt5350.dtsi
index 5282e5b..8a9ef5c 100644
--- a/target/linux/ramips/dts/rt5350.dtsi
+++ b/target/linux/ramips/dts/rt5350.dtsi
@@ -20,6 +20,11 @@ 
 		compatible = "mti,cpu-interrupt-controller";
 	};
 
+	aliases {
+		spi0 = &spi0;
+		spi1 = &spi1;
+	};
+
 	palmbus@10000000 {
 		compatible = "palmbus";
 		reg = <0x10000000 0x200000>;
@@ -150,9 +155,25 @@ 
 			status = "disabled";
 		};
 
-		spi@b00 {
-			compatible = "ralink,rt5350-spi";
-			reg = <0xb00 0x100>;
+		spi0: spi@b00 {
+			compatible = "ralink,rt5350-spi", "ralink,rt2880-spi";
+			reg = <0xb00 0x40>;
+
+			resets = <&rstctrl 18>;
+			reset-names = "spi";
+
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi_pins>;
+
+			status = "disabled";
+		};
+
+		spi1: spi@b40 {
+			compatible = "ralink,rt5350-spi", "ralink,rt2880-spi";
+			reg = <0xb40 0x60>;
 
 			resets = <&rstctrl 18>;
 			reset-names = "spi";
@@ -161,7 +182,7 @@ 
 			#size-cells = <1>;
 
 			pinctrl-names = "default";
-			pinctrl-0 = <&spi_pins &spi_cs1>;
+			pinctrl-0 = <&spi_cs1>;
 
 			status = "disabled";
 		};
diff --git a/target/linux/ramips/patches-3.18/0050-SPI-ralink-add-Ralink-SoC-spi-driver.patch b/target/linux/ramips/patches-3.18/0050-SPI-ralink-add-Ralink-SoC-spi-driver.patch
index cb691f3..00637a7 100644
--- a/target/linux/ramips/patches-3.18/0050-SPI-ralink-add-Ralink-SoC-spi-driver.patch
+++ b/target/linux/ramips/patches-3.18/0050-SPI-ralink-add-Ralink-SoC-spi-driver.patch
@@ -41,12 +41,13 @@  Acked-by: John Crispin <blogic@openwrt.org>
  spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
 --- /dev/null
 +++ b/drivers/spi/spi-rt2880.c
-@@ -0,0 +1,432 @@
+@@ -0,0 +1,901 @@
 +/*
 + * spi-rt2880.c -- Ralink RT288x/RT305x SPI controller driver
 + *
 + * Copyright (C) 2011 Sergiy <piratfm@gmail.com>
 + * Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>
++ * Copyright (C) 2015 Michael Lee <igvtee@gmail.com>
 + *
 + * Some parts are based on spi-orion.c:
 + *   Author: Shadi Ammouri <shadi@marvell.com>
@@ -65,29 +66,45 @@  Acked-by: John Crispin <blogic@openwrt.org>
 +#include <linux/io.h>
 +#include <linux/reset.h>
 +#include <linux/spi/spi.h>
++#include <linux/of_device.h>
 +#include <linux/platform_device.h>
++#include <linux/gpio.h>
++#include <linux/mtd/spi-nor.h>
++#include <linux/list.h>
++
++#include <ralink_regs.h>
 +
 +#define DRIVER_NAME			"spi-rt2880"
-+/* only one slave is supported*/
-+#define RALINK_NUM_CHIPSELECTS		1
-+/* in usec */
-+#define RALINK_SPI_WAIT_MAX_LOOP	2000
 +
 +#define RAMIPS_SPI_STAT			0x00
 +#define RAMIPS_SPI_CFG			0x10
 +#define RAMIPS_SPI_CTL			0x14
 +#define RAMIPS_SPI_DATA			0x20
++#define RAMIPS_SPI_ADDR			0x24
++#define RAMIPS_SPI_BS			0x28
++#define RAMIPS_SPI_USER			0x2C
++#define RAMIPS_SPI_TXFIFO		0x30
++#define RAMIPS_SPI_RXFIFO		0x34
 +#define RAMIPS_SPI_FIFO_STAT		0x38
++#define RAMIPS_SPI_MODE			0x3C
++#define RAMIPS_SPI_DEV_OFFSET		0x40
++#define RAMIPS_SPI_DMA			0x80
++#define RAMIPS_SPI_DMASTAT		0x84
++#define RAMIPS_SPI_ARBITER		0xF0
 +
 +/* SPISTAT register bit field */
 +#define SPISTAT_BUSY			BIT(0)
 +
 +/* SPICFG register bit field */
-+#define SPICFG_LSBFIRST			0
++#define SPICFG_ADDRMODE			BIT(12)
++#define SPICFG_RXENVDIS			BIT(11)
++#define SPICFG_RXCAP			BIT(10)
++#define SPICFG_SPIENMODE		BIT(9)
 +#define SPICFG_MSBFIRST			BIT(8)
 +#define SPICFG_SPICLKPOL		BIT(6)
 +#define SPICFG_RXCLKEDGE_FALLING	BIT(5)
 +#define SPICFG_TXCLKEDGE_FALLING	BIT(4)
++#define SPICFG_HIZSPI			BIT(3)
 +#define SPICFG_SPICLK_PRESCALE_MASK	0x7
 +#define SPICFG_SPICLK_DIV2		0
 +#define SPICFG_SPICLK_DIV4		1
@@ -99,21 +116,109 @@  Acked-by: John Crispin <blogic@openwrt.org>
 +#define SPICFG_SPICLK_DISABLE		7
 +
 +/* SPICTL register bit field */
++#define SPICTL_START			BIT(4)
 +#define SPICTL_HIZSDO			BIT(3)
 +#define SPICTL_STARTWR			BIT(2)
 +#define SPICTL_STARTRD			BIT(1)
 +#define SPICTL_SPIENA			BIT(0)
 +
++/* SPIUSER register bit field */
++#define SPIUSER_USERMODE		BIT(21)
++#define SPIUSER_INSTR_PHASE		BIT(20)
++#define SPIUSER_ADDR_PHASE_MASK		0x7
++#define SPIUSER_ADDR_PHASE_OFFSET	17
++#define SPIUSER_MODE_PHASE		BIT(16)
++#define SPIUSER_DUMMY_PHASE_MASK	0x3
++#define SPIUSER_DUMMY_PHASE_OFFSET	14
++#define SPIUSER_DATA_PHASE_MASK		0x3
++#define SPIUSER_DATA_PHASE_OFFSET	12
++#define SPIUSER_DATA_READ		(BIT(0) << SPIUSER_DATA_PHASE_OFFSET)
++#define SPIUSER_DATA_WRITE		(BIT(1) << SPIUSER_DATA_PHASE_OFFSET)
++#define SPIUSER_ADDR_TYPE_OFFSET	9
++#define SPIUSER_MODE_TYPE_OFFSET	6
++#define SPIUSER_DUMMY_TYPE_OFFSET	3
++#define SPIUSER_DATA_TYPE_OFFSET	0
++#define SPIUSER_TRANSFER_MASK		0x7
++#define SPIUSER_TRANSFER_SINGLE		BIT(0)
++#define SPIUSER_TRANSFER_DUAL		BIT(1)
++#define SPIUSER_TRANSFER_QUAD		BIT(2)
++
++#define SPIUSER_TRANSFER_TYPE(type) ( \
++	(type << SPIUSER_ADDR_TYPE_OFFSET) | \
++	(type << SPIUSER_MODE_TYPE_OFFSET) | \
++	(type << SPIUSER_DUMMY_TYPE_OFFSET) | \
++	(type << SPIUSER_DATA_TYPE_OFFSET) \
++)
++
 +/* SPIFIFOSTAT register bit field */
++#define SPIFIFOSTAT_TXEMPTY		BIT(19)
++#define SPIFIFOSTAT_RXEMPTY		BIT(18)
 +#define SPIFIFOSTAT_TXFULL		BIT(17)
++#define SPIFIFOSTAT_RXFULL		BIT(16)
++#define SPIFIFOSTAT_FIFO_MASK		0xff
++#define SPIFIFOSTAT_TX_OFFSET		8
++#define SPIFIFOSTAT_RX_OFFSET		0
++
++#define SPI_FIFO_DEPTH			16
++
++/* SPIMODE register bit field */
++#define SPIMODE_MODE_OFFSET		24
++#define SPIMODE_DUMMY_OFFSET		0
++
++/* SPIARB register bit field */
++#define SPICTL_ARB_EN			BIT(31)
++#define SPICTL_CSCTL1			BIT(16)
++#define SPI1_POR			BIT(1)
++#define SPI0_POR			BIT(0)
++
++#define RT2880_SPI_MODE_BITS	(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | \
++		SPI_CS_HIGH)
++#define MT7620_SPI_MODE_BITS	(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | \
++		SPI_CS_HIGH | SPI_RX_DUAL | SPI_RX_QUAD)
++
++static atomic_t hw_reset_count = ATOMIC_INIT(0);
++
++/* cmd flags */
++#define SPI_CMD_ADDR	BIT(0)
++#define SPI_CMD_DUMMY	BIT(1)
++#define SPI_CMD_TX	BIT(2)
++#define SPI_CMD_RX	BIT(3)
++#define SPI_CMD_USER	BIT(4)
++#define SPI_CMD_EN4B	BIT(5)
++#define SPI_CMD_EX4B	BIT(6)
++
++/* cmd status */
++#define SPI_STATE_OPCODE	0
++#define SPI_STATE_DATA		1
++
++struct mt7620_spi_cmd {
++	u8 opcode;
++	u8 status;
++	u16 flags;
++	u32 addr;
++	const u8 *tx_buf;
++	u8 *rx_buf;
++	u32 len;
++};
++
++#define SPI_FLAG_HW_MODE	0x1
++struct rt2880_spi_ops {
++	u32 flags;
++	int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
++			struct spi_transfer *transfer);
++};
 +
 +struct rt2880_spi {
 +	struct spi_master	*master;
 +	void __iomem		*base;
-+	unsigned int		sys_freq;
-+	unsigned int		speed;
++	u32			speed;
++	u16			wait_loops;
++	u16			mode;
 +	struct clk		*clk;
-+	spinlock_t		lock;
++	int			addr_width;
++
++	struct rt2880_spi_ops	*ops;
++	struct mt7620_spi_cmd	cmd;
 +};
 +
 +static inline struct rt2880_spi *spidev_to_rt2880_spi(struct spi_device *spi)
@@ -126,7 +231,8 @@  Acked-by: John Crispin <blogic@openwrt.org>
 +	return ioread32(rs->base + reg);
 +}
 +
-+static inline void rt2880_spi_write(struct rt2880_spi *rs, u32 reg, u32 val)
++static inline void rt2880_spi_write(struct rt2880_spi *rs, u32 reg,
++		const u32 val)
 +{
 +	iowrite32(val, rs->base + reg);
 +}
@@ -134,132 +240,172 @@  Acked-by: John Crispin <blogic@openwrt.org>
 +static inline void rt2880_spi_setbits(struct rt2880_spi *rs, u32 reg, u32 mask)
 +{
 +	void __iomem *addr = rs->base + reg;
-+	unsigned long flags;
-+	u32 val;
-+
-+	spin_lock_irqsave(&rs->lock, flags);
-+	val = ioread32(addr);
-+	val |= mask;
-+	iowrite32(val, addr);
-+	spin_unlock_irqrestore(&rs->lock, flags);
++
++	iowrite32((ioread32(addr) | mask), addr);
 +}
 +
 +static inline void rt2880_spi_clrbits(struct rt2880_spi *rs, u32 reg, u32 mask)
 +{
 +	void __iomem *addr = rs->base + reg;
-+	unsigned long flags;
-+	u32 val;
-+
-+	spin_lock_irqsave(&rs->lock, flags);
-+	val = ioread32(addr);
-+	val &= ~mask;
-+	iowrite32(val, addr);
-+	spin_unlock_irqrestore(&rs->lock, flags);
++
++	iowrite32((ioread32(addr) & ~mask), addr);
 +}
 +
-+static int rt2880_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
++static u32 rt2880_spi_baudrate_get(struct spi_device *spi, unsigned int speed)
 +{
 +	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
 +	u32 rate;
 +	u32 prescale;
-+	u32 reg;
-+
-+	dev_dbg(&spi->dev, "speed:%u\n", speed);
 +
 +	/*
 +	 * the supported rates are: 2, 4, 8, ... 128
 +	 * round up as we look for equal or less speed
 +	 */
-+	rate = DIV_ROUND_UP(rs->sys_freq, speed);
-+	dev_dbg(&spi->dev, "rate-1:%u\n", rate);
++	rate = DIV_ROUND_UP(clk_get_rate(rs->clk), speed);
 +	rate = roundup_pow_of_two(rate);
-+	dev_dbg(&spi->dev, "rate-2:%u\n", rate);
-+
-+	/* check if requested speed is too small */
-+	if (rate > 128)
-+		return -EINVAL;
-+
-+	if (rate < 2)
-+		rate = 2;
 +
 +	/* Convert the rate to SPI clock divisor value.	*/
 +	prescale = ilog2(rate / 2);
-+	dev_dbg(&spi->dev, "prescale:%u\n", prescale);
 +
-+	reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG);
-+	reg = ((reg & ~SPICFG_SPICLK_PRESCALE_MASK) | prescale);
-+	rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg);
++	/* some tolerance. double and add 100 */
++	rs->wait_loops = (8 * HZ * loops_per_jiffy) /
++		(clk_get_rate(rs->clk) / rate);
++	rs->wait_loops = (rs->wait_loops << 1) + 100;
 +	rs->speed = speed;
-+	return 0;
++
++	dev_dbg(&spi->dev, "speed: %lu/%u, rate: %u, prescal: %u, loops: %hu\n",
++			clk_get_rate(rs->clk) / rate, speed, rate, prescale,
++			rs->wait_loops);
++
++	return prescale;
 +}
 +
-+/*
-+ * called only when no transfer is active on the bus
-+ */
-+static int
-+rt2880_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
++static u32 get_arbiter_offset(struct spi_master *master)
 +{
-+	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
-+	unsigned int speed = spi->max_speed_hz;
-+	int rc;
-+
-+	if ((t != NULL) && t->speed_hz)
-+		speed = t->speed_hz;
++	u32 offset;
 +
-+	if (rs->speed != speed) {
-+		dev_dbg(&spi->dev, "speed_hz:%u\n", speed);
-+		rc = rt2880_spi_baudrate_set(spi, speed);
-+		if (rc)
-+			return rc;
-+	}
++	offset = RAMIPS_SPI_ARBITER;
++	if (master->bus_num == 1)
++		offset -= RAMIPS_SPI_DEV_OFFSET;
 +
-+	return 0;
++	return offset;
 +}
 +
-+static void rt2880_spi_set_cs(struct rt2880_spi *rs, int enable)
++static void rt2880_spi_set_cs(struct spi_device *spi, bool enable)
 +{
++	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
++
 +	if (enable)
++		rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
++	else
 +		rt2880_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
++}
++
++static void mt7620_set_4byte(struct rt2880_spi *rs, bool enable)
++{
++	if (enable)
++		rt2880_spi_setbits(rs, RAMIPS_SPI_CFG, SPICFG_ADDRMODE);
 +	else
-+		rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
++		rt2880_spi_clrbits(rs, RAMIPS_SPI_CFG, SPICFG_ADDRMODE);
 +}
 +
-+static inline int rt2880_spi_wait_till_ready(struct rt2880_spi *rs)
++static int rt2880_spi_wait_ready(struct rt2880_spi *rs, int len)
 +{
-+	int i;
++	int loop = rs->wait_loops * len;
++
++	while ((rt2880_spi_read(rs, RAMIPS_SPI_STAT) & SPISTAT_BUSY) && --loop)
++		cpu_relax();
 +
-+	for (i = 0; i < RALINK_SPI_WAIT_MAX_LOOP; i++) {
-+		u32 status;
++	if (loop)
++		return 0;
 +
-+		status = rt2880_spi_read(rs, RAMIPS_SPI_STAT);
-+		if ((status & SPISTAT_BUSY) == 0)
-+			return 0;
++	return -ETIMEDOUT;
++}
++
++/* return free fifo count */
++static int mt7620_spi_fifo_ready(struct rt2880_spi *rs, u32 check_bit, bool tx)
++{
++	int loop = rs->wait_loops;
++	u32 stat;
 +
++	stat = rt2880_spi_read(rs, RAMIPS_SPI_FIFO_STAT);
++	while ((stat & check_bit) && --loop) {
 +		cpu_relax();
-+		udelay(1);
++		stat = rt2880_spi_read(rs, RAMIPS_SPI_FIFO_STAT);
++	}
++
++	if (loop) {
++		if (tx)
++			stat >>= SPIFIFOSTAT_TX_OFFSET;
++		stat &= SPIFIFOSTAT_FIFO_MASK;
++		if (tx)
++			stat = SPI_FIFO_DEPTH - stat;
++
++		return (int)stat;
 +	}
 +
 +	return -ETIMEDOUT;
 +}
 +
-+static unsigned int
-+rt2880_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
++static void rt2880_dump_reg(struct spi_master *master)
 +{
-+	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
-+	unsigned count = 0;
-+	u8 *rx = xfer->rx_buf;
-+	const u8 *tx = xfer->tx_buf;
-+	int err;
++	struct rt2880_spi *rs = spi_master_get_devdata(master);
 +
-+	dev_dbg(&spi->dev, "read (%d): %s %s\n", xfer->len,
-+		  (tx != NULL) ? "tx" : "  ",
-+		  (rx != NULL) ? "rx" : "  ");
++	if (rs->ops->flags && (master->bus_num == 0)) {
++		dev_dbg(&master->dev, "stat: %08x, cfg: %08x, ctl: %08x, " \
++				"data: %08x, addr: %08x, bs: %08x, " \
++				"user: %08x, txfifo: %08x, rxfifo: %08x, " \
++				"fifo_sta: %08x, mode: %08x, dma: " \
++				"%08x, dmastat: %08x, arb: %08x\n",
++				rt2880_spi_read(rs, RAMIPS_SPI_STAT),
++				rt2880_spi_read(rs, RAMIPS_SPI_CFG),
++				rt2880_spi_read(rs, RAMIPS_SPI_CTL),
++				rt2880_spi_read(rs, RAMIPS_SPI_DATA),
++				rt2880_spi_read(rs, RAMIPS_SPI_ADDR),
++				rt2880_spi_read(rs, RAMIPS_SPI_BS),
++				rt2880_spi_read(rs, RAMIPS_SPI_USER),
++				rt2880_spi_read(rs, RAMIPS_SPI_TXFIFO),
++				rt2880_spi_read(rs, RAMIPS_SPI_RXFIFO),
++				rt2880_spi_read(rs, RAMIPS_SPI_FIFO_STAT),
++				rt2880_spi_read(rs, RAMIPS_SPI_MODE),
++				rt2880_spi_read(rs, RAMIPS_SPI_DMA),
++				rt2880_spi_read(rs, RAMIPS_SPI_DMASTAT),
++				rt2880_spi_read(rs, RAMIPS_SPI_ARBITER));
++	} else {
++		dev_dbg(&master->dev, "stat: %08x, cfg: %08x, ctl: %08x, " \
++				"data: %08x, arb: %08x\n",
++				rt2880_spi_read(rs, RAMIPS_SPI_STAT),
++				rt2880_spi_read(rs, RAMIPS_SPI_CFG),
++				rt2880_spi_read(rs, RAMIPS_SPI_CTL),
++				rt2880_spi_read(rs, RAMIPS_SPI_DATA),
++				rt2880_spi_read(rs, get_arbiter_offset(master)));
++	}
++}
++
++static int rt2880_spi_transfer_one(struct spi_master *master,
++		struct spi_device *spi, struct spi_transfer *xfer)
++{
++	struct rt2880_spi *rs = spi_master_get_devdata(master);
++	unsigned len;
++	const u8 *tx = xfer->tx_buf;
++	u8 *rx = xfer->rx_buf;
++	int err = 0;
++
++	/* change clock speed  */
++	if (unlikely(rs->speed != xfer->speed_hz)) {
++		u32 reg;
++		reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG);
++		reg &= ~SPICFG_SPICLK_PRESCALE_MASK;
++		reg |= rt2880_spi_baudrate_get(spi, xfer->speed_hz);
++		rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg);
++	}
 +
 +	if (tx) {
-+		for (count = 0; count < xfer->len; count++) {
-+			rt2880_spi_write(rs, RAMIPS_SPI_DATA, tx[count]);
++		len = xfer->len;
++		while (len-- > 0) {
++			rt2880_spi_write(rs, RAMIPS_SPI_DATA, *tx++);
 +			rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR);
-+			err = rt2880_spi_wait_till_ready(rs);
++			err = rt2880_spi_wait_ready(rs, 1);
 +			if (err) {
 +				dev_err(&spi->dev, "TX failed, err=%d\n", err);
 +				goto out;
@@ -268,125 +414,436 @@  Acked-by: John Crispin <blogic@openwrt.org>
 +	}
 +
 +	if (rx) {
-+		for (count = 0; count < xfer->len; count++) {
++		len = xfer->len;
++		while (len-- > 0) {
 +			rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD);
-+			err = rt2880_spi_wait_till_ready(rs);
++			err = rt2880_spi_wait_ready(rs, 1);
 +			if (err) {
 +				dev_err(&spi->dev, "RX failed, err=%d\n", err);
 +				goto out;
 +			}
-+			rx[count] = (u8) rt2880_spi_read(rs, RAMIPS_SPI_DATA);
++			*rx++ = (u8) rt2880_spi_read(rs, RAMIPS_SPI_DATA);
++		}
++	}
++
++out:
++	return err;
++}
++
++static unsigned int m25p_cmd2addr(int addr_width, const u8 *cmd)
++{
++	unsigned int addr;
++
++	/* opcode is in cmd[0] */
++	addr = cmd[1] << (addr_width * 8 - 8);
++	addr |= cmd[2] << (addr_width * 8 - 16);
++	addr |= cmd[3] << (addr_width * 8 - 24);
++	addr |= cmd[4] << (addr_width * 8 - 32);
++
++	return addr;
++}
++
++static int setup_spi_cmd(struct rt2880_spi *rs, const u8 *tx, u32 len,
++		struct mt7620_spi_cmd *cmd)
++{
++	int ret = 0;
++	u32 reg;
++
++	switch (tx[0]) {
++	/* hw support command */
++	case SPINOR_OP_RDSR:
++	case SPINOR_OP_RDID:
++		cmd->flags = SPI_CMD_RX;
++		break;
++	case SPINOR_OP_WRSR:
++		cmd->flags = SPI_CMD_TX;
++		cmd->tx_buf = tx + 1;
++		cmd->len = len - 1;
++		break;
++	case SPINOR_OP_WREN:
++	case SPINOR_OP_WRDI:
++		break;
++	case SPINOR_OP_SE:
++		cmd->flags = SPI_CMD_ADDR;
++		break;
++	case SPINOR_OP_READ:
++		cmd->flags = SPI_CMD_ADDR | SPI_CMD_RX;
++		break;
++	case SPINOR_OP_PP:
++		cmd->flags = SPI_CMD_ADDR | SPI_CMD_TX;
++		break;
++	case SPINOR_OP_READ_FAST:
++	case SPINOR_OP_READ_1_1_2:
++	case SPINOR_OP_READ_1_1_4:
++		cmd->flags = SPI_CMD_ADDR | SPI_CMD_DUMMY | SPI_CMD_RX;
++		break;
++	/* hw unsupport command */
++	case SPINOR_OP_EN4B:
++		cmd->flags = SPI_CMD_USER | SPI_CMD_EN4B;
++		break;
++	case SPINOR_OP_EX4B:
++		cmd->flags = SPI_CMD_USER | SPI_CMD_EX4B;
++		break;
++	case SPINOR_OP_BRWR:
++		if (tx[1] == BIT(7))
++			cmd->flags = SPI_CMD_TX | SPI_CMD_USER | SPI_CMD_EN4B;
++		else
++			cmd->flags = SPI_CMD_TX | SPI_CMD_USER | SPI_CMD_EX4B;
++		cmd->tx_buf = tx + 1;
++		cmd->len = len - 1;
++		break;
++	/* don't know hw support or not. assume unsupport */
++	case SPINOR_OP_CHIP_ERASE:
++		cmd->flags = SPI_CMD_USER;
++		break;
++	case SPINOR_OP_RDFSR:
++	case SPINOR_OP_RDCR:
++		cmd->flags = SPI_CMD_RX | SPI_CMD_USER;
++		break;
++	default:
++		ret = -EINVAL;
++		goto out;
++	}
++
++	/* setup 4 bytes address mode */
++	if (cmd->flags & SPI_CMD_EN4B) {
++		mt7620_set_4byte(rs, true);
++		rs->addr_width = 4;
++	} else if (cmd->flags & SPI_CMD_EX4B) {
++		mt7620_set_4byte(rs, false);
++		rs->addr_width = 3;
++	}
++
++	/* opcode */
++	cmd->opcode = tx[0];
++	rt2880_spi_write(rs, RAMIPS_SPI_DATA, cmd->opcode);
++
++	/* address */
++	if (cmd->flags & SPI_CMD_ADDR) {
++		cmd->addr = m25p_cmd2addr(rs->addr_width, tx);
++		if (rs->addr_width == 3)
++			rt2880_spi_write(rs, RAMIPS_SPI_ADDR, (cmd->addr << 8));
++		else if (rs->addr_width == 4)
++			rt2880_spi_write(rs, RAMIPS_SPI_ADDR, cmd->addr);
++		else {
++			ret = -EINVAL;
++			goto out;
 +		}
++	} else
++		rt2880_spi_write(rs, RAMIPS_SPI_ADDR, 0);
++
++	/* setup user mode */
++	if (cmd->flags & SPI_CMD_USER) {
++		reg = SPIUSER_USERMODE | SPIUSER_INSTR_PHASE |
++			SPIUSER_TRANSFER_TYPE(SPIUSER_TRANSFER_SINGLE);
++		if (cmd->flags & SPI_CMD_ADDR)
++			reg |= (rs->addr_width << SPIUSER_ADDR_PHASE_OFFSET);
++		if (cmd->flags & SPI_CMD_DUMMY)
++			reg |= (1 << SPIUSER_DUMMY_PHASE_OFFSET);
++		if (cmd->flags & SPI_CMD_TX)
++			reg |= SPIUSER_DATA_WRITE;
++		else if (cmd->flags & SPI_CMD_RX)
++			reg |= SPIUSER_DATA_READ;
++		rt2880_spi_write(rs, RAMIPS_SPI_USER, reg);
 +	}
 +
 +out:
-+	return count;
++	return ret;
 +}
 +
-+static int rt2880_spi_transfer_one_message(struct spi_master *master,
-+					   struct spi_message *m)
++static int mt7620_spi_transfer_one(struct spi_master *master,
++		struct spi_device *spi, struct spi_transfer *xfer)
 +{
 +	struct rt2880_spi *rs = spi_master_get_devdata(master);
-+	struct spi_device *spi = m->spi;
-+	struct spi_transfer *t = NULL;
-+	int par_override = 0;
-+	int status = 0;
-+	int cs_active = 0;
-+
-+	/* Load defaults */
-+	status = rt2880_spi_setup_transfer(spi, NULL);
-+	if (status < 0)
-+		goto msg_done;
-+
-+	list_for_each_entry(t, &m->transfers, transfer_list) {
-+		if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
-+			dev_err(&spi->dev,
-+				"message rejected: invalid transfer data buffers\n");
-+			status = -EIO;
-+			goto msg_done;
++	struct mt7620_spi_cmd *cmd;
++	const u8 *tx;
++	u8 *rx;
++	int len, ret = 0;
++
++	cmd = &rs->cmd;
++	if (cmd->status == SPI_STATE_OPCODE) {
++		if (!xfer->tx_buf)
++			dev_err(&spi->dev, "only support spi flash device\n");
++
++		memset(cmd, 0, sizeof(*cmd));
++		ret = setup_spi_cmd(rs, xfer->tx_buf, xfer->len, cmd);
++		if (ret < 0) {
++			dev_err(&spi->dev, "unknown spi command %02x\n",
++					*(u8 *)xfer->tx_buf);
++			goto err;
 +		}
 +
-+		if (t->speed_hz && t->speed_hz < (rs->sys_freq / 128)) {
-+			dev_err(&spi->dev,
-+				"message rejected: device min speed (%d Hz) exceeds required transfer speed (%d Hz)\n",
-+				(rs->sys_freq / 128), t->speed_hz);
-+			status = -EIO;
-+			goto msg_done;
++		/* need data at next transfer */
++		if ((cmd->flags & (SPI_CMD_TX | SPI_CMD_RX)) &&
++				!cmd->tx_buf && !cmd->rx_buf) {
++			cmd->status = SPI_STATE_DATA;
++			return ret;
++		}
++	} else {
++		if (((cmd->flags & SPI_CMD_TX) && !xfer->tx_buf) ||
++				((cmd->flags & SPI_CMD_RX) && !xfer->rx_buf)) {
++			dev_err(&spi->dev, "no spi data found\n");
++			ret = -EINVAL;
++			goto err;
 +		}
++		cmd->len = xfer->len;
++		cmd->tx_buf = xfer->tx_buf;
++		cmd->rx_buf = xfer->rx_buf;
++	}
++
++	/* set block size */
++	rt2880_spi_write(rs, RAMIPS_SPI_BS, cmd->len);
++
++	/* start transaction */
++	rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_START);
++
++	tx = cmd->tx_buf;
++	rx = cmd->rx_buf;
 +
-+		if (par_override || t->speed_hz || t->bits_per_word) {
-+			par_override = 1;
-+			status = rt2880_spi_setup_transfer(spi, t);
-+			if (status < 0)
-+				goto msg_done;
-+			if (!t->speed_hz && !t->bits_per_word)
-+				par_override = 0;
++	/* handle data */
++	if (tx) {
++		len = cmd->len;
++		while (len) {
++			/* wait until tx fifo not full */
++			ret = mt7620_spi_fifo_ready(rs, SPIFIFOSTAT_TXFULL,
++					true);
++			if (ret < 0) {
++				dev_err(&spi->dev, "tx fifo full timeout\n");
++				goto err;
++			}
++			ret = min(len, ret);
++			len -= ret;
++			while (ret--)
++				rt2880_spi_write(rs, RAMIPS_SPI_TXFIFO, *tx++);
 +		}
++	}
 +
-+		if (!cs_active) {
-+			rt2880_spi_set_cs(rs, 1);
-+			cs_active = 1;
++	if (rx) {
++		len = cmd->len;
++		while (len) {
++			/* wait until rx fifo not empty */
++			ret = mt7620_spi_fifo_ready(rs, SPIFIFOSTAT_RXEMPTY,
++					false);
++			if (ret < 0) {
++				dev_err(&spi->dev, "rx fifo empty timeout\n");
++				goto err;
++			}
++			ret = min(len, ret);
++			len -= ret;
++			while (ret--)
++				*rx++ = (u8)rt2880_spi_read(rs,
++						RAMIPS_SPI_RXFIFO);
 +		}
++	}
++
++	len = min((int)cmd->len + 1, SPI_FIFO_DEPTH);
++	ret = rt2880_spi_wait_ready(rs, len);
++	if (ret)
++		dev_err(&spi->dev, "wait timeout\n");
++
++err:
++	cmd->status = SPI_STATE_OPCODE;
++
++	/* clean user mode */
++	if (cmd->flags & SPI_CMD_USER)
++		rt2880_spi_write(rs, RAMIPS_SPI_USER, 0);
++
++	if (ret)
++		rt2880_dump_reg(master);
 +
-+		if (t->len)
-+			m->actual_length += rt2880_spi_write_read(spi, t);
++	return ret;
++}
++
++/* copy from spi.c */
++static void spi_set_cs(struct spi_device *spi, bool enable)
++{
++	if (spi->mode & SPI_CS_HIGH)
++		enable = !enable;
++
++	if (spi->cs_gpio >= 0)
++		gpio_set_value(spi->cs_gpio, !enable);
++	else if (spi->master->set_cs)
++		spi->master->set_cs(spi, !enable);
++}
 +
-+		if (t->delay_usecs)
-+			udelay(t->delay_usecs);
++static int rt2880_spi_setup(struct spi_device *spi)
++{
++	struct spi_master *master = spi->master;
++	struct rt2880_spi *rs = spi_master_get_devdata(master);
++	u32 reg, old_reg, arbit_off;
 +
-+		if (t->cs_change) {
-+			rt2880_spi_set_cs(rs, 0);
-+			cs_active = 0;
++	if ((spi->max_speed_hz > master->max_speed_hz) ||
++			(spi->max_speed_hz < master->min_speed_hz)) {
++		dev_err(&spi->dev, "invalide requested speed %d Hz\n",
++				spi->max_speed_hz);
++		return -EINVAL;
++	}
++
++	if (!(master->bits_per_word_mask &
++				BIT(spi->bits_per_word - 1))) {
++		dev_err(&spi->dev, "invalide bits_per_word %d\n",
++				spi->bits_per_word);
++		return -EINVAL;
++	}
++
++	/* the hardware seems can't work on mode0 force it to mode3 */
++	if ((spi->mode & (SPI_CPOL | SPI_CPHA)) == SPI_MODE_0) {
++		dev_warn(&spi->dev, "force spi mode3\n");
++		spi->mode |= SPI_MODE_3;
++	}
++
++	/* chip polarity */
++	arbit_off = get_arbiter_offset(master);
++	reg = old_reg = rt2880_spi_read(rs, arbit_off);
++	if (spi->mode & SPI_CS_HIGH) {
++		switch (master->bus_num) {
++		case 1:
++			reg |= SPI1_POR;
++			break;
++		default:
++			reg |= SPI0_POR;
++			break;
++		}
++	} else {
++		switch (master->bus_num) {
++		case 1:
++			reg &= ~SPI1_POR;
++			break;
++		default:
++			reg &= ~SPI0_POR;
++			break;
 +		}
 +	}
 +
-+msg_done:
-+	if (cs_active)
-+		rt2880_spi_set_cs(rs, 0);
++	/* enable spi1 */
++	if (master->bus_num == 1) {
++		reg |= SPICTL_ARB_EN;
++		/* enable cs1 support? */
++		if (rs->ops->flags & SPI_FLAG_HW_MODE)
++			reg |= SPICTL_CSCTL1;
++	}
 +
-+	m->status = status;
-+	spi_finalize_current_message(master);
++	if (reg != old_reg)
++		rt2880_spi_write(rs, arbit_off, reg);
++
++	/* deselected the spi device */
++	spi_set_cs(spi, false);
 +
 +	return 0;
 +}
 +
-+static int rt2880_spi_setup(struct spi_device *spi)
++static int rt2880_spi_prepare_message(struct spi_master *master,
++		struct spi_message *msg)
 +{
-+	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
++	struct rt2880_spi *rs = spi_master_get_devdata(master);
++	struct spi_device *spi = msg->spi;
++	u32 reg;
 +
-+	if ((spi->max_speed_hz == 0) ||
-+	    (spi->max_speed_hz > (rs->sys_freq / 2)))
-+		spi->max_speed_hz = (rs->sys_freq / 2);
++	if ((rs->mode == spi->mode) && (rs->speed == spi->max_speed_hz)) {
++		if (rs->ops->flags & SPI_FLAG_HW_MODE) {
++			rt2880_spi_setbits(rs, RAMIPS_SPI_CFG,
++					(SPICFG_RXENVDIS | SPICFG_SPIENMODE));
++			rs->cmd.status = SPI_STATE_OPCODE;
++		}
++		return 0;
++	}
 +
-+	if (spi->max_speed_hz < (rs->sys_freq / 128)) {
-+		dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n",
-+			spi->max_speed_hz);
-+		return -EINVAL;
++#if 0
++	/* set spido to tri-state */
++	rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_HIZSDO);
++#endif
++
++	reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG);
++
++	reg &= ~(SPICFG_MSBFIRST | SPICFG_SPICLKPOL |
++			SPICFG_RXCLKEDGE_FALLING |
++			SPICFG_TXCLKEDGE_FALLING |
++			SPICFG_SPICLK_PRESCALE_MASK);
++
++	/* MSB */
++	if (!(spi->mode & SPI_LSB_FIRST))
++		reg |= SPICFG_MSBFIRST;
++
++	/* spi mode */
++	switch (spi->mode & (SPI_CPOL | SPI_CPHA)) {
++	case SPI_MODE_0:
++		reg |= SPICFG_TXCLKEDGE_FALLING;
++		break;
++	case SPI_MODE_1:
++		reg |= SPICFG_RXCLKEDGE_FALLING;
++		break;
++	case SPI_MODE_2:
++		reg |= SPICFG_SPICLKPOL | SPICFG_RXCLKEDGE_FALLING;
++		break;
++	case SPI_MODE_3:
++		reg |= SPICFG_SPICLKPOL | SPICFG_TXCLKEDGE_FALLING;
++		break;
 +	}
++	rs->mode = spi->mode;
++
++#if 0
++	/* set spiclk and spiena to tri-state */
++	reg |= SPICFG_HIZSPI;
++#endif
++
++	/* clock divide */
++	reg |= rt2880_spi_baudrate_get(spi, spi->max_speed_hz);
++
++	/* set chip select is control by hw */
++	if (rs->ops->flags & SPI_FLAG_HW_MODE) {
++		reg |= (SPICFG_RXENVDIS | SPICFG_SPIENMODE);
++		rs->cmd.status = SPI_STATE_OPCODE;
++	}
++
++	rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg);
 +
-+	/*
-+	 * baudrate & width will be set rt2880_spi_setup_transfer
-+	 */
 +	return 0;
 +}
 +
-+static void rt2880_spi_reset(struct rt2880_spi *rs)
++static int rt2880_spi_unprepare_message(struct spi_master *master,
++		struct spi_message *message)
 +{
-+	rt2880_spi_write(rs, RAMIPS_SPI_CFG,
-+			 SPICFG_MSBFIRST | SPICFG_TXCLKEDGE_FALLING |
-+			 SPICFG_SPICLK_DIV16 | SPICFG_SPICLKPOL);
-+	rt2880_spi_write(rs, RAMIPS_SPI_CTL, SPICTL_HIZSDO | SPICTL_SPIENA);
++	struct rt2880_spi *rs = spi_master_get_devdata(master);
++
++	/* disable chip select is control by hw */
++	if (rs->ops->flags & SPI_FLAG_HW_MODE)
++		rt2880_spi_clrbits(rs, RAMIPS_SPI_CFG,
++				(SPICFG_RXENVDIS | SPICFG_SPIENMODE));
++
++	return 0;
 +}
 +
++static struct rt2880_spi_ops spi_ops[] = {
++	{
++		.transfer_one = rt2880_spi_transfer_one,
++	}, {
++		.flags = SPI_FLAG_HW_MODE,
++		.transfer_one = mt7620_spi_transfer_one,
++	},
++};
++
++static const struct of_device_id rt2880_spi_match[] = {
++	{ .compatible = "ralink,rt2880-spi", .data = &spi_ops[0]},
++	{ .compatible = "ralink,mt7620a-spi", .data = &spi_ops[1]},
++	{},
++};
++MODULE_DEVICE_TABLE(of, rt2880_spi_match);
++
 +static int rt2880_spi_probe(struct platform_device *pdev)
 +{
++	const struct of_device_id *match;
 +	struct spi_master *master;
 +	struct rt2880_spi *rs;
-+	unsigned long flags;
 +	void __iomem *base;
 +	struct resource *r;
-+	int status = 0;
 +	struct clk *clk;
++	struct rt2880_spi_ops *ops;
++	int ret;
++
++	match = of_match_device(rt2880_spi_match, &pdev->dev);
++	if (!match)
++		return -EINVAL;
++	ops = (struct rt2880_spi_ops *)match->data;
 +
 +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 +	base = devm_ioremap_resource(&pdev->dev, r);
@@ -395,45 +852,63 @@  Acked-by: John Crispin <blogic@openwrt.org>
 +
 +	clk = devm_clk_get(&pdev->dev, NULL);
 +	if (IS_ERR(clk)) {
-+		dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n",
-+			status);
++		dev_err(&pdev->dev, "unable to get SYS clock\n");
 +		return PTR_ERR(clk);
 +	}
 +
-+	status = clk_prepare_enable(clk);
-+	if (status)
-+		return status;
++	ret = clk_prepare_enable(clk);
++	if (ret)
++		goto err_clk;
 +
 +	master = spi_alloc_master(&pdev->dev, sizeof(*rs));
 +	if (master == NULL) {
 +		dev_dbg(&pdev->dev, "master allocation failed\n");
-+		return -ENOMEM;
++		ret = -ENOMEM;
++		goto err_clk;
 +	}
 +
-+	/* we support only mode 0, and no options */
-+	master->mode_bits = 0;
-+
-+	master->setup = rt2880_spi_setup;
-+	master->transfer_one_message = rt2880_spi_transfer_one_message;
-+	master->num_chipselect = RALINK_NUM_CHIPSELECTS;
-+	master->bits_per_word_mask = SPI_BPW_MASK(8);
 +	master->dev.of_node = pdev->dev.of_node;
++	if (ops->flags)
++		master->mode_bits = MT7620_SPI_MODE_BITS;
++	else
++		master->mode_bits = RT2880_SPI_MODE_BITS;
++	master->bits_per_word_mask = SPI_BPW_MASK(8);
++	master->min_speed_hz = clk_get_rate(clk) / 128;
++	master->max_speed_hz = clk_get_rate(clk) / 2;
++	master->flags = SPI_MASTER_HALF_DUPLEX;
++	master->setup = rt2880_spi_setup;
++	master->prepare_message = rt2880_spi_prepare_message;
++	master->unprepare_message = rt2880_spi_unprepare_message;
++	master->set_cs = rt2880_spi_set_cs;
++	master->transfer_one = ops->transfer_one;
 +
 +	dev_set_drvdata(&pdev->dev, master);
 +
 +	rs = spi_master_get_devdata(master);
++	rs->master = master;
 +	rs->base = base;
 +	rs->clk = clk;
-+	rs->master = master;
-+	rs->sys_freq = clk_get_rate(rs->clk);
-+	dev_dbg(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
-+	spin_lock_irqsave(&rs->lock, flags);
++	rs->addr_width = 3;
++	rs->ops = ops;
++
++	if (atomic_inc_return(&hw_reset_count) == 1)
++		device_reset(&pdev->dev);
 +
-+	device_reset(&pdev->dev);
++	ret = devm_spi_register_master(&pdev->dev, master);
++	if (ret < 0) {
++		dev_err(&pdev->dev, "devm_spi_register_master error.\n");
++		goto err_master;
++	}
 +
-+	rt2880_spi_reset(rs);
++	return ret;
 +
-+	return spi_register_master(master);
++err_master:
++	spi_master_put(master);
++	kfree(master);
++err_clk:
++	clk_disable_unprepare(clk);
++
++	return ret;
 +}
 +
 +static int rt2880_spi_remove(struct platform_device *pdev)
@@ -444,20 +919,14 @@  Acked-by: John Crispin <blogic@openwrt.org>
 +	master = dev_get_drvdata(&pdev->dev);
 +	rs = spi_master_get_devdata(master);
 +
-+	clk_disable(rs->clk);
-+	spi_unregister_master(master);
++	clk_disable_unprepare(rs->clk);
++	atomic_dec(&hw_reset_count);
 +
 +	return 0;
 +}
 +
 +MODULE_ALIAS("platform:" DRIVER_NAME);
 +
-+static const struct of_device_id rt2880_spi_match[] = {
-+	{ .compatible = "ralink,rt2880-spi" },
-+	{},
-+};
-+MODULE_DEVICE_TABLE(of, rt2880_spi_match);
-+
 +static struct platform_driver rt2880_spi_driver = {
 +	.driver = {
 +		.name = DRIVER_NAME,
diff --git a/target/linux/ramips/patches-3.18/0051-rt5350-spi-second-device.patch b/target/linux/ramips/patches-3.18/0051-rt5350-spi-second-device.patch
deleted file mode 100644
index 2da8151..0000000
--- a/target/linux/ramips/patches-3.18/0051-rt5350-spi-second-device.patch
+++ /dev/null
@@ -1,368 +0,0 @@ 
-From 27b11d4f1888e1a3d6d75b46d4d5a4d86fc03891 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Wed, 6 Aug 2014 10:53:40 +0200
-Subject: [PATCH 51/57] SPI: MIPS: ralink: add rt5350 dual SPI support
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
----
- drivers/spi/spi-rt2880.c |  218 +++++++++++++++++++++++++++++++++++++++++++---
- 1 file changed, 205 insertions(+), 13 deletions(-)
-
---- a/drivers/spi/spi-rt2880.c
-+++ b/drivers/spi/spi-rt2880.c
-@@ -21,19 +21,25 @@
- #include <linux/io.h>
- #include <linux/reset.h>
- #include <linux/spi/spi.h>
-+#include <linux/of_device.h>
- #include <linux/platform_device.h>
- 
-+#include <ralink_regs.h>
-+
-+#define SPI_BPW_MASK(bits) BIT((bits) - 1)
-+
- #define DRIVER_NAME			"spi-rt2880"
--/* only one slave is supported*/
--#define RALINK_NUM_CHIPSELECTS		1
- /* in usec */
- #define RALINK_SPI_WAIT_MAX_LOOP	2000
- 
--#define RAMIPS_SPI_STAT			0x00
--#define RAMIPS_SPI_CFG			0x10
--#define RAMIPS_SPI_CTL			0x14
--#define RAMIPS_SPI_DATA			0x20
--#define RAMIPS_SPI_FIFO_STAT		0x38
-+#define RAMIPS_SPI_DEV_OFFSET		0x40
-+
-+#define RAMIPS_SPI_STAT(cs)		(0x00 + (cs * RAMIPS_SPI_DEV_OFFSET))
-+#define RAMIPS_SPI_CFG(cs)		(0x10 + (cs * RAMIPS_SPI_DEV_OFFSET))
-+#define RAMIPS_SPI_CTL(cs)		(0x14 + (cs * RAMIPS_SPI_DEV_OFFSET))
-+#define RAMIPS_SPI_DATA(cs)		(0x20 + (cs * RAMIPS_SPI_DEV_OFFSET))
-+#define RAMIPS_SPI_FIFO_STAT(cs)	(0x38 + (cs * RAMIPS_SPI_DEV_OFFSET))
-+#define RAMIPS_SPI_ARBITER		0xF0
- 
- /* SPISTAT register bit field */
- #define SPISTAT_BUSY			BIT(0)
-@@ -63,6 +69,19 @@
- /* SPIFIFOSTAT register bit field */
- #define SPIFIFOSTAT_TXFULL		BIT(17)
- 
-+#define SPICTL_ARB_EN			BIT(31)
-+#define SPI1_POR			BIT(1)
-+#define SPI0_POR			BIT(0)
-+
-+#define RT2880_SPI_MODE_BITS	(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH)
-+
-+struct rt2880_spi;
-+
-+struct rt2880_spi_ops {
-+	void (*init_hw)(struct rt2880_spi *rs);
-+	int num_cs;
-+};
-+
- struct rt2880_spi {
- 	struct spi_master	*master;
- 	void __iomem		*base;
-@@ -70,6 +89,8 @@ struct rt2880_spi {
- 	unsigned int		speed;
- 	struct clk		*clk;
- 	spinlock_t		lock;
-+
-+	struct rt2880_spi_ops	*ops;
- };
- 
- static inline struct rt2880_spi *spidev_to_rt2880_spi(struct spi_device *spi)
-@@ -115,6 +136,7 @@ static inline void rt2880_spi_clrbits(st
- 
- static int rt2880_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
- {
-+	int cs = spi->chip_select;
- 	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
- 	u32 rate;
- 	u32 prescale;
-@@ -142,9 +164,9 @@ static int rt2880_spi_baudrate_set(struc
- 	prescale = ilog2(rate / 2);
- 	dev_dbg(&spi->dev, "prescale:%u\n", prescale);
- 
--	reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG);
-+	reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG(cs));
- 	reg = ((reg & ~SPICFG_SPICLK_PRESCALE_MASK) | prescale);
--	rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg);
-+	rt2880_spi_write(rs, RAMIPS_SPI_CFG(cs), reg);
- 	rs->speed = speed;
- 	return 0;
- }
-@@ -157,7 +179,8 @@ rt2880_spi_setup_transfer(struct spi_dev
- {
- 	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
- 	unsigned int speed = spi->max_speed_hz;
--	int rc;
-+	int rc, cs = spi->chip_select;
-+	u32 reg;
- 
- 	if ((t != NULL) && t->speed_hz)
- 		speed = t->speed_hz;
-@@ -169,25 +192,68 @@ rt2880_spi_setup_transfer(struct spi_dev
- 			return rc;
- 	}
- 
-+	reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG(cs));
-+
-+	reg = (reg & ~SPICFG_MSBFIRST);
-+	if (!(spi->mode & SPI_LSB_FIRST))
-+		reg |= SPICFG_MSBFIRST;
-+
-+	reg = (reg & ~(SPICFG_SPICLKPOL | SPICFG_RXCLKEDGE_FALLING |SPICFG_TXCLKEDGE_FALLING));
-+	switch(spi->mode & (SPI_CPOL | SPI_CPHA)) {
-+		case SPI_MODE_0:
-+			reg |= SPICFG_SPICLKPOL | SPICFG_TXCLKEDGE_FALLING;
-+			break;
-+		case SPI_MODE_1:
-+			reg |= SPICFG_SPICLKPOL | SPICFG_RXCLKEDGE_FALLING;
-+			break;
-+		case SPI_MODE_2:
-+			reg |= SPICFG_RXCLKEDGE_FALLING;
-+			break;
-+		case SPI_MODE_3:
-+			reg |= SPICFG_TXCLKEDGE_FALLING;
-+			break;
-+	}
-+
-+	rt2880_spi_write(rs, RAMIPS_SPI_CFG(cs), reg);
-+
-+	reg = SPICTL_ARB_EN;
-+	if (spi->mode & SPI_CS_HIGH) {
-+		switch(cs) {
-+			case 0:
-+				reg |= SPI0_POR;
-+				break;
-+			case 1:
-+				reg |= SPI1_POR;
-+				break;
-+		}
-+	}
-+
-+	rt2880_spi_write(rs, RAMIPS_SPI_ARBITER, reg);
-+
- 	return 0;
- }
- 
--static void rt2880_spi_set_cs(struct rt2880_spi *rs, int enable)
-+static void rt2880_spi_set_cs(struct spi_device *spi, int enable)
- {
-+	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
-+	int cs = spi->chip_select;
-+
- 	if (enable)
--		rt2880_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
-+		rt2880_spi_clrbits(rs, RAMIPS_SPI_CTL(cs), SPICTL_SPIENA);
- 	else
--		rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
-+		rt2880_spi_setbits(rs, RAMIPS_SPI_CTL(cs), SPICTL_SPIENA);
- }
- 
--static inline int rt2880_spi_wait_till_ready(struct rt2880_spi *rs)
-+static inline int rt2880_spi_wait_till_ready(struct spi_device *spi)
- {
-+	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
-+	int cs = spi->chip_select;
- 	int i;
- 
- 	for (i = 0; i < RALINK_SPI_WAIT_MAX_LOOP; i++) {
- 		u32 status;
- 
--		status = rt2880_spi_read(rs, RAMIPS_SPI_STAT);
-+		status = rt2880_spi_read(rs, RAMIPS_SPI_STAT(cs));
- 		if ((status & SPISTAT_BUSY) == 0)
- 			return 0;
- 
-@@ -199,9 +265,10 @@ static inline int rt2880_spi_wait_till_r
- }
- 
- static unsigned int
--rt2880_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
-+rt2880_spi_write_read(struct spi_device *spi, struct list_head *list, struct spi_transfer *xfer)
- {
- 	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
-+	int cs = spi->chip_select;
- 	unsigned count = 0;
- 	u8 *rx = xfer->rx_buf;
- 	const u8 *tx = xfer->tx_buf;
-@@ -213,9 +280,9 @@ rt2880_spi_write_read(struct spi_device
- 
- 	if (tx) {
- 		for (count = 0; count < xfer->len; count++) {
--			rt2880_spi_write(rs, RAMIPS_SPI_DATA, tx[count]);
--			rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR);
--			err = rt2880_spi_wait_till_ready(rs);
-+			rt2880_spi_write(rs, RAMIPS_SPI_DATA(cs), tx[count]);
-+			rt2880_spi_setbits(rs, RAMIPS_SPI_CTL(cs), SPICTL_STARTWR);
-+			err = rt2880_spi_wait_till_ready(spi);
- 			if (err) {
- 				dev_err(&spi->dev, "TX failed, err=%d\n", err);
- 				goto out;
-@@ -225,13 +292,13 @@ rt2880_spi_write_read(struct spi_device
- 
- 	if (rx) {
- 		for (count = 0; count < xfer->len; count++) {
--			rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD);
--			err = rt2880_spi_wait_till_ready(rs);
-+			rt2880_spi_setbits(rs, RAMIPS_SPI_CTL(cs), SPICTL_STARTRD);
-+			err = rt2880_spi_wait_till_ready(spi);
- 			if (err) {
- 				dev_err(&spi->dev, "RX failed, err=%d\n", err);
- 				goto out;
- 			}
--			rx[count] = (u8) rt2880_spi_read(rs, RAMIPS_SPI_DATA);
-+			rx[count] = (u8) rt2880_spi_read(rs, RAMIPS_SPI_DATA(cs));
- 		}
- 	}
- 
-@@ -280,25 +347,25 @@ static int rt2880_spi_transfer_one_messa
- 		}
- 
- 		if (!cs_active) {
--			rt2880_spi_set_cs(rs, 1);
-+			rt2880_spi_set_cs(spi, 1);
- 			cs_active = 1;
- 		}
- 
- 		if (t->len)
--			m->actual_length += rt2880_spi_write_read(spi, t);
-+			m->actual_length += rt2880_spi_write_read(spi, &m->transfers, t);
- 
- 		if (t->delay_usecs)
- 			udelay(t->delay_usecs);
- 
- 		if (t->cs_change) {
--			rt2880_spi_set_cs(rs, 0);
-+			rt2880_spi_set_cs(spi, 0);
- 			cs_active = 0;
- 		}
- 	}
- 
- msg_done:
- 	if (cs_active)
--		rt2880_spi_set_cs(rs, 0);
-+		rt2880_spi_set_cs(spi, 0);
- 
- 	m->status = status;
- 	spi_finalize_current_message(master);
-@@ -311,7 +378,7 @@ static int rt2880_spi_setup(struct spi_d
- 	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
- 
- 	if ((spi->max_speed_hz == 0) ||
--	    (spi->max_speed_hz > (rs->sys_freq / 2)))
-+		(spi->max_speed_hz > (rs->sys_freq / 2)))
- 		spi->max_speed_hz = (rs->sys_freq / 2);
- 
- 	if (spi->max_speed_hz < (rs->sys_freq / 128)) {
-@@ -328,14 +395,47 @@ static int rt2880_spi_setup(struct spi_d
- 
- static void rt2880_spi_reset(struct rt2880_spi *rs)
- {
--	rt2880_spi_write(rs, RAMIPS_SPI_CFG,
-+	rt2880_spi_write(rs, RAMIPS_SPI_CFG(0),
- 			 SPICFG_MSBFIRST | SPICFG_TXCLKEDGE_FALLING |
- 			 SPICFG_SPICLK_DIV16 | SPICFG_SPICLKPOL);
--	rt2880_spi_write(rs, RAMIPS_SPI_CTL, SPICTL_HIZSDO | SPICTL_SPIENA);
-+	rt2880_spi_write(rs, RAMIPS_SPI_CTL(0), SPICTL_HIZSDO | SPICTL_SPIENA);
- }
- 
-+static void rt5350_spi_reset(struct rt2880_spi *rs)
-+{
-+	int cs;
-+
-+	rt2880_spi_write(rs, RAMIPS_SPI_ARBITER,
-+			 SPICTL_ARB_EN);
-+
-+	for (cs = 0; cs < rs->ops->num_cs; cs++) {
-+		rt2880_spi_write(rs, RAMIPS_SPI_CFG(cs),
-+				 SPICFG_MSBFIRST | SPICFG_TXCLKEDGE_FALLING |
-+				 SPICFG_SPICLK_DIV16 | SPICFG_SPICLKPOL);
-+		rt2880_spi_write(rs, RAMIPS_SPI_CTL(cs), SPICTL_HIZSDO | SPICTL_SPIENA);
-+	}
-+}
-+
-+static struct rt2880_spi_ops spi_ops[] = {
-+	{
-+		.init_hw = rt2880_spi_reset,
-+		.num_cs = 1,
-+	}, {
-+		.init_hw = rt5350_spi_reset,
-+		.num_cs = 2,
-+	},
-+};
-+
-+static const struct of_device_id rt2880_spi_match[] = {
-+	{ .compatible = "ralink,rt2880-spi", .data = &spi_ops[0]},
-+	{ .compatible = "ralink,rt5350-spi", .data = &spi_ops[1]},
-+	{},
-+};
-+MODULE_DEVICE_TABLE(of, rt2880_spi_match);
-+
- static int rt2880_spi_probe(struct platform_device *pdev)
- {
-+	const struct of_device_id *match;
- 	struct spi_master *master;
- 	struct rt2880_spi *rs;
- 	unsigned long flags;
-@@ -343,6 +443,12 @@ static int rt2880_spi_probe(struct platf
- 	struct resource *r;
- 	int status = 0;
- 	struct clk *clk;
-+	struct rt2880_spi_ops *ops;
-+
-+	match = of_match_device(rt2880_spi_match, &pdev->dev);
-+	if (!match)
-+		return -EINVAL;
-+	ops = (struct rt2880_spi_ops *)match->data;
- 
- 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- 	base = devm_ioremap_resource(&pdev->dev, r);
-@@ -366,14 +472,13 @@ static int rt2880_spi_probe(struct platf
- 		return -ENOMEM;
- 	}
- 
--	/* we support only mode 0, and no options */
--	master->mode_bits = 0;
-+	master->mode_bits = RT2880_SPI_MODE_BITS;
- 
- 	master->setup = rt2880_spi_setup;
- 	master->transfer_one_message = rt2880_spi_transfer_one_message;
--	master->num_chipselect = RALINK_NUM_CHIPSELECTS;
- 	master->bits_per_word_mask = SPI_BPW_MASK(8);
- 	master->dev.of_node = pdev->dev.of_node;
-+	master->num_chipselect = ops->num_cs;
- 
- 	dev_set_drvdata(&pdev->dev, master);
- 
-@@ -382,12 +487,13 @@ static int rt2880_spi_probe(struct platf
- 	rs->clk = clk;
- 	rs->master = master;
- 	rs->sys_freq = clk_get_rate(rs->clk);
-+	rs->ops = ops;
- 	dev_dbg(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
- 	spin_lock_irqsave(&rs->lock, flags);
- 
- 	device_reset(&pdev->dev);
- 
--	rt2880_spi_reset(rs);
-+	rs->ops->init_hw(rs);
- 
- 	return spi_register_master(master);
- }
-@@ -408,12 +514,6 @@ static int rt2880_spi_remove(struct plat
- 
- MODULE_ALIAS("platform:" DRIVER_NAME);
- 
--static const struct of_device_id rt2880_spi_match[] = {
--	{ .compatible = "ralink,rt2880-spi" },
--	{},
--};
--MODULE_DEVICE_TABLE(of, rt2880_spi_match);
--
- static struct platform_driver rt2880_spi_driver = {
- 	.driver = {
- 		.name = DRIVER_NAME,