From 7bc411c5d9e3bc59806b90954731ce94d96e820e Mon Sep 17 00:00:00 2001 From: juhosg Date: Tue, 27 Mar 2012 19:38:17 +0000 Subject: [PATCH] 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 --- .../ar71xx/files/arch/mips/ath79/dev-m25p80.c | 1 + .../files/arch/mips/ath79/mach-alfa-ap96.c | 1 + .../files/arch/mips/ath79/mach-tl-wr2543n.c | 1 - ...-spi-bitbang-export-spi_bitbang_bufs.patch | 28 +++ ...1-spi-add-type-field-to-spi_transfer.patch | 23 +++ ...462-mtd-m25p80-set-spi-transfer-type.patch | 15 ++ .../463-spi-ath79-add-fast-flash-read.patch | 185 ++++++++++++++++++ ...-spi-bitbang-export-spi_bitbang_bufs.patch | 28 +++ ...1-spi-add-type-field-to-spi_transfer.patch | 23 +++ ...462-mtd-m25p80-set-spi-transfer-type.patch | 15 ++ .../463-spi-ath79-add-fast-flash-read.patch | 185 ++++++++++++++++++ 11 files changed, 504 insertions(+), 1 deletion(-) create mode 100644 target/linux/ar71xx/patches-3.2/460-spi-bitbang-export-spi_bitbang_bufs.patch create mode 100644 target/linux/ar71xx/patches-3.2/461-spi-add-type-field-to-spi_transfer.patch create mode 100644 target/linux/ar71xx/patches-3.2/462-mtd-m25p80-set-spi-transfer-type.patch create mode 100644 target/linux/ar71xx/patches-3.2/463-spi-ath79-add-fast-flash-read.patch create mode 100644 target/linux/ar71xx/patches-3.3/460-spi-bitbang-export-spi_bitbang_bufs.patch create mode 100644 target/linux/ar71xx/patches-3.3/461-spi-add-type-field-to-spi_transfer.patch create mode 100644 target/linux/ar71xx/patches-3.3/462-mtd-m25p80-set-spi-transfer-type.patch create mode 100644 target/linux/ar71xx/patches-3.3/463-spi-ath79-add-fast-flash-read.patch 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 */