openwrtv4/target/linux/ar71xx/patches-3.2/463-spi-ath79-add-fast-flash-read.patch
Gabor Juhos b77217d963 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%

SVN-Revision: 31118
2012-03-27 19:38:17 +00:00

185 lines
4.6 KiB
Diff

--- 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 */