Hello all.

I working with Allwinner A13 microprocessor. I use spi-nor flash on device
and I want storing some information belong to about of device. So I want
use spi-nor flash as usb mass storage. And for this I ran tiny linux
(kernel version is 5.12-rc3) on spi-nor flash and I can use spi-nor flash
as mass storage(~5Mb).

But I got some errors like below:
[ 9557.574285] spi_master spi0: spi0.0: timeout transferring 64
bytes@1000000Hz for 110(100)ms
[ 9557.592685] spi-nor spi0.0: SPI transfer failed: -110
[ 9557.601985] spi_master spi0: failed to transfer one message from queue
[ 9557.613673] jffs2: Write of 68 bytes at 0x0045d264 failed. returned
-110, retlen 0
[ 9557.631970] jffs2: Not marking the space at 0x0045d264 as dirty because
the flash driver returned retlen zero


So I some researched and I found repo of hramrach for spi working with
dma(*https://github.com/hramrach/linux-sunxi
<https://github.com/hramrach/linux-sunxi> *branch is: *sunxi-spi-pio-dma*
).
I merged dma works of hramrach to my kernel 5.12-rc3 and unfortunately I
got same error as below.


[  198.747427] Buffer I/O error on dev loop0, logical block 131, lost async
page write
[  198.864358] spi_master spi0: spi0.0: timeout transferring 64
bytes@1000000Hz for 101250(100)ms
[  198.877517] spi-nor spi0.0: SPI transfer failed: -110
[  198.886939] spi_master spi0: failed to transfer one message from queue
[  198.897785] jffs2: Write of 98 bytes at 0x0043a7c0 failed. returned
-110, retlen 0
[  198.909638] jffs2: Not marking the space at 0x0043a7c0 as dirty because
the flash driver returned retlen zero
[  199.034358] spi_master spi0: spi0.0: timeout transferring 64
bytes@1000000Hz for 101080(100)ms
[  199.047536] spi-nor spi0.0: SPI transfer failed: -110
[  199.056950] spi_master spi0: failed to transfer one message from queue
[  199.067793] jffs2: Write of 98 bytes at 0x0043a7c0 failed. returned
-110, retlen 0
[  199.079704] jffs2: Not marking the space at 0x0043a7c0 as dirty because
the flash driver returned retlen zero
[  199.098175] loop: Write error at byte offset 68096, length 512.


The difference seems between before and after patch is about time. But
unfortunately error still occurs.
Generally when ran `mkdosfs` command and when I tried transfer file
big size(~2MB) I take error.


My diff patch is:

diff --color -Nuar linux/dmaengine.h linux-new/dmaengine.h
--- linux/dmaengine.h 2021-06-17 13:35:46.033174969 +0300
+++ linux-new/dmaengine.h 2021-06-17 13:35:33.088291303 +0300
@@ -1484,7 +1484,8 @@
 struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
        dma_filter_fn fn, void *fn_param,
        struct device_node *np);
-
+struct dma_chan *dma_request_slave_channel_reason(struct device *dev,
+  const char *name);
 struct dma_chan *dma_request_chan(struct device *dev, const char *name);
 struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask);

@@ -1513,6 +1514,11 @@
 {
  return NULL;
 }
+struct dma_chan *dma_request_slave_channel_reason(struct device *dev,
+  const char *name);
+{
+ return ERR_PTR(-ENODEV);
+}
 static inline struct dma_chan *dma_request_chan(struct device *dev,
  const char *name)
 {
diff --color -Nuar spi/spi-sun4i.c spi-new/spi-sun4i.c
--- spi/spi-sun4i.c 2021-06-17 13:17:52.253811064 +0300
+++ spi-new/spi-sun4i.c 2021-06-17 13:16:28.868703981 +0300
@@ -15,6 +15,8 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>

 #include <linux/spi/spi.h>

@@ -30,6 +32,7 @@
 #define SUN4I_CTL_CPHA BIT(2)
 #define SUN4I_CTL_CPOL BIT(3)
 #define SUN4I_CTL_CS_ACTIVE_LOW BIT(4)
+#define SUN4I_CTL_DMAMC_DEDICATED   BIT(5)
 #define SUN4I_CTL_LMTF BIT(6)
 #define SUN4I_CTL_TF_RST BIT(8)
 #define SUN4I_CTL_RF_RST BIT(9)
@@ -49,6 +52,8 @@
 #define SUN4I_INT_STA_REG 0x10

 #define SUN4I_DMA_CTL_REG 0x14
+#define SUN4I_DMA_CTL_RF_READY                 BIT(0)
+#define SUN4I_DMA_CTL_TF_NOT_FULL              BIT(10)

 #define SUN4I_WAIT_REG 0x18

@@ -85,6 +90,7 @@
  const u8 *tx_buf;
  u8 *rx_buf;
  int len;
+ bool has_dma;
 };

 static inline u32 sun4i_spi_read(struct sun4i_spi *sspi, u32 reg)
@@ -159,6 +165,14 @@
  }
 }

+static bool sun4i_spi_can_dma(struct spi_master *master,
+      struct spi_device *spi,
+      struct spi_transfer *tfr)
+{
+ struct sun4i_spi *sspi = spi_master_get_devdata(spi->master);
+ return sspi->has_dma && tfr->len >= SUN4I_FIFO_DEPTH;
+}
+
 static void sun4i_spi_set_cs(struct spi_device *spi, bool enable)
 {
  struct sun4i_spi *sspi = spi_master_get_devdata(spi->master);
@@ -206,18 +220,22 @@
   struct spi_transfer *tfr)
 {
  struct sun4i_spi *sspi = spi_master_get_devdata(master);
- unsigned int mclk_rate, div, timeout;
- unsigned int start, end, tx_time;
+ struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL;
+ // unsigned int mclk_rate, div, timeout;
+ unsigned int speed, mclk_rate, div, timeout;
+ // unsigned int start, end, tx_time;
  unsigned int tx_len = 0;
+ u32 reg, trigger = 0;
  int ret = 0;
- u32 reg;
+ unsigned int start, end, tx_time;
+ //u32 reg;

  /* We don't support transfer larger than the FIFO */
  if (tfr->len > SUN4I_MAX_XFER_SIZE)
- return -EMSGSIZE;
+ return -EINVAL; //-EMSGSIZE;

- if (tfr->tx_buf && tfr->len >= SUN4I_MAX_XFER_SIZE)
- return -EMSGSIZE;
+ /*if (tfr->tx_buf && tfr->len >= SUN4I_MAX_XFER_SIZE)
+ return -EMSGSIZE; */

  reinit_completion(&sspi->done);
  sspi->tx_buf = tfr->tx_buf;
@@ -227,7 +245,6 @@
  /* Clear pending interrupts */
  sun4i_spi_write(sspi, SUN4I_INT_STA_REG, ~0);

-
  reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);

  /* Reset FIFOs */
@@ -265,10 +282,18 @@

  sun4i_spi_write(sspi, SUN4I_CTL_REG, reg);

+ speed = spi->max_speed_hz;
+ if(!speed) {
+ speed = 100000;
+ dev_warn(&spi->dev, "SPI speed not set. Using %uHz.", speed);
+ }
+
  /* Ensure that we have a parent clock fast enough */
  mclk_rate = clk_get_rate(sspi->mclk);
- if (mclk_rate < (2 * tfr->speed_hz)) {
- clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
+ //if (mclk_rate < (2 * tfr->speed_hz)) {
+ // clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
+ if (mclk_rate < (2 * speed)) {
+ clk_set_rate(sspi->mclk, 2 * speed);
  mclk_rate = clk_get_rate(sspi->mclk);
  }

@@ -286,14 +311,16 @@
  * First try CDR2, and if we can't reach the expected
  * frequency, fall back to CDR1.
  */
- div = mclk_rate / (2 * tfr->speed_hz);
+ // div = mclk_rate / (2 * tfr->speed_hz);
+ div = mclk_rate / (2 * speed);
  if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
  if (div > 0)
  div--;

  reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS;
  } else {
- div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
+ // div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
+ div = ilog2(mclk_rate) - ilog2(speed);
  reg = SUN4I_CLK_CTL_CDR1(div);
  }

@@ -312,33 +339,104 @@
  * Filling the FIFO fully causes timeout for some reason
  * at least on spi2 on A10s
  */
- sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1);
+ // sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1);

  /* Enable the interrupts */
- sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC |
- SUN4I_INT_CTL_RF_F34);
- /* Only enable Tx FIFO interrupt if we really need it */
- if (tx_len > SUN4I_FIFO_DEPTH)
- sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TF_E34);
+ // sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC |
SUN4I_INT_CTL_RF_F34);
+ // /* Only enable Tx FIFO interrupt if we really need it */
+ // if (tx_len > SUN4I_FIFO_DEPTH)
+ // sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TF_E34);
+
+ sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC);
+
+ if (sun4i_spi_can_dma(master, spi, tfr)) {
+ dev_dbg(&sspi->master->dev, "Using DMA mode for transfer\n");
+
+ if (sspi->tx_buf) {
+ desc_tx = dmaengine_prep_slave_sg(master->dma_tx,
+ tfr->tx_sg.sgl, tfr->tx_sg.nents,
+ DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_tx) {
+ dev_err(&sspi->master->dev,
+ "Couldn't prepare dma slave\n");
+ return -EIO;
+ }
+
+ trigger |= SUN4I_DMA_CTL_TF_NOT_FULL;
+
+ dmaengine_submit(desc_tx);
+ dma_async_issue_pending(master->dma_tx);
+
+ }
+
+ if (sspi->rx_buf) {
+ desc_rx = dmaengine_prep_slave_sg(master->dma_rx,
+ tfr->rx_sg.sgl, tfr->rx_sg.nents,
+ DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_rx) {
+ dev_err(&sspi->master->dev,
+ "Couldn't prepare dma slave\n");
+ return -EIO;
+ }
+
+ trigger |= SUN4I_DMA_CTL_RF_READY;
+
+ dmaengine_submit(desc_rx);
+ dma_async_issue_pending(master->dma_rx);
+ }
+
+ /* Enable Dedicated DMA requests */
+ reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
+ reg |= SUN4I_CTL_DMAMC_DEDICATED;
+ sun4i_spi_write(sspi, SUN4I_CTL_REG, reg);
+ sun4i_spi_write(sspi, SUN4I_DMA_CTL_REG, trigger);
+ } else {
+ dev_dbg(&sspi->master->dev, "Using PIO mode for transfer\n");
+
+ /* Enable the FIFO interrupts */
+ sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_RF_F34);
+ /* Only enable Tx FIFO interrupt if we really need it */
+ if (tx_len > SUN4I_FIFO_DEPTH)
+ sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TF_E34);
+
+ /* Disable DMA requests */
+ reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
+ sun4i_spi_write(sspi, SUN4I_CTL_REG,
+ reg & ~SUN4I_CTL_DMAMC_DEDICATED);
+ sun4i_spi_write(sspi, SUN4I_DMA_CTL_REG, 0);
+
+ /* Fill the TX FIFO */
+ sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1);
+ }

  /* Start the transfer */
  reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
  sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH);
-
+
+ /*
  tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U);
  start = jiffies;
  timeout = wait_for_completion_timeout(&sspi->done,
       msecs_to_jiffies(tx_time));
- end = jiffies;
+ end = jiffies; */
+
+ tx_time = max_t(int, tfr->len * 8 * 2 / (speed / 1000), 100);
+ start = jiffies;
+ timeout = wait_for_completion_timeout(&sspi->done,
msecs_to_jiffies(tx_time));  // Added manually
  if (!timeout) {
  dev_warn(&master->dev,
  "%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
- dev_name(&spi->dev), tfr->len, tfr->speed_hz,
+ dev_name(&spi->dev), tfr->len, speed,
  jiffies_to_msecs(end - start), tx_time);
  ret = -ETIMEDOUT;
  goto out;
  }

+ if (sun4i_spi_can_dma(master, spi, tfr) && desc_rx)
+ /* The receive transfer should be the last one to finish */
+ dma_wait_for_async_tx(desc_rx);

 out:
  sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0);
@@ -424,6 +522,70 @@
  return 0;
 }

+static void sun4i_spi_request_dma(struct platform_device *pdev,
+  struct resource *res,
+  struct spi_master *master)
+{
+ struct sun4i_spi *sspi = spi_master_get_devdata(master);
+ struct dma_slave_config dma_sconfig;
+ int ret;
+
+ master->dma_tx = dma_request_slave_channel_reason(&pdev->dev, "tx");
+ if (IS_ERR(master->dma_tx)) {
+ dev_warn(&pdev->dev, "Unable to acquire DMA channel TX (%li)\n",
+ PTR_ERR(master->dma_tx));
+ goto err_dma;
+ }
+
+ dma_sconfig.direction = DMA_MEM_TO_DEV;
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.dst_addr = res->start + SUN4I_TXDATA_REG;
+ dma_sconfig.src_maxburst = 1;
+ dma_sconfig.dst_maxburst = 1;
+
+ ret = dmaengine_slave_config(master->dma_tx, &dma_sconfig);
+ if (ret) {
+ dev_warn(&pdev->dev, "Unable to configure TX DMA slave (%i)\n",
+ ret);
+ goto err_tx_dma_release;
+ }
+
+ master->dma_rx = dma_request_slave_channel_reason(&pdev->dev, "rx");
+ if (IS_ERR(master->dma_rx)) {
+ dev_warn(&pdev->dev, "Unable to acquire DMA channel RX (%li)\n",
+ PTR_ERR(master->dma_rx));
+ goto err_tx_dma_release;
+ }
+
+ dma_sconfig.direction = DMA_DEV_TO_MEM;
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.src_addr = res->start + SUN4I_RXDATA_REG;
+ dma_sconfig.src_maxburst = 1;
+ dma_sconfig.dst_maxburst = 1;
+
+ ret = dmaengine_slave_config(master->dma_rx, &dma_sconfig);
+ if (ret) {
+ dev_warn(&pdev->dev, "Unable to configure RX DMA slave (%i)\n",
+ ret);
+ goto err_rx_dma_release;
+ }
+
+ sspi->has_dma = true;
+ return;
+
+err_rx_dma_release:
+ dma_release_channel(master->dma_rx);
+err_tx_dma_release:
+ dma_release_channel(master->dma_tx);
+err_dma:
+ master->dma_rx = NULL;
+ master->dma_tx = NULL;
+ sspi->has_dma = false;
+ return;
+}
+
 static int sun4i_spi_probe(struct platform_device *pdev)
 {
  struct spi_master *master;
@@ -458,7 +620,9 @@
  goto err_free_master;
  }

+ init_completion(&sspi->done);
  sspi->master = master;
+ master->can_dma = sun4i_spi_can_dma;
  master->max_speed_hz = 100 * 1000 * 1000;
  master->min_speed_hz = 3 * 1000;
  master->set_cs = sun4i_spi_set_cs;
@@ -493,7 +657,8 @@
  ret = sun4i_spi_runtime_resume(&pdev->dev);
  if (ret) {
  dev_err(&pdev->dev, "Couldn't resume the device\n");
- goto err_free_master;
+ goto err_rx_dma_release;
+ //goto err_free_master;
  }

  pm_runtime_set_active(&pdev->dev);
@@ -511,6 +676,11 @@
 err_pm_disable:
  pm_runtime_disable(&pdev->dev);
  sun4i_spi_runtime_suspend(&pdev->dev);
+err_rx_dma_release:
+ if (master->dma_rx)
+ dma_release_channel(master->dma_rx);
+ if (master->dma_tx)
+ dma_release_channel(master->dma_tx);
 err_free_master:
  spi_master_put(master);
  return ret;
@@ -518,8 +688,12 @@

 static int sun4i_spi_remove(struct platform_device *pdev)
 {
+ struct spi_master *master = platform_get_drvdata(pdev);
  pm_runtime_force_suspend(&pdev->dev);

+ dma_release_channel(master->dma_rx);
+ dma_release_channel(master->dma_tx);
+
  return 0;
 }


Any clue would be very helpful.

Thanks.

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to linux-sunxi+unsubscr...@googlegroups.com.
To view this discussion on the web, visit 
https://groups.google.com/d/msgid/linux-sunxi/CA%2BH9mKg-8OTf2a_7Tv2z6ga9f9tZeSSFuTn4RZznwymubQyzyQ%40mail.gmail.com.

Reply via email to