summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjuhosg <juhosg@3c298f89-4303-0410-b956-a3cf2f4a3e73>2012-03-27 19:38:17 +0000
committerjuhosg <juhosg@3c298f89-4303-0410-b956-a3cf2f4a3e73>2012-03-27 19:38:17 +0000
commit7bc411c5d9e3bc59806b90954731ce94d96e820e (patch)
treeb1e88176d38705944d4e9b3e1205dfc14463e811
parentac19ad9ae47b97d9e0cd9d7775cbb3589c5602bf (diff)
ar71xx: boost SPI flash read performance
mtd_speedtest results: page read speed old new delta DB120 929 KiB/s 2597 KiB/s +179.55% TL-WR1043ND v1 754 KiB/s 2166 KiB/s +187.27% TL-WR703N v1 745 KiB/s 2176 KiB/s +192.08% TL-MR3220 v1 752 KiB/s 2154 KiB/s +186.44% TL-WR2543ND v1 564 KiB/s 2130 KiB/s +277.66% TL-WR741ND v2 525 KiB/s 1767 KiB/s +236.57% ALFA-AP96 702 KiB/s 1977 KiB/s +181.62% WNDR3700 697 KiB/s 1965 KiB/s +181.92% git-svn-id: svn://svn.openwrt.org/openwrt/trunk@31118 3c298f89-4303-0410-b956-a3cf2f4a3e73
-rw-r--r--target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c1
-rw-r--r--target/linux/ar71xx/files/arch/mips/ath79/mach-alfa-ap96.c1
-rw-r--r--target/linux/ar71xx/files/arch/mips/ath79/mach-tl-wr2543n.c1
-rw-r--r--target/linux/ar71xx/patches-3.2/460-spi-bitbang-export-spi_bitbang_bufs.patch28
-rw-r--r--target/linux/ar71xx/patches-3.2/461-spi-add-type-field-to-spi_transfer.patch23
-rw-r--r--target/linux/ar71xx/patches-3.2/462-mtd-m25p80-set-spi-transfer-type.patch15
-rw-r--r--target/linux/ar71xx/patches-3.2/463-spi-ath79-add-fast-flash-read.patch185
-rw-r--r--target/linux/ar71xx/patches-3.3/460-spi-bitbang-export-spi_bitbang_bufs.patch28
-rw-r--r--target/linux/ar71xx/patches-3.3/461-spi-add-type-field-to-spi_transfer.patch23
-rw-r--r--target/linux/ar71xx/patches-3.3/462-mtd-m25p80-set-spi-transfer-type.patch15
-rw-r--r--target/linux/ar71xx/patches-3.3/463-spi-ath79-add-fast-flash-read.patch185
11 files changed, 504 insertions, 1 deletions
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
index 9ac19d83a..66115b1c4 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
@@ -51,6 +51,7 @@ void __init ath79_register_m25p80(struct flash_platform_data *pdata)
{
ath79_spi_data.bus_num = 0;
ath79_spi_data.num_chipselect = 1;
+ ath79_spi0_cdata.is_flash = true;
ath79_spi_info[0].platform_data = pdata;
ath79_register_spi(&ath79_spi_data, ath79_spi_info, 1);
}
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-alfa-ap96.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-alfa-ap96.c
index f7315a749..15abb081f 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/mach-alfa-ap96.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-alfa-ap96.c
@@ -62,6 +62,7 @@ static struct mmc_spi_platform_data alfa_ap96_mmc_data = {
static struct ath79_spi_controller_data ap96_spi0_cdata = {
.cs_type = ATH79_SPI_CS_TYPE_INTERNAL,
.cs_line = 0,
+ .is_flash = true,
};
static struct ath79_spi_controller_data ap96_spi1_cdata = {
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-wr2543n.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-wr2543n.c
index d4a66cd16..f282a03d8 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-wr2543n.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-wr2543n.c
@@ -40,7 +40,6 @@ static const char *tl_wr2543n_part_probes[] = {
static struct flash_platform_data tl_wr2543n_flash_data = {
.part_probes = tl_wr2543n_part_probes,
- .max_read_len = 64,
};
static struct gpio_led tl_wr2543n_leds_gpio[] __initdata = {
diff --git a/target/linux/ar71xx/patches-3.2/460-spi-bitbang-export-spi_bitbang_bufs.patch b/target/linux/ar71xx/patches-3.2/460-spi-bitbang-export-spi_bitbang_bufs.patch
new file mode 100644
index 000000000..919b85cb7
--- /dev/null
+++ b/target/linux/ar71xx/patches-3.2/460-spi-bitbang-export-spi_bitbang_bufs.patch
@@ -0,0 +1,28 @@
+--- a/drivers/spi/spi-bitbang.c
++++ b/drivers/spi/spi-bitbang.c
+@@ -234,13 +234,14 @@ void spi_bitbang_cleanup(struct spi_devi
+ }
+ EXPORT_SYMBOL_GPL(spi_bitbang_cleanup);
+
+-static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
++int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
+ {
+ struct spi_bitbang_cs *cs = spi->controller_state;
+ unsigned nsecs = cs->nsecs;
+
+ return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t);
+ }
++EXPORT_SYMBOL_GPL(spi_bitbang_bufs);
+
+ /*----------------------------------------------------------------------*/
+
+--- a/include/linux/spi/spi_bitbang.h
++++ b/include/linux/spi/spi_bitbang.h
+@@ -44,6 +44,7 @@ extern void spi_bitbang_cleanup(struct s
+ extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m);
+ extern int spi_bitbang_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t);
++extern int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t);
+
+ /* start or stop queue processing */
+ extern int spi_bitbang_start(struct spi_bitbang *spi);
diff --git a/target/linux/ar71xx/patches-3.2/461-spi-add-type-field-to-spi_transfer.patch b/target/linux/ar71xx/patches-3.2/461-spi-add-type-field-to-spi_transfer.patch
new file mode 100644
index 000000000..e87d7fc0f
--- /dev/null
+++ b/target/linux/ar71xx/patches-3.2/461-spi-add-type-field-to-spi_transfer.patch
@@ -0,0 +1,23 @@
+--- a/include/linux/spi/spi.h
++++ b/include/linux/spi/spi.h
+@@ -344,6 +344,12 @@ extern struct spi_master *spi_busnum_to_
+
+ /*---------------------------------------------------------------------------*/
+
++enum spi_transfer_type {
++ SPI_TRANSFER_GENERIC = 0,
++ SPI_TRANSFER_FLASH_READ_CMD,
++ SPI_TRANSFER_FLASH_READ_DATA,
++};
++
+ /*
+ * I/O INTERFACE between SPI controller and protocol drivers
+ *
+@@ -446,6 +452,7 @@ struct spi_transfer {
+ u8 bits_per_word;
+ u16 delay_usecs;
+ u32 speed_hz;
++ enum spi_transfer_type type;
+
+ struct list_head transfer_list;
+ };
diff --git a/target/linux/ar71xx/patches-3.2/462-mtd-m25p80-set-spi-transfer-type.patch b/target/linux/ar71xx/patches-3.2/462-mtd-m25p80-set-spi-transfer-type.patch
new file mode 100644
index 000000000..48e69c0d6
--- /dev/null
+++ b/target/linux/ar71xx/patches-3.2/462-mtd-m25p80-set-spi-transfer-type.patch
@@ -0,0 +1,15 @@
+--- a/drivers/mtd/devices/m25p80.c
++++ b/drivers/mtd/devices/m25p80.c
+@@ -372,10 +372,12 @@ static int m25p80_read(struct mtd_info *
+ * OPCODE_FAST_READ (if available) is faster.
+ * Should add 1 byte DUMMY_BYTE.
+ */
++ t[0].type = SPI_TRANSFER_FLASH_READ_CMD;
+ t[0].tx_buf = flash->command;
+ t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
+ spi_message_add_tail(&t[0], &m);
+
++ t[1].type = SPI_TRANSFER_FLASH_READ_DATA;
+ spi_message_add_tail(&t[1], &m);
+
+ /* Byte count starts at zero. */
diff --git a/target/linux/ar71xx/patches-3.2/463-spi-ath79-add-fast-flash-read.patch b/target/linux/ar71xx/patches-3.2/463-spi-ath79-add-fast-flash-read.patch
new file mode 100644
index 000000000..75db84ceb
--- /dev/null
+++ b/target/linux/ar71xx/patches-3.2/463-spi-ath79-add-fast-flash-read.patch
@@ -0,0 +1,185 @@
+--- a/drivers/spi/spi-ath79.c
++++ b/drivers/spi/spi-ath79.c
+@@ -37,6 +37,11 @@
+
+ #define ATH79_SPI_CS_LINE_MAX 2
+
++enum ath79_spi_state {
++ ATH79_SPI_STATE_WAIT_CMD = 0,
++ ATH79_SPI_STATE_WAIT_READ,
++};
++
+ struct ath79_spi {
+ struct spi_bitbang bitbang;
+ u32 ioc_base;
+@@ -44,6 +49,11 @@ struct ath79_spi {
+ void __iomem *base;
+ struct clk *clk;
+ unsigned rrw_delay;
++
++ enum ath79_spi_state state;
++ u32 clk_div;
++ unsigned long read_addr;
++ unsigned long ahb_rate;
+ };
+
+ static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg)
+@@ -108,9 +118,6 @@ static void ath79_spi_enable(struct ath7
+ /* save CTRL register */
+ sp->reg_ctrl = ath79_spi_rr(sp, AR71XX_SPI_REG_CTRL);
+ sp->ioc_base = ath79_spi_rr(sp, AR71XX_SPI_REG_IOC);
+-
+- /* TODO: setup speed? */
+- ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43);
+ }
+
+ static void ath79_spi_disable(struct ath79_spi *sp)
+@@ -222,6 +229,110 @@ static u32 ath79_spi_txrx_mode0(struct s
+ return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS);
+ }
+
++static int ath79_spi_do_read_flash_data(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
++
++ /* disable GPIO mode */
++ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0);
++
++ memcpy_fromio(t->rx_buf, sp->base + sp->read_addr, t->len);
++
++ /* enable GPIO mode */
++ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
++
++ /* restore IOC register */
++ ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
++
++ return t->len;
++}
++
++static int ath79_spi_do_read_flash_cmd(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
++ int len;
++ const u8 *p;
++
++ sp->read_addr = 0;
++
++ len = t->len - 1;
++ p = t->tx_buf;
++
++ while (len--) {
++ p++;
++ sp->read_addr <<= 8;
++ sp->read_addr |= *p;
++ }
++
++ return t->len;
++}
++
++static bool ath79_spi_is_read_cmd(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ return t->type == SPI_TRANSFER_FLASH_READ_CMD;
++}
++
++static bool ath79_spi_is_data_read(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ return t->type == SPI_TRANSFER_FLASH_READ_DATA;
++}
++
++static int ath79_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
++{
++ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
++ int ret;
++
++ switch (sp->state) {
++ case ATH79_SPI_STATE_WAIT_CMD:
++ if (ath79_spi_is_read_cmd(spi, t)) {
++ ret = ath79_spi_do_read_flash_cmd(spi, t);
++ sp->state = ATH79_SPI_STATE_WAIT_READ;
++ } else {
++ ret = spi_bitbang_bufs(spi, t);
++ }
++ break;
++
++ case ATH79_SPI_STATE_WAIT_READ:
++ if (ath79_spi_is_data_read(spi, t)) {
++ ret = ath79_spi_do_read_flash_data(spi, t);
++ } else {
++ dev_warn(&spi->dev, "flash data read expected\n");
++ ret = -EIO;
++ }
++ sp->state = ATH79_SPI_STATE_WAIT_CMD;
++ break;
++
++ default:
++ BUG();
++ }
++
++ return ret;
++}
++
++static int ath79_spi_setup_transfer(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
++ struct ath79_spi_controller_data *cdata;
++ int ret;
++
++ ret = spi_bitbang_setup_transfer(spi, t);
++ if (ret)
++ return ret;
++
++ cdata = spi->controller_data;
++ if (cdata->is_flash)
++ sp->bitbang.txrx_bufs = ath79_spi_txrx_bufs;
++ else
++ sp->bitbang.txrx_bufs = spi_bitbang_bufs;
++
++ return ret;
++}
++
+ static __devinit int ath79_spi_probe(struct platform_device *pdev)
+ {
+ struct spi_master *master;
+@@ -244,6 +355,8 @@ static __devinit int ath79_spi_probe(str
+ sp = spi_master_get_devdata(master);
+ platform_set_drvdata(pdev, sp);
+
++ sp->state = ATH79_SPI_STATE_WAIT_CMD;
++
+ master->setup = ath79_spi_setup;
+ master->cleanup = ath79_spi_cleanup;
+ master->bus_num = pdata->bus_num;
+@@ -252,7 +365,7 @@ static __devinit int ath79_spi_probe(str
+ sp->bitbang.master = spi_master_get(master);
+ sp->bitbang.chipselect = ath79_spi_chipselect;
+ sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0;
+- sp->bitbang.setup_transfer = spi_bitbang_setup_transfer;
++ sp->bitbang.setup_transfer = ath79_spi_setup_transfer;
+ sp->bitbang.flags = SPI_CS_HIGH;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+@@ -277,7 +390,8 @@ static __devinit int ath79_spi_probe(str
+ if (ret)
+ goto err_clk_put;
+
+- rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ);
++ sp->ahb_rate = clk_get_rate(sp->clk);
++ rate = DIV_ROUND_UP(sp->ahb_rate, MHZ);
+ if (!rate) {
+ ret = -EINVAL;
+ goto err_clk_disable;
+--- a/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
++++ b/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
+@@ -24,6 +24,7 @@ enum ath79_spi_cs_type {
+ struct ath79_spi_controller_data {
+ enum ath79_spi_cs_type cs_type;
+ unsigned cs_line;
++ bool is_flash;
+ };
+
+ #endif /* _ATH79_SPI_PLATFORM_H */
diff --git a/target/linux/ar71xx/patches-3.3/460-spi-bitbang-export-spi_bitbang_bufs.patch b/target/linux/ar71xx/patches-3.3/460-spi-bitbang-export-spi_bitbang_bufs.patch
new file mode 100644
index 000000000..919b85cb7
--- /dev/null
+++ b/target/linux/ar71xx/patches-3.3/460-spi-bitbang-export-spi_bitbang_bufs.patch
@@ -0,0 +1,28 @@
+--- a/drivers/spi/spi-bitbang.c
++++ b/drivers/spi/spi-bitbang.c
+@@ -234,13 +234,14 @@ void spi_bitbang_cleanup(struct spi_devi
+ }
+ EXPORT_SYMBOL_GPL(spi_bitbang_cleanup);
+
+-static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
++int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
+ {
+ struct spi_bitbang_cs *cs = spi->controller_state;
+ unsigned nsecs = cs->nsecs;
+
+ return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t);
+ }
++EXPORT_SYMBOL_GPL(spi_bitbang_bufs);
+
+ /*----------------------------------------------------------------------*/
+
+--- a/include/linux/spi/spi_bitbang.h
++++ b/include/linux/spi/spi_bitbang.h
+@@ -44,6 +44,7 @@ extern void spi_bitbang_cleanup(struct s
+ extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m);
+ extern int spi_bitbang_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t);
++extern int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t);
+
+ /* start or stop queue processing */
+ extern int spi_bitbang_start(struct spi_bitbang *spi);
diff --git a/target/linux/ar71xx/patches-3.3/461-spi-add-type-field-to-spi_transfer.patch b/target/linux/ar71xx/patches-3.3/461-spi-add-type-field-to-spi_transfer.patch
new file mode 100644
index 000000000..3c7e98169
--- /dev/null
+++ b/target/linux/ar71xx/patches-3.3/461-spi-add-type-field-to-spi_transfer.patch
@@ -0,0 +1,23 @@
+--- a/include/linux/spi/spi.h
++++ b/include/linux/spi/spi.h
+@@ -355,6 +355,12 @@ extern struct spi_master *spi_busnum_to_
+
+ /*---------------------------------------------------------------------------*/
+
++enum spi_transfer_type {
++ SPI_TRANSFER_GENERIC = 0,
++ SPI_TRANSFER_FLASH_READ_CMD,
++ SPI_TRANSFER_FLASH_READ_DATA,
++};
++
+ /*
+ * I/O INTERFACE between SPI controller and protocol drivers
+ *
+@@ -457,6 +463,7 @@ struct spi_transfer {
+ u8 bits_per_word;
+ u16 delay_usecs;
+ u32 speed_hz;
++ enum spi_transfer_type type;
+
+ struct list_head transfer_list;
+ };
diff --git a/target/linux/ar71xx/patches-3.3/462-mtd-m25p80-set-spi-transfer-type.patch b/target/linux/ar71xx/patches-3.3/462-mtd-m25p80-set-spi-transfer-type.patch
new file mode 100644
index 000000000..48e69c0d6
--- /dev/null
+++ b/target/linux/ar71xx/patches-3.3/462-mtd-m25p80-set-spi-transfer-type.patch
@@ -0,0 +1,15 @@
+--- a/drivers/mtd/devices/m25p80.c
++++ b/drivers/mtd/devices/m25p80.c
+@@ -372,10 +372,12 @@ static int m25p80_read(struct mtd_info *
+ * OPCODE_FAST_READ (if available) is faster.
+ * Should add 1 byte DUMMY_BYTE.
+ */
++ t[0].type = SPI_TRANSFER_FLASH_READ_CMD;
+ t[0].tx_buf = flash->command;
+ t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
+ spi_message_add_tail(&t[0], &m);
+
++ t[1].type = SPI_TRANSFER_FLASH_READ_DATA;
+ spi_message_add_tail(&t[1], &m);
+
+ /* Byte count starts at zero. */
diff --git a/target/linux/ar71xx/patches-3.3/463-spi-ath79-add-fast-flash-read.patch b/target/linux/ar71xx/patches-3.3/463-spi-ath79-add-fast-flash-read.patch
new file mode 100644
index 000000000..75db84ceb
--- /dev/null
+++ b/target/linux/ar71xx/patches-3.3/463-spi-ath79-add-fast-flash-read.patch
@@ -0,0 +1,185 @@
+--- a/drivers/spi/spi-ath79.c
++++ b/drivers/spi/spi-ath79.c
+@@ -37,6 +37,11 @@
+
+ #define ATH79_SPI_CS_LINE_MAX 2
+
++enum ath79_spi_state {
++ ATH79_SPI_STATE_WAIT_CMD = 0,
++ ATH79_SPI_STATE_WAIT_READ,
++};
++
+ struct ath79_spi {
+ struct spi_bitbang bitbang;
+ u32 ioc_base;
+@@ -44,6 +49,11 @@ struct ath79_spi {
+ void __iomem *base;
+ struct clk *clk;
+ unsigned rrw_delay;
++
++ enum ath79_spi_state state;
++ u32 clk_div;
++ unsigned long read_addr;
++ unsigned long ahb_rate;
+ };
+
+ static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg)
+@@ -108,9 +118,6 @@ static void ath79_spi_enable(struct ath7
+ /* save CTRL register */
+ sp->reg_ctrl = ath79_spi_rr(sp, AR71XX_SPI_REG_CTRL);
+ sp->ioc_base = ath79_spi_rr(sp, AR71XX_SPI_REG_IOC);
+-
+- /* TODO: setup speed? */
+- ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43);
+ }
+
+ static void ath79_spi_disable(struct ath79_spi *sp)
+@@ -222,6 +229,110 @@ static u32 ath79_spi_txrx_mode0(struct s
+ return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS);
+ }
+
++static int ath79_spi_do_read_flash_data(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
++
++ /* disable GPIO mode */
++ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0);
++
++ memcpy_fromio(t->rx_buf, sp->base + sp->read_addr, t->len);
++
++ /* enable GPIO mode */
++ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
++
++ /* restore IOC register */
++ ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
++
++ return t->len;
++}
++
++static int ath79_spi_do_read_flash_cmd(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
++ int len;
++ const u8 *p;
++
++ sp->read_addr = 0;
++
++ len = t->len - 1;
++ p = t->tx_buf;
++
++ while (len--) {
++ p++;
++ sp->read_addr <<= 8;
++ sp->read_addr |= *p;
++ }
++
++ return t->len;
++}
++
++static bool ath79_spi_is_read_cmd(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ return t->type == SPI_TRANSFER_FLASH_READ_CMD;
++}
++
++static bool ath79_spi_is_data_read(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ return t->type == SPI_TRANSFER_FLASH_READ_DATA;
++}
++
++static int ath79_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
++{
++ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
++ int ret;
++
++ switch (sp->state) {
++ case ATH79_SPI_STATE_WAIT_CMD:
++ if (ath79_spi_is_read_cmd(spi, t)) {
++ ret = ath79_spi_do_read_flash_cmd(spi, t);
++ sp->state = ATH79_SPI_STATE_WAIT_READ;
++ } else {
++ ret = spi_bitbang_bufs(spi, t);
++ }
++ break;
++
++ case ATH79_SPI_STATE_WAIT_READ:
++ if (ath79_spi_is_data_read(spi, t)) {
++ ret = ath79_spi_do_read_flash_data(spi, t);
++ } else {
++ dev_warn(&spi->dev, "flash data read expected\n");
++ ret = -EIO;
++ }
++ sp->state = ATH79_SPI_STATE_WAIT_CMD;
++ break;
++
++ default:
++ BUG();
++ }
++
++ return ret;
++}
++
++static int ath79_spi_setup_transfer(struct spi_device *spi,
++ struct spi_transfer *t)
++{
++ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
++ struct ath79_spi_controller_data *cdata;
++ int ret;
++
++ ret = spi_bitbang_setup_transfer(spi, t);
++ if (ret)
++ return ret;
++
++ cdata = spi->controller_data;
++ if (cdata->is_flash)
++ sp->bitbang.txrx_bufs = ath79_spi_txrx_bufs;
++ else
++ sp->bitbang.txrx_bufs = spi_bitbang_bufs;
++
++ return ret;
++}
++
+ static __devinit int ath79_spi_probe(struct platform_device *pdev)
+ {
+ struct spi_master *master;
+@@ -244,6 +355,8 @@ static __devinit int ath79_spi_probe(str
+ sp = spi_master_get_devdata(master);
+ platform_set_drvdata(pdev, sp);
+
++ sp->state = ATH79_SPI_STATE_WAIT_CMD;
++
+ master->setup = ath79_spi_setup;
+ master->cleanup = ath79_spi_cleanup;
+ master->bus_num = pdata->bus_num;
+@@ -252,7 +365,7 @@ static __devinit int ath79_spi_probe(str
+ sp->bitbang.master = spi_master_get(master);
+ sp->bitbang.chipselect = ath79_spi_chipselect;
+ sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0;
+- sp->bitbang.setup_transfer = spi_bitbang_setup_transfer;
++ sp->bitbang.setup_transfer = ath79_spi_setup_transfer;
+ sp->bitbang.flags = SPI_CS_HIGH;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+@@ -277,7 +390,8 @@ static __devinit int ath79_spi_probe(str
+ if (ret)
+ goto err_clk_put;
+
+- rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ);
++ sp->ahb_rate = clk_get_rate(sp->clk);
++ rate = DIV_ROUND_UP(sp->ahb_rate, MHZ);
+ if (!rate) {
+ ret = -EINVAL;
+ goto err_clk_disable;
+--- a/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
++++ b/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
+@@ -24,6 +24,7 @@ enum ath79_spi_cs_type {
+ struct ath79_spi_controller_data {
+ enum ath79_spi_cs_type cs_type;
+ unsigned cs_line;
++ bool is_flash;
+ };
+
+ #endif /* _ATH79_SPI_PLATFORM_H */