Author: manu
Date: Sun Jan 14 22:05:29 2018
New Revision: 327979
URL: https://svnweb.freebsd.org/changeset/base/327979

Log:
  allwinner: mmc: Multiple improvement
  
    - Add a per compatible configuration struct
    - Not all SoC uses the same size for DMA transfert, add this into the
      configuration data
    - Use new timing mode for some SoC (A64 mmc)
    - Auto calibrate clock for A64 mmc/emmc
    - A64 mmc controller need masking of data0
    - Add support for vmmc/vqmmc regulator
    - Add more capabilities, r/w speed is better for eMMC
    - MMC_CAP_SIGNALING_180 gives weird result so do not enable it for now.
    - Add new register documented in H3/A64 user manual
  
  Tested-On: Pine64-LTS (A64), eMMC still doesn't work
  Tested-On: A64-Olinuxino (A64), sd and eMMC are working
  Tested-On: NanoPi Neo Plus2 (H5), sd and eMMC are working
  Tested-On: OrangePi PC2 (H5), sd only (no eMMC)
  Tested-On: OrangePi One (H3), sd only (no eMMC)
  Tested-On: BananaPi M2 (A31s), sd only (no eMMC)

Modified:
  head/sys/arm/allwinner/aw_mmc.c
  head/sys/arm/allwinner/aw_mmc.h

Modified: head/sys/arm/allwinner/aw_mmc.c
==============================================================================
--- head/sys/arm/allwinner/aw_mmc.c     Sun Jan 14 21:21:10 2018        
(r327978)
+++ head/sys/arm/allwinner/aw_mmc.c     Sun Jan 14 22:05:29 2018        
(r327979)
@@ -50,22 +50,50 @@ __FBSDID("$FreeBSD$");
 #include <arm/allwinner/aw_mmc.h>
 #include <dev/extres/clk/clk.h>
 #include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
 
 #define        AW_MMC_MEMRES           0
 #define        AW_MMC_IRQRES           1
 #define        AW_MMC_RESSZ            2
 #define        AW_MMC_DMA_SEGS         ((MAXPHYS / PAGE_SIZE) + 1)
-#define        AW_MMC_DMA_MAX_SIZE     0x2000
 #define        AW_MMC_DMA_FTRGLEVEL    0x20070008
 #define        AW_MMC_RESET_RETRY      1000
 
 #define        CARD_ID_FREQUENCY       400000
 
+struct aw_mmc_conf {
+       uint32_t        dma_xferlen;
+       bool            mask_data0;
+       bool            can_calibrate;
+       bool            new_timing;
+};
+
+static const struct aw_mmc_conf a10_mmc_conf = {
+       .dma_xferlen = 0x2000,
+};
+
+static const struct aw_mmc_conf a13_mmc_conf = {
+       .dma_xferlen = 0x10000,
+};
+
+static const struct aw_mmc_conf a64_mmc_conf = {
+       .dma_xferlen = 0x10000,
+       .mask_data0 = true,
+       .can_calibrate = true,
+       .new_timing = true,
+};
+
+static const struct aw_mmc_conf a64_emmc_conf = {
+       .dma_xferlen = 0x2000,
+       .can_calibrate = true,
+};
+
 static struct ofw_compat_data compat_data[] = {
-       {"allwinner,sun4i-a10-mmc", 1},
-       {"allwinner,sun5i-a13-mmc", 1},
-       {"allwinner,sun7i-a20-mmc", 1},
-       {"allwinner,sun50i-a64-mmc", 1},
+       {"allwinner,sun4i-a10-mmc", (uintptr_t)&a10_mmc_conf},
+       {"allwinner,sun5i-a13-mmc", (uintptr_t)&a13_mmc_conf},
+       {"allwinner,sun7i-a20-mmc", (uintptr_t)&a13_mmc_conf},
+       {"allwinner,sun50i-a64-mmc", (uintptr_t)&a64_mmc_conf},
+       {"allwinner,sun50i-a64-emmc", (uintptr_t)&a64_emmc_conf},
        {NULL,             0}
 };
 
@@ -82,9 +110,13 @@ struct aw_mmc_softc {
        struct mmc_request *    aw_req;
        struct mtx              aw_mtx;
        struct resource *       aw_res[AW_MMC_RESSZ];
+       struct aw_mmc_conf *    aw_mmc_conf;
        uint32_t                aw_intr;
        uint32_t                aw_intr_wait;
        void *                  aw_intrhand;
+       int32_t                 aw_vdd;
+       regulator_t             aw_reg_vmmc;
+       regulator_t             aw_reg_vqmmc;
 
        /* Fields required for DMA access. */
        bus_addr_t              aw_dma_desc_phys;
@@ -151,6 +183,9 @@ aw_mmc_attach(device_t dev)
        node = ofw_bus_get_node(dev);
        sc = device_get_softc(dev);
        sc->aw_dev = dev;
+
+       sc->aw_mmc_conf = (struct aw_mmc_conf *)ofw_bus_search_compatible(dev, 
compat_data)->ocd_data;
+
        sc->aw_req = NULL;
        if (bus_alloc_resources(dev, aw_mmc_res_spec, sc->aw_res) != 0) {
                device_printf(dev, "cannot allocate device resources\n");
@@ -230,11 +265,22 @@ aw_mmc_attach(device_t dev)
        if (OF_getencprop(node, "bus-width", &bus_width, sizeof(uint32_t)) <= 0)
                bus_width = 4;
 
+       if (regulator_get_by_ofw_property(dev, 0, "vmmc-supply",
+           &sc->aw_reg_vmmc) == 0 && bootverbose)
+               device_printf(dev, "vmmc-supply regulator found\n");
+       if (regulator_get_by_ofw_property(dev, 0, "vqmmc-supply",
+           &sc->aw_reg_vqmmc) == 0 && bootverbose)
+               device_printf(dev, "vqmmc-supply regulator found\n");
+
        sc->aw_host.f_min = 400000;
        sc->aw_host.f_max = 52000000;
        sc->aw_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
-       sc->aw_host.mode = mode_sd;
-       sc->aw_host.caps = MMC_CAP_HSPEED;
+       sc->aw_host.caps = MMC_CAP_HSPEED | MMC_CAP_UHS_SDR12 |
+                          MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
+                          MMC_CAP_UHS_DDR50 | MMC_CAP_MMC_DDR52;
+
+       sc->aw_host.caps |= MMC_CAP_SIGNALING_330 /* | MMC_CAP_SIGNALING_180 */;
+
        if (bus_width >= 4)
                sc->aw_host.caps |= MMC_CAP_4_BIT_DATA;
        if (bus_width >= 8)
@@ -311,8 +357,8 @@ aw_mmc_setup_dma(struct aw_mmc_softc *sc)
        error = bus_dma_tag_create(bus_get_dma_tag(sc->aw_dev),
            AW_MMC_DMA_ALIGN, 0,
            BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
-           AW_MMC_DMA_MAX_SIZE * AW_MMC_DMA_SEGS, AW_MMC_DMA_SEGS,
-           AW_MMC_DMA_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL,
+           sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS, AW_MMC_DMA_SEGS,
+           sc->aw_mmc_conf->dma_xferlen, BUS_DMA_ALLOCNOW, NULL, NULL,
            &sc->aw_dma_buf_tag);
        if (error)
                return (error);
@@ -366,7 +412,7 @@ aw_mmc_prepare_dma(struct aw_mmc_softc *sc)
        uint32_t val;
 
        cmd = sc->aw_req->cmd;
-       if (cmd->data->len > AW_MMC_DMA_MAX_SIZE * AW_MMC_DMA_SEGS)
+       if (cmd->data->len > (sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS))
                return (EFBIG);
        error = bus_dmamap_load(sc->aw_dma_buf_tag, sc->aw_dma_buf_map,
            cmd->data->data, cmd->data->len, aw_dma_cb, sc, 0);
@@ -562,7 +608,8 @@ aw_mmc_intr(void *arg)
                goto end;
        }
        if (rint & AW_MMC_INT_ERR_BIT) {
-               device_printf(sc->aw_dev, "error rint: 0x%08X\n", rint);
+               if (bootverbose)
+                       device_printf(sc->aw_dev, "error rint: 0x%08X\n", rint);
                if (rint & AW_MMC_INT_RESP_TIMEOUT)
                        sc->aw_req->cmd->error = MMC_ERR_TIMEOUT;
                else
@@ -704,6 +751,9 @@ aw_mmc_read_ivar(device_t bus, device_t child, int whi
        case MMCBR_IVAR_CAPS:
                *(int *)result = sc->aw_host.caps;
                break;
+       case MMCBR_IVAR_TIMING:
+               *(int *)result = sc->aw_host.ios.timing;
+               break;
        case MMCBR_IVAR_MAX_DATA:
                *(int *)result = 65535;
                break;
@@ -746,6 +796,9 @@ aw_mmc_write_ivar(device_t bus, device_t child, int wh
        case MMCBR_IVAR_VDD:
                sc->aw_host.ios.vdd = value;
                break;
+       case MMCBR_IVAR_TIMING:
+               sc->aw_host.ios.timing = value;
+               break;
        /* These are read-only */
        case MMCBR_IVAR_CAPS:
        case MMCBR_IVAR_HOST_OCR:
@@ -761,42 +814,93 @@ aw_mmc_write_ivar(device_t bus, device_t child, int wh
 static int
 aw_mmc_update_clock(struct aw_mmc_softc *sc, uint32_t clkon)
 {
-       uint32_t cmdreg;
+       uint32_t reg;
        int retry;
-       uint32_t ckcr;
 
-       ckcr = AW_MMC_READ_4(sc, AW_MMC_CKCR);
-       ckcr &= ~(AW_MMC_CKCR_CCLK_ENB | AW_MMC_CKCR_CCLK_CTRL);
+       reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
+       reg &= ~(AW_MMC_CKCR_CCLK_ENB | AW_MMC_CKCR_CCLK_CTRL |
+           AW_MMC_CKCR_CCLK_MASK_DATA0);
 
        if (clkon)
-               ckcr |= AW_MMC_CKCR_CCLK_ENB;
+               reg |= AW_MMC_CKCR_CCLK_ENB;
+       if (sc->aw_mmc_conf->mask_data0)
+               reg |= AW_MMC_CKCR_CCLK_MASK_DATA0;
 
-       AW_MMC_WRITE_4(sc, AW_MMC_CKCR, ckcr);
+       AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg);
 
-       cmdreg = AW_MMC_CMDR_LOAD | AW_MMC_CMDR_PRG_CLK |
+       reg = AW_MMC_CMDR_LOAD | AW_MMC_CMDR_PRG_CLK |
            AW_MMC_CMDR_WAIT_PRE_OVER;
-       AW_MMC_WRITE_4(sc, AW_MMC_CMDR, cmdreg);
+       AW_MMC_WRITE_4(sc, AW_MMC_CMDR, reg);
        retry = 0xfffff;
-       while (--retry > 0) {
-               if ((AW_MMC_READ_4(sc, AW_MMC_CMDR) & AW_MMC_CMDR_LOAD) == 0) {
-                       AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff);
-                       return (0);
-               }
+
+       while (reg & AW_MMC_CMDR_LOAD && --retry > 0) {
+               reg = AW_MMC_READ_4(sc, AW_MMC_CMDR);
                DELAY(10);
        }
        AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff);
-       device_printf(sc->aw_dev, "timeout updating clock\n");
 
-       return (ETIMEDOUT);
+       if (reg & AW_MMC_CMDR_LOAD) {
+               device_printf(sc->aw_dev, "timeout updating clock\n");
+               return (ETIMEDOUT);
+       }
+
+       if (sc->aw_mmc_conf->mask_data0) {
+               reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
+               reg &= ~AW_MMC_CKCR_CCLK_MASK_DATA0;
+               AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg);
+       }
+
+       return (0);
 }
 
+static void
+aw_mmc_set_power(struct aw_mmc_softc *sc, int32_t vdd)
+{
+       int min_uvolt, max_uvolt;
+
+       sc->aw_vdd = vdd;
+
+       if (sc->aw_reg_vmmc == NULL && sc->aw_reg_vqmmc == NULL)
+               return;
+
+       switch (1 << vdd) {
+       case MMC_OCR_LOW_VOLTAGE:
+               min_uvolt = max_uvolt = 1800000;
+               break;
+       case MMC_OCR_320_330:
+               min_uvolt = 3200000;
+               max_uvolt = 3300000;
+               break;
+       case MMC_OCR_330_340:
+               min_uvolt = 3300000;
+               max_uvolt = 3400000;
+               break;
+       }
+
+       if (sc->aw_reg_vmmc)
+               if (regulator_set_voltage(sc->aw_reg_vmmc,
+                   min_uvolt, max_uvolt) != 0)
+                       device_printf(sc->aw_dev,
+                           "Cannot set vmmc to %d<->%d\n",
+                           min_uvolt,
+                           max_uvolt);
+       if (sc->aw_reg_vqmmc)
+               if (regulator_set_voltage(sc->aw_reg_vqmmc,
+                   min_uvolt, max_uvolt) != 0)
+                       device_printf(sc->aw_dev,
+                           "Cannot set vqmmc to %d<->%d\n",
+                           min_uvolt,
+                           max_uvolt);
+}
+
 static int
 aw_mmc_update_ios(device_t bus, device_t child)
 {
        int error;
        struct aw_mmc_softc *sc;
        struct mmc_ios *ios;
-       uint32_t ckcr;
+       unsigned int clock;
+       uint32_t reg, div = 1;
 
        sc = device_get_softc(bus);
 
@@ -815,27 +919,66 @@ aw_mmc_update_ios(device_t bus, device_t child)
                break;
        }
 
+       /* Set the voltage */
+       if (ios->power_mode == power_off) {
+               if (bootverbose)
+                       device_printf(sc->aw_dev, "Powering down sd/mmc\n");
+               if (sc->aw_reg_vmmc)
+                       regulator_disable(sc->aw_reg_vmmc);
+               if (sc->aw_reg_vqmmc)
+                       regulator_disable(sc->aw_reg_vqmmc);
+       } else if (sc->aw_vdd != ios->vdd)
+               aw_mmc_set_power(sc, ios->vdd);
+
+       /* Enable ddr mode if needed */
+       reg = AW_MMC_READ_4(sc, AW_MMC_GCTL);
+       if (ios->timing == bus_timing_uhs_ddr50 ||
+         ios->timing == bus_timing_mmc_ddr52)
+               reg |= AW_MMC_CTRL_DDR_MOD_SEL;
+       else
+               reg &= ~AW_MMC_CTRL_DDR_MOD_SEL;
+       AW_MMC_WRITE_4(sc, AW_MMC_GCTL, reg);
+
        if (ios->clock) {
+               clock = ios->clock;
 
                /* Disable clock */
                error = aw_mmc_update_clock(sc, 0);
                if (error != 0)
                        return (error);
 
+               if (ios->timing == bus_timing_mmc_ddr52 &&
+                   (sc->aw_mmc_conf->new_timing ||
+                   ios->bus_width == bus_width_8)) {
+                       div = 2;
+                       clock <<= 1;
+               }
+
                /* Reset the divider. */
-               ckcr = AW_MMC_READ_4(sc, AW_MMC_CKCR);
-               ckcr &= ~AW_MMC_CKCR_CCLK_DIV;
-               AW_MMC_WRITE_4(sc, AW_MMC_CKCR, ckcr);
+               reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
+               reg &= ~AW_MMC_CKCR_CCLK_DIV;
+               reg |= div - 1;
+               AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg);
 
+               /* New timing mode if needed */
+               if (sc->aw_mmc_conf->new_timing) {
+                       reg = AW_MMC_READ_4(sc, AW_MMC_NTSR);
+                       reg |= AW_MMC_NTSR_MODE_SELECT;
+                       AW_MMC_WRITE_4(sc, AW_MMC_NTSR, reg);
+               }
+
                /* Set the MMC clock. */
-               error = clk_set_freq(sc->aw_clk_mmc, ios->clock,
+               error = clk_set_freq(sc->aw_clk_mmc, clock,
                    CLK_SET_ROUND_DOWN);
                if (error != 0) {
                        device_printf(sc->aw_dev,
                            "failed to set frequency to %u Hz: %d\n",
-                           ios->clock, error);
+                           clock, error);
                        return (error);
                }
+
+               if (sc->aw_mmc_conf->can_calibrate)
+                       AW_MMC_WRITE_4(sc, AW_MMC_SAMP_DL, 
AW_MMC_SAMP_DL_SW_EN);
 
                /* Enable clock. */
                error = aw_mmc_update_clock(sc, 1);

Modified: head/sys/arm/allwinner/aw_mmc.h
==============================================================================
--- head/sys/arm/allwinner/aw_mmc.h     Sun Jan 14 21:21:10 2018        
(r327978)
+++ head/sys/arm/allwinner/aw_mmc.h     Sun Jan 14 22:05:29 2018        
(r327979)
@@ -47,14 +47,23 @@
 #define        AW_MMC_STAR             0x3C    /* Status Register */
 #define        AW_MMC_FWLR             0x40    /* FIFO Threshold Watermark 
Register */
 #define        AW_MMC_FUNS             0x44    /* Function Select Register */
-#define        AW_MMC_HWRST            0x78    /* Hardware reset (not 
documented) */
+#define        AW_MMC_CSDC             0x54    /* CRC status detect controler 
register (A64 smhc2 only) */
+#define        AW_MMC_A12A             0x58    /* Auto command 12 argument 
register */
+#define        AW_MMC_NTSR             0x5C    /* SD new timing register (H3, 
A64 smhc0/1 only) */
+#define        AW_MMC_HWRST            0x78    /* Hardware reset */
 #define        AW_MMC_DMAC             0x80    /* IDMAC Control Register */
 #define        AW_MMC_DLBA             0x84    /* IDMAC Desc List Base Address 
Reg */
 #define        AW_MMC_IDST             0x88    /* IDMAC Status Register */
 #define        AW_MMC_IDIE             0x8C    /* IDMAC Interrupt Enable 
Register */
-#define        AW_MMC_FIFO             0x100   /* FIFO Access Address 
(A10/A20) */
-#define        A31_MMC_FIFO            0x200   /* FIFO Access Address (A31) */
 
+#define        AW_MMC_DDR_SBIT_DET     0x10C   /* eMMC4.5 DDR Start Bit 
Detection control register */
+#define        AW_MMC_DRV_DL           0x140   /* Drive Delay control register 
*/
+#define        AW_MMC_SAMP_DL          0x144   /* Sample Delay controle 
register */
+#define        AW_MMC_DS_DL            0x148   /* Data strobe delay control 
register */
+
+#define        AW_MMC_FIFO             0x100   /* FIFO Access Address 
(A10/A20) */
+#define        A31_MMC_FIFO            0x200   /* FIFO Access Address (A31) */
+
 /* AW_MMC_GCTL */
 #define        AW_MMC_CTRL_SOFT_RST            (1U << 0)
 #define        AW_MMC_CTRL_FIFO_RST            (1U << 1)
@@ -70,6 +79,7 @@
 /* AW_MMC_CKCR */
 #define        AW_MMC_CKCR_CCLK_ENB            (1U << 16)
 #define        AW_MMC_CKCR_CCLK_CTRL           (1U << 17)
+#define        AW_MMC_CKCR_CCLK_MASK_DATA0     (1U << 31)
 #define        AW_MMC_CKCR_CCLK_DIV            0xff
 
 /* AW_MMC_TMOR */
@@ -153,6 +163,9 @@
 #define        AW_MMC_SEND_AUTOSTOP_CC_SD      (1U << 9)
 #define        AW_MMC_CE_ATA_DEV_INT_ENB       (1U << 10)
 
+/* AW_MMC_NTSR */
+#define        AW_MMC_NTSR_MODE_SELECT         (1U << 31)
+
 /* IDMA CONTROLLER BUS MOD BIT FIELD */
 #define        AW_MMC_DMAC_IDMAC_SOFT_RST      (1U << 0)
 #define        AW_MMC_DMAC_IDMAC_FIX_BURST     (1U << 1)
@@ -183,6 +196,12 @@
         AW_MMC_IDST_DES_UNAVL_INT | AW_MMC_IDST_ABN_INT_SUM)
 #define        AW_MMC_IDST_COMPLETE                            \
        (AW_MMC_IDST_TX_INT | AW_MMC_IDST_RX_INT)
+
+/* AW_MMC_DDR_SBIT_DET */
+#define        AW_MMC_DDR_SBIT_HS_MD_EN        (1U << 31)
+
+/* AW_MMC_SAMP */
+#define        AW_MMC_SAMP_DL_SW_EN            (1U << 7)
 
 /* The DMA descriptor table. */
 struct aw_mmc_dma_desc {
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to