sdhci: use PIO when DMA can't satisfy the request

Some controllers have been designed on the assumption that all transfers
will be 32-bit aligned, both in start address and in size. This is not a
guarantee the SDHCI specification provides and not one we can provide.

Revert back to PIO for individual requests in order to work around the
hardware bug.

Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 758a741..a5300f2 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -43,6 +43,10 @@
 #define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS		(1<<4)
 /* Controller has an unusable DMA engine */
 #define SDHCI_QUIRK_BROKEN_DMA				(1<<5)
+/* Controller can only DMA from 32-bit aligned addresses */
+#define SDHCI_QUIRK_32BIT_DMA_ADDR			(1<<6)
+/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
+#define SDHCI_QUIRK_32BIT_DMA_SIZE			(1<<7)
 
 static const struct pci_device_id pci_ids[] __devinitdata = {
 	{
@@ -429,7 +433,29 @@
 
 	writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
 
-	if (host->flags & SDHCI_USE_DMA) {
+	if (host->flags & SDHCI_USE_DMA)
+		host->flags |= SDHCI_REQ_USE_DMA;
+
+	if (unlikely((host->flags & SDHCI_REQ_USE_DMA) &&
+		(host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) &&
+		((data->blksz * data->blocks) & 0x3))) {
+		DBG("Reverting to PIO because of transfer size (%d)\n",
+			data->blksz * data->blocks);
+		host->flags &= ~SDHCI_REQ_USE_DMA;
+	}
+
+	/*
+	 * The assumption here being that alignment is the same after
+	 * translation to device address space.
+	 */
+	if (unlikely((host->flags & SDHCI_REQ_USE_DMA) &&
+		(host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
+		(data->sg->offset & 0x3))) {
+		DBG("Reverting to PIO because of bad alignment\n");
+		host->flags &= ~SDHCI_REQ_USE_DMA;
+	}
+
+	if (host->flags & SDHCI_REQ_USE_DMA) {
 		int count;
 
 		count = pci_map_sg(host->chip->pdev, data->sg, data->sg_len,
@@ -466,7 +492,7 @@
 		mode |= SDHCI_TRNS_MULTI;
 	if (data->flags & MMC_DATA_READ)
 		mode |= SDHCI_TRNS_READ;
-	if (host->flags & SDHCI_USE_DMA)
+	if (host->flags & SDHCI_REQ_USE_DMA)
 		mode |= SDHCI_TRNS_DMA;
 
 	writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE);
@@ -482,7 +508,7 @@
 	data = host->data;
 	host->data = NULL;
 
-	if (host->flags & SDHCI_USE_DMA) {
+	if (host->flags & SDHCI_REQ_USE_DMA) {
 		pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len,
 			(data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE);
 	}