mmc: sdhci: Add 64-bit ADMA support
authorAdrian Hunter <adrian.hunter@intel.com>
Tue, 4 Nov 2014 10:42:46 +0000 (12:42 +0200)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 10 Nov 2014 11:40:53 +0000 (12:40 +0100)
Add 64-bit ADMA support including:
- add 64-bit ADMA descriptor
- add SDHCI_USE_64_BIT_DMA flag
- set upper 32-bits of DMA addresses
- ability to select 64-bit ADMA
- ability to use 64-bit ADMA sizes and alignment
- display "ADMA 64-bit" when host is added

It is assumed that a 64-bit capable device has set a 64-bit DMA mask
and *must* do 64-bit DMA.  A driver has the opportunity to change
that during the first call to ->enable_dma().  Similarly
SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
implement.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
include/linux/mmc/sdhci.h

index ec093490a24f379caecb5b52ac74621097e355f8..f895ab07fcc2a43faaa8ac7aa841951e9b3fe17f 100644 (file)
@@ -117,10 +117,17 @@ static void sdhci_dumpregs(struct sdhci_host *host)
        pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
                sdhci_readw(host, SDHCI_HOST_CONTROL2));
 
-       if (host->flags & SDHCI_USE_ADMA)
-               pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
-                      readl(host->ioaddr + SDHCI_ADMA_ERROR),
-                      readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
+       if (host->flags & SDHCI_USE_ADMA) {
+               if (host->flags & SDHCI_USE_64_BIT_DMA)
+                       pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n",
+                                readl(host->ioaddr + SDHCI_ADMA_ERROR),
+                                readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI),
+                                readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
+               else
+                       pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
+                                readl(host->ioaddr + SDHCI_ADMA_ERROR),
+                                readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
+       }
 
        pr_debug(DRIVER_NAME ": ===========================================\n");
 }
@@ -446,19 +453,25 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
        local_irq_restore(*flags);
 }
 
-static void sdhci_adma_write_desc(void *desc, u32 addr, int len, unsigned cmd)
+static void sdhci_adma_write_desc(struct sdhci_host *host, void *desc,
+                                 dma_addr_t addr, int len, unsigned cmd)
 {
-       struct sdhci_adma2_32_desc *dma_desc = desc;
+       struct sdhci_adma2_64_desc *dma_desc = desc;
 
+       /* 32-bit and 64-bit descriptors have these members in same position */
        dma_desc->cmd = cpu_to_le16(cmd);
        dma_desc->len = cpu_to_le16(len);
-       dma_desc->addr = cpu_to_le32(addr);
+       dma_desc->addr_lo = cpu_to_le32((u32)addr);
+
+       if (host->flags & SDHCI_USE_64_BIT_DMA)
+               dma_desc->addr_hi = cpu_to_le32((u64)addr >> 32);
 }
 
 static void sdhci_adma_mark_end(void *desc)
 {
-       struct sdhci_adma2_32_desc *dma_desc = desc;
+       struct sdhci_adma2_64_desc *dma_desc = desc;
 
+       /* 32-bit and 64-bit descriptors have 'cmd' in same position */
        dma_desc->cmd |= cpu_to_le16(ADMA2_END);
 }
 
@@ -527,7 +540,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
                        }
 
                        /* tran, valid */
-                       sdhci_adma_write_desc(desc, align_addr, offset,
+                       sdhci_adma_write_desc(host, desc, align_addr, offset,
                                              ADMA2_TRAN_VALID);
 
                        BUG_ON(offset > 65536);
@@ -544,7 +557,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
                BUG_ON(len > 65536);
 
                /* tran, valid */
-               sdhci_adma_write_desc(desc, addr, len, ADMA2_TRAN_VALID);
+               sdhci_adma_write_desc(host, desc, addr, len, ADMA2_TRAN_VALID);
                desc += host->desc_sz;
 
                /*
@@ -568,7 +581,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
                */
 
                /* nop, end, valid */
-               sdhci_adma_write_desc(desc, 0, 0, ADMA2_NOP_END_VALID);
+               sdhci_adma_write_desc(host, desc, 0, 0, ADMA2_NOP_END_VALID);
        }
 
        /*
@@ -827,6 +840,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
                        } else {
                                sdhci_writel(host, host->adma_addr,
                                        SDHCI_ADMA_ADDRESS);
+                               if (host->flags & SDHCI_USE_64_BIT_DMA)
+                                       sdhci_writel(host,
+                                                    (u64)host->adma_addr >> 32,
+                                                    SDHCI_ADMA_ADDRESS_HI);
                        }
                } else {
                        int sg_cnt;
@@ -860,10 +877,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
                ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
                ctrl &= ~SDHCI_CTRL_DMA_MASK;
                if ((host->flags & SDHCI_REQ_USE_DMA) &&
-                       (host->flags & SDHCI_USE_ADMA))
-                       ctrl |= SDHCI_CTRL_ADMA32;
-               else
+                       (host->flags & SDHCI_USE_ADMA)) {
+                       if (host->flags & SDHCI_USE_64_BIT_DMA)
+                               ctrl |= SDHCI_CTRL_ADMA64;
+                       else
+                               ctrl |= SDHCI_CTRL_ADMA32;
+               } else {
                        ctrl |= SDHCI_CTRL_SDMA;
+               }
                sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
        }
 
@@ -2296,12 +2317,19 @@ static void sdhci_adma_show_error(struct sdhci_host *host)
        sdhci_dumpregs(host);
 
        while (true) {
-               struct sdhci_adma2_32_desc *dma_desc = desc;
-
-               DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n",
-                   name, desc, le32_to_cpu(dma_desc->addr),
-                   le16_to_cpu(dma_desc->len),
-                   le16_to_cpu(dma_desc->cmd));
+               struct sdhci_adma2_64_desc *dma_desc = desc;
+
+               if (host->flags & SDHCI_USE_64_BIT_DMA)
+                       DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n",
+                           name, desc, le32_to_cpu(dma_desc->addr_hi),
+                           le32_to_cpu(dma_desc->addr_lo),
+                           le16_to_cpu(dma_desc->len),
+                           le16_to_cpu(dma_desc->cmd));
+               else
+                       DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n",
+                           name, desc, le32_to_cpu(dma_desc->addr_lo),
+                           le16_to_cpu(dma_desc->len),
+                           le16_to_cpu(dma_desc->cmd));
 
                desc += host->desc_sz;
 
@@ -2852,6 +2880,16 @@ int sdhci_add_host(struct sdhci_host *host)
                host->flags &= ~SDHCI_USE_ADMA;
        }
 
+       /*
+        * It is assumed that a 64-bit capable device has set a 64-bit DMA mask
+        * and *must* do 64-bit DMA.  A driver has the opportunity to change
+        * that during the first call to ->enable_dma().  Similarly
+        * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
+        * implement.
+        */
+       if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT)
+               host->flags |= SDHCI_USE_64_BIT_DMA;
+
        if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
                if (host->ops->enable_dma) {
                        if (host->ops->enable_dma(host)) {
@@ -2863,6 +2901,10 @@ int sdhci_add_host(struct sdhci_host *host)
                }
        }
 
+       /* SDMA does not support 64-bit DMA */
+       if (host->flags & SDHCI_USE_64_BIT_DMA)
+               host->flags &= ~SDHCI_USE_SDMA;
+
        if (host->flags & SDHCI_USE_ADMA) {
                /*
                 * The DMA descriptor table size is calculated as the maximum
@@ -2870,13 +2912,23 @@ int sdhci_add_host(struct sdhci_host *host)
                 * descriptor for each segment, plus 1 for a nop end descriptor,
                 * all multipled by the descriptor size.
                 */
-               host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
-                                     SDHCI_ADMA2_32_DESC_SZ;
-               host->align_buffer_sz = SDHCI_MAX_SEGS *
-                                       SDHCI_ADMA2_32_ALIGN;
-               host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
-               host->align_sz = SDHCI_ADMA2_32_ALIGN;
-               host->align_mask = SDHCI_ADMA2_32_ALIGN - 1;
+               if (host->flags & SDHCI_USE_64_BIT_DMA) {
+                       host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
+                                             SDHCI_ADMA2_64_DESC_SZ;
+                       host->align_buffer_sz = SDHCI_MAX_SEGS *
+                                               SDHCI_ADMA2_64_ALIGN;
+                       host->desc_sz = SDHCI_ADMA2_64_DESC_SZ;
+                       host->align_sz = SDHCI_ADMA2_64_ALIGN;
+                       host->align_mask = SDHCI_ADMA2_64_ALIGN - 1;
+               } else {
+                       host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
+                                             SDHCI_ADMA2_32_DESC_SZ;
+                       host->align_buffer_sz = SDHCI_MAX_SEGS *
+                                               SDHCI_ADMA2_32_ALIGN;
+                       host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
+                       host->align_sz = SDHCI_ADMA2_32_ALIGN;
+                       host->align_mask = SDHCI_ADMA2_32_ALIGN - 1;
+               }
                host->adma_table = dma_alloc_coherent(mmc_dev(mmc),
                                                      host->adma_table_sz,
                                                      &host->adma_addr,
@@ -3289,7 +3341,8 @@ int sdhci_add_host(struct sdhci_host *host)
 
        pr_info("%s: SDHCI controller on %s [%s] using %s\n",
                mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
-               (host->flags & SDHCI_USE_ADMA) ? "ADMA" :
+               (host->flags & SDHCI_USE_ADMA) ?
+               (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" :
                (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
 
        sdhci_enable_card_detection(host);
index 14c8b6773dbb9bc108e1435729ef00e4620425fa..c2ec7fcd8a1f081227edc042253aa71b426860b2 100644 (file)
 /* 55-57 reserved */
 
 #define SDHCI_ADMA_ADDRESS     0x58
+#define SDHCI_ADMA_ADDRESS_HI  0x5C
 
 /* 60-FB reserved */
 
@@ -279,6 +280,23 @@ struct sdhci_adma2_32_desc {
        __le32  addr;
 }  __packed __aligned(SDHCI_ADMA2_32_ALIGN);
 
+/* ADMA2 64-bit DMA descriptor size */
+#define SDHCI_ADMA2_64_DESC_SZ 12
+
+/* ADMA2 64-bit DMA alignment */
+#define SDHCI_ADMA2_64_ALIGN   8
+
+/*
+ * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte
+ * aligned.
+ */
+struct sdhci_adma2_64_desc {
+       __le16  cmd;
+       __le16  len;
+       __le32  addr_lo;
+       __le32  addr_hi;
+}  __packed __aligned(4);
+
 #define ADMA2_TRAN_VALID       0x21
 #define ADMA2_NOP_END_VALID    0x3
 #define ADMA2_END              0x2
index 2a72e951083388eade31cb42aabeed9d743c24b8..931ac5e0545358bd2e5cfc9c06452cbcb4361df4 100644 (file)
@@ -100,6 +100,8 @@ struct sdhci_host {
 #define SDHCI_QUIRK2_BROKEN_DDR50                      (1<<7)
 /* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
 #define SDHCI_QUIRK2_STOP_WITH_TC                      (1<<8)
+/* Controller does not support 64-bit DMA */
+#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA                 (1<<9)
 
        int irq;                /* Device IRQ */
        void __iomem *ioaddr;   /* Mapped address */
@@ -130,6 +132,7 @@ struct sdhci_host {
 #define SDHCI_SDIO_IRQ_ENABLED (1<<9)  /* SDIO irq enabled */
 #define SDHCI_SDR104_NEEDS_TUNING (1<<10)      /* SDR104/HS200 needs tuning */
 #define SDHCI_USING_RETUNING_TIMER (1<<11)     /* Host is using a retuning timer for the card */
+#define SDHCI_USE_64_BIT_DMA   (1<<12) /* Use 64-bit DMA */
 
        unsigned int version;   /* SDHCI spec. version */