Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=c9fddbc4f844f5a16b5957c61fe2cfcb5c12f990
Commit:     c9fddbc4f844f5a16b5957c61fe2cfcb5c12f990
Parent:     c6573c94670882079174e2ea0da4abf1a0da51fe
Author:     Pierre Ossman <[EMAIL PROTECTED]>
AuthorDate: Sun Dec 2 19:52:11 2007 +0100
Committer:  Pierre Ossman <[EMAIL PROTECTED]>
CommitDate: Wed Dec 12 20:01:00 2007 +0100

    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 <[EMAIL PROTECTED]>
---
 drivers/mmc/host/sdhci.c |   32 +++++++++++++++++++++++++++++---
 drivers/mmc/host/sdhci.h |    3 ++-
 2 files changed, 31 insertions(+), 4 deletions(-)

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 @@ static unsigned int debug_quirks = 0;
 #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 @@ static void sdhci_prepare_data(struct sdhci_host *host, 
struct mmc_data *data)
 
        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 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
                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 @@ static void sdhci_finish_data(struct sdhci_host *host)
        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);
        }
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 05195ea..e4d77b0 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -171,7 +171,8 @@ struct sdhci_host {
        spinlock_t              lock;           /* Mutex */
 
        int                     flags;          /* Host attributes */
-#define SDHCI_USE_DMA          (1<<0)
+#define SDHCI_USE_DMA          (1<<0)          /* Host is DMA capable */
+#define SDHCI_REQ_USE_DMA      (1<<1)          /* Use DMA for this req. */
 
        unsigned int            max_clk;        /* Max possible freq (MHz) */
        unsigned int            timeout_clk;    /* Timeout freq (KHz) */
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to