Skip to content

Commit bec0806

Browse files
Ben Dooksglikely
authored andcommitted
spi_s3c24xx: add FIQ pseudo-DMA support
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 <ben@simtec.co.uk> Signed-off-by: Simtec Linux Team <linux@simtec.co.uk> Cc: David Brownell <david-b@pacbell.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
1 parent e24c745 commit bec0806

File tree

6 files changed

+392
-14
lines changed

6 files changed

+392
-14
lines changed

arch/arm/mach-s3c2410/include/mach/spi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ struct s3c2410_spi_info {
1818
unsigned int num_cs; /* total chipselects */
1919
int bus_num; /* bus number to use. */
2020

21+
unsigned int use_fiq:1; /* use fiq */
22+
2123
void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);
2224
void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
2325
};

drivers/spi/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,17 @@ config SPI_S3C24XX
219219
help
220220
SPI driver for Samsung S3C24XX series ARM SoCs
221221

222+
config SPI_S3C24XX_FIQ
223+
bool "S3C24XX driver with FIQ pseudo-DMA"
224+
depends on SPI_S3C24XX
225+
select FIQ
226+
help
227+
Enable FIQ support for the S3C24XX SPI driver to provide pseudo
228+
DMA by using the fast-interrupt request framework, This allows
229+
the driver to get DMA-like performance when there are either
230+
no free DMA channels, or when doing transfers that required both
231+
TX and RX data paths.
232+
222233
config SPI_S3C24XX_GPIO
223234
tristate "Samsung S3C24XX series SPI by GPIO"
224235
depends on ARCH_S3C2410 && EXPERIMENTAL

drivers/spi/Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o
3232
obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o
3333
obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o
3434
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
35-
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
35+
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o
3636
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
3737
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
3838
obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o
@@ -41,6 +41,11 @@ obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
4141
obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o
4242
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
4343
obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o
44+
45+
# special build for s3c24xx spi driver with fiq support
46+
spi_s3c24xx_hw-y := spi_s3c24xx.o
47+
spi_s3c24xx_hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi_s3c24xx_fiq.o
48+
4449
# ... add above this line ...
4550

4651
# SPI protocol drivers (device/link on bus)

drivers/spi/spi_s3c24xx.c

Lines changed: 231 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* linux/drivers/spi/spi_s3c24xx.c
22
*
33
* Copyright (c) 2006 Ben Dooks
4-
* Copyright (c) 2006 Simtec Electronics
4+
* Copyright 2006-2009 Simtec Electronics
55
* Ben Dooks <ben@simtec.co.uk>
66
*
77
* This program is free software; you can redistribute it and/or modify
@@ -28,6 +28,11 @@
2828
#include <plat/regs-spi.h>
2929
#include <mach/spi.h>
3030

31+
#include <plat/fiq.h>
32+
#include <asm/fiq.h>
33+
34+
#include "spi_s3c24xx_fiq.h"
35+
3136
/**
3237
* s3c24xx_spi_devstate - per device data
3338
* @hz: Last frequency calculated for @sppre field.
@@ -42,6 +47,13 @@ struct s3c24xx_spi_devstate {
4247
u8 sppre;
4348
};
4449

50+
enum spi_fiq_mode {
51+
FIQ_MODE_NONE = 0,
52+
FIQ_MODE_TX = 1,
53+
FIQ_MODE_RX = 2,
54+
FIQ_MODE_TXRX = 3,
55+
};
56+
4557
struct s3c24xx_spi {
4658
/* bitbang has to be first */
4759
struct spi_bitbang bitbang;
@@ -52,6 +64,11 @@ struct s3c24xx_spi {
5264
int len;
5365
int count;
5466

67+
struct fiq_handler fiq_handler;
68+
enum spi_fiq_mode fiq_mode;
69+
unsigned char fiq_inuse;
70+
unsigned char fiq_claimed;
71+
5572
void (*set_cs)(struct s3c2410_spi_info *spi,
5673
int cs, int pol);
5774

@@ -67,6 +84,7 @@ struct s3c24xx_spi {
6784
struct s3c2410_spi_info *pdata;
6885
};
6986

87+
7088
#define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT)
7189
#define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP)
7290

@@ -127,7 +145,7 @@ static int s3c24xx_spi_update_state(struct spi_device *spi,
127145
}
128146

129147
if (spi->mode != cs->mode) {
130-
u8 spcon = SPCON_DEFAULT;
148+
u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
131149

132150
if (spi->mode & SPI_CPHA)
133151
spcon |= S3C2410_SPCON_CPHA_FMTB;
@@ -214,25 +232,211 @@ static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
214232
return hw->tx ? hw->tx[count] : 0;
215233
}
216234

235+
#ifdef CONFIG_SPI_S3C24XX_FIQ
236+
/* Support for FIQ based pseudo-DMA to improve the transfer speed.
237+
*
238+
* This code uses the assembly helper in spi_s3c24xx_spi.S which is
239+
* used by the FIQ core to move data between main memory and the peripheral
240+
* block. Since this is code running on the processor, there is no problem
241+
* with cache coherency of the buffers, so we can use any buffer we like.
242+
*/
243+
244+
/**
245+
* struct spi_fiq_code - FIQ code and header
246+
* @length: The length of the code fragment, excluding this header.
247+
* @ack_offset: The offset from @data to the word to place the IRQ ACK bit at.
248+
* @data: The code itself to install as a FIQ handler.
249+
*/
250+
struct spi_fiq_code {
251+
u32 length;
252+
u32 ack_offset;
253+
u8 data[0];
254+
};
255+
256+
extern struct spi_fiq_code s3c24xx_spi_fiq_txrx;
257+
extern struct spi_fiq_code s3c24xx_spi_fiq_tx;
258+
extern struct spi_fiq_code s3c24xx_spi_fiq_rx;
259+
260+
/**
261+
* ack_bit - turn IRQ into IRQ acknowledgement bit
262+
* @irq: The interrupt number
263+
*
264+
* Returns the bit to write to the interrupt acknowledge register.
265+
*/
266+
static inline u32 ack_bit(unsigned int irq)
267+
{
268+
return 1 << (irq - IRQ_EINT0);
269+
}
270+
271+
/**
272+
* s3c24xx_spi_tryfiq - attempt to claim and setup FIQ for transfer
273+
* @hw: The hardware state.
274+
*
275+
* Claim the FIQ handler (only one can be active at any one time) and
276+
* then setup the correct transfer code for this transfer.
277+
*
278+
* This call updates all the necessary state information if sucessful,
279+
* so the caller does not need to do anything more than start the transfer
280+
* as normal, since the IRQ will have been re-routed to the FIQ handler.
281+
*/
282+
void s3c24xx_spi_tryfiq(struct s3c24xx_spi *hw)
283+
{
284+
struct pt_regs regs;
285+
enum spi_fiq_mode mode;
286+
struct spi_fiq_code *code;
287+
int ret;
288+
289+
if (!hw->fiq_claimed) {
290+
/* try and claim fiq if we haven't got it, and if not
291+
* then return and simply use another transfer method */
292+
293+
ret = claim_fiq(&hw->fiq_handler);
294+
if (ret)
295+
return;
296+
}
297+
298+
if (hw->tx && !hw->rx)
299+
mode = FIQ_MODE_TX;
300+
else if (hw->rx && !hw->tx)
301+
mode = FIQ_MODE_RX;
302+
else
303+
mode = FIQ_MODE_TXRX;
304+
305+
regs.uregs[fiq_rspi] = (long)hw->regs;
306+
regs.uregs[fiq_rrx] = (long)hw->rx;
307+
regs.uregs[fiq_rtx] = (long)hw->tx + 1;
308+
regs.uregs[fiq_rcount] = hw->len - 1;
309+
regs.uregs[fiq_rirq] = (long)S3C24XX_VA_IRQ;
310+
311+
set_fiq_regs(&regs);
312+
313+
if (hw->fiq_mode != mode) {
314+
u32 *ack_ptr;
315+
316+
hw->fiq_mode = mode;
317+
318+
switch (mode) {
319+
case FIQ_MODE_TX:
320+
code = &s3c24xx_spi_fiq_tx;
321+
break;
322+
case FIQ_MODE_RX:
323+
code = &s3c24xx_spi_fiq_rx;
324+
break;
325+
case FIQ_MODE_TXRX:
326+
code = &s3c24xx_spi_fiq_txrx;
327+
break;
328+
default:
329+
code = NULL;
330+
}
331+
332+
BUG_ON(!code);
333+
334+
ack_ptr = (u32 *)&code->data[code->ack_offset];
335+
*ack_ptr = ack_bit(hw->irq);
336+
337+
set_fiq_handler(&code->data, code->length);
338+
}
339+
340+
s3c24xx_set_fiq(hw->irq, true);
341+
342+
hw->fiq_mode = mode;
343+
hw->fiq_inuse = 1;
344+
}
345+
346+
/**
347+
* s3c24xx_spi_fiqop - FIQ core code callback
348+
* @pw: Data registered with the handler
349+
* @release: Whether this is a release or a return.
350+
*
351+
* Called by the FIQ code when another module wants to use the FIQ, so
352+
* return whether we are currently using this or not and then update our
353+
* internal state.
354+
*/
355+
static int s3c24xx_spi_fiqop(void *pw, int release)
356+
{
357+
struct s3c24xx_spi *hw = pw;
358+
int ret = 0;
359+
360+
if (release) {
361+
if (hw->fiq_inuse)
362+
ret = -EBUSY;
363+
364+
/* note, we do not need to unroute the FIQ, as the FIQ
365+
* vector code de-routes it to signal the end of transfer */
366+
367+
hw->fiq_mode = FIQ_MODE_NONE;
368+
hw->fiq_claimed = 0;
369+
} else {
370+
hw->fiq_claimed = 1;
371+
}
372+
373+
return ret;
374+
}
375+
376+
/**
377+
* s3c24xx_spi_initfiq - setup the information for the FIQ core
378+
* @hw: The hardware state.
379+
*
380+
* Setup the fiq_handler block to pass to the FIQ core.
381+
*/
382+
static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *hw)
383+
{
384+
hw->fiq_handler.dev_id = hw;
385+
hw->fiq_handler.name = dev_name(hw->dev);
386+
hw->fiq_handler.fiq_op = s3c24xx_spi_fiqop;
387+
}
388+
389+
/**
390+
* s3c24xx_spi_usefiq - return if we should be using FIQ.
391+
* @hw: The hardware state.
392+
*
393+
* Return true if the platform data specifies whether this channel is
394+
* allowed to use the FIQ.
395+
*/
396+
static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *hw)
397+
{
398+
return hw->pdata->use_fiq;
399+
}
400+
401+
/**
402+
* s3c24xx_spi_usingfiq - return if channel is using FIQ
403+
* @spi: The hardware state.
404+
*
405+
* Return whether the channel is currently using the FIQ (separate from
406+
* whether the FIQ is claimed).
407+
*/
408+
static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *spi)
409+
{
410+
return spi->fiq_inuse;
411+
}
412+
#else
413+
414+
static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *s) { }
415+
static inline void s3c24xx_spi_tryfiq(struct s3c24xx_spi *s) { }
416+
static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *s) { return false; }
417+
static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *s) { return false; }
418+
419+
#endif /* CONFIG_SPI_S3C24XX_FIQ */
420+
217421
static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
218422
{
219423
struct s3c24xx_spi *hw = to_hw(spi);
220424

221-
dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
222-
t->tx_buf, t->rx_buf, t->len);
223-
224425
hw->tx = t->tx_buf;
225426
hw->rx = t->rx_buf;
226427
hw->len = t->len;
227428
hw->count = 0;
228429

229430
init_completion(&hw->done);
230431

432+
hw->fiq_inuse = 0;
433+
if (s3c24xx_spi_usefiq(hw) && t->len >= 3)
434+
s3c24xx_spi_tryfiq(hw);
435+
231436
/* send the first byte */
232437
writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
233438

234439
wait_for_completion(&hw->done);
235-
236440
return hw->count;
237441
}
238442

@@ -254,17 +458,27 @@ static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
254458
goto irq_done;
255459
}
256460

257-
hw->count++;
461+
if (!s3c24xx_spi_usingfiq(hw)) {
462+
hw->count++;
258463

259-
if (hw->rx)
260-
hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
464+
if (hw->rx)
465+
hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
261466

262-
count++;
467+
count++;
468+
469+
if (count < hw->len)
470+
writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
471+
else
472+
complete(&hw->done);
473+
} else {
474+
hw->count = hw->len;
475+
hw->fiq_inuse = 0;
476+
477+
if (hw->rx)
478+
hw->rx[hw->len-1] = readb(hw->regs + S3C2410_SPRDAT);
263479

264-
if (count < hw->len)
265-
writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
266-
else
267480
complete(&hw->done);
481+
}
268482

269483
irq_done:
270484
return IRQ_HANDLED;
@@ -322,6 +536,10 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev)
322536
platform_set_drvdata(pdev, hw);
323537
init_completion(&hw->done);
324538

539+
/* initialise fiq handler */
540+
541+
s3c24xx_spi_initfiq(hw);
542+
325543
/* setup the master state. */
326544

327545
/* the spi->mode bits understood by this driver: */

0 commit comments

Comments
 (0)