aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi_s3c24xx.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-12-17 18:59:05 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-17 18:59:05 -0500
commitbf931a01a2c024a54204b4b02276af6e8d99a2c0 (patch)
treebd76e2f25430b1394c4dc54cd674bdc08b1bfaf2 /drivers/spi/spi_s3c24xx.c
parent4e46aa083853a84c770e00b03746bdeaf5c5aeff (diff)
parentd53342bf9616ecd7e4a03fece167d0f53b195e87 (diff)
Merge branch 'next-spi' of git://git.secretlab.ca/git/linux-2.6
* 'next-spi' of git://git.secretlab.ca/git/linux-2.6: spi: spi_txx9.c: use resource_size() spi: spi_sh_sci.c: use resource_size() spi: spi_mpc8xxx.c: use resource_size() spi: spi_bfin5xx.c: use resource_size() spi: atmel_spi.c: use resource_size() spi: Add s3c64xx SPI Controller driver atmel_spi: fix dma addr calculation for len > BUFFER_SIZE spi_s3c24xx: add FIQ pseudo-DMA support spi: controller driver for Designware SPI core spidev: add proper section markers spidev: use DECLARE_BITMAP instead of declaring the array
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 276591569c8b..c010733877ae 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: */