All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] (re-send) spi-imx: improve timing of bursts
@ 2014-03-28 11:05 Daniel Sobe
       [not found] ` <b6c440a488274f0b89a8bb4047d8ee3b-xRIOyFSmT1bbVUvU2aK0zQfhPeD8jYilXA4E9RH9d+qIuWR1G4zioA@public.gmane.org>
  0 siblings, 1 reply; 3+ messages in thread
From: Daniel Sobe @ 2014-03-28 11:05 UTC (permalink / raw)
  To: linux-spi-u79uwXL29TY76Z2rM5mHXA


Hi,

The patch below addresses 2 improvements:

1) Burstsize of a transfer adjusted up to the maximum burstsize of the  hardware --> no unwanted CS toggling between data words
2) Variable trigger level allows to increase priority for data transfer,  enables continuous transfers up to the burstsize. --> remove gaps  every 64 words

The original behaviour of the driver is untouched. The additional features can be enabled via sysfs.

With file "bigburst" the driver is instructed to use the maximum  possible burst length, depending on the length of the transfer it  received. Due to the hardware of the iMX6 this only makes sense if a  transfer has its bits per word set to 32.

With file "txthres" the transmit FIFO level is set (in the range of 1 to  64), all other values disable the functionality. An interrupt will be  triggered if the specified FIFO level (or lower) is reached, re-filling  the FIFO up to the max resp. the remaining data of the transfer. If disabled, the original behaviour (refilling  when FIFO is empty) is active. The functionality creates a higher  interrupt load but allows continuous transfers up to the selected  burstsize.

I'd like to receive feedback on the patch, improving it and making it  suitable for a possible inclusion into the kernel. Unfortunately, I  could only test the patch on a very old kernel. The patch itself applies  against a recent kernel (3.14-rc6) and I checked that it compiles without warnings.

Please let me also know if there's something formally wrong with this patch.

Signed-off-by: Daniel Sobe <daniel.sobe-3arQi8VN3Tc@public.gmane.org>

--- linux-original/drivers/spi/spi-imx.c    2014-03-17 17:12:35.691791218 +0100
+++ linux-stable/drivers/spi/spi-imx.c    2014-03-17 17:09:44.451795597 +0100
@@ -51,12 +51,14 @@
 /* generic defines to abstract from the different register layouts */
 #define MXC_INT_RR    (1 << 0) /* Receive data ready interrupt */
 #define MXC_INT_TE    (1 << 1) /* Transmit FIFO empty interrupt */
+#define MXC_INT_THE     (1 << 2) /* Transmit FIFO threshold interrupt */
 
 struct spi_imx_config {
     unsigned int speed_hz;
     unsigned int bpw;
     unsigned int mode;
     u8 cs;
+    unsigned long burstlen;
 };
 
 enum spi_imx_devtype {
@@ -154,6 +156,10 @@
 static int mxc_clkdivs[] = {0, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192,
     256, 384, 512, 768, 1024};
 
+// (sysfs-)tunable attributes
+static int allow_big_bursts = 0; // if set to 1 this enables 32-bit big bursts
+static int tx_fifo_thres = -1;   // if positive, use TX data request interrupt when applicable
+
 /* MX21, MX27 */
 static unsigned int spi_imx_clkdiv_1(unsigned int fin,
         unsigned int fspi, unsigned int max)
@@ -200,8 +206,13 @@
 
 #define MX51_ECSPI_INT        0x10
 #define MX51_ECSPI_INT_TEEN        (1 <<  0)
+#define MX51_ECSPI_INT_TDREN        (1 <<  1)
 #define MX51_ECSPI_INT_RREN        (1 <<  3)
 
+#define MX51_ECSPI_DMA      0x14
+#define MX51_ECSPI_DMA_TX_THRES_OFFSET  0
+#define MX51_ECSPI_DMA_TX_THRES_MASK    (0x3F)
+
 #define MX51_ECSPI_STAT        0x18
 #define MX51_ECSPI_STAT_RR        (1 <<  3)
 
@@ -250,6 +261,12 @@
     if (enable & MXC_INT_TE)
         val |= MX51_ECSPI_INT_TEEN;
 
+    // enable Tx threshold interrupt for faster reaction when transmitting long messages
+    if (enable & MXC_INT_THE) {
+        val |= MX51_ECSPI_INT_TDREN;
+        writel((unsigned int) tx_fifo_thres, spi_imx->base + MX51_ECSPI_DMA);
+    }
+
     if (enable & MXC_INT_RR)
         val |= MX51_ECSPI_INT_RREN;
 
@@ -270,6 +287,7 @@
 {
     u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0;
     u32 clk = config->speed_hz, delay;
+    unsigned long burstlen;
 
     /*
      * The hardware seems to have a race condition when changing modes. The
@@ -286,6 +304,27 @@
     /* set chip select to use */
     ctrl |= MX51_ECSPI_CTRL_CS(config->cs);
 
+    // if possible and requested, increase burstlength
+    // over whole message in 32 bit mode
+    // (which is the only mode where HW supports this)
+    // all other modes still use burstlength == bpw
+    if ((config->bpw == 32) && (allow_big_bursts == 1)) {
+        if (config->burstlen <= 4096) {
+            burstlen = config->burstlen;
+        } else {
+            // burst longer than burst register width -- need to scale appropriately
+            // so that the hardware will always terminate the burst appropriately
+            burstlen = 16;
+            do {
+                burstlen = burstlen << 1;
+            } while ((config->burstlen % (burstlen << 1)) == 0);
+        }
+    } else {
+      burstlen = config->bpw;
+    }
+
+    ctrl |= (burstlen - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
+
     ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
 
     cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs);
@@ -666,6 +705,10 @@
         spi_imx->txfifo++;
     }
 
+    // always reconfigure IRQ source (for the TX data request IRQ to work properly)
+    spi_imx->devtype_data->intctrl(spi_imx,
+        ((spi_imx->txfifo == spi_imx_get_fifosize(spi_imx)) && (tx_fifo_thres != -1)) ? MXC_INT_THE : MXC_INT_TE);
+
     spi_imx->devtype_data->trigger(spi_imx);
 }
 
@@ -708,6 +751,7 @@
     config.speed_hz  = t ? t->speed_hz : spi->max_speed_hz;
     config.mode = spi->mode;
     config.cs = spi->chip_select;
+    config.burstlen = t ? (t->len * 8) : config.bpw;
 
     if (!config.speed_hz)
         config.speed_hz = spi->max_speed_hz;
@@ -745,8 +789,6 @@
 
     spi_imx_push(spi_imx);
 
-    spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_TE);
-
     wait_for_completion(&spi_imx->xfer_done);
 
     return transfer->len;
@@ -801,6 +843,63 @@
     return 0;
 }
 
+static ssize_t spidrv_setbigburst(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t size)
+{
+    unsigned long tmp;
+    tmp = simple_strtoul(buf, NULL, 10);
+    if (tmp == 1) {
+        allow_big_bursts = 1;
+        } else {
+        allow_big_bursts = 0;
+    }
+    return size;
+}
+
+static ssize_t spidrv_getbigburst(struct device *dev,
+                                  struct device_attribute *attr,
+                                  char *buf)
+{
+    sprintf (buf, "%d\n", allow_big_bursts);
+    return (ssize_t)(strlen (buf) + 1);
+}
+
+static ssize_t spidrv_settxthres(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t size)
+{
+    unsigned long tmp;
+    tmp = simple_strtoul(buf, NULL, 10);
+    if ((tmp > 0) && (tmp < 65)) {
+        tx_fifo_thres = tmp;
+    } else {
+        tx_fifo_thres = -1;
+    }
+    return size;
+}
+
+static ssize_t spidrv_gettxthres(struct device *dev,
+                                 struct device_attribute *attr,
+                                 char *buf)
+{
+    sprintf (buf, "%d\n", tx_fifo_thres);
+    return (ssize_t)(strlen (buf) + 1);
+}
+
+static DEVICE_ATTR(bigburst, 0666, spidrv_getbigburst, spidrv_setbigburst);
+static DEVICE_ATTR(txthres,  0666, spidrv_gettxthres , spidrv_settxthres);
+
+static struct attribute *spidrv_attributes[] = {
+    &dev_attr_bigburst.attr,
+    &dev_attr_txthres.attr,
+    NULL
+};
+
+static const struct attribute_group spidrv_group = {
+    .attrs = spidrv_attributes,
+};
+
 static int spi_imx_probe(struct platform_device *pdev)
 {
     struct device_node *np = pdev->dev.of_node;
@@ -840,6 +939,10 @@
     spi_imx = spi_master_get_devdata(master);
     spi_imx->bitbang.master = master;
 
+    if (sysfs_create_group(&pdev->dev.kobj, &spidrv_group)) {
+        return -ENOMEM;
+    }
+
     for (i = 0; i < master->num_chipselect; i++) {
         int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
         if (!gpio_is_valid(cs_gpio) && mxc_platform_info)


--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2014-08-18  7:31 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-28 11:05 [PATCH] (re-send) spi-imx: improve timing of bursts Daniel Sobe
     [not found] ` <b6c440a488274f0b89a8bb4047d8ee3b-xRIOyFSmT1bbVUvU2aK0zQfhPeD8jYilXA4E9RH9d+qIuWR1G4zioA@public.gmane.org>
2014-08-16 15:13   ` Mark Brown
     [not found]     ` <20140816151336.GA5813-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
2014-08-18  7:31       ` Daniel Sobe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.