Skip to content

Commit

Permalink
s3cmci: DMA fixes
Browse files Browse the repository at this point in the history
Fixes for the DMA transfer mode of the driver to try and improve the state
of the code:

- Ensure that dma_complete is set during the end of the command phase
  so that transfers do not stall awaiting the completion

- Update the DMA debugging to provide a bit more useful information
  such as how many DMA descriptors where not processed and print the
  DMA addresses in hexadecimal.

- Fix the DMA channel request code to actually request DMA for the
  S3CMCI block instead of whatever '0' signified.

- Add fallback to PIO if we cannot get the DMA channel, as many of the
  devices with this block only have a limited number of DMA channels.

- Only try and claim and free the DMA channel if we are trying to use it.

This improves the driver DMA code to the point where it can now identify a
card and read the partition table.  However the DMA can still stall when
trying to move data between the host and memory.

Signed-off-by: Ben Dooks <ben@simtec.co.uk>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Ben Dooks authored and Linus Torvalds committed Oct 1, 2009
1 parent 26f1494 commit 68c5ed5
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 18 deletions.
62 changes: 47 additions & 15 deletions drivers/mmc/host/s3cmci.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,21 @@ static inline bool s3cmci_host_usedma(struct s3cmci_host *host)
#endif
}

/**
* s3cmci_host_canpio - return true if host has pio code available
*
* Return true if the driver has been compiled with the PIO support code
* available.
*/
static inline bool s3cmci_host_canpio(void)
{
#ifdef CONFIG_MMC_S3C_PIO
return true;
#else
return false;
#endif
}

static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
{
u32 newmask;
Expand Down Expand Up @@ -786,6 +801,7 @@ static void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch,
dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
size, mci_dsta, mci_dcnt);

host->dma_complete = 1;
host->complete_what = COMPLETION_FINALIZE;

out:
Expand Down Expand Up @@ -816,7 +832,8 @@ static void finalize_request(struct s3cmci_host *host)
if (cmd->data && (cmd->error == 0) &&
(cmd->data->error == 0)) {
if (s3cmci_host_usedma(host) && (!host->dma_complete)) {
dbg(host, dbg_dma, "DMA Missing!\n");
dbg(host, dbg_dma, "DMA Missing (%d)!\n",
host->dma_complete);
return;
}
}
Expand Down Expand Up @@ -1065,15 +1082,15 @@ static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
{
int dma_len, i;
int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
int rw = data->flags & MMC_DATA_WRITE;

BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);

s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);

dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
(rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);

if (dma_len == 0)
return -ENOMEM;
Expand All @@ -1084,11 +1101,11 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
for (i = 0; i < dma_len; i++) {
int res;

dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
sg_dma_address(&data->sg[i]),
sg_dma_len(&data->sg[i]));
dbg(host, dbg_dma, "enqueue %i: %08x@%u\n", i,
sg_dma_address(&data->sg[i]),
sg_dma_len(&data->sg[i]));

res = s3c2410_dma_enqueue(host->dma, (void *) host,
res = s3c2410_dma_enqueue(host->dma, host,
sg_dma_address(&data->sg[i]),
sg_dma_len(&data->sg[i]));

Expand Down Expand Up @@ -1581,8 +1598,6 @@ static int __devinit s3cmci_probe(struct platform_device *pdev)
host->complete_what = COMPLETION_NONE;
host->pio_active = XFER_NONE;

host->dma = S3CMCI_DMA;

#ifdef CONFIG_MMC_S3C_PIODMA
host->dodma = host->pdata->dma;
#endif
Expand Down Expand Up @@ -1665,18 +1680,29 @@ static int __devinit s3cmci_probe(struct platform_device *pdev)
gpio_direction_input(host->pdata->gpio_wprotect);
}

if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL) < 0) {
dev_err(&pdev->dev, "unable to get DMA channel.\n");
ret = -EBUSY;
goto probe_free_gpio_wp;
/* depending on the dma state, get a dma channel to use. */

if (s3cmci_host_usedma(host)) {
host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,
host);
if (host->dma < 0) {
dev_err(&pdev->dev, "cannot get DMA channel.\n");
if (!s3cmci_host_canpio()) {
ret = -EBUSY;
goto probe_free_gpio_wp;
} else {
dev_warn(&pdev->dev, "falling back to PIO.\n");
host->dodma = 0;
}
}
}

host->clk = clk_get(&pdev->dev, "sdi");
if (IS_ERR(host->clk)) {
dev_err(&pdev->dev, "failed to find clock source.\n");
ret = PTR_ERR(host->clk);
host->clk = NULL;
goto probe_free_host;
goto probe_free_dma;
}

ret = clk_enable(host->clk);
Expand Down Expand Up @@ -1738,6 +1764,10 @@ static int __devinit s3cmci_probe(struct platform_device *pdev)
clk_free:
clk_put(host->clk);

probe_free_dma:
if (s3cmci_host_usedma(host))
s3c2410_dma_free(host->dma, &s3cmci_dma_client);

probe_free_gpio_wp:
if (host->pdata->gpio_wprotect)
gpio_free(host->pdata->gpio_wprotect);
Expand Down Expand Up @@ -1796,7 +1826,9 @@ static int __devexit s3cmci_remove(struct platform_device *pdev)
clk_put(host->clk);

tasklet_disable(&host->pio_tasklet);
s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);

if (s3cmci_host_usedma(host))
s3c2410_dma_free(host->dma, &s3cmci_dma_client);

free_irq(host->irq, host);

Expand Down
3 changes: 0 additions & 3 deletions drivers/mmc/host/s3cmci.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
* published by the Free Software Foundation.
*/

/* FIXME: DMA Resource management ?! */
#define S3CMCI_DMA 0

enum s3cmci_waitfor {
COMPLETION_NONE,
COMPLETION_FINALIZE,
Expand Down

0 comments on commit 68c5ed5

Please sign in to comment.