Add pseudo-DMA by FIQ to the S3C24XX SPI driver. This allows the driver
to get DMA-like performance where there are either no free DMA channels
or when doing transfers that required both TX and RX data paths.

Since this patch requires the addition of an assembly file to hold the
FIQ code, we rename the module (instead of adding a rename of the .c file
to this patch). We expect most users are loading this via udev and thus
there should be no change to the userland configuration.

Signed-off-by: Ben Dooks <b...@simtec.co.uk>
Signed-off-by: Simtec Linux Team <li...@simtec.co.uk>

---

Index: b/drivers/spi/Makefile
===================================================================
--- a/drivers/spi/Makefile      2009-11-18 15:53:44.000000000 +0000
+++ b/drivers/spi/Makefile      2009-11-18 15:55:17.000000000 +0000
@@ -28,11 +28,17 @@ obj-$(CONFIG_SPI_MPC52xx_PSC)               += mpc52x
 obj-$(CONFIG_SPI_MPC8xxx)              += spi_mpc8xxx.o
 obj-$(CONFIG_SPI_PPC4xx)               += spi_ppc4xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)         += spi_s3c24xx_gpio.o
-obj-$(CONFIG_SPI_S3C24XX)              += spi_s3c24xx.o
+obj-$(CONFIG_SPI_S3C24XX)              += spi_s3c24xx_hw.o
 obj-$(CONFIG_SPI_TXX9)                 += spi_txx9.o
 obj-$(CONFIG_SPI_XILINX)               += xilinx_spi.o
 obj-$(CONFIG_SPI_SH_SCI)               += spi_sh_sci.o
 obj-$(CONFIG_SPI_STMP3XXX)             += spi_stmp.o
+
+# special build for s3c24xx spi driver with fiq support
+spi_s3c24xx_hw-y                       := spi_s3c24xx.o
+spi_s3c24xx_hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi_s3c24xx_fiq.o
+
+
 #      ... add above this line ...
 
 # SPI protocol drivers (device/link on bus)
Index: b/drivers/spi/spi_s3c24xx_fiq.S
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/spi/spi_s3c24xx_fiq.S     2009-11-18 15:55:32.000000000 +0000
@@ -0,0 +1,116 @@
+/* linux/drivers/spi/spi_s3c24xx_fiq.S
+ *
+ * Copyright 2009 Simtec Electronics
+ *     Ben Dooks <b...@simtec.co.uk>
+ *
+ * S3C24XX SPI - FIQ pseudo-DMA transfer code
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+#include <mach/map.h>
+#include <mach/regs-irq.h>
+#include <plat/regs-spi.h>
+
+#include "spi_s3c24xx_fiq.h"
+
+       .text
+
+       @ entry to these routines is as follows, with the register names
+       @ defined in fiq.h so that they can be shared with the C files which
+       @ setup the calling registers.
+       @
+       @ fiq_rirq      The base of the IRQ registers to find S3C2410_SRCPND
+       @ fiq_rtmp      Temporary register to hold tx/rx data
+       @ fiq_rspi      The base of the SPI register block
+       @ fiq_rtx       The tx buffer pointer
+       @ fiq_rrx       The rx buffer pointer
+       @ fiq_rcount    The number of bytes to move
+
+       @ each entry starts with a word entry of how long it is
+       @ and an offset to the irq acknowledgment word
+
+ENTRY(s3c24xx_spi_fiq_rx)
+s3c24xx_spi_fix_rx:
+       .word   fiq_rx_end - fiq_rx_start
+       .word   fiq_rx_irq_ack - fiq_rx_start
+fiq_rx_start:
+       ldr     fiq_rtmp, fiq_rx_irq_ack
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
+
+       ldrb    fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
+       strb    fiq_rtmp, [ fiq_rrx ], #1
+
+       mov     fiq_rtmp, #0xff
+       strb    fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
+
+       subs    fiq_rcount, fiq_rcount, #1
+       subnes  pc, lr, #4              @@ return, still have work to do
+
+       @@ set IRQ controller so that next op will trigger IRQ
+       mov     fiq_rtmp, #0
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
+       subs    pc, lr, #4
+
+fiq_rx_irq_ack:
+       .word   0
+fiq_rx_end:
+
+ENTRY(s3c24xx_spi_fiq_txrx)
+s3c24xx_spi_fiq_txrx:
+       .word   fiq_txrx_end - fiq_txrx_start
+       .word   fiq_txrx_irq_ack - fiq_txrx_start
+fiq_txrx_start:
+
+       ldrb    fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
+       strb    fiq_rtmp, [ fiq_rrx ], #1
+
+       ldr     fiq_rtmp, fiq_txrx_irq_ack
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
+
+       ldrb    fiq_rtmp, [ fiq_rtx ], #1
+       strb    fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
+
+       subs    fiq_rcount, fiq_rcount, #1
+       subnes  pc, lr, #4              @@ return, still have work to do
+
+       mov     fiq_rtmp, #0
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
+       subs    pc, lr, #4
+
+fiq_txrx_irq_ack:
+       .word   0
+
+fiq_txrx_end:
+
+ENTRY(s3c24xx_spi_fiq_tx)
+s3c24xx_spi_fix_tx:
+       .word   fiq_tx_end - fiq_tx_start
+       .word   fiq_tx_irq_ack - fiq_tx_start
+fiq_tx_start:
+       ldrb    fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
+
+       ldr     fiq_rtmp, fiq_tx_irq_ack
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
+
+       ldrb    fiq_rtmp, [ fiq_rtx ], #1
+       strb    fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
+
+       subs    fiq_rcount, fiq_rcount, #1
+       subnes  pc, lr, #4              @@ return, still have work to do
+
+       mov     fiq_rtmp, #0
+       str     fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
+       subs    pc, lr, #4
+
+fiq_tx_irq_ack:
+       .word   0
+
+fiq_tx_end:
+
+       .end
Index: b/drivers/spi/Kconfig
===================================================================
--- a/drivers/spi/Kconfig       2009-11-18 15:53:44.000000000 +0000
+++ b/drivers/spi/Kconfig       2009-11-18 15:55:17.000000000 +0000
@@ -205,6 +205,17 @@ config SPI_S3C24XX
        help
          SPI driver for Samsung S3C24XX series ARM SoCs
 
+config SPI_S3C24XX_FIQ
+       bool "S3C24XX driver with FIQ pseudo-DMA"
+       depends on SPI_S3C24XX
+       select FIQ
+       help
+         Enable FIQ support for the S3C24XX SPI driver to provide pseudo
+         DMA by using the fast-interrupt request framework, This allows
+         the driver to get DMA-like performance when there are either
+         no free DMA channels, or when doing transfers that required both
+         TX and RX data paths.
+
 config SPI_S3C24XX_GPIO
        tristate "Samsung S3C24XX series SPI by GPIO"
        depends on ARCH_S3C2410 && EXPERIMENTAL
Index: b/drivers/spi/spi_s3c24xx.c
===================================================================
--- a/drivers/spi/spi_s3c24xx.c 2009-11-18 15:53:44.000000000 +0000
+++ b/drivers/spi/spi_s3c24xx.c 2009-11-18 15:55:23.000000000 +0000
@@ -1,7 +1,7 @@
 /* linux/drivers/spi/spi_s3c24xx.c
  *
  * Copyright (c) 2006 Ben Dooks
- * Copyright (c) 2006 Simtec Electronics
+ * Copyright 2006-2009 Simtec Electronics
  *     Ben Dooks <b...@simtec.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -28,6 +28,11 @@
 #include <plat/regs-spi.h>
 #include <mach/spi.h>
 
+#include <plat/fiq.h>
+#include <asm/fiq.h>
+
+#include "spi_s3c24xx_fiq.h"
+
 /**
  * s3c24xx_spi_devstate - per device data
  * @hz: Last frequency calculated for @sppre field.
@@ -42,6 +47,13 @@ struct s3c24xx_spi_devstate {
        u8              sppre;
 };
 
+enum spi_fiq_mode {
+       FIQ_MODE_NONE   = 0,
+       FIQ_MODE_TX     = 1,
+       FIQ_MODE_RX     = 2,
+       FIQ_MODE_TXRX   = 3,
+};
+
 struct s3c24xx_spi {
        /* bitbang has to be first */
        struct spi_bitbang       bitbang;
@@ -52,6 +64,11 @@ struct s3c24xx_spi {
        int                      len;
        int                      count;
 
+       struct fiq_handler       fiq_handler;
+       enum spi_fiq_mode        fiq_mode;
+       unsigned char            fiq_inuse;
+       unsigned char            fiq_claimed;
+
        void                    (*set_cs)(struct s3c2410_spi_info *spi,
                                          int cs, int pol);
 
@@ -67,6 +84,7 @@ struct s3c24xx_spi {
        struct s3c2410_spi_info *pdata;
 };
 
+
 #define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT)
 #define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP)
 
@@ -127,7 +145,7 @@ static int s3c24xx_spi_update_state(stru
        }
 
        if (spi->mode != cs->mode) {
-               u8 spcon = SPCON_DEFAULT;
+               u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
 
                if (spi->mode & SPI_CPHA)
                        spcon |= S3C2410_SPCON_CPHA_FMTB;
@@ -214,13 +232,196 @@ static inline unsigned int hw_txbyte(str
        return hw->tx ? hw->tx[count] : 0;
 }
 
+#ifdef CONFIG_SPI_S3C24XX_FIQ
+/* Support for FIQ based pseudo-DMA to improve the transfer speed.
+ *
+ * This code uses the assembly helper in spi_s3c24xx_spi.S which is
+ * used by the FIQ core to move data between main memory and the peripheral
+ * block. Since this is code running on the processor, there is no problem
+ * with cache coherency of the buffers, so we can use any buffer we like.
+ */
+
+/**
+ * struct spi_fiq_code - FIQ code and header
+ * @length: The length of the code fragment, excluding this header.
+ * @ack_offset: The offset from @data to the word to place the IRQ ACK bit at.
+ * @data: The code itself to install as a FIQ handler.
+ */
+struct spi_fiq_code {
+       u32     length;
+       u32     ack_offset;
+       u8      data[0];
+};
+
+extern struct spi_fiq_code s3c24xx_spi_fiq_txrx;
+extern struct spi_fiq_code s3c24xx_spi_fiq_tx;
+extern struct spi_fiq_code s3c24xx_spi_fiq_rx;
+
+/**
+ * ack_bit - turn IRQ into IRQ acknowledgement bit
+ * @irq: The interrupt number
+ *
+ * Returns the bit to write to the interrupt acknowledge register.
+ */
+static inline u32 ack_bit(unsigned int irq)
+{
+       return 1 << (irq - IRQ_EINT0);
+}
+
+/**
+ * s3c24xx_spi_tryfiq - attempt to claim and setup FIQ for transfer
+ * @hw: The hardware state.
+ *
+ * Claim the FIQ handler (only one can be active at any one time) and
+ * then setup the correct transfer code for this transfer.
+ *
+ * This call updates all the necessary state information if sucessful,
+ * so the caller does not need to do anything more than start the transfer
+ * as normal, since the IRQ will have been re-routed to the FIQ handler.
+*/
+void s3c24xx_spi_tryfiq(struct s3c24xx_spi *hw)
+{
+       struct pt_regs regs;
+       enum spi_fiq_mode mode;
+       struct spi_fiq_code *code;
+       int ret;
+
+       if (!hw->fiq_claimed) {
+               /* try and claim fiq if we haven't got it, and if not
+                * then return and simply use another transfer method */
+
+               ret = claim_fiq(&hw->fiq_handler);
+               if (ret)
+                       return;
+       }
+
+       if (hw->tx && !hw->rx)
+               mode = FIQ_MODE_TX;
+       else if (hw->rx && !hw->tx)
+               mode = FIQ_MODE_RX;
+       else
+               mode = FIQ_MODE_TXRX;
+
+       regs.uregs[fiq_rspi] = (long)hw->regs;
+       regs.uregs[fiq_rrx]  = (long)hw->rx;
+       regs.uregs[fiq_rtx]  = (long)hw->tx + 1;
+       regs.uregs[fiq_rcount] = hw->len - 1;
+       regs.uregs[fiq_rirq] = (long)S3C24XX_VA_IRQ;
+
+       set_fiq_regs(&regs);
+
+       if (hw->fiq_mode != mode) {
+               u32 *ack_ptr;
+
+               hw->fiq_mode = mode;
+
+               switch (mode) {
+               case FIQ_MODE_TX:
+                       code = &s3c24xx_spi_fiq_tx;
+                       break;
+               case FIQ_MODE_RX:
+                       code = &s3c24xx_spi_fiq_rx;
+                       break;
+               case FIQ_MODE_TXRX:
+                       code = &s3c24xx_spi_fiq_txrx;
+                       break;
+               default:
+                       code = NULL;
+               }
+
+               BUG_ON(!code);
+
+               ack_ptr = (u32 *)&code->data[code->ack_offset];
+               *ack_ptr = ack_bit(hw->irq);
+
+               set_fiq_handler(&code->data, code->length);
+       }
+
+       s3c24xx_set_fiq(hw->irq, true);
+
+       hw->fiq_mode = mode;
+       hw->fiq_inuse = 1;
+}
+
+/**
+ * s3c24xx_spi_fiqop - FIQ core code callback
+ * @pw: Data registered with the handler
+ * @release: Whether this is a release or a return.
+ *
+ * Called by the FIQ code when another module wants to use the FIQ, so
+ * return whether we are currently using this or not and then update our
+ * internal state.
+ */
+static int s3c24xx_spi_fiqop(void *pw, int release)
+{
+       struct s3c24xx_spi *hw = pw;
+       int ret = 0;
+
+       if (release) {
+               if (hw->fiq_inuse)
+                       ret = -EBUSY;
+
+               /* note, we do not need to unroute the FIQ, as the FIQ
+                * vector code de-routes it to signal the end of transfer */
+
+               hw->fiq_mode = FIQ_MODE_NONE;
+               hw->fiq_claimed = 0;
+       } else {
+               hw->fiq_claimed = 1;
+       }
+
+       return ret;
+}
+
+/**
+ * s3c24xx_spi_initfiq - setup the information for the FIQ core
+ * @hw: The hardware state.
+ *
+ * Setup the fiq_handler block to pass to the FIQ core.
+ */
+static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *hw)
+{
+       hw->fiq_handler.dev_id = hw;
+       hw->fiq_handler.name = dev_name(hw->dev);
+       hw->fiq_handler.fiq_op = s3c24xx_spi_fiqop;
+}
+
+/**
+ * s3c24xx_spi_usefiq - return if we should be using FIQ.
+ * @hw: The hardware state.
+ *
+ * Return true if the platform data specifies whether this channel is
+ * allowed to use the FIQ.
+ */
+static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *hw)
+{
+       return hw->pdata->use_fiq;
+}
+
+/**
+ * s3c24xx_spi_usingfiq - return if channel is using FIQ
+ * @spi: The hardware state.
+ *
+ * Return whether the channel is currently using the FIQ (separate from
+ * whether the FIQ is claimed).
+ */
+static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *spi)
+{
+       return spi->fiq_inuse;
+}
+#else
+
+static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *s) { }
+static inline void s3c24xx_spi_tryfiq(struct s3c24xx_spi *s) { }
+static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *s) { return false; }
+static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *s) { return false; 
}
+
+#endif /* CONFIG_SPI_S3C24XX_FIQ */
+
 static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
 {
        struct s3c24xx_spi *hw = to_hw(spi);
 
-       dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
-               t->tx_buf, t->rx_buf, t->len);
-
        hw->tx = t->tx_buf;
        hw->rx = t->rx_buf;
        hw->len = t->len;
@@ -228,11 +429,14 @@ static int s3c24xx_spi_txrx(struct spi_d
 
        init_completion(&hw->done);
 
+       hw->fiq_inuse = 0;
+       if (s3c24xx_spi_usefiq(hw) && t->len >= 3)
+               s3c24xx_spi_tryfiq(hw);
+
        /* send the first byte */
        writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
 
        wait_for_completion(&hw->done);
-
        return hw->count;
 }
 
@@ -254,17 +458,27 @@ static irqreturn_t s3c24xx_spi_irq(int i
                goto irq_done;
        }
 
-       hw->count++;
+       if (!s3c24xx_spi_usingfiq(hw)) {
+               hw->count++;
 
-       if (hw->rx)
-               hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
+               if (hw->rx)
+                       hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
 
-       count++;
+               count++;
+
+               if (count < hw->len)
+                       writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
+               else
+                       complete(&hw->done);
+       } else {
+               hw->count = hw->len;
+               hw->fiq_inuse = 0;
+
+               if (hw->rx)
+                       hw->rx[hw->len-1] = readb(hw->regs + S3C2410_SPRDAT);
 
-       if (count < hw->len)
-               writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
-       else
                complete(&hw->done);
+       }
 
  irq_done:
        return IRQ_HANDLED;
@@ -322,6 +536,10 @@ static int __init s3c24xx_spi_probe(stru
        platform_set_drvdata(pdev, hw);
        init_completion(&hw->done);
 
+       /* initialise fiq handler */
+
+       s3c24xx_spi_initfiq(hw);
+
        /* setup the master state. */
 
        /* the spi->mode bits understood by this driver: */
Index: b/drivers/spi/spi_s3c24xx_fiq.h
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/spi/spi_s3c24xx_fiq.h     2009-11-18 15:55:38.000000000 +0000
@@ -0,0 +1,26 @@
+/* linux/drivers/spi/spi_s3c24xx_fiq.h
+ *
+ * Copyright 2009 Simtec Electronics
+ *     Ben Dooks <b...@simtec.co.uk>
+ *
+ * S3C24XX SPI - FIQ pseudo-DMA transfer support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* We have R8 through R13 to play with */
+
+#ifdef __ASSEMBLY__
+#define __REG_NR(x)     r##x
+#else
+#define __REG_NR(x)     (x)
+#endif
+
+#define fiq_rspi       __REG_NR(8)
+#define fiq_rtmp       __REG_NR(9)
+#define fiq_rrx                __REG_NR(10)
+#define fiq_rtx                __REG_NR(11)
+#define fiq_rcount     __REG_NR(12)
+#define fiq_rirq       __REG_NR(13)
Index: b/arch/arm/mach-s3c2410/include/mach/spi.h
===================================================================
--- a/arch/arm/mach-s3c2410/include/mach/spi.h  2009-11-18 15:53:44.000000000 
+0000
+++ b/arch/arm/mach-s3c2410/include/mach/spi.h  2009-11-18 15:55:17.000000000 
+0000
@@ -18,6 +18,8 @@ struct s3c2410_spi_info {
        unsigned int             num_cs;        /* total chipselects */
        int                      bus_num;       /* bus number to use. */
 
+       unsigned int             use_fiq:1;     /* use fiq */
+
        void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);
        void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
 };


------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

Reply via email to