diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index a8e18fe..5bbae05 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -36,6 +36,10 @@ #include "pxamci.h" +/* FIXME: For debugging purposes */ +#undef cpu_is_pxa25x +#define cpu_is_pxa25x cpu_is_pxa27x + #define DRIVER_NAME "pxa2xx-mci" #define NR_SG 1 @@ -67,6 +71,10 @@ struct pxamci_host { unsigned int dma_dir; unsigned int dma_drcmrrx; unsigned int dma_drcmrtx; + + dma_addr_t bounce_dma; + void *bounce_cpu; + unsigned int offset; }; static void pxamci_stop_clock(struct pxamci_host *host) @@ -116,7 +124,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) unsigned int timeout; bool dalgn = 0; u32 dcmd; - int i; + int i, j; host->data = data; @@ -148,22 +156,56 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir); - for (i = 0; i < host->dma_len; i++) { + for (i = 0, j = 0; i < host->dma_len; i++, j++) { unsigned int length = sg_dma_len(&data->sg[i]); - host->sg_cpu[i].dcmd = dcmd | length; - if (length & 31 && !(data->flags & MMC_DATA_READ)) - host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN; + unsigned int addr = sg_dma_address(&data->sg[i]); + unsigned int offset = addr & 7; /* Not aligned to 8-byte boundary? */ - if (sg_dma_address(&data->sg[i]) & 0x7) - dalgn = 1; + if (offset) { + if (cpu_is_pxa21x() || cpu_is_pxa25x()) { + /* + * PXA25x doesn't support unaligned DMA + * at all, use a bounce buffer once for + * up to 7 unaligned bytes. + */ + BUG_ON(host->offset != 0); + offset = (8 - offset) & 7; + host->sg_cpu[j].dcmd = dcmd | offset; + if (length & 31 && !(data->flags & MMC_DATA_READ)) + host->sg_cpu[j].dcmd |= DCMD_ENDIRQEN; + if (data->flags & MMC_DATA_READ) { + host->sg_cpu[j].dsadr = + host->res->start + MMC_RXFIFO; + host->sg_cpu[j].dtadr = + host->bounce_dma; + } else { + memcpy(host->bounce_cpu, X, offset); + host->sg_cpu[j].dsadr = + host->bounce_dma; + host->sg_cpu[j].dtadr = + host->res->start + MMC_TXFIFO; + } + host->offset = offset; + host->dma_len++; + length -= offset; + addr += offset; + j++; + } else { + /* PXA27x, PXA3xx can handle unaligned DMA */ + dalgn = 1; + } + } + host->sg_cpu[j].dcmd = dcmd | length; + if (length & 31 && !(data->flags & MMC_DATA_READ)) + host->sg_cpu[j].dcmd |= DCMD_ENDIRQEN; if (data->flags & MMC_DATA_READ) { - host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; - host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); + host->sg_cpu[j].dsadr = host->res->start + MMC_RXFIFO; + host->sg_cpu[j].dtadr = addr; } else { - host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]); - host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; + host->sg_cpu[j].dsadr = addr; + host->sg_cpu[j].dtadr = host->res->start + MMC_TXFIFO; } - host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * + host->sg_cpu[j].ddadr = host->sg_dma + (j + 1) * sizeof(struct pxa_dma_desc); } host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP; @@ -174,10 +216,12 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) * unaligned (to 8-byte boundaries) data, so switch on byte alignment * mode only if we have unaligned data. */ - if (dalgn) - DALGN |= (1 << host->dma); - else - DALGN &= ~(1 << host->dma); + if (!cpu_is_pxa21x() && !cpu_is_pxa25x()) { + if (dalgn) + DALGN |= (1 << host->dma); + else + DALGN &= ~(1 << host->dma); + } DDADR(host->dma) = host->sg_dma; DCSR(host->dma) = DCSR_RUN; } @@ -281,6 +325,12 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) if (!data) return 0; + if ((cpu_is_pxa21x() || cpu_is_pxa25x()) + && host->offset && data->flags & MMC_DATA_READ) { + memcpy(XX, host->bounce_cpu, host->offset); + host->offset = 0; + } + DCSR(host->dma) = 0; dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, host->dma_dir); @@ -562,11 +612,20 @@ static int pxamci_probe(struct platform_device *pdev) MMC_CAP_SD_HIGHSPEED; } - host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); + host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, + &host->sg_dma, GFP_KERNEL); if (!host->sg_cpu) { ret = -ENOMEM; goto out; } + if (cpu_is_pxa25x()) { + host->bounce = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, + &host->aligned, GFP_KERNEL); + if (!host->bounce) { + ret = -ENOMEM; + goto out; + } + } spin_lock_init(&host->lock); host->res = r; @@ -629,7 +688,11 @@ static int pxamci_probe(struct platform_device *pdev) if (host->base) iounmap(host->base); if (host->sg_cpu) - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + dma_free_coherent(&pdev->dev, PAGE_SIZE, + host->sg_cpu, host->sg_dma); + if (cpu_is_pxa25x() && host->bounce_cpu) + dma_free_coherent(&pdev->dev, PAGE_SIZE, + host->bounce_cpu, host->bounce_dma); if (host->clk) clk_put(host->clk); } @@ -665,6 +728,9 @@ static int pxamci_remove(struct platform_device *pdev) pxa_free_dma(host->dma); iounmap(host->base); dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + if (cpu_is_pxa25x()) + dma_free_coherent(&pdev->dev, PAGE_SIZE, + host->bounce_cpu, host->bounce_dma); clk_put(host->clk);