diff options
author | David Brownell <david-b@pacbell.net> | 2007-07-17 07:04:07 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-17 13:23:05 -0400 |
commit | 8da0859a246838c81fe57d952b91d419e9c44179 (patch) | |
tree | ce91afbd74d63087b8fdd2527e018634045d5a22 /drivers | |
parent | 698ca47e8dba93f4b001b06b4c7037b09ac6eb09 (diff) |
atmel_spi: minor updates
Minor updates to atmel_spi:
- DMA:
* Comments to explain the DMA policies
* Report any mapping errors from spi_transfer()
* Remove extra loop for DMA mapping
- Diagnostics: report minimum clock rate, if we need to reject a
spi_setup() request because that rate is too low.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/spi/atmel_spi.c | 57 |
1 files changed, 45 insertions, 12 deletions
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 7524a84bdd50..01ad6320d96c 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c | |||
@@ -184,18 +184,39 @@ static void atmel_spi_next_message(struct spi_master *master) | |||
184 | atmel_spi_next_xfer(master, msg); | 184 | atmel_spi_next_xfer(master, msg); |
185 | } | 185 | } |
186 | 186 | ||
187 | static void | 187 | /* |
188 | * For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma: | ||
189 | * - The buffer is either valid for CPU access, else NULL | ||
190 | * - If the buffer is valid, so is its DMA addresss | ||
191 | * | ||
192 | * This driver manages the dma addresss unless message->is_dma_mapped. | ||
193 | */ | ||
194 | static int | ||
188 | atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer) | 195 | atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer) |
189 | { | 196 | { |
197 | struct device *dev = &as->pdev->dev; | ||
198 | |||
190 | xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS; | 199 | xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS; |
191 | if (xfer->tx_buf) | 200 | if (xfer->tx_buf) { |
192 | xfer->tx_dma = dma_map_single(&as->pdev->dev, | 201 | xfer->tx_dma = dma_map_single(dev, |
193 | (void *) xfer->tx_buf, xfer->len, | 202 | (void *) xfer->tx_buf, xfer->len, |
194 | DMA_TO_DEVICE); | 203 | DMA_TO_DEVICE); |
195 | if (xfer->rx_buf) | 204 | if (dma_mapping_error(xfer->tx_dma)) |
196 | xfer->rx_dma = dma_map_single(&as->pdev->dev, | 205 | return -ENOMEM; |
206 | } | ||
207 | if (xfer->rx_buf) { | ||
208 | xfer->rx_dma = dma_map_single(dev, | ||
197 | xfer->rx_buf, xfer->len, | 209 | xfer->rx_buf, xfer->len, |
198 | DMA_FROM_DEVICE); | 210 | DMA_FROM_DEVICE); |
211 | if (dma_mapping_error(xfer->tx_dma)) { | ||
212 | if (xfer->tx_buf) | ||
213 | dma_unmap_single(dev, | ||
214 | xfer->tx_dma, xfer->len, | ||
215 | DMA_TO_DEVICE); | ||
216 | return -ENOMEM; | ||
217 | } | ||
218 | } | ||
219 | return 0; | ||
199 | } | 220 | } |
200 | 221 | ||
201 | static void atmel_spi_dma_unmap_xfer(struct spi_master *master, | 222 | static void atmel_spi_dma_unmap_xfer(struct spi_master *master, |
@@ -398,8 +419,9 @@ static int atmel_spi_setup(struct spi_device *spi) | |||
398 | scbr = ((bus_hz + spi->max_speed_hz - 1) | 419 | scbr = ((bus_hz + spi->max_speed_hz - 1) |
399 | / spi->max_speed_hz); | 420 | / spi->max_speed_hz); |
400 | if (scbr >= (1 << SPI_SCBR_SIZE)) { | 421 | if (scbr >= (1 << SPI_SCBR_SIZE)) { |
401 | dev_dbg(&spi->dev, "setup: %d Hz too slow, scbr %u\n", | 422 | dev_dbg(&spi->dev, |
402 | spi->max_speed_hz, scbr); | 423 | "setup: %d Hz too slow, scbr %u; min %ld Hz\n", |
424 | spi->max_speed_hz, scbr, bus_hz/255); | ||
403 | return -EINVAL; | 425 | return -EINVAL; |
404 | } | 426 | } |
405 | } else | 427 | } else |
@@ -465,12 +487,19 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) | |||
465 | dev_dbg(&spi->dev, "no protocol options yet\n"); | 487 | dev_dbg(&spi->dev, "no protocol options yet\n"); |
466 | return -ENOPROTOOPT; | 488 | return -ENOPROTOOPT; |
467 | } | 489 | } |
468 | } | ||
469 | 490 | ||
470 | /* scrub dcache "early" */ | 491 | /* |
471 | if (!msg->is_dma_mapped) { | 492 | * DMA map early, for performance (empties dcache ASAP) and |
472 | list_for_each_entry(xfer, &msg->transfers, transfer_list) | 493 | * better fault reporting. This is a DMA-only driver. |
473 | atmel_spi_dma_map_xfer(as, xfer); | 494 | * |
495 | * NOTE that if dma_unmap_single() ever starts to do work on | ||
496 | * platforms supported by this driver, we would need to clean | ||
497 | * up mappings for previously-mapped transfers. | ||
498 | */ | ||
499 | if (!msg->is_dma_mapped) { | ||
500 | if (atmel_spi_dma_map_xfer(as, xfer) < 0) | ||
501 | return -ENOMEM; | ||
502 | } | ||
474 | } | 503 | } |
475 | 504 | ||
476 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | 505 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { |
@@ -537,6 +566,10 @@ static int __init atmel_spi_probe(struct platform_device *pdev) | |||
537 | 566 | ||
538 | as = spi_master_get_devdata(master); | 567 | as = spi_master_get_devdata(master); |
539 | 568 | ||
569 | /* | ||
570 | * Scratch buffer is used for throwaway rx and tx data. | ||
571 | * It's coherent to minimize dcache pollution. | ||
572 | */ | ||
540 | as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE, | 573 | as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE, |
541 | &as->buffer_dma, GFP_KERNEL); | 574 | &as->buffer_dma, GFP_KERNEL); |
542 | if (!as->buffer) | 575 | if (!as->buffer) |