Commit c783837b authored by Nicolas Pitre's avatar Nicolas Pitre Committed by Pierre Ossman

pxamci: support arbitrary block size

The PXA has two transmit FIFOes, each32 byte deep.  when one FIFO is
full and the other one has been transmitted, they are automatically
swapped and DMA is triggered for another 32 byte burst.  However, when
there is less than 32 bytes left to send, the FIFO swap has to be done
manually. This is required for some SDIO transfers which are not
required to be multiples of 32 bytes.

A DMA completion interrupt is set for each descriptor which length isn't
a multiple of 32 in order to force the FIFO swap.  While at it, the DMA
interrupt handler has been made a bit more resilient against errors.
Signed-off-by: default avatarNicolas Pitre <nico@marvell.com>
Signed-off-by: default avatarPierre Ossman <drzeus@drzeus.cx>
parent 599473cf
...@@ -142,6 +142,10 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) ...@@ -142,6 +142,10 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
host->dma_dir); host->dma_dir);
for (i = 0; i < host->dma_len; i++) { for (i = 0; i < host->dma_len; i++) {
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;
if (data->flags & MMC_DATA_READ) { if (data->flags & MMC_DATA_READ) {
host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
...@@ -149,7 +153,6 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) ...@@ -149,7 +153,6 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]); host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]);
host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
} }
host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]);
host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
sizeof(struct pxa_dma_desc); sizeof(struct pxa_dma_desc);
} }
...@@ -414,8 +417,18 @@ static const struct mmc_host_ops pxamci_ops = { ...@@ -414,8 +417,18 @@ static const struct mmc_host_ops pxamci_ops = {
static void pxamci_dma_irq(int dma, void *devid) static void pxamci_dma_irq(int dma, void *devid)
{ {
printk(KERN_ERR "DMA%d: IRQ???\n", dma); struct pxamci_host *host = devid;
DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; int dcsr = DCSR(dma);
DCSR(dma) = dcsr & ~DCSR_STOPIRQEN;
if (dcsr & DCSR_ENDINTR) {
writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
} else {
printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
mmc_hostname(host->mmc), dma, dcsr);
host->data->error = -EIO;
pxamci_data_done(host, 0);
}
} }
static irqreturn_t pxamci_detect_irq(int irq, void *devid) static irqreturn_t pxamci_detect_irq(int irq, void *devid)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment