aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi_s3c24xx.c
diff options
context:
space:
mode:
authorBen Dooks <ben@simtec.co.uk>2009-12-15 01:20:24 -0500
committerGrant Likely <grant.likely@secretlab.ca>2009-12-17 10:57:01 -0500
commitbec0806cfec6ded1a7e097bb95279e521a796129 (patch)
treef4be92d1c09a1785ea693d9f99ad2dbfc26bbbac /drivers/spi/spi_s3c24xx.c
parente24c745272072fd2abe55209f1949b7b7ee602a7 (diff)
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>
Diffstat (limited to 'drivers/spi/spi_s3c24xx.c')
-rw-r--r--drivers/spi/spi_s3c24xx.c244
1 files changed, 231 insertions, 13 deletions
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index 33d94f76b9ef..4b0d25b497ac 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -1,7 +1,7 @@
1/* linux/drivers/spi/spi_s3c24xx.c 1/* linux/drivers/spi/spi_s3c24xx.c
2 * 2 *
3 * Copyright (c) 2006 Ben Dooks 3 * Copyright (c) 2006 Ben Dooks
4 * Copyright (c) 2006 Simtec Electronics 4 * Copyright 2006-2009 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk> 5 * Ben Dooks <ben@simtec.co.uk>
6 * 6 *
7 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
@@ -28,6 +28,11 @@
28#include <plat/regs-spi.h> 28#include <plat/regs-spi.h>
29#include <mach/spi.h> 29#include <mach/spi.h>
30 30
31#include <plat/fiq.h>
32#include <asm/fiq.h>
33
34#include "spi_s3c24xx_fiq.h"
35
31/** 36/**
32 * s3c24xx_spi_devstate - per device data 37 * s3c24xx_spi_devstate - per device data
33 * @hz: Last frequency calculated for @sppre field. 38 * @hz: Last frequency calculated for @sppre field.
@@ -42,6 +47,13 @@ struct s3c24xx_spi_devstate {
42 u8 sppre; 47 u8 sppre;
43}; 48};
44 49
50enum 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
45struct s3c24xx_spi { 57struct s3c24xx_spi {
46 /* bitbang has to be first */ 58 /* bitbang has to be first */
47 struct spi_bitbang bitbang; 59 struct spi_bitbang bitbang;
@@ -52,6 +64,11 @@ struct s3c24xx_spi {
52 int len; 64 int len;
53 int count; 65 int count;
54 66
67 struct fiq_handler fiq_handler;
68 enum spi_fiq_mode fiq_mode;
69 unsigned char fiq_inuse;
70 unsigned char fiq_claimed;
71
55 void (*set_cs)(struct s3c2410_spi_info *spi, 72 void (*set_cs)(struct s3c2410_spi_info *spi,
56 int cs, int pol); 73 int cs, int pol);
57 74
@@ -67,6 +84,7 @@ struct s3c24xx_spi {
67 struct s3c2410_spi_info *pdata; 84 struct s3c2410_spi_info *pdata;
68}; 85};
69 86
87
70#define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT) 88#define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT)
71#define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP) 89#define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP)
72 90
@@ -127,7 +145,7 @@ static int s3c24xx_spi_update_state(struct spi_device *spi,
127 } 145 }
128 146
129 if (spi->mode != cs->mode) { 147 if (spi->mode != cs->mode) {
130 u8 spcon = SPCON_DEFAULT; 148 u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
131 149
132 if (spi->mode & SPI_CPHA) 150 if (spi->mode & SPI_CPHA)
133 spcon |= S3C2410_SPCON_CPHA_FMTB; 151 spcon |= S3C2410_SPCON_CPHA_FMTB;
@@ -214,13 +232,196 @@ static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
214 return hw->tx ? hw->tx[count] : 0; 232 return hw->tx ? hw->tx[count] : 0;
215} 233}
216 234
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 */
250struct spi_fiq_code {
251 u32 length;
252 u32 ack_offset;
253 u8 data[0];
254};
255
256extern struct spi_fiq_code s3c24xx_spi_fiq_txrx;
257extern struct spi_fiq_code s3c24xx_spi_fiq_tx;
258extern 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 */
266static 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*/
282void 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 */
355static 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 */
382static 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 */
396static 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 */
408static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *spi)
409{
410 return spi->fiq_inuse;
411}
412#else
413
414static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *s) { }
415static inline void s3c24xx_spi_tryfiq(struct s3c24xx_spi *s) { }
416static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *s) { return false; }
417static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *s) { return false; }
418
419#endif /* CONFIG_SPI_S3C24XX_FIQ */
420
217static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t) 421static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
218{ 422{
219 struct s3c24xx_spi *hw = to_hw(spi); 423 struct s3c24xx_spi *hw = to_hw(spi);
220 424
221 dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
222 t->tx_buf, t->rx_buf, t->len);
223
224 hw->tx = t->tx_buf; 425 hw->tx = t->tx_buf;
225 hw->rx = t->rx_buf; 426 hw->rx = t->rx_buf;
226 hw->len = t->len; 427 hw->len = t->len;
@@ -228,11 +429,14 @@ static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
228 429
229 init_completion(&hw->done); 430 init_completion(&hw->done);
230 431
432 hw->fiq_inuse = 0;
433 if (s3c24xx_spi_usefiq(hw) && t->len >= 3)
434 s3c24xx_spi_tryfiq(hw);
435
231 /* send the first byte */ 436 /* send the first byte */
232 writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT); 437 writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
233 438
234 wait_for_completion(&hw->done); 439 wait_for_completion(&hw->done);
235
236 return hw->count; 440 return hw->count;
237} 441}
238 442
@@ -254,17 +458,27 @@ static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
254 goto irq_done; 458 goto irq_done;
255 } 459 }
256 460
257 hw->count++; 461 if (!s3c24xx_spi_usingfiq(hw)) {
462 hw->count++;
258 463
259 if (hw->rx) 464 if (hw->rx)
260 hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT); 465 hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
261 466
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);
263 479
264 if (count < hw->len)
265 writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
266 else
267 complete(&hw->done); 480 complete(&hw->done);
481 }
268 482
269 irq_done: 483 irq_done:
270 return IRQ_HANDLED; 484 return IRQ_HANDLED;
@@ -322,6 +536,10 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev)
322 platform_set_drvdata(pdev, hw); 536 platform_set_drvdata(pdev, hw);
323 init_completion(&hw->done); 537 init_completion(&hw->done);
324 538
539 /* initialise fiq handler */
540
541 s3c24xx_spi_initfiq(hw);
542
325 /* setup the master state. */ 543 /* setup the master state. */
326 544
327 /* the spi->mode bits understood by this driver: */ 545 /* the spi->mode bits understood by this driver: */