From 68c5ed592fdae16982ffe36aef89faba70a32cfc Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Thu, 1 Oct 2009 15:44:19 -0700 Subject: s3cmci: DMA fixes 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 Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/host/s3cmci.c | 62 +++++++++++++++++++++++++++++++++++------------ drivers/mmc/host/s3cmci.h | 3 --- 2 files changed, 47 insertions(+), 18 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 0adf31895f2..0af972275d4 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -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; @@ -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: @@ -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; } } @@ -1065,7 +1082,7 @@ 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); @@ -1073,7 +1090,7 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) 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; @@ -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])); @@ -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 @@ -1665,10 +1680,21 @@ 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"); @@ -1676,7 +1702,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev) 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); @@ -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); @@ -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); diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h index 62fac660230..c76b53dbeb6 100644 --- a/drivers/mmc/host/s3cmci.h +++ b/drivers/mmc/host/s3cmci.h @@ -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, -- cgit v1.2.3