diff options
| author | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-01-11 14:35:53 -0500 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-02-04 08:25:49 -0500 |
| commit | c8ebae37034c0ead62eb4df8ef88e999ddb8d5cf (patch) | |
| tree | c9925f03a9c627d7408ef483d7920e25a927e633 | |
| parent | 51d4375dd72f352594f1a4f1d7598bf9a75b8dfe (diff) | |
ARM: mmci: add dmaengine-based DMA support
Based on a patch from Linus Walleij.
Add dmaengine based support for DMA to the MMCI driver, using the
Primecell DMA engine interface. The changes over Linus' driver are:
- rename txsize_threshold to dmasize_threshold, as this reflects the
purpose more.
- use 'mmci_dma_' as the function prefix rather than 'dma_mmci_'.
- clean up requesting of dma channels.
- don't release a single channel twice when it's shared between tx and rx.
- get rid of 'dma_enable' bool - instead check whether the channel is NULL.
- detect incomplete DMA at the end of a transfer. Some DMA controllers
(eg, PL08x) are unable to be configured for scatter DMA and also listen
to all four DMA request signals [BREQ,SREQ,LBREQ,LSREQ] from the MMCI.
They can do one or other but not both. As MMCI uses LBREQ/LSREQ for the
final burst/words, PL08x does not transfer the last few words.
- map and unmap DMA buffers using the DMA engine struct device, not the
MMCI struct device - the DMA engine is doing the DMA transfer, not us.
- avoid double-unmapping of the DMA buffers on MMCI data errors.
- don't check for negative values from the dmaengine tx submission
function - Dan says this must never fail.
- use new dmaengine helper functions rather than using the ugly function
pointers directly.
- allow DMA code to be fully optimized away using dma_inprogress() which
is defined to constant 0 if DMA engine support is disabled.
- request maximum segment size from the DMA engine struct device and
set this appropriately.
- removed checking of buffer alignment - the DMA engine should deal with
its own restrictions on buffer alignment, not the individual DMA engine
users.
- removed setting DMAREQCTL - this confuses some DMA controllers as it
causes LBREQ to be asserted for the last seven transfers, rather than
six SREQ and one LSREQ.
- removed burst setting - the DMA controller should not burst past the
transfer size required to complete the DMA operation.
Tested-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
| -rw-r--r-- | drivers/mmc/host/mmci.c | 282 | ||||
| -rw-r--r-- | drivers/mmc/host/mmci.h | 13 | ||||
| -rw-r--r-- | include/linux/amba/mmci.h | 17 |
3 files changed, 304 insertions, 8 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index db2a358143d..8a29c9f4a81 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | * linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver | 2 | * linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. | 4 | * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. |
| 5 | * Copyright (C) 2010 ST-Ericsson AB. | 5 | * Copyright (C) 2010 ST-Ericsson SA |
| 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 |
| 8 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
| @@ -25,8 +25,10 @@ | |||
| 25 | #include <linux/clk.h> | 25 | #include <linux/clk.h> |
| 26 | #include <linux/scatterlist.h> | 26 | #include <linux/scatterlist.h> |
| 27 | #include <linux/gpio.h> | 27 | #include <linux/gpio.h> |
| 28 | #include <linux/amba/mmci.h> | ||
| 29 | #include <linux/regulator/consumer.h> | 28 | #include <linux/regulator/consumer.h> |
| 29 | #include <linux/dmaengine.h> | ||
| 30 | #include <linux/dma-mapping.h> | ||
| 31 | #include <linux/amba/mmci.h> | ||
| 30 | 32 | ||
| 31 | #include <asm/div64.h> | 33 | #include <asm/div64.h> |
| 32 | #include <asm/io.h> | 34 | #include <asm/io.h> |
| @@ -186,6 +188,248 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) | |||
| 186 | sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); | 188 | sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); |
| 187 | } | 189 | } |
| 188 | 190 | ||
| 191 | /* | ||
| 192 | * All the DMA operation mode stuff goes inside this ifdef. | ||
| 193 | * This assumes that you have a generic DMA device interface, | ||
| 194 | * no custom DMA interfaces are supported. | ||
| 195 | */ | ||
| 196 | #ifdef CONFIG_DMA_ENGINE | ||
| 197 | static void __devinit mmci_dma_setup(struct mmci_host *host) | ||
| 198 | { | ||
| 199 | struct mmci_platform_data *plat = host->plat; | ||
| 200 | const char *rxname, *txname; | ||
| 201 | dma_cap_mask_t mask; | ||
| 202 | |||
| 203 | if (!plat || !plat->dma_filter) { | ||
| 204 | dev_info(mmc_dev(host->mmc), "no DMA platform data\n"); | ||
| 205 | return; | ||
| 206 | } | ||
| 207 | |||
| 208 | /* Try to acquire a generic DMA engine slave channel */ | ||
| 209 | dma_cap_zero(mask); | ||
| 210 | dma_cap_set(DMA_SLAVE, mask); | ||
| 211 | |||
| 212 | /* | ||
| 213 | * If only an RX channel is specified, the driver will | ||
| 214 | * attempt to use it bidirectionally, however if it is | ||
| 215 | * is specified but cannot be located, DMA will be disabled. | ||
| 216 | */ | ||
| 217 | if (plat->dma_rx_param) { | ||
| 218 | host->dma_rx_channel = dma_request_channel(mask, | ||
| 219 | plat->dma_filter, | ||
| 220 | plat->dma_rx_param); | ||
| 221 | /* E.g if no DMA hardware is present */ | ||
| 222 | if (!host->dma_rx_channel) | ||
| 223 | dev_err(mmc_dev(host->mmc), "no RX DMA channel\n"); | ||
| 224 | } | ||
| 225 | |||
| 226 | if (plat->dma_tx_param) { | ||
| 227 | host->dma_tx_channel = dma_request_channel(mask, | ||
| 228 | plat->dma_filter, | ||
| 229 | plat->dma_tx_param); | ||
| 230 | if (!host->dma_tx_channel) | ||
| 231 | dev_warn(mmc_dev(host->mmc), "no TX DMA channel\n"); | ||
| 232 | } else { | ||
| 233 | host->dma_tx_channel = host->dma_rx_channel; | ||
| 234 | } | ||
| 235 | |||
| 236 | if (host->dma_rx_channel) | ||
| 237 | rxname = dma_chan_name(host->dma_rx_channel); | ||
| 238 | else | ||
| 239 | rxname = "none"; | ||
| 240 | |||
| 241 | if (host->dma_tx_channel) | ||
| 242 | txname = dma_chan_name(host->dma_tx_channel); | ||
| 243 | else | ||
| 244 | txname = "none"; | ||
| 245 | |||
| 246 | dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n", | ||
| 247 | rxname, txname); | ||
| 248 | |||
| 249 | /* | ||
| 250 | * Limit the maximum segment size in any SG entry according to | ||
| 251 | * the parameters of the DMA engine device. | ||
| 252 | */ | ||
| 253 | if (host->dma_tx_channel) { | ||
| 254 | struct device *dev = host->dma_tx_channel->device->dev; | ||
| 255 | unsigned int max_seg_size = dma_get_max_seg_size(dev); | ||
| 256 | |||
| 257 | if (max_seg_size < host->mmc->max_seg_size) | ||
| 258 | host->mmc->max_seg_size = max_seg_size; | ||
| 259 | } | ||
| 260 | if (host->dma_rx_channel) { | ||
| 261 | struct device *dev = host->dma_rx_channel->device->dev; | ||
| 262 | unsigned int max_seg_size = dma_get_max_seg_size(dev); | ||
| 263 | |||
| 264 | if (max_seg_size < host->mmc->max_seg_size) | ||
| 265 | host->mmc->max_seg_size = max_seg_size; | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | /* | ||
| 270 | * This is used in __devinit or __devexit so inline it | ||
| 271 | * so it can be discarded. | ||
| 272 | */ | ||
| 273 | static inline void mmci_dma_release(struct mmci_host *host) | ||
| 274 | { | ||
| 275 | struct mmci_platform_data *plat = host->plat; | ||
| 276 | |||
| 277 | if (host->dma_rx_channel) | ||
| 278 | dma_release_channel(host->dma_rx_channel); | ||
| 279 | if (host->dma_tx_channel && plat->dma_tx_param) | ||
| 280 | dma_release_channel(host->dma_tx_channel); | ||
| 281 | host->dma_rx_channel = host->dma_tx_channel = NULL; | ||
| 282 | } | ||
| 283 | |||
| 284 | static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) | ||
| 285 | { | ||
| 286 | struct dma_chan *chan = host->dma_current; | ||
| 287 | enum dma_data_direction dir; | ||
| 288 | u32 status; | ||
| 289 | int i; | ||
| 290 | |||
| 291 | /* Wait up to 1ms for the DMA to complete */ | ||
| 292 | for (i = 0; ; i++) { | ||
| 293 | status = readl(host->base + MMCISTATUS); | ||
| 294 | if (!(status & MCI_RXDATAAVLBLMASK) || i >= 100) | ||
| 295 | break; | ||
| 296 | udelay(10); | ||
| 297 | } | ||
| 298 | |||
| 299 | /* | ||
| 300 | * Check to see whether we still have some data left in the FIFO - | ||
| 301 | * this catches DMA controllers which are unable to monitor the | ||
| 302 | * DMALBREQ and DMALSREQ signals while allowing us to DMA to non- | ||
| 303 | * contiguous buffers. On TX, we'll get a FIFO underrun error. | ||
| 304 | */ | ||
| 305 | if (status & MCI_RXDATAAVLBLMASK) { | ||
| 306 | dmaengine_terminate_all(chan); | ||
| 307 | if (!data->error) | ||
| 308 | data->error = -EIO; | ||
| 309 | } | ||
| 310 | |||
| 311 | if (data->flags & MMC_DATA_WRITE) { | ||
| 312 | dir = DMA_TO_DEVICE; | ||
| 313 | } else { | ||
| 314 | dir = DMA_FROM_DEVICE; | ||
| 315 | } | ||
| 316 | |||
| 317 | dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); | ||
| 318 | |||
| 319 | /* | ||
| 320 | * Use of DMA with scatter-gather is impossible. | ||
| 321 | * Give up with DMA and switch back to PIO mode. | ||
| 322 | */ | ||
| 323 | if (status & MCI_RXDATAAVLBLMASK) { | ||
| 324 | dev_err(mmc_dev(host->mmc), "buggy DMA detected. Taking evasive action.\n"); | ||
| 325 | mmci_dma_release(host); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | static void mmci_dma_data_error(struct mmci_host *host) | ||
| 330 | { | ||
| 331 | dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); | ||
| 332 | dmaengine_terminate_all(host->dma_current); | ||
| 333 | } | ||
| 334 | |||
| 335 | static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) | ||
| 336 | { | ||
| 337 | struct variant_data *variant = host->variant; | ||
| 338 | struct dma_slave_config conf = { | ||
| 339 | .src_addr = host->phybase + MMCIFIFO, | ||
| 340 | .dst_addr = host->phybase + MMCIFIFO, | ||
| 341 | .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, | ||
| 342 | .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, | ||
| 343 | .src_maxburst = variant->fifohalfsize >> 2, /* # of words */ | ||
| 344 | .dst_maxburst = variant->fifohalfsize >> 2, /* # of words */ | ||
| 345 | }; | ||
| 346 | struct mmc_data *data = host->data; | ||
| 347 | struct dma_chan *chan; | ||
| 348 | struct dma_device *device; | ||
| 349 | struct dma_async_tx_descriptor *desc; | ||
| 350 | int nr_sg; | ||
| 351 | |||
| 352 | host->dma_current = NULL; | ||
| 353 | |||
| 354 | if (data->flags & MMC_DATA_READ) { | ||
| 355 | conf.direction = DMA_FROM_DEVICE; | ||
| 356 | chan = host->dma_rx_channel; | ||
| 357 | } else { | ||
| 358 | conf.direction = DMA_TO_DEVICE; | ||
| 359 | chan = host->dma_tx_channel; | ||
| 360 | } | ||
| 361 | |||
| 362 | /* If there's no DMA channel, fall back to PIO */ | ||
