ar71xx: boost SPI flash read performance
[openwrt.git] / target / linux / ar71xx / patches-3.2 / 463-spi-ath79-add-fast-flash-read.patch
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 (file)
index 0000000..75db84c
--- /dev/null
@@ -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 */
This page took 0.025261 seconds and 4 git commands to generate.