[OpenWrt-Devel] [PATCH 6/7] ramips: improve mt7621 spi flash read speed
Michael Lee
igvtee at gmail.com
Tue Sep 22 09:26:02 EDT 2015
From: michael lee <igvtee at gmail.com>
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 <igvtee at gmail.com>
---
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 at 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 at 1E000000 {
compatible = "palmbus";
reg = <0x1E000000 0x100000>;
@@ -84,7 +88,7 @@
no-loopback-test;
};
- spi at b00 {
+ spi0: spi at 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 at 10000000 {
compatible = "palmbus";
reg = <0x10000000 0x200000>;
@@ -102,7 +106,7 @@
};
};
- spi at b00 {
+ spi0: spi at 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 <piratfm at gmail.com>
+ * Copyright (C) 2011-2013 Gabor Juhos <juhosg at openwrt.org>
+ * Copyright (C) 2014-2015 Felix Fietkau <nbd at openwrt.org>
++ * Copyright (C) 2015 Michael Lee <igvtee at gmail.com>
+ *
+ * Some parts are based on spi-orion.c:
+ * Author: Shadi Ammouri <shadi at marvell.com>
@@ -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
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list