From: michael lee <[email protected]>

only support spi flash command (half duplex).
no need chunk io patch. done by driver.

test results on mt7621. use dd read bs=512 with 32128 records
old driver : 30.52s
new driver : 34.31s
new driver + no chunk io : 16.65s
new driver + no chunk io + fast read clock from 10Mhz to 50Mhz : 5.00s

Signed-off-by: Michael Lee <[email protected]>
---
 target/linux/ramips/dts/MT7628.dts                 |   1 -
 target/linux/ramips/dts/mt7621.dtsi                |   7 +-
 target/linux/ramips/dts/mt7628an.dtsi              |   6 +-
 .../0044-mtd-add-chunked-read-io-to-m25p80.patch   | 103 ---
 ...0061-SPI-ralink-add-mt7621-SoC-spi-driver.patch | 794 ++++++++++++++-------
 5 files changed, 553 insertions(+), 358 deletions(-)
 delete mode 100644 
target/linux/ramips/patches-3.18/0044-mtd-add-chunked-read-io-to-m25p80.patch

diff --git a/target/linux/ramips/dts/MT7628.dts 
b/target/linux/ramips/dts/MT7628.dts
index dd6647f..87d12d2 100644
--- a/target/linux/ramips/dts/MT7628.dts
+++ b/target/linux/ramips/dts/MT7628.dts
@@ -31,7 +31,6 @@
                                reg = <0 0>;
                                linux,modalias = "m25p80", "en25q64";
                                spi-max-frequency = <10000000>;
-                               m25p,chunked-io = <32>;
 
                                partition@0 {
                                        label = "u-boot";
diff --git a/target/linux/ramips/dts/mt7621.dtsi 
b/target/linux/ramips/dts/mt7621.dtsi
index bc79d39..cd115b1 100644
--- a/target/linux/ramips/dts/mt7621.dtsi
+++ b/target/linux/ramips/dts/mt7621.dtsi
@@ -20,6 +20,10 @@
                compatible = "mti,cpu-interrupt-controller";
        };
 
+       aliases {
+               spi0 = &spi0;
+       };
+
        palmbus@1E000000 {
                compatible = "palmbus";
                reg = <0x1E000000 0x100000>;
@@ -84,7 +88,7 @@
                        no-loopback-test;
                };
 
-               spi@b00 {
+               spi0: spi@b00 {
                        status = "okay";
 
                        compatible = "ralink,mt7621-spi";
@@ -104,7 +108,6 @@
                                #size-cells = <1>;
                                reg = <0 0>;
                                spi-max-frequency = <10000000>;
-                               m25p,chunked-io = <32>;
                        };
                };
        };
diff --git a/target/linux/ramips/dts/mt7628an.dtsi 
b/target/linux/ramips/dts/mt7628an.dtsi
index 02f9df3..eb8a6ee 100644
--- a/target/linux/ramips/dts/mt7628an.dtsi
+++ b/target/linux/ramips/dts/mt7628an.dtsi
@@ -20,6 +20,10 @@
                compatible = "mti,cpu-interrupt-controller";
        };
 
+       aliases {
+               spi0 = &spi0;
+       };
+
        palmbus@10000000 {
                compatible = "palmbus";
                reg = <0x10000000 0x200000>;
@@ -102,7 +106,7 @@
                        };
                };
 
-               spi@b00 {
+               spi0: spi@b00 {
                        compatible = "ralink,mt7621-spi";
                        reg = <0xb00 0x100>;
 
diff --git 
a/target/linux/ramips/patches-3.18/0044-mtd-add-chunked-read-io-to-m25p80.patch 
b/target/linux/ramips/patches-3.18/0044-mtd-add-chunked-read-io-to-m25p80.patch
deleted file mode 100644
index 1716e1c..0000000
--- 
a/target/linux/ramips/patches-3.18/0044-mtd-add-chunked-read-io-to-m25p80.patch
+++ /dev/null
@@ -1,103 +0,0 @@
---- a/drivers/mtd/devices/m25p80.c
-+++ b/drivers/mtd/devices/m25p80.c
-@@ -19,6 +19,7 @@
- #include <linux/errno.h>
- #include <linux/module.h>
- #include <linux/device.h>
-+#include <linux/of.h>
- 
- #include <linux/mtd/mtd.h>
- #include <linux/mtd/partitions.h>
-@@ -32,6 +33,7 @@ struct m25p {
-       struct spi_device       *spi;
-       struct spi_nor          spi_nor;
-       struct mtd_info         mtd;
-+      u16                     chunk_size;
-       u8                      command[MAX_CMD_SIZE];
- };
- 
-@@ -157,6 +159,61 @@ static int m25p80_read(struct spi_nor *n
-       return 0;
- }
- 
-+static void m25p80_chunked_write(struct spi_nor *nor, loff_t _from, size_t 
_len,
-+                      size_t *_retlen, const u_char *_buf)
-+{
-+      struct m25p *flash = nor->priv;
-+      int chunk_size;
-+      int retlen = 0;
-+
-+      chunk_size = flash->chunk_size;
-+      if (!chunk_size)
-+              chunk_size = _len;
-+
-+      if (nor->addr_width > 3)
-+              chunk_size -= nor->addr_width - 3;
-+
-+      while (retlen < _len) {
-+              size_t len = min_t(int, chunk_size, _len - retlen);
-+              const u_char *buf = _buf + retlen;
-+              loff_t from = _from + retlen;
-+
-+              nor->wait_till_ready(nor);
-+              nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
-+
-+              m25p80_write(nor, from, len, &retlen, buf);
-+      }
-+      *_retlen += retlen;
-+}
-+
-+static int m25p80_chunked_read(struct spi_nor *nor, loff_t _from, size_t _len,
-+                      size_t *_retlen, u_char *_buf)
-+{
-+      struct m25p *flash = nor->priv;
-+      int chunk_size;
-+
-+      chunk_size = flash->chunk_size;
-+      if (!chunk_size)
-+              chunk_size = _len;
-+
-+      *_retlen = 0;
-+
-+      while (*_retlen < _len) {
-+              size_t len = min_t(int, chunk_size, _len - *_retlen);
-+              u_char *buf = _buf + *_retlen;
-+              loff_t from = _from + *_retlen;
-+              int retlen = 0;
-+              int ret = m25p80_read(nor, from, len, &retlen, buf);
-+
-+              if (ret)
-+                      return ret;
-+
-+              *_retlen += retlen;
-+      }
-+
-+      return 0;
-+}
-+
- static int m25p80_erase(struct spi_nor *nor, loff_t offset)
- {
-       struct m25p *flash = nor->priv;
-@@ -197,6 +254,7 @@ static int m25p_probe(struct spi_device
-       struct spi_nor *nor;
-       enum read_mode mode = SPI_NOR_NORMAL;
-       char *flash_name = NULL;
-+      u32 val;
-       int ret;
- 
-       data = dev_get_platdata(&spi->dev);
-@@ -244,6 +302,14 @@ static int m25p_probe(struct spi_device
-       if (ret)
-               return ret;
- 
-+      if (spi->dev.of_node &&
-+          !of_property_read_u32(spi->dev.of_node, "m25p,chunked-io", &val)) {
-+              dev_warn(&spi->dev, "using chunked io\n");
-+              nor->read = m25p80_chunked_read;
-+              nor->write = m25p80_chunked_write;
-+              flash->chunk_size = val;
-+      }
-+
-       ppdata.of_node = spi->dev.of_node;
- 
-       return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,
diff --git 
a/target/linux/ramips/patches-3.18/0061-SPI-ralink-add-mt7621-SoC-spi-driver.patch
 
b/target/linux/ramips/patches-3.18/0061-SPI-ralink-add-mt7621-SoC-spi-driver.patch
index 2ba1ee8..539e8bd 100644
--- 
a/target/linux/ramips/patches-3.18/0061-SPI-ralink-add-mt7621-SoC-spi-driver.patch
+++ 
b/target/linux/ramips/patches-3.18/0061-SPI-ralink-add-mt7621-SoC-spi-driver.patch
@@ -1,6 +1,6 @@
 --- a/drivers/spi/Kconfig
 +++ b/drivers/spi/Kconfig
-@@ -439,6 +439,12 @@
+@@ -439,6 +439,12 @@ config SPI_RT2880
        help
          This selects a driver for the Ralink RT288x/RT305x SPI Controller.
  
@@ -15,7 +15,7 @@
        depends on ARCH_S3C24XX
 --- a/drivers/spi/Makefile
 +++ b/drivers/spi/Makefile
-@@ -46,6 +46,7 @@
+@@ -46,6 +46,7 @@ obj-$(CONFIG_SPI_LM70_LLP)           += spi-lm70l
  obj-$(CONFIG_SPI_MPC512x_PSC)         += spi-mpc512x-psc.o
  obj-$(CONFIG_SPI_MPC52xx_PSC)         += spi-mpc52xx-psc.o
  obj-$(CONFIG_SPI_MPC52xx)             += spi-mpc52xx.o
@@ -25,13 +25,14 @@
  obj-$(CONFIG_SPI_OC_TINY)             += spi-oc-tiny.o
 --- /dev/null
 +++ b/drivers/spi/spi-mt7621.c
-@@ -0,0 +1,479 @@
+@@ -0,0 +1,771 @@
 +/*
 + * spi-mt7621.c -- MediaTek MT7621 SPI controller driver
 + *
 + * Copyright (C) 2011 Sergiy <[email protected]>
 + * Copyright (C) 2011-2013 Gabor Juhos <[email protected]>
 + * Copyright (C) 2014-2015 Felix Fietkau <[email protected]>
++ * Copyright (C) 2015 Michael Lee <[email protected]>
 + *
 + * Some parts are based on spi-orion.c:
 + *   Author: Shadi Ammouri <[email protected]>
@@ -53,49 +54,140 @@
 +#include <linux/of_device.h>
 +#include <linux/platform_device.h>
 +#include <linux/swab.h>
++#include <linux/mtd/spi-nor.h>
 +
 +#include <ralink_regs.h>
 +
-+#define SPI_BPW_MASK(bits) BIT((bits) - 1)
-+
 +#define DRIVER_NAME                   "spi-mt7621"
-+/* in usec */
-+#define RALINK_SPI_WAIT_MAX_LOOP      2000
-+
-+/* SPISTAT register bit field */
-+#define SPISTAT_BUSY                  BIT(0)
 +
 +#define MT7621_SPI_TRANS      0x00
-+#define SPITRANS_BUSY         BIT(16)
-+
 +#define MT7621_SPI_OPCODE     0x04
 +#define MT7621_SPI_DATA0      0x08
-+#define MT7621_SPI_DATA4      0x18
-+#define SPI_CTL_TX_RX_CNT_MASK        0xff
-+#define SPI_CTL_START         BIT(8)
-+
-+#define MT7621_SPI_POLAR      0x38
 +#define MT7621_SPI_MASTER     0x28
 +#define MT7621_SPI_MOREBUF    0x2c
++#define MT7621_SPI_QUEUE_CTL  0x30
++#define MT7621_SPI_STATUS     0x34
++#define MT7621_SPI_POLAR      0x38
 +#define MT7621_SPI_SPACE      0x3c
 +
-+#define MT7621_CPHA           BIT(5)
-+#define MT7621_CPOL           BIT(4)
-+#define MT7621_LSB_FIRST      BIT(3)
++/* MT7621_SPI_TRANS */
++#define SPITRANS_ADDREXT_MASK         0xff
++#define SPITRANS_ADDREXT_OFFSET               24
++#define SPITRANS_ADDRSIZE_MASK                0x3
++#define SPITRANS_ADDRSIZE_OFFSET      19
++#define SPITRANS_BUSY                 BIT(16)
++#define SPITRANS_START                        BIT(8)
++#define SPITRANS_BYTECNT_MASK         0xf
++#define SPITRANS_MISO_OFFSET          4
++#define SPITRANS_MOSI_OFFSET          0
++
++/* MT7621_SPI_OPCODE */
++#define SPIOP_MB_OPCODE_OFFSET                24
++#define SPIOP_MB_ADDR_MASK            0xffffff
++
++/* MT7621_SPI_MASTER */
++#define SPIMASTER_CS_MASK             0x7
++#define SPIMASTER_CS_OFFSET           29
++#define SPIMASTER_CLK_HIGH            BIT(28)
++#define SPIMASTER_CLKSEL_MASK         0xfff
++#define SPIMASTER_CLKSEL_OFFSET               16
++#define SPIMASTER_CSDSEL_MASK         0x1f
++#define SPIMASTER_CSDSEL_OFFSET               11
++#define SPIMASTER_FULL_DUPLEX         BIT(10)
++#define SPIMASTER_INTR_ENABLE         BIT(9)
++#define SPIMASTER_START_6CLK          BIT(8)
++#define SPIMASTER_PREFETCH_ENABLE     BIT(7)
++#define SPIMASTER_BIDIR_MODE          BIT(6)
++#define SPIMASTER_CPHA                        BIT(5)
++#define SPIMASTER_CPOL                        BIT(4)
++#define SPIMASTER_LSB                 BIT(3)
++#define SPIMASTER_MB_MODE             BIT(2)
++#define SPIMASTER_SERIAL_MASK         0x3
++
++/* MT7621_SPI_MOREBUF */
++#define SPIMB_CMD_MASK                        0x3f
++#define SPIMB_CMD_OFFSET              24
++#define SPIMB_MISO_MASK                       0x1ff
++#define SPIMB_MISO_OFFSET             12
++#define SPIMB_MOSI_MASK                       0x1ff
++#define SPIMB_MOSI_OFFSET             0
++
++/* MT7621_SPI_QUEUE_CTL */
++#define SPIQCTL_PAGE_MASK             0x3f
++#define SPIQCTL_PAGE_OFFSET           26
++#define SPIQCTL_BUSY                  BIT(12)
++#define SPIQCTL_ADDRSIZE_MASK         0x3
++#define SPIQCTL_ADDRSIZER_OFFSET      10
++#define SPIQCTL_ADDRSIZE_OFFSET               8
++#define SPIQCTL_MOSI_MASK             0xf
++#define SPIQCTL_FASTSEL_MASK          0x7
++
++/* MT7621_SPI_STATUS */
++#define SPISTA_MODE_MASK              0x3
++#define SPISTA_MODE_OFFSET            4
++#define SPISTA_OK                     BIT(0)
++
++/* MT7621_SPI_POLAR */
++#define SPIPOL_CSPOL_MASK             0xff
++#define SPIPOL_CSPOL_OFFSET           0
++#define SPIPOL_CSPOL_HIGH             1
++
++/* define MT7621_SPI_SPACE */
++#define SPISPA_CS_MASK                        0x7
++#define SPISPA_CS_OFFSET              12
++#define SPISPA_CLKSEL_MASK            0xfff
++#define SPISPA_CLKSEL_OFFSET          0
++
++#define MT7621_SPI_MODE_BITS  (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | \
++              SPI_CS_HIGH)
++
++struct mt7621_mb_reg {
++      u32 mosi_bit:12,
++          miso_bit:12,
++          cmd_bit:8;
++};
 +
-+#define RT2880_SPI_MODE_BITS  (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | 
SPI_CS_HIGH)
++struct mt7621_spi_data {
++      union {
++              u32 mb_reg;
++              struct mt7621_mb_reg cnt;
++      };
++      union {
++              u32 data[9];
++              u8 buf[36];
++      };
++};
 +
-+struct mt7621_spi;
++/* cmd flags */
++#define SPI_CMD_ADDR  BIT(0)
++#define SPI_CMD_DATA  BIT(1)
++#define SPI_CMD_TX    BIT(2)
++#define SPI_CMD_RX    BIT(3)
++#define SPI_CMD_EN4B  BIT(4)
++#define SPI_CMD_EX4B  BIT(5)
++
++/* cmd status */
++#define SPI_STATE_OPCODE      0
++#define SPI_STATE_DATA                1
++
++struct mt7621_spi_cmd {
++      u16 status;
++      u16 flags;
++      u32 addr;
++      u32 opaddr_len;
++      const u8 *opaddr_data;
++};
 +
 +struct mt7621_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 mt7621_spi_ops   *ops;
++      struct mt7621_spi_cmd   cmd;
 +};
 +
 +static inline struct mt7621_spi *spidev_to_mt7621_spi(struct spi_device *spi)
@@ -113,292 +205,483 @@
 +      iowrite32(val, rs->base + reg);
 +}
 +
-+static void mt7621_spi_reset(struct mt7621_spi *rs, int duplex)
++static inline void mt7621_spi_setbits(struct mt7621_spi *rs, u32 reg, u32 
mask)
 +{
-+      u32 master = mt7621_spi_read(rs, MT7621_SPI_MASTER);
-+
-+      master &= ~(0xfff << 16);
-+      master |= 1 << 16;
-+      master |= 7 << 29;
-+      master |= 1 << 2;
-+      if (duplex)
-+              master |= 1 << 10;
++      void __iomem *addr = rs->base + reg;
 +
-+      mt7621_spi_write(rs, MT7621_SPI_MASTER, master);
++      iowrite32((ioread32(addr) | mask), addr);
 +}
 +
-+static void mt7621_spi_set_cs(struct spi_device *spi, int enable)
++static inline void mt7621_spi_clrbits(struct mt7621_spi *rs, u32 reg, u32 
mask)
 +{
-+      struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
-+      int cs = spi->chip_select;
-+      u32 polar = 0;
++      void __iomem *addr = rs->base + reg;
 +
-+        mt7621_spi_reset(rs, cs);
-+      if (enable)
-+              polar = BIT(cs);
-+      mt7621_spi_write(rs, MT7621_SPI_POLAR, polar);
++      iowrite32((ioread32(addr) & ~mask), addr);
 +}
 +
-+static int mt7621_spi_prepare(struct spi_device *spi, unsigned int speed)
++static u32 mt7621_spi_baudrate_get(struct spi_device *spi, unsigned int speed)
 +{
 +      struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
 +      u32 rate;
-+      u32 reg;
-+
-+      dev_dbg(&spi->dev, "speed:%u\n", speed);
++      u32 prescale;
 +
-+      rate = DIV_ROUND_UP(rs->sys_freq, speed);
-+      dev_dbg(&spi->dev, "rate-1:%u\n", rate);
++      /*
++       * the supported rates are: 2, 3, 4, ... 4096
++       * round up as we look for equal or less speed
++       */
++      rate = DIV_ROUND_UP(clk_get_rate(rs->clk), speed);
 +
-+      if (rate > 4097)
-+              return -EINVAL;
++      /* Convert the rate to SPI clock divisor value. */
++      prescale = rate - 2;
 +
-+      if (rate < 2)
-+              rate = 2;
-+
-+      reg = mt7621_spi_read(rs, MT7621_SPI_MASTER);
-+      reg &= ~(0xfff << 16);
-+      reg |= (rate - 2) << 16;
++      /* 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;
 +
-+      reg &= ~MT7621_LSB_FIRST;
-+      if (spi->mode & SPI_LSB_FIRST)
-+              reg |= MT7621_LSB_FIRST;
 +
-+      reg &= ~(MT7621_CPHA | MT7621_CPOL);
-+      switch(spi->mode & (SPI_CPOL | SPI_CPHA)) {
-+              case SPI_MODE_0:
-+                      break;
-+              case SPI_MODE_1:
-+                      reg |= MT7621_CPHA;
-+                      break;
-+              case SPI_MODE_2:
-+                      reg |= MT7621_CPOL;
-+                      break;
-+              case SPI_MODE_3:
-+                      reg |= MT7621_CPOL | MT7621_CPHA;
-+                      break;
-+      }
-+      mt7621_spi_write(rs, MT7621_SPI_MASTER, reg);
++      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 0;
++      return (prescale << SPIMASTER_CLKSEL_OFFSET);
 +}
 +
-+static inline int mt7621_spi_wait_till_ready(struct spi_device *spi)
++static void mt7621_spi_set_cs(struct spi_device *spi, bool enable)
 +{
 +      struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
-+      int i;
++      u32 reg;
 +
-+      for (i = 0; i < RALINK_SPI_WAIT_MAX_LOOP; i++) {
-+              u32 status;
++      if (spi->mode & SPI_CS_HIGH)
++              enable = !enable;
++      enable = !enable;
 +
-+              status = mt7621_spi_read(rs, MT7621_SPI_TRANS);
-+              if ((status & SPITRANS_BUSY) == 0) {
-+                      return 0;
-+              }
-+              cpu_relax();
-+              udelay(1);
++      reg = mt7621_spi_read(rs, MT7621_SPI_MASTER);
++      reg &= ~(SPIMASTER_CS_MASK << SPIMASTER_CS_OFFSET);
++
++      if (enable)
++              reg |= (spi->chip_select << SPIMASTER_CS_OFFSET);
++      else {
++              /* when disable just enable cs 8 instead */
++              reg |= (SPIMASTER_CS_MASK << SPIMASTER_CS_OFFSET);
 +      }
 +
++      mt7621_spi_write(rs, MT7621_SPI_MASTER, reg);
++}
++
++static inline int mt7621_spi_wait_ready(struct mt7621_spi *rs, int len)
++{
++      int loop = rs->wait_loops * len;
++
++      while ((mt7621_spi_read(rs, MT7621_SPI_TRANS) & SPITRANS_BUSY) && 
--loop)
++              cpu_relax();
++
++      if (loop)
++              return 0;
++
 +      return -ETIMEDOUT;
 +}
 +
-+static int mt7621_spi_transfer_half_duplex(struct spi_master *master,
-+                                         struct spi_message *m)
++static void mt7621_dump_reg(struct spi_master *master, const char *func)
 +{
 +      struct mt7621_spi *rs = spi_master_get_devdata(master);
-+      struct spi_device *spi = m->spi;
-+      unsigned int speed = spi->max_speed_hz;
-+      struct spi_transfer *t = NULL;
-+      int status = 0;
-+      int i, len = 0;
-+      int rx_len = 0;
-+      u32 data[9] = { 0 };
-+      u32 val;
 +
-+      mt7621_spi_wait_till_ready(spi);
++      dev_dbg(&master->dev, "%s trans: %08x, opcode: %08x, data0: %08x, "
++                      "data1: %08x, data2: %08x, data3: %08x, " \
++                      "data4: %08x, data5: %08x, data6: %08x, " \
++                      "data7: %08x, master: %08x, morebuf: %08x, " \
++                      "qctl: %08x, status: %08x, polar: %08x, " \
++                      "space: %08x\n",
++                      func,
++                      mt7621_spi_read(rs, MT7621_SPI_TRANS),
++                      mt7621_spi_read(rs, MT7621_SPI_OPCODE),
++                      mt7621_spi_read(rs, MT7621_SPI_DATA0),
++                      mt7621_spi_read(rs, MT7621_SPI_DATA0 + 4),
++                      mt7621_spi_read(rs, MT7621_SPI_DATA0 + 8),
++                      mt7621_spi_read(rs, MT7621_SPI_DATA0 + 12),
++                      mt7621_spi_read(rs, MT7621_SPI_DATA0 + 16),
++                      mt7621_spi_read(rs, MT7621_SPI_DATA0 + 20),
++                      mt7621_spi_read(rs, MT7621_SPI_DATA0 + 24),
++                      mt7621_spi_read(rs, MT7621_SPI_DATA0 + 28),
++                      mt7621_spi_read(rs, MT7621_SPI_MASTER),
++                      mt7621_spi_read(rs, MT7621_SPI_MOREBUF),
++                      mt7621_spi_read(rs, MT7621_SPI_QUEUE_CTL),
++                      mt7621_spi_read(rs, MT7621_SPI_STATUS),
++                      mt7621_spi_read(rs, MT7621_SPI_POLAR),
++                      mt7621_spi_read(rs, MT7621_SPI_SPACE));
++}
 +
-+      list_for_each_entry(t, &m->transfers, transfer_list) {
-+              const u8 *buf = t->tx_buf;
++static void m25p_addr2cmd(unsigned int addr, u8 *cmd, int addr_width)
++{
++      /* opcode is in cmd[0] */
++      cmd[1] = addr >> (addr_width * 8 -  8);
++      cmd[2] = addr >> (addr_width * 8 - 16);
++      cmd[3] = addr >> (addr_width * 8 - 24);
++      cmd[4] = addr >> (addr_width * 8 - 32);
++}
 +
-+              if (t->rx_buf)
-+                      rx_len += t->len;
++static unsigned int m25p_cmd2addr(int addr_width, const u8 *cmd)
++{
++      unsigned int addr;
 +
-+              if (!buf)
-+                      continue;
++      /* 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);
 +
-+              if (WARN_ON(len + t->len > 36)) {
-+                      status = -EIO;
-+                      goto msg_done;
-+              }
++      return addr;
++}
 +
-+              for (i = 0; i < t->len; i++, len++)
-+                      data[len / 4] |= buf[i] << (8 * (len & 3));
++static int setup_spi_cmd(struct mt7621_spi *rs, const u8 *tx, u32 len,
++              struct mt7621_spi_cmd *cmd)
++{
++      int ret = 0;
++
++      switch (tx[0]) {
++      case SPINOR_OP_READ:
++      case SPINOR_OP_READ_FAST:
++              cmd->flags = SPI_CMD_ADDR;
++      case SPINOR_OP_RDSR:
++      case SPINOR_OP_RDID:
++      case SPINOR_OP_RDFSR:
++      case SPINOR_OP_RDCR:
++              cmd->flags |= SPI_CMD_RX;
++              break;
++      case SPINOR_OP_BRWR:
++              if (tx[1] == BIT(7))
++                      cmd->flags = SPI_CMD_EN4B;
++              else
++                      cmd->flags = SPI_CMD_EX4B;
++      case SPINOR_OP_WRSR:
++              cmd->flags |= SPI_CMD_DATA | SPI_CMD_TX;
++              break;
++      case SPINOR_OP_PP:
++              cmd->flags = SPI_CMD_ADDR | SPI_CMD_TX;
++              break;
++      case SPINOR_OP_SE:
++              cmd->flags = SPI_CMD_ADDR;
++      case SPINOR_OP_WREN:
++      case SPINOR_OP_WRDI:
++      case SPINOR_OP_CHIP_ERASE:
++              break;
++      case SPINOR_OP_EN4B:
++              cmd->flags = SPI_CMD_EN4B;
++              break;
++      case SPINOR_OP_EX4B:
++              cmd->flags = SPI_CMD_EX4B;
++              break;
++      default:
++              ret = -EINVAL;
++              goto out;
 +      }
 +
-+      if (WARN_ON(rx_len > 32)) {
-+              status = -EIO;
-+              goto msg_done;
-+      }
++      cmd->opaddr_len = len;
++      cmd->opaddr_data = tx;
 +
-+      if (mt7621_spi_prepare(spi, speed)) {
-+              status = -EIO;
-+              goto msg_done;
-+      }
-+      data[0] = swab32(data[0]);
-+      if (len < 4)
-+              data[0] >>= (4 - len) * 8;
++      /* setup 4 bytes address */
++      if (cmd->flags & SPI_CMD_EN4B)
++              rs->addr_width = 4;
++      else if (cmd->flags & SPI_CMD_EX4B)
++              rs->addr_width = 3;
 +
-+      for (i = 0; i < len; i += 4)
-+              mt7621_spi_write(rs, MT7621_SPI_OPCODE + i, data[i / 4]);
++      /* address */
++      if (cmd->flags & SPI_CMD_ADDR)
++              cmd->addr = m25p_cmd2addr(rs->addr_width, tx);
++
++out:
++      return ret;
++}
 +
-+      val = (min_t(int, len, 4) * 8) << 24;
-+      if (len > 4)
-+              val |= (len - 4) * 8;
-+      val |= (rx_len * 8) << 12;
-+      mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
++static int mt7621_spi_start(struct mt7621_spi *rs, struct mt7621_spi_data 
*data)
++{
++      int i, len, ret;
 +
-+      mt7621_spi_set_cs(spi, 1);
++      /* opcode/addr */
++      mt7621_spi_write(rs, MT7621_SPI_OPCODE, data->data[0]);
 +
-+      val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
-+      val |= SPI_CTL_START;
-+      mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
++      /* tx data */
++      len = data->cnt.mosi_bit >> 3;
++      for (i = 0; i < len; i += 4)
++              mt7621_spi_write(rs, (MT7621_SPI_DATA0 + i),
++                              data->data[1 + (i / 4)]);
 +
-+      mt7621_spi_wait_till_ready(spi);
++      /* set more buf size */
++      mt7621_spi_write(rs, MT7621_SPI_MOREBUF, data->mb_reg);
 +
-+      mt7621_spi_set_cs(spi, 0);
++      /* start transaction */
++      mt7621_spi_setbits(rs, MT7621_SPI_TRANS, SPITRANS_START);
 +
-+      for (i = 0; i < rx_len; i += 4)
-+              data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i);
++      len = (data->cnt.cmd_bit + data->cnt.miso_bit + data->cnt.mosi_bit) >> 
3;
++      ret = mt7621_spi_wait_ready(rs, len);
++      if (ret)
++              return ret;
 +
-+      m->actual_length = len + rx_len;
++      /* rx data */
++      len = data->cnt.miso_bit >> 3;
++      if (len)
++              for (i = 0; i < len; i += 4)
++                      data->data[i / 4] = mt7621_spi_read(rs,
++                                      (MT7621_SPI_DATA0 + i));
 +
-+      len = 0;
-+      list_for_each_entry(t, &m->transfers, transfer_list) {
-+              u8 *buf = t->rx_buf;
++      return ret;
++}
 +
-+              if (!buf)
-+                      continue;
++static int mt7621_read_sr(struct mt7621_spi *rs)
++{
++      struct mt7621_spi_data data = {0};
++      int ret;
 +
-+              for (i = 0; i < t->len; i++, len++)
-+                      buf[i] = data[len / 4] >> (8 * (len & 3));
-+      }
++      data.cnt.cmd_bit = 8;
++      data.cnt.miso_bit = 8;
++      data.data[0] = SPINOR_OP_RDSR;
 +
-+msg_done:
-+      m->status = status;
-+      spi_finalize_current_message(master);
++      ret = mt7621_spi_start(rs, &data);
++      if (ret)
++              return ret;
 +
-+      return 0;
++      return (int)data.buf[0];
 +}
 +
-+static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
-+                                         struct spi_message *m)
++static int mt7621_wait_till_ready(struct mt7621_spi *rs)
 +{
-+      struct mt7621_spi *rs = spi_master_get_devdata(master);
-+      struct spi_device *spi = m->spi;
-+      unsigned int speed = spi->max_speed_hz;
-+      struct spi_transfer *t = NULL;
-+      int status = 0;
-+      int i, len = 0;
-+      int rx_len = 0;
-+      u32 data[9] = { 0 };
-+      u32 val = 0;
++      unsigned long deadline;
++      int sr;
++
++      deadline = jiffies + (40 * HZ);
 +
-+      mt7621_spi_wait_till_ready(spi);
++      do {
++              usleep_range(10, 100);
++
++              sr = mt7621_read_sr(rs);
++              if (sr < 0)
++                      break;
++              else if (!(sr & SR_WIP))
++                      return 0;
++      } while (!time_after_eq(jiffies, deadline));
 +
-+      list_for_each_entry(t, &m->transfers, transfer_list) {
-+              const u8 *buf = t->tx_buf;
++      return -ETIMEDOUT;
++}
 +
-+              if (t->rx_buf)
-+                      rx_len += t->len;
++static int mt7621_write_enable(struct mt7621_spi *rs)
++{
++      struct mt7621_spi_data data = {0};
++      int ret;
 +
-+              if (!buf)
-+                      continue;
++      data.cnt.cmd_bit = 8;
++      data.data[0] = SPINOR_OP_WREN;
 +
-+              if (WARN_ON(len + t->len > 16)) {
-+                      status = -EIO;
-+                      goto msg_done;
++      ret = mt7621_spi_start(rs, &data);
++
++      return ret;
++}
++
++static int mt7621_spi_transfer_one(struct spi_master *master,
++              struct spi_device *spi, struct spi_transfer *xfer)
++{
++      struct mt7621_spi *rs = spi_master_get_devdata(master);
++      struct mt7621_spi_cmd *cmd;
++      struct mt7621_spi_data data;
++      const u8 *tx;
++      u8 *rx;
++      int len, tx_len, rx_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;
 +              }
 +
-+              for (i = 0; i < t->len; i++, len++)
-+                      data[len / 4] |= buf[i] << (8 * (len & 3));
++              /* need data at next transfer */
++              if ((cmd->flags & (SPI_CMD_TX | SPI_CMD_RX)) &&
++                              !(cmd->flags & SPI_CMD_DATA)) {
++                      cmd->status = SPI_STATE_DATA;
++                      return ret;
++              }
++              /* just opcode and address. no need other data */
++              len = 0;
++              tx = rx = NULL;
++      } 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;
++              }
++              len = xfer->len;
++              tx = xfer->tx_buf;
++              rx = xfer->rx_buf;
 +      }
 +
-+      if (WARN_ON(rx_len > 16)) {
-+              status = -EIO;
-+              goto msg_done;
-+      }
++      memcpy(data.buf, cmd->opaddr_data, cmd->opaddr_len);
++      do {
++              /* handle tx data */
++              if (tx) {
++                      tx_len = min(len, (int)(36 - cmd->opaddr_len));
++
++                      memcpy((data.buf + cmd->opaddr_len), tx, tx_len);
++                      tx += tx_len;
++                      len -= tx_len;
++                      cmd->addr += tx_len;
++              } else
++                      tx_len = 0;
++
++              tx_len += cmd->opaddr_len;
++              data.cnt.cmd_bit = min(tx_len * 8, 32);
++              data.cnt.mosi_bit = (tx_len * 8) - data.cnt.cmd_bit;
++
++              /* fill opaddr reg */
++              data.data[0] = cpu_to_be32(data.data[0]);
++              if (data.cnt.cmd_bit < 32)
++                      data.data[0] >>= (32 - data.cnt.cmd_bit);
++
++              if (rx) {
++                      rx_len = min(len, 32);
++                      data.cnt.miso_bit = rx_len * 8;
++              } else
++                      data.cnt.miso_bit = 0;
++
++              /* start transfer */
++              ret = mt7621_spi_start(rs, &data);
++              if (ret) {
++                      dev_err(&spi->dev, "start wait timeout\n");
++                      goto err;
++              }
 +
-+      if (mt7621_spi_prepare(spi, speed)) {
-+              status = -EIO;
-+              goto msg_done;
-+      }
++              /* handle rx data */
++              if (rx) {
++                      memcpy(rx, data.buf, rx_len);
++                      rx += rx_len;
++                      len -= rx_len;
++                      cmd->addr += rx_len;
++              }
 +
-+      for (i = 0; i < len; i += 4)
-+              mt7621_spi_write(rs, MT7621_SPI_DATA0 + i, data[i / 4]);
++              /* work around hw limit */
++              if (len) {
++                      if (tx) {
++                              ret = mt7621_wait_till_ready(rs);
++                              if (ret) {
++                                      dev_err(&spi->dev, "wait timeout\n");
++                                      goto err;
++                              }
++                              ret = mt7621_write_enable(rs);
++                              if (ret) {
++                                      dev_err(&spi->dev, "write enable 
timeout\n");
++                                      goto err;
++                              }
++                      }
++                      if (cmd->flags & SPI_CMD_ADDR) {
++                              memcpy(data.buf, cmd->opaddr_data,
++                                              cmd->opaddr_len);
++                              /* update address for next loop */
++                              m25p_addr2cmd(cmd->addr, data.buf,
++                                              rs->addr_width);
++                      }
++              }
++      } while (len);
 +
-+      val |= len * 8;
-+      val |= (rx_len * 8) << 12;
-+      mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
++err:
++      cmd->status = SPI_STATE_OPCODE;
++      if (ret)
++              mt7621_dump_reg(master, __func__);
 +
-+      mt7621_spi_set_cs(spi, 1);
++      return ret;
++}
 +
-+      val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
-+      val |= SPI_CTL_START;
-+      mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
++static void spi_set_cs(struct spi_device *spi, bool enable)
++{
++      if (spi->mode & SPI_CS_HIGH)
++              enable = !enable;
 +
-+      mt7621_spi_wait_till_ready(spi);
++      if (spi->master->set_cs)
++              spi->master->set_cs(spi, !enable);
++}
 +
-+      mt7621_spi_set_cs(spi, 0);
++static int mt7621_spi_setup(struct spi_device *spi)
++{
++      struct spi_master *master = spi->master;
++      struct mt7621_spi *rs = spi_master_get_devdata(master);
 +
-+      for (i = 0; i < rx_len; i += 4)
-+              data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA4 + i);
++      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;
++      }
 +
-+      //m->actual_length = len + rx_len;
-+      m->actual_length = rx_len;
++      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;
++      }
 +
-+      len = 0;
-+      list_for_each_entry(t, &m->transfers, transfer_list) {
-+              u8 *buf = t->rx_buf;
++      /* chip polarity */
++      if (spi->mode & SPI_CS_HIGH)
++              mt7621_spi_setbits(rs, MT7621_SPI_POLAR,
++                              (SPIPOL_CSPOL_HIGH << spi->chip_select));
++      else
++              mt7621_spi_clrbits(rs, MT7621_SPI_POLAR,
++                              (SPIPOL_CSPOL_HIGH << spi->chip_select));
 +
-+              if (!buf)
-+                      continue;
++      /* enable more buffer mode */
++      mt7621_spi_setbits(rs, MT7621_SPI_MASTER, SPIMASTER_MB_MODE);
 +
-+              for (i = 0; i < t->len; i++, len++)
-+                      buf[i] = data[len / 4] >> (8 * (len & 3));
-+      }
++      /* deselected the spi device */
++      spi_set_cs(spi, false);
 +
-+msg_done:
-+      m->status = status;
-+      spi_finalize_current_message(master);
++      mt7621_dump_reg(master, __func__);
 +
 +      return 0;
 +}
 +
-+static int mt7621_spi_transfer_one_message(struct spi_master *master,
-+                                         struct spi_message *m)
++static int mt7621_spi_prepare_message(struct spi_master *master,
++              struct spi_message *msg)
 +{
-+      struct spi_device *spi = m->spi;
-+      int cs = spi->chip_select;
-+
-+      if (cs)
-+              return mt7621_spi_transfer_full_duplex(master, m);
-+      return mt7621_spi_transfer_half_duplex(master, m);
-+}
++      struct mt7621_spi *rs = spi_master_get_devdata(master);
++      struct spi_device *spi = msg->spi;
++      u32 reg;
 +
-+static int mt7621_spi_setup(struct spi_device *spi)
-+{
-+      struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
++      if ((rs->mode == spi->mode) && (rs->speed == spi->max_speed_hz))
++              return 0;
 +
-+      if ((spi->max_speed_hz == 0) ||
-+              (spi->max_speed_hz > (rs->sys_freq / 2)))
-+              spi->max_speed_hz = (rs->sys_freq / 2);
++      reg = mt7621_spi_read(rs, MT7621_SPI_MASTER);
++      reg &= ~((SPIMASTER_CLKSEL_MASK << SPIMASTER_CLKSEL_OFFSET) |
++                      SPIMASTER_CPHA | SPIMASTER_CPOL |
++                      SPIMASTER_LSB);
 +
-+      if (spi->max_speed_hz < (rs->sys_freq / 4097)) {
-+              dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n",
-+                      spi->max_speed_hz);
-+              return -EINVAL;
++      /* LSB */
++      if (spi->mode & SPI_LSB_FIRST)
++              reg |= SPIMASTER_LSB;
++
++      /* spi mode */
++      switch (spi->mode & (SPI_CPOL | SPI_CPHA)) {
++      case SPI_MODE_0:
++              break;
++      case SPI_MODE_1:
++              reg |= SPIMASTER_CPHA;
++              break;
++      case SPI_MODE_2:
++              reg |= SPIMASTER_CPOL;
++              break;
++      case SPI_MODE_3:
++              reg |= SPIMASTER_CPOL | SPIMASTER_CPHA;
++              break;
 +      }
++      rs->mode = spi->mode;
++
++      /* clock divide */
++      reg |= mt7621_spi_baudrate_get(spi, spi->max_speed_hz);
++
++      mt7621_spi_write(rs, MT7621_SPI_MASTER, reg);
 +
 +      return 0;
 +}
@@ -414,17 +697,14 @@
 +      const struct of_device_id *match;
 +      struct spi_master *master;
 +      struct mt7621_spi *rs;
-+      unsigned long flags;
 +      void __iomem *base;
 +      struct resource *r;
-+      int status = 0;
 +      struct clk *clk;
-+      struct mt7621_spi_ops *ops;
++      int ret;
 +
 +      match = of_match_device(mt7621_spi_match, &pdev->dev);
 +      if (!match)
 +              return -EINVAL;
-+      ops = (struct mt7621_spi_ops *)match->data;
 +
 +      r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 +      base = devm_ioremap_resource(&pdev->dev, r);
@@ -433,45 +713,58 @@
 +
 +      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_info(&pdev->dev, "master allocation failed\n");
-+              return -ENOMEM;
++              dev_err(&pdev->dev, "master allocation failed\n");
++              ret = -ENOMEM;
++              goto err_clk;
 +      }
 +
-+      master->mode_bits = RT2880_SPI_MODE_BITS;
-+
-+      master->setup = mt7621_spi_setup;
-+      master->transfer_one_message = mt7621_spi_transfer_one_message;
-+      master->bits_per_word_mask = SPI_BPW_MASK(8);
-+      master->dev.of_node = pdev->dev.of_node;
 +      master->num_chipselect = 2;
++      master->dev.of_node = pdev->dev.of_node;
++      master->mode_bits = MT7621_SPI_MODE_BITS;
++      master->bits_per_word_mask = SPI_BPW_MASK(8);
++      master->min_speed_hz = clk_get_rate(clk) / 4097;
++      master->max_speed_hz = clk_get_rate(clk) / 2;
++      master->flags = SPI_MASTER_HALF_DUPLEX;
++      master->setup = mt7621_spi_setup;
++      master->prepare_message = mt7621_spi_prepare_message;
++      master->set_cs = mt7621_spi_set_cs;
++      master->transfer_one = mt7621_spi_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);
-+      rs->ops = ops;
-+      dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
-+      spin_lock_irqsave(&rs->lock, flags);
++      rs->addr_width = 3;
 +
 +      device_reset(&pdev->dev);
 +
-+      mt7621_spi_reset(rs, 0);
++      ret = devm_spi_register_master(&pdev->dev, master);
++      if (ret < 0) {
++              dev_err(&pdev->dev, "devm_spi_register_master error.\n");
++              goto err_master;
++      }
++
++      return ret;
++
++err_master:
++      spi_master_put(master);
++      kfree(master);
++err_clk:
++      clk_disable_unprepare(clk);
 +
-+      return spi_register_master(master);
++      return ret;
 +}
 +
 +static int mt7621_spi_remove(struct platform_device *pdev)
@@ -482,8 +775,7 @@
 +      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);
 +
 +      return 0;
 +}
-- 
2.3.6
_______________________________________________
openwrt-devel mailing list
[email protected]
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel

Reply via email to