Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/sdmmc Add ADMA2 support, which enables scatter gathe...



details:   https://anonhg.NetBSD.org/src/rev/97d3856d0f26
branches:  trunk
changeset: 809724:97d3856d0f26
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Wed Jul 29 12:11:13 2015 +0000

description:
Add ADMA2 support, which enables scatter gather DMA for data transfers on
controllers that support it.

diffstat:

 sys/dev/sdmmc/sdhc.c    |  154 +++++++++++++++++++++++++++++++++++++++++++++--
 sys/dev/sdmmc/sdhcreg.h |   35 +++++++++-
 sys/dev/sdmmc/sdhcvar.h |    3 +-
 3 files changed, 180 insertions(+), 12 deletions(-)

diffs (truncated from 330 to 300 lines):

diff -r 9713c480c66e -r 97d3856d0f26 sys/dev/sdmmc/sdhc.c
--- a/sys/dev/sdmmc/sdhc.c      Wed Jul 29 12:05:10 2015 +0000
+++ b/sys/dev/sdmmc/sdhc.c      Wed Jul 29 12:11:13 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: sdhc.c,v 1.62 2015/07/28 07:14:48 skrll Exp $  */
+/*     $NetBSD: sdhc.c,v 1.63 2015/07/29 12:11:13 jmcneill Exp $       */
 /*     $OpenBSD: sdhc.c,v 1.25 2009/01/13 19:44:20 grange Exp $        */
 
 /*
@@ -23,7 +23,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.62 2015/07/28 07:14:48 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.63 2015/07/29 12:11:13 jmcneill Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_sdmmc.h"
@@ -86,6 +86,13 @@
 #define SHF_USE_4BIT_MODE      0x0002
 #define SHF_USE_8BIT_MODE      0x0004
 #define SHF_MODE_DMAEN         0x0008 /* needs SDHC_DMA_ENABLE in mode */
+#define SHF_USE_ADMA2_32       0x0010
+#define SHF_USE_ADMA2_64       0x0020
+#define SHF_USE_ADMA2_MASK     0x0030
+
+       bus_dmamap_t            adma_map;
+       bus_dma_segment_t       adma_segs[1];
+       void                    *adma2;
 };
 
 #define HDEVNAME(hp)   (device_xname((hp)->sc->sc_dev))
@@ -246,6 +253,7 @@
        struct sdhc_host *hp;
        uint32_t caps;
        uint16_t sdhcver;
+       int error;
 
        /* Allocate one more host structure. */
        hp = malloc(sizeof(struct sdhc_host), M_DEVBUF, M_WAITOK|M_ZERO);
@@ -321,11 +329,30 @@
            (ISSET(sc->sc_flags, SDHC_FLAG_USE_DMA &&
             ISSET(caps, SDHC_DMA_SUPPORT)))) {
                SET(hp->flags, SHF_USE_DMA);
-               if (!ISSET(sc->sc_flags, SDHC_FLAG_EXTERNAL_DMA) ||
-                   ISSET(sc->sc_flags, SDHC_FLAG_EXTDMA_DMAEN))
+
+               if (ISSET(sc->sc_flags, SDHC_FLAG_USE_ADMA2) &&
+                   ISSET(caps, SDHC_ADMA2_SUPP)) {
                        SET(hp->flags, SHF_MODE_DMAEN);
-
-               aprint_normal(", DMA");
+                       /*
+                        * 64-bit mode was present in the 2.00 spec, removed
+                        * from 3.00, and re-added in 4.00 with a different
+                        * descriptor layout. We only support 2.00 and 3.00
+                        * descriptors for now.
+                        */
+                       if (hp->specver == SDHC_SPEC_VERS_200 &&
+                           ISSET(caps, SDHC_64BIT_SYS_BUS)) {
+                               SET(hp->flags, SHF_USE_ADMA2_64);
+                               aprint_normal(", 64-bit ADMA2");
+                       } else {
+                               SET(hp->flags, SHF_USE_ADMA2_32);
+                               aprint_normal(", 32-bit ADMA2");
+                       }
+               } else {
+                       if (!ISSET(sc->sc_flags, SDHC_FLAG_EXTERNAL_DMA) ||
+                           ISSET(sc->sc_flags, SDHC_FLAG_EXTDMA_DMAEN))
+                               SET(hp->flags, SHF_MODE_DMAEN);
+                       aprint_normal(", SDMA");
+               }
        } else {
                aprint_normal(", PIO");
        }
@@ -417,6 +444,47 @@
        aprint_normal(", %u byte blocks", hp->maxblklen);
        aprint_normal("\n");
 
+       if (ISSET(hp->flags, SHF_USE_ADMA2_MASK)) {
+               int rseg;
+
+               /* Allocate ADMA2 descriptor memory */
+               error = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE,
+                   PAGE_SIZE, hp->adma_segs, 1, &rseg, BUS_DMA_WAITOK);
+               if (error) {
+                       aprint_error_dev(sc->sc_dev,
+                           "ADMA2 dmamem_alloc failed (%d)\n", error);
+                       goto adma_done;
+               }
+               error = bus_dmamem_map(sc->sc_dmat, hp->adma_segs, rseg,
+                   PAGE_SIZE, (void **)&hp->adma2, BUS_DMA_WAITOK);
+               if (error) {
+                       aprint_error_dev(sc->sc_dev,
+                           "ADMA2 dmamem_map failed (%d)\n", error);
+                       goto adma_done;
+               }
+               error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE,
+                   0, BUS_DMA_WAITOK, &hp->adma_map);
+               if (error) {
+                       aprint_error_dev(sc->sc_dev,
+                           "ADMA2 dmamap_create failed (%d)\n", error);
+                       goto adma_done;
+               }
+               error = bus_dmamap_load(sc->sc_dmat, hp->adma_map,
+                   hp->adma2, PAGE_SIZE, NULL,
+                   BUS_DMA_WAITOK|BUS_DMA_WRITE);
+               if (error) {
+                       aprint_error_dev(sc->sc_dev,
+                           "ADMA2 dmamap_load failed (%d)\n", error);
+                       goto adma_done;
+               }
+
+               memset(hp->adma2, 0, PAGE_SIZE);
+
+adma_done:
+               if (error)
+                       CLR(hp->flags, SHF_USE_ADMA2_MASK);
+       }
+
        /*
         * Attach the generic SD/MMC bus driver.  (The bus driver must
         * not invoke any chipset functions before it is attached.)
@@ -496,6 +564,12 @@
                        bus_space_unmap(hp->iot, hp->ioh, hp->ios);
                        hp->ios = 0;
                }
+               if (ISSET(hp->flags, SHF_USE_ADMA2_MASK)) {
+                       bus_dmamap_unload(sc->sc_dmat, hp->adma_map);
+                       bus_dmamap_destroy(sc->sc_dmat, hp->adma_map);
+                       bus_dmamem_unmap(sc->sc_dmat, hp->adma2, PAGE_SIZE);
+                       bus_dmamem_free(sc->sc_dmat, hp->adma_segs, 1);
+               }
                free(hp, M_DEVBUF);
                sc->sc_host[n] = NULL;
        }
@@ -1294,9 +1368,57 @@
        }
 
        /* Set DMA start address. */
-       if (ISSET(mode, SDHC_DMA_ENABLE) &&
-           !ISSET(sc->sc_flags, SDHC_FLAG_EXTERNAL_DMA))
+       if (ISSET(hp->flags, SHF_USE_ADMA2_MASK) && cmd->c_datalen > 0) {
+               for (int seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) {
+                       paddr_t paddr =
+                           cmd->c_dmamap->dm_segs[seg].ds_addr;
+                       uint16_t len =
+                           cmd->c_dmamap->dm_segs[seg].ds_len == 65536 ?
+                           0 : cmd->c_dmamap->dm_segs[seg].ds_len;
+                       uint16_t attr =
+                           SDHC_ADMA2_VALID | SDHC_ADMA2_ACT_TRANS;
+                       if (seg == cmd->c_dmamap->dm_nsegs - 1) {
+                               attr |= SDHC_ADMA2_END;
+                       }
+                       if (ISSET(hp->flags, SHF_USE_ADMA2_32)) {
+                               struct sdhc_adma2_descriptor32 *desc =
+                                   hp->adma2;
+                               desc[seg].attribute = htole16(attr);
+                               desc[seg].length = htole16(len);
+                               desc[seg].address = htole32(paddr);
+                       } else {
+                               struct sdhc_adma2_descriptor64 *desc =
+                                   hp->adma2;
+                               desc[seg].attribute = htole16(attr);
+                               desc[seg].length = htole16(len);
+                               desc[seg].address = htole32(paddr & 0xffffffff);
+                               desc[seg].address_hi = htole32(
+                                   (uint64_t)paddr >> 32);
+                       }
+               }
+               if (ISSET(hp->flags, SHF_USE_ADMA2_32)) {
+                       struct sdhc_adma2_descriptor32 *desc = hp->adma2;
+                       desc[cmd->c_dmamap->dm_nsegs].attribute = htole16(0);
+               } else {
+                       struct sdhc_adma2_descriptor64 *desc = hp->adma2;
+                       desc[cmd->c_dmamap->dm_nsegs].attribute = htole16(0);
+               }
+               bus_dmamap_sync(sc->sc_dmat, hp->adma_map, 0, PAGE_SIZE,
+                   BUS_DMASYNC_PREWRITE);
+               HCLR1(hp, SDHC_HOST_CTL, SDHC_DMA_SELECT);
+               HSET1(hp, SDHC_HOST_CTL, SDHC_DMA_SELECT_ADMA2);
+
+               const paddr_t desc_addr = hp->adma_map->dm_segs[0].ds_addr;
+
+               HWRITE4(hp, SDHC_ADMA_SYSTEM_ADDR, desc_addr & 0xffffffff);
+               if (ISSET(hp->flags, SHF_USE_ADMA2_64)) {
+                       HWRITE4(hp, SDHC_ADMA_SYSTEM_ADDR + 4,
+                           (uint64_t)desc_addr >> 32);
+               }
+       } else if (ISSET(mode, SDHC_DMA_ENABLE) &&
+           !ISSET(sc->sc_flags, SDHC_FLAG_EXTERNAL_DMA)) {
                HWRITE4(hp, SDHC_DMA_ADDR, cmd->c_dmamap->dm_segs[0].ds_addr);
+       }
 
        /*
         * Start a CPU data transfer.  Writing to the high order byte
@@ -1386,6 +1508,11 @@
                        error = ETIMEDOUT;
                        break;
                }
+
+               if (ISSET(hp->flags, SHF_USE_ADMA2_MASK)) {
+                       continue;
+               }
+
                if ((status & SDHC_DMA_INTERRUPT) == 0) {
                        continue;
                }
@@ -1410,6 +1537,11 @@
                KASSERT(seg < cmd->c_dmamap->dm_nsegs);
        }
 
+       if (ISSET(hp->flags, SHF_USE_ADMA2_MASK)) {
+               bus_dmamap_sync(hp->sc->sc_dmat, hp->adma_map, 0,
+                   PAGE_SIZE, BUS_DMASYNC_POSTWRITE);
+       }
+
        return error;
 }
 
@@ -1769,6 +1901,12 @@
                /* Claim this interrupt. */
                done = 1;
 
+               if (ISSET(error, SDHC_ADMA_ERROR)) {
+                       uint8_t adma_err = HREAD1(hp, SDHC_ADMA_ERROR_STATUS);
+                       printf("%s: ADMA error, status %02x\n", HDEVNAME(hp),
+                           adma_err);
+               }
+
                /*
                 * Service error interrupts.
                 */
diff -r 9713c480c66e -r 97d3856d0f26 sys/dev/sdmmc/sdhcreg.h
--- a/sys/dev/sdmmc/sdhcreg.h   Wed Jul 29 12:05:10 2015 +0000
+++ b/sys/dev/sdmmc/sdhcreg.h   Wed Jul 29 12:11:13 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: sdhcreg.h,v 1.13 2015/05/02 12:10:24 jmcneill Exp $    */
+/*     $NetBSD: sdhcreg.h,v 1.14 2015/07/29 12:11:14 jmcneill Exp $    */
 /*     $OpenBSD: sdhcreg.h,v 1.4 2006/07/30 17:20:40 fgsch Exp $       */
 
 /*
@@ -75,6 +75,9 @@
 #define  SDHC_CMD_INHIBIT_MASK         0x0003
 #define SDHC_HOST_CTL                  0x28
 #define  SDHC_8BIT_MODE                        (1<<5)
+#define  SDHC_DMA_SELECT               (3<<3)
+#define  SDHC_DMA_SELECT_SDMA          (0<<3)
+#define  SDHC_DMA_SELECT_ADMA2         (2<<3)
 #define  SDHC_HIGH_SPEED               (1<<2)
 #define  SDHC_ESDHC_8BIT_MODE          (1<<2)  /* eSDHC */
 #define  SDHC_4BIT_MODE                        (1<<1)
@@ -121,6 +124,7 @@
 #define  SDHC_NINTR_STATUS_MASK                0x81ff
 #define SDHC_EINTR_STATUS              0x32
 #define  SDHC_DMA_ERROR                        (1<<12)
+#define  SDHC_ADMA_ERROR               (1<<9)
 #define  SDHC_AUTO_CMD12_ERROR         (1<<8)
 #define  SDHC_CURRENT_LIMIT_ERROR      (1<<7)
 #define  SDHC_DATA_END_BIT_ERROR       (1<<6)
@@ -130,13 +134,13 @@
 #define  SDHC_CMD_END_BIT_ERROR                (1<<2)
 #define  SDHC_CMD_CRC_ERROR            (1<<1)
 #define  SDHC_CMD_TIMEOUT_ERROR                (1<<0)
-#define  SDHC_EINTR_STATUS_MASK                0x01ff  /* excluding vendor signals */
+#define  SDHC_EINTR_STATUS_MASK                0x03ff  /* excluding vendor signals */
 #define SDHC_NINTR_STATUS_EN           0x34
 #define SDHC_EINTR_STATUS_EN           0x36
 #define SDHC_NINTR_SIGNAL_EN           0x38
 #define  SDHC_NINTR_SIGNAL_MASK                0x01ff
 #define SDHC_EINTR_SIGNAL_EN           0x3a
-#define  SDHC_EINTR_SIGNAL_MASK                0x01ff  /* excluding vendor signals */
+#define  SDHC_EINTR_SIGNAL_MASK                0x03ff  /* excluding vendor signals */
 #define SDHC_CMD12_ERROR_STATUS                0x3c
 #define SDHC_CAPABILITIES              0x40
 #define  SDHC_SHARED_BUS_SLOT          (1<<31)
@@ -163,6 +167,10 @@
 #define  SDHC_TIMEOUT_FREQ_UNIT                (1<<7)  /* 0=KHz, 1=MHz */
 #define  SDHC_TIMEOUT_FREQ_SHIFT       0
 #define  SDHC_TIMEOUT_FREQ_MASK                0x1f
+#define SDHC_ADMA_ERROR_STATUS         0x54
+#define  SDHC_ADMA_LENGTH_MISMATCH     (1<<2)
+#define  SDHC_ADMA_ERROR_STATE         (3<<0)
+#define SDHC_ADMA_SYSTEM_ADDR          0x58
 #define SDHC_WATERMARK_LEVEL           0x44    /* ESDHC */
 #define  SDHC_WATERMARK_WRITE_SHIFT    16
 #define  SDHC_WATERMARK_WRITE_MASK     0xff
@@ -214,4 +222,25 @@
 #define SDHC_CAPABILITIES_BITS                                         \
        "\20\33Vdd1.8V\32Vdd3.0V\31Vdd3.3V\30SUSPEND\27DMA\26HIGHSPEED"
 
+#define SDHC_ADMA2_VALID       (1<<0)
+#define SDHC_ADMA2_END         (1<<1)
+#define SDHC_ADMA2_INT         (1<<2)
+#define SDHC_ADMA2_ACT         (3<<4)
+#define SDHC_ADMA2_ACT_NOP     (0<<4)
+#define SDHC_ADMA2_ACT_TRANS   (2<<4)
+#define SDHC_ADMA2_ACT_LINK    (3<<4)
+
+struct sdhc_adma2_descriptor32 {



Home | Main Index | Thread Index | Old Index