Author: br
Date: Tue Oct  7 17:39:30 2014
New Revision: 272712
URL: https://svnweb.freebsd.org/changeset/base/272712

Log:
  Add driver for Synopsys DesignWare Mobile Storage Host Controller.
  
  Sponsored by: DARPA, AFRL

Added:
  head/sys/dev/mmc/host/
  head/sys/dev/mmc/host/dwmmc.c   (contents, props changed)
  head/sys/dev/mmc/host/dwmmc.h   (contents, props changed)
Modified:
  head/sys/arm/altera/socfpga/files.socfpga
  head/sys/arm/altera/socfpga/socfpga_machdep.c
  head/sys/arm/conf/EXYNOS5.common
  head/sys/arm/conf/SOCKIT
  head/sys/arm/samsung/exynos/exynos5_machdep.c
  head/sys/arm/samsung/exynos/files.exynos5
  head/sys/boot/fdt/dts/arm/exynos5420-arndale-octa.dts
  head/sys/boot/fdt/dts/arm/exynos5420-peach-pit.dts
  head/sys/boot/fdt/dts/arm/exynos5420.dtsi
  head/sys/boot/fdt/dts/arm/socfpga-sockit.dts
  head/sys/boot/fdt/dts/arm/socfpga.dtsi
  head/sys/dev/mmc/mmc.c

Modified: head/sys/arm/altera/socfpga/files.socfpga
==============================================================================
--- head/sys/arm/altera/socfpga/files.socfpga   Tue Oct  7 17:23:11 2014        
(r272711)
+++ head/sys/arm/altera/socfpga/files.socfpga   Tue Oct  7 17:39:30 2014        
(r272712)
@@ -19,3 +19,4 @@ arm/altera/socfpga/socfpga_manager.c          st
 arm/altera/socfpga/socfpga_rstmgr.c            standard
 
 dev/dwc/if_dwc.c                               optional dwc
+dev/mmc/host/dwmmc.c                           optional dwmmc

Modified: head/sys/arm/altera/socfpga/socfpga_machdep.c
==============================================================================
--- head/sys/arm/altera/socfpga/socfpga_machdep.c       Tue Oct  7 17:23:11 
2014        (r272711)
+++ head/sys/arm/altera/socfpga/socfpga_machdep.c       Tue Oct  7 17:39:30 
2014        (r272712)
@@ -89,6 +89,9 @@ platform_devmap_init(void)
         */
        arm_devmap_add_entry(0xffb00000, 0x100000);
 
+       /* dwmmc */
+       arm_devmap_add_entry(0xff700000, 0x100000);
+
        return (0);
 }
 

Modified: head/sys/arm/conf/EXYNOS5.common
==============================================================================
--- head/sys/arm/conf/EXYNOS5.common    Tue Oct  7 17:23:11 2014        
(r272711)
+++ head/sys/arm/conf/EXYNOS5.common    Tue Oct  7 17:39:30 2014        
(r272712)
@@ -80,7 +80,7 @@ options       NFS_ROOT                # NFS usable as /, re
 
 device         mmc                     # mmc/sd bus
 device         mmcsd                   # mmc/sd flash cards
-device         sdhci                   # generic sdhci
+device         dwmmc
 
 options        ROOTDEVNAME=\"ufs:/dev/da0\"
 

Modified: head/sys/arm/conf/SOCKIT
==============================================================================
--- head/sys/arm/conf/SOCKIT    Tue Oct  7 17:23:11 2014        (r272711)
+++ head/sys/arm/conf/SOCKIT    Tue Oct  7 17:39:30 2014        (r272712)
@@ -82,7 +82,7 @@ options       NFS_ROOT                # NFS usable as /, re
 
 device         mmc                     # mmc/sd bus
 device         mmcsd                   # mmc/sd flash cards
-device         sdhci                   # generic sdhci
+device         dwmmc
 
 options        ROOTDEVNAME=\"ufs:/dev/da0\"
 

Modified: head/sys/arm/samsung/exynos/exynos5_machdep.c
==============================================================================
--- head/sys/arm/samsung/exynos/exynos5_machdep.c       Tue Oct  7 17:23:11 
2014        (r272711)
+++ head/sys/arm/samsung/exynos/exynos5_machdep.c       Tue Oct  7 17:39:30 
2014        (r272712)
@@ -78,6 +78,9 @@ platform_devmap_init(void)
        /* UART */
        arm_devmap_add_entry(0x12C00000, 0x100000);
 
+       /* DWMMC */
+       arm_devmap_add_entry(0x12200000, 0x100000);
+
        return (0);
 }
 

Modified: head/sys/arm/samsung/exynos/files.exynos5
==============================================================================
--- head/sys/arm/samsung/exynos/files.exynos5   Tue Oct  7 17:23:11 2014        
(r272711)
+++ head/sys/arm/samsung/exynos/files.exynos5   Tue Oct  7 17:39:30 2014        
(r272712)
@@ -33,4 +33,4 @@ arm/samsung/exynos/chrome_ec.c                        optiona
 arm/samsung/exynos/chrome_ec_spi.c             optional        chrome_ec_spi
 arm/samsung/exynos/chrome_kb.c                 optional        chrome_kb
 
-#dev/sdhci/sdhci_fdt.c                         optional        sdhci
+dev/mmc/host/dwmmc.c                           optional        dwmmc

Modified: head/sys/boot/fdt/dts/arm/exynos5420-arndale-octa.dts
==============================================================================
--- head/sys/boot/fdt/dts/arm/exynos5420-arndale-octa.dts       Tue Oct  7 
17:23:11 2014        (r272711)
+++ head/sys/boot/fdt/dts/arm/exynos5420-arndale-octa.dts       Tue Oct  7 
17:39:30 2014        (r272712)
@@ -47,8 +47,19 @@
                        status = "okay";
                };
 
-               sdhci@12220000 {
-                       status = "disabled";
+               mmc2: dwmmc@12220000 {
+                       status = "okay";
+                       num-slots = <1>;
+                       supports-highspeed;
+                       samsung,dw-mshc-ciu-div = <3>;
+                       samsung,dw-mshc-sdr-timing = <2 3>;
+                       samsung,dw-mshc-ddr-timing = <1 2>;
+                       bus-frequency = <50000000>;
+
+                       slot@0 {
+                               reg = <0>;
+                               bus-width = <4>;
+                       };
                };
        };
 

Modified: head/sys/boot/fdt/dts/arm/exynos5420-peach-pit.dts
==============================================================================
--- head/sys/boot/fdt/dts/arm/exynos5420-peach-pit.dts  Tue Oct  7 17:23:11 
2014        (r272711)
+++ head/sys/boot/fdt/dts/arm/exynos5420-peach-pit.dts  Tue Oct  7 17:39:30 
2014        (r272712)
@@ -68,5 +68,20 @@
                usbdrd_phy1: phy@12500000 {
                        vbus-supply = < 218 >;
                };
+
+               mmc2: dwmmc@12220000 {
+                       status = "okay";
+                       num-slots = <1>;
+                       supports-highspeed;
+                       samsung,dw-mshc-ciu-div = <3>;
+                       samsung,dw-mshc-sdr-timing = <2 3>;
+                       samsung,dw-mshc-ddr-timing = <1 2>;
+                       bus-frequency = <50000000>;
+
+                       slot@0 {
+                               reg = <0>;
+                               bus-width = <4>;
+                       };
+               };
        };
 };

Modified: head/sys/boot/fdt/dts/arm/exynos5420.dtsi
==============================================================================
--- head/sys/boot/fdt/dts/arm/exynos5420.dtsi   Tue Oct  7 17:23:11 2014        
(r272711)
+++ head/sys/boot/fdt/dts/arm/exynos5420.dtsi   Tue Oct  7 17:39:30 2014        
(r272712)
@@ -81,5 +81,32 @@
                xhci@12400000 {
                        status = "okay";
                };
+
+               mmc0: dwmmc@12200000 {
+                       compatible = "samsung,exynos5420-dw-mshc-smu";
+                       reg = <0x12200000 0x10000>;
+                       interrupts = <107>;
+                       interrupt-parent = <&GIC>;
+                       fifo-depth = <0x40>;
+                       status = "disabled";
+               };
+
+               mmc1: dwmmc@12210000 {
+                       compatible = "samsung,exynos5420-dw-mshc-smu";
+                       reg = <0x12210000 0x10000>;
+                       interrupts = <108>;
+                       interrupt-parent = <&GIC>;
+                       fifo-depth = <0x40>;
+                       status = "disabled";
+               };
+
+               mmc2: dwmmc@12220000 {
+                       compatible = "samsung,exynos5420-dw-mshc";
+                       reg = <0x12220000 0x10000>;
+                       interrupts = <109>;
+                       interrupt-parent = <&GIC>;
+                       fifo-depth = <0x40>;
+                       status = "disabled";
+               };
        };
 };

Modified: head/sys/boot/fdt/dts/arm/socfpga-sockit.dts
==============================================================================
--- head/sys/boot/fdt/dts/arm/socfpga-sockit.dts        Tue Oct  7 17:23:11 
2014        (r272711)
+++ head/sys/boot/fdt/dts/arm/socfpga-sockit.dts        Tue Oct  7 17:39:30 
2014        (r272712)
@@ -55,6 +55,19 @@
                gmac1: ethernet@ff702000 {
                        status = "okay";
                };
+
+               mmc: dwmmc@ff704000 {
+                       status = "okay";
+                       num-slots = <1>;
+                       supports-highspeed;
+                       broken-cd;
+                       bus-frequency = <25000000>;
+
+                       slot@0 {
+                               reg = <0>;
+                               bus-width = <4>;
+                       };      
+               };
        };
 
        chosen {

Modified: head/sys/boot/fdt/dts/arm/socfpga.dtsi
==============================================================================
--- head/sys/boot/fdt/dts/arm/socfpga.dtsi      Tue Oct  7 17:23:11 2014        
(r272711)
+++ head/sys/boot/fdt/dts/arm/socfpga.dtsi      Tue Oct  7 17:39:30 2014        
(r272712)
@@ -152,5 +152,14 @@
                        phy-mode = "rgmii";
                        status = "disabled";
                };
+
+               mmc: dwmmc@ff704000 {
+                       compatible = "altr,socfpga-dw-mshc";
+                       reg = <0xff704000 0x1000>;
+                       interrupts = <171>;
+                       interrupt-parent = <&GIC>;
+                       fifo-depth = <0x400>;
+                       status = "disabled";
+               };
        };
 };

Added: head/sys/dev/mmc/host/dwmmc.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/mmc/host/dwmmc.c       Tue Oct  7 17:39:30 2014        
(r272712)
@@ -0,0 +1,1103 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <b...@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Synopsys DesignWare Mobile Storage Host Controller
+ * Chapter 14, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/mmc/host/dwmmc.h>
+
+#include "mmcbr_if.h"
+
+#define dprintf(x, arg...)
+
+#define        READ4(_sc, _reg) \
+       bus_read_4((_sc)->res[0], _reg)
+#define        WRITE4(_sc, _reg, _val) \
+       bus_write_4((_sc)->res[0], _reg, _val)
+
+#define        DIV_ROUND_UP(n, d)              (((n) + (d) - 1) / (d))
+
+#define        DWMMC_LOCK(_sc)                 mtx_lock(&(_sc)->sc_mtx)
+#define        DWMMC_UNLOCK(_sc)               mtx_unlock(&(_sc)->sc_mtx)
+#define        DWMMC_LOCK_INIT(_sc) \
+       mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
+           "dwmmc", MTX_DEF)
+#define        DWMMC_LOCK_DESTROY(_sc)         mtx_destroy(&_sc->sc_mtx);
+#define        DWMMC_ASSERT_LOCKED(_sc)        mtx_assert(&_sc->sc_mtx, 
MA_OWNED);
+#define        DWMMC_ASSERT_UNLOCKED(_sc)      mtx_assert(&_sc->sc_mtx, 
MA_NOTOWNED);
+
+#define        PENDING_CMD     0x01
+#define        PENDING_STOP    0x02
+#define        CARD_INIT_DONE  0x04
+
+#define        DWMMC_DATA_ERR_FLAGS    (SDMMC_INTMASK_DRT | SDMMC_INTMASK_DCRC 
\
+                               |SDMMC_INTMASK_HTO | SDMMC_INTMASK_SBE \
+                               |SDMMC_INTMASK_EBE)
+#define        DWMMC_CMD_ERR_FLAGS     (SDMMC_INTMASK_RTO | SDMMC_INTMASK_RCRC 
\
+                               |SDMMC_INTMASK_RE)
+#define        DWMMC_ERR_FLAGS         (DWMMC_DATA_ERR_FLAGS | 
DWMMC_CMD_ERR_FLAGS \
+                               |SDMMC_INTMASK_HLE)
+
+#define        DES0_DIC        (1 << 1)
+#define        DES0_LD         (1 << 2)
+#define        DES0_FS         (1 << 3)
+#define        DES0_CH         (1 << 4)
+#define        DES0_ER         (1 << 5)
+#define        DES0_CES        (1 << 30)
+#define        DES0_OWN        (1 << 31)
+
+#define        DES1_BS1_MASK   0xfff
+#define        DES1_BS1_SHIFT  0
+
+struct idmac_desc {
+       uint32_t        des0;   /* control */
+       uint32_t        des1;   /* bufsize */
+       uint32_t        des2;   /* buf1 phys addr */
+       uint32_t        des3;   /* buf2 phys addr or next descr */
+};
+
+#define        DESC_COUNT      256
+#define        DESC_SIZE       (sizeof(struct idmac_desc) * DESC_COUNT)
+#define        DEF_MSIZE       0x2     /* Burst size of multiple transaction */
+
+struct dwmmc_softc {
+       struct resource         *res[2];
+       bus_space_tag_t         bst;
+       bus_space_handle_t      bsh;
+       device_t                dev;
+       void                    *intr_cookie;
+       struct mmc_host         host;
+       struct mtx              sc_mtx;
+       struct mmc_request      *req;
+       struct mmc_command      *curcmd;
+       uint32_t                flags;
+       uint32_t                hwtype;
+       uint32_t                use_auto_stop;
+
+       bus_dma_tag_t           desc_tag;
+       bus_dmamap_t            desc_map;
+       struct idmac_desc       *desc_ring;
+       bus_addr_t              desc_ring_paddr;
+       bus_dma_tag_t           buf_tag;
+       bus_dmamap_t            buf_map;
+
+       uint32_t                bus_busy;
+       uint32_t                dto_rcvd;
+       uint32_t                acd_rcvd;
+       uint32_t                cmd_done;
+       uint32_t                bus_hz;
+       uint32_t                fifo_depth;
+       uint32_t                num_slots;
+       uint32_t                sdr_timing;
+       uint32_t                ddr_timing;
+};
+
+static void dwmmc_next_operation(struct dwmmc_softc *);
+static int dwmmc_setup_bus(struct dwmmc_softc *, int);
+static int dma_done(struct dwmmc_softc *, struct mmc_command *);
+static int dma_stop(struct dwmmc_softc *);
+
+static struct resource_spec dwmmc_spec[] = {
+       { SYS_RES_MEMORY,       0,      RF_ACTIVE },
+       { SYS_RES_IRQ,          0,      RF_ACTIVE },
+       { -1, 0 }
+};
+
+enum {
+       HWTYPE_NONE,
+       HWTYPE_ALTERA,
+       HWTYPE_EXYNOS,
+};
+
+#define        HWTYPE_MASK             (0x0000ffff)
+#define        HWFLAG_MASK             (0xffff << 16)
+
+static struct ofw_compat_data compat_data[] = {
+       {"altr,socfpga-dw-mshc",        HWTYPE_ALTERA},
+       {"samsung,exynos5420-dw-mshc",  HWTYPE_EXYNOS},
+       {NULL,                          HWTYPE_NONE},
+};
+
+static void
+dwmmc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+
+       if (error != 0)
+               return;
+       *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
+static void
+dwmmc_ring_setup(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+       struct dwmmc_softc *sc;
+       int idx;
+
+       if (error != 0)
+               return;
+
+       sc = arg;
+
+       dprintf("nsegs %d seg0len %lu\n", nsegs, segs[0].ds_len);
+
+       for (idx = 0; idx < nsegs; idx++) {
+               sc->desc_ring[idx].des0 = (DES0_OWN | DES0_DIC | DES0_CH);
+               sc->desc_ring[idx].des1 = segs[idx].ds_len;
+               sc->desc_ring[idx].des2 = segs[idx].ds_addr;
+
+               if (idx == 0)
+                       sc->desc_ring[idx].des0 |= DES0_FS;
+
+               if (idx == (nsegs - 1)) {
+                       sc->desc_ring[idx].des0 &= ~(DES0_DIC | DES0_CH);
+                       sc->desc_ring[idx].des0 |= DES0_LD;
+               }
+       }
+}
+
+static int
+dwmmc_ctrl_reset(struct dwmmc_softc *sc, int reset_bits)
+{
+       int reg;
+       int i;
+
+       reg = READ4(sc, SDMMC_CTRL);
+       reg |= (reset_bits);
+       WRITE4(sc, SDMMC_CTRL, reg);
+
+       /* Wait reset done */
+       for (i = 0; i < 100; i++) {
+               if (!(READ4(sc, SDMMC_CTRL) & reset_bits))
+                       return (0);
+               DELAY(10);
+       };
+
+       device_printf(sc->dev, "Reset failed\n");
+
+       return (1);
+}
+
+static int
+dma_setup(struct dwmmc_softc *sc)
+{
+       int error;
+       int nidx;
+       int idx;
+
+       /*
+        * Set up TX descriptor ring, descriptors, and dma maps.
+        */
+       error = bus_dma_tag_create(
+           bus_get_dma_tag(sc->dev),   /* Parent tag. */
+           4096, 0,                    /* alignment, boundary */
+           BUS_SPACE_MAXADDR_32BIT,    /* lowaddr */
+           BUS_SPACE_MAXADDR,          /* highaddr */
+           NULL, NULL,                 /* filter, filterarg */
+           DESC_SIZE, 1,               /* maxsize, nsegments */
+           DESC_SIZE,                  /* maxsegsize */
+           0,                          /* flags */
+           NULL, NULL,                 /* lockfunc, lockarg */
+           &sc->desc_tag);
+       if (error != 0) {
+               device_printf(sc->dev,
+                   "could not create ring DMA tag.\n");
+               return (1);
+       }
+
+       error = bus_dmamem_alloc(sc->desc_tag, (void**)&sc->desc_ring,
+           BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO,
+           &sc->desc_map);
+       if (error != 0) {
+               device_printf(sc->dev,
+                   "could not allocate descriptor ring.\n");
+               return (1);
+       }
+
+       error = bus_dmamap_load(sc->desc_tag, sc->desc_map,
+           sc->desc_ring, DESC_SIZE, dwmmc_get1paddr,
+           &sc->desc_ring_paddr, 0);
+       if (error != 0) {
+               device_printf(sc->dev,
+                   "could not load descriptor ring map.\n");
+               return (1);
+       }
+
+       for (idx = 0; idx < DESC_COUNT; idx++) {
+               sc->desc_ring[idx].des0 = DES0_CH;
+               sc->desc_ring[idx].des1 = 0;
+               nidx = (idx + 1) % DESC_COUNT;
+               sc->desc_ring[idx].des3 = sc->desc_ring_paddr + \
+                   (nidx * sizeof(struct idmac_desc));
+       }
+
+       error = bus_dma_tag_create(
+           bus_get_dma_tag(sc->dev),   /* Parent tag. */
+           4096, 0,                    /* alignment, boundary */
+           BUS_SPACE_MAXADDR_32BIT,    /* lowaddr */
+           BUS_SPACE_MAXADDR,          /* highaddr */
+           NULL, NULL,                 /* filter, filterarg */
+           DESC_COUNT*MMC_SECTOR_SIZE, /* maxsize */
+           DESC_COUNT,                 /* nsegments */
+           MMC_SECTOR_SIZE,            /* maxsegsize */
+           0,                          /* flags */
+           NULL, NULL,                 /* lockfunc, lockarg */
+           &sc->buf_tag);
+       if (error != 0) {
+               device_printf(sc->dev,
+                   "could not create ring DMA tag.\n");
+               return (1);
+       }
+
+       error = bus_dmamap_create(sc->buf_tag, 0,
+           &sc->buf_map);
+       if (error != 0) {
+               device_printf(sc->dev,
+                   "could not create TX buffer DMA map.\n");
+               return (1);
+       }
+
+       return (0);
+}
+
+static void
+dwmmc_cmd_done(struct dwmmc_softc *sc)
+{
+       struct mmc_command *cmd;
+
+       cmd = sc->curcmd;
+       if (cmd == NULL)
+               return;
+
+       if (cmd->flags & MMC_RSP_PRESENT) {
+               if (cmd->flags & MMC_RSP_136) {
+                       cmd->resp[3] = READ4(sc, SDMMC_RESP0);
+                       cmd->resp[2] = READ4(sc, SDMMC_RESP1);
+                       cmd->resp[1] = READ4(sc, SDMMC_RESP2);
+                       cmd->resp[0] = READ4(sc, SDMMC_RESP3);
+               } else {
+                       cmd->resp[3] = 0;
+                       cmd->resp[2] = 0;
+                       cmd->resp[1] = 0;
+                       cmd->resp[0] = READ4(sc, SDMMC_RESP0);
+               }
+       }
+}
+
+static void
+dwmmc_tasklet(struct dwmmc_softc *sc)
+{
+       struct mmc_command *cmd;
+
+       cmd = sc->curcmd;
+       if (cmd == NULL)
+               return;
+
+       if (cmd->error != MMC_ERR_NONE) {
+               dwmmc_next_operation(sc);
+       } else if (!cmd->data && sc->cmd_done) {
+               dwmmc_next_operation(sc);
+       } else if (cmd->data && sc->dto_rcvd) {
+               if ((cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
+                    cmd->opcode == MMC_READ_MULTIPLE_BLOCK) &&
+                    sc->use_auto_stop) {
+                       if (sc->acd_rcvd)
+                               dwmmc_next_operation(sc);
+               } else {
+                       dwmmc_next_operation(sc);
+               }
+       }
+}
+
+static void
+dwmmc_intr(void *arg)
+{
+       struct mmc_command *cmd;
+       struct dwmmc_softc *sc;
+       uint32_t reg;
+
+       sc = arg;
+
+       DWMMC_LOCK(sc);
+
+       cmd = sc->curcmd;
+
+       /* First handle SDMMC controller interrupts */
+       reg = READ4(sc, SDMMC_MINTSTS);
+       if (reg) {
+               dprintf("%s 0x%08x\n", __func__, reg);
+
+               if (reg & DWMMC_CMD_ERR_FLAGS) {
+                       WRITE4(sc, SDMMC_RINTSTS, DWMMC_CMD_ERR_FLAGS);
+                       dprintf("cmd err 0x%08x cmd 0x%08x\n",
+                               reg, cmd->opcode);
+                       cmd->error = MMC_ERR_TIMEOUT;
+               }
+
+               if (reg & DWMMC_DATA_ERR_FLAGS) {
+                       WRITE4(sc, SDMMC_RINTSTS, DWMMC_DATA_ERR_FLAGS);
+                       dprintf("data err 0x%08x cmd 0x%08x\n",
+                               reg, cmd->opcode);
+                       cmd->error = MMC_ERR_FAILED;
+
+                       dma_done(sc, cmd);
+                       dma_stop(sc);
+                       DWMMC_UNLOCK(sc);
+                       return;
+               }
+
+               if (reg & SDMMC_INTMASK_CMD_DONE) {
+                       dwmmc_cmd_done(sc);
+                       sc->cmd_done = 1;
+                       WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_CMD_DONE);
+               }
+
+               if (reg & SDMMC_INTMASK_ACD) {
+                       sc->acd_rcvd = 1;
+                       WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_ACD);
+               }
+
+               if (reg & SDMMC_INTMASK_DTO) {
+                       sc->dto_rcvd = 1;
+                       WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_DTO);
+               }
+
+               if (reg & SDMMC_INTMASK_CD) {
+                       /* XXX: Handle card detect */
+                       WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_CD);
+               }
+       }
+
+       /* Now handle DMA interrupts */
+       reg = READ4(sc, SDMMC_IDSTS);
+       if (reg) {
+               dprintf("dma intr 0x%08x\n", reg);
+               if (reg & (SDMMC_IDINTEN_TI | SDMMC_IDINTEN_RI)) {
+                       WRITE4(sc, SDMMC_IDSTS, (SDMMC_IDINTEN_TI |
+                                                SDMMC_IDINTEN_RI));
+                       WRITE4(sc, SDMMC_IDSTS, SDMMC_IDINTEN_NI);
+                       dma_done(sc, cmd);
+               }
+       }
+
+       dwmmc_tasklet(sc);
+
+       DWMMC_UNLOCK(sc);
+}
+
+static int
+parse_fdt(struct dwmmc_softc *sc)
+{
+       pcell_t dts_value[3];
+       phandle_t node;
+       int len;
+
+       if ((node = ofw_bus_get_node(sc->dev)) == -1)
+               return (ENXIO);
+
+       /* fifo-depth */
+       if ((len = OF_getproplen(node, "fifo-depth")) <= 0)
+               return (ENXIO);
+       OF_getencprop(node, "fifo-depth", dts_value, len);
+       sc->fifo_depth = dts_value[0];
+
+       /* num-slots */
+       if ((len = OF_getproplen(node, "num-slots")) <= 0)
+               return (ENXIO);
+       OF_getencprop(node, "num-slots", dts_value, len);
+       sc->num_slots = dts_value[0];
+
+       /*
+        * We need some platform-specific code to know
+        * what the clock is supplied for our device.
+        * For now rely on the value specified in FDT.
+        */
+       if ((len = OF_getproplen(node, "bus-frequency")) <= 0)
+               return (ENXIO);
+       OF_getencprop(node, "bus-frequency", dts_value, len);
+       sc->bus_hz = dts_value[0];
+
+       /*
+        * Platform-specific stuff
+        * XXX: Move to separate file
+        */
+
+       if ((sc->hwtype & HWTYPE_MASK) != HWTYPE_EXYNOS)
+               return (0);
+
+       if ((len = OF_getproplen(node, "samsung,dw-mshc-ciu-div")) <= 0)
+               return (ENXIO);
+       OF_getencprop(node, "samsung,dw-mshc-ciu-div", dts_value, len);
+       sc->sdr_timing = (dts_value[0] << SDMMC_CLKSEL_DIVIDER_SHIFT);
+       sc->ddr_timing = (dts_value[0] << SDMMC_CLKSEL_DIVIDER_SHIFT);
+
+       if ((len = OF_getproplen(node, "samsung,dw-mshc-sdr-timing")) <= 0)
+               return (ENXIO);
+       OF_getencprop(node, "samsung,dw-mshc-sdr-timing", dts_value, len);
+       sc->sdr_timing |= ((dts_value[0] << SDMMC_CLKSEL_SAMPLE_SHIFT) |
+                         (dts_value[1] << SDMMC_CLKSEL_DRIVE_SHIFT));
+
+       if ((len = OF_getproplen(node, "samsung,dw-mshc-ddr-timing")) <= 0)
+               return (ENXIO);
+       OF_getencprop(node, "samsung,dw-mshc-ddr-timing", dts_value, len);
+       sc->ddr_timing |= ((dts_value[0] << SDMMC_CLKSEL_SAMPLE_SHIFT) |
+                         (dts_value[1] << SDMMC_CLKSEL_DRIVE_SHIFT));
+
+       return (0);
+}
+
+static int
+dwmmc_probe(device_t dev)
+{
+       uintptr_t hwtype;
+
+       if (!ofw_bus_status_okay(dev))
+               return (ENXIO);
+
+       hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+       if (hwtype == HWTYPE_NONE)
+               return (ENXIO);
+
+       device_set_desc(dev, "Synopsys DesignWare Mobile "
+                               "Storage Host Controller");
+       return (BUS_PROBE_DEFAULT);
+}
+
+static int
+dwmmc_attach(device_t dev)
+{
+       struct dwmmc_softc *sc;
+       device_t child;
+       int error;
+       int slot;
+
+       sc = device_get_softc(dev);
+
+       sc->dev = dev;
+       sc->hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+       /* Why not to use Auto Stop? It save a hundred of irq per second */
+       sc->use_auto_stop = 1;
+
+       error = parse_fdt(sc);
+       if (error != 0) {
+               device_printf(dev, "Can't get FDT property.\n");
+               return (ENXIO);
+       }
+
+       DWMMC_LOCK_INIT(sc);
+
+       if (bus_alloc_resources(dev, dwmmc_spec, sc->res)) {
+               device_printf(dev, "could not allocate resources\n");
+               return (ENXIO);
+       }
+
+       /* Memory interface */
+       sc->bst = rman_get_bustag(sc->res[0]);
+       sc->bsh = rman_get_bushandle(sc->res[0]);
+
+       /* Setup interrupt handler. */
+       error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE,
+           NULL, dwmmc_intr, sc, &sc->intr_cookie);
+       if (error != 0) {
+               device_printf(dev, "could not setup interrupt handler.\n");
+               return (ENXIO);
+       }
+
+       device_printf(dev, "Hardware version ID is %04x\n",
+               READ4(sc, SDMMC_VERID) & 0xffff);
+
+       WRITE4(sc, EMMCP_MPSBEGIN0, 0);
+       WRITE4(sc, EMMCP_SEND0, 0);
+       WRITE4(sc, EMMCP_CTRL0, (MPSCTRL_SECURE_READ_BIT |
+                                MPSCTRL_SECURE_WRITE_BIT |
+                                MPSCTRL_NON_SECURE_READ_BIT |
+                                MPSCTRL_NON_SECURE_WRITE_BIT |
+                                MPSCTRL_VALID));
+
+       /* XXX: we support operation for slot index 0 only */
+       slot = 0;
+       WRITE4(sc, SDMMC_PWREN, (1 << slot));
+
+       /* Reset all */
+       if (dwmmc_ctrl_reset(sc, (SDMMC_CTRL_RESET |
+                                 SDMMC_CTRL_FIFO_RESET |
+                                 SDMMC_CTRL_DMA_RESET)))
+               return (ENXIO);
+
+       dwmmc_setup_bus(sc, sc->host.f_min);
+
+       if (dma_setup(sc))
+               return (ENXIO);
+
+       /* Install desc base */
+       WRITE4(sc, SDMMC_DBADDR, sc->desc_ring_paddr);
+
+       /* Enable DMA interrupts */
+       WRITE4(sc, SDMMC_IDSTS, SDMMC_IDINTEN_MASK);
+       WRITE4(sc, SDMMC_IDINTEN, (SDMMC_IDINTEN_NI |
+                                  SDMMC_IDINTEN_RI |
+                                  SDMMC_IDINTEN_TI));
+
+       /* Clear and disable interrups for a while */
+       WRITE4(sc, SDMMC_RINTSTS, 0xffffffff);
+       WRITE4(sc, SDMMC_INTMASK, 0);
+
+       /* Maximum timeout */
+       WRITE4(sc, SDMMC_TMOUT, 0xffffffff);
+
+       /* Enable interrupts */
+       WRITE4(sc, SDMMC_RINTSTS, 0xffffffff);
+       WRITE4(sc, SDMMC_INTMASK, (SDMMC_INTMASK_CMD_DONE |
+                                  SDMMC_INTMASK_DTO |
+                                  SDMMC_INTMASK_ACD |
+                                  SDMMC_INTMASK_TXDR |
+                                  SDMMC_INTMASK_RXDR |
+                                  DWMMC_ERR_FLAGS |
+                                  SDMMC_INTMASK_CD));
+       WRITE4(sc, SDMMC_CTRL, SDMMC_CTRL_INT_ENABLE);
+
+       sc->host.f_min = 400000;
+       sc->host.f_max = 200000000;
+       sc->host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
+       sc->host.caps = MMC_CAP_4_BIT_DATA;
+
+       child = device_add_child(dev, "mmc", 0);
+       return (bus_generic_attach(dev));
+}
+
+static int
+dwmmc_setup_bus(struct dwmmc_softc *sc, int freq)
+{
+       int tout;
+       int div;
+
+       if (freq == 0) {
+               WRITE4(sc, SDMMC_CLKENA, 0);
+               WRITE4(sc, SDMMC_CMD, (SDMMC_CMD_WAIT_PRVDATA |
+                       SDMMC_CMD_UPD_CLK_ONLY | SDMMC_CMD_START));
+
+               tout = 1000;
+               do {
+                       if (tout-- < 0) {
+                               device_printf(sc->dev, "Failed update clk\n");
+                               return (1);
+                       }
+               } while (READ4(sc, SDMMC_CMD) & SDMMC_CMD_START);
+
+               return (0);
+       }
+
+       WRITE4(sc, SDMMC_CLKENA, 0);
+       WRITE4(sc, SDMMC_CLKSRC, 0);
+
+       div = (sc->bus_hz != freq) ? DIV_ROUND_UP(sc->bus_hz, 2 * freq) : 0;
+
+       WRITE4(sc, SDMMC_CLKDIV, div);
+       WRITE4(sc, SDMMC_CMD, (SDMMC_CMD_WAIT_PRVDATA |
+                       SDMMC_CMD_UPD_CLK_ONLY | SDMMC_CMD_START));
+
+       tout = 1000;
+       do {
+               if (tout-- < 0) {
+                       device_printf(sc->dev, "Failed to update clk");
+                       return (1);
+               }
+       } while (READ4(sc, SDMMC_CMD) & SDMMC_CMD_START);
+
+       WRITE4(sc, SDMMC_CLKENA, (SDMMC_CLKENA_CCLK_EN | SDMMC_CLKENA_LP));
+       WRITE4(sc, SDMMC_CMD, SDMMC_CMD_WAIT_PRVDATA |
+                       SDMMC_CMD_UPD_CLK_ONLY | SDMMC_CMD_START);
+
+       tout = 1000;
+       do {
+               if (tout-- < 0) {
+                       device_printf(sc->dev, "Failed to enable clk\n");
+                       return (1);
+               }
+       } while (READ4(sc, SDMMC_CMD) & SDMMC_CMD_START);
+
+       return (0);
+}
+
+static int
+dwmmc_update_ios(device_t brdev, device_t reqdev)
+{
+       struct dwmmc_softc *sc;
+       struct mmc_ios *ios;
+
+       sc = device_get_softc(brdev);
+       ios = &sc->host.ios;
+
+       dprintf("Setting up clk %u bus_width %d\n",
+               ios->clock, ios->bus_width);
+
+       dwmmc_setup_bus(sc, ios->clock);
+
+       if (ios->bus_width == bus_width_8)
+               WRITE4(sc, SDMMC_CTYPE, SDMMC_CTYPE_8BIT);
+       else if (ios->bus_width == bus_width_4)
+               WRITE4(sc, SDMMC_CTYPE, SDMMC_CTYPE_4BIT);
+       else
+               WRITE4(sc, SDMMC_CTYPE, 0);
+
+       if ((sc->hwtype & HWTYPE_MASK) == HWTYPE_EXYNOS) {
+               /* XXX: take care about DDR or SDR use here */
+               WRITE4(sc, SDMMC_CLKSEL, sc->sdr_timing);
+       }
+
+       /*
+        * XXX: take care about DDR bit
+        *
+        * reg = READ4(sc, SDMMC_UHS_REG);
+        * reg |= (SDMMC_UHS_REG_DDR);
+        * WRITE4(sc, SDMMC_UHS_REG, reg);
+        */
+
+       return (0);
+}
+
+static int
+dma_done(struct dwmmc_softc *sc, struct mmc_command *cmd)
+{
+       struct mmc_data *data;
+
+       data = cmd->data;
+
+       if (data->flags & MMC_DATA_WRITE)
+               bus_dmamap_sync(sc->buf_tag, sc->buf_map,
+                       BUS_DMASYNC_POSTWRITE);
+       else
+               bus_dmamap_sync(sc->buf_tag, sc->buf_map,
+                       BUS_DMASYNC_POSTREAD);
+
+       bus_dmamap_unload(sc->buf_tag, sc->buf_map);
+
+       return (0);
+}
+
+static int
+dma_stop(struct dwmmc_softc *sc)
+{
+       int reg;
+
+       reg = READ4(sc, SDMMC_CTRL);
+       reg &= ~(SDMMC_CTRL_USE_IDMAC);
+       reg |= (SDMMC_CTRL_DMA_RESET);
+       WRITE4(sc, SDMMC_CTRL, reg);
+
+       reg = READ4(sc, SDMMC_BMOD);
+       reg &= ~(SDMMC_BMOD_DE | SDMMC_BMOD_FB);
+       reg |= (SDMMC_BMOD_SWR);
+       WRITE4(sc, SDMMC_BMOD, reg);
+
+       return (0);
+}
+
+static int
+dma_prepare(struct dwmmc_softc *sc, struct mmc_command *cmd)
+{
+       struct mmc_data *data;
+       int len;
+       int err;
+       int reg;
+
+       data = cmd->data;
+       len = data->len;
+
+       reg = READ4(sc, SDMMC_INTMASK);
+       reg &= ~(SDMMC_INTMASK_TXDR | SDMMC_INTMASK_RXDR);
+       WRITE4(sc, SDMMC_INTMASK, reg);
+
+       err = bus_dmamap_load(sc->buf_tag, sc->buf_map,
+               data->data, data->len, dwmmc_ring_setup,
+               sc, BUS_DMA_NOWAIT);
+       if (err != 0)
+               panic("dmamap_load failed\n");
+
+       if (data->flags & MMC_DATA_WRITE)
+               bus_dmamap_sync(sc->buf_tag, sc->buf_map,
+                       BUS_DMASYNC_PREWRITE);
+       else
+               bus_dmamap_sync(sc->buf_tag, sc->buf_map,
+                       BUS_DMASYNC_PREREAD);
+
+       reg = (DEF_MSIZE << SDMMC_FIFOTH_MSIZE_S);
+       reg |= ((sc->fifo_depth / 2) - 1) << SDMMC_FIFOTH_RXWMARK_S;
+       reg |= (sc->fifo_depth / 2) << SDMMC_FIFOTH_TXWMARK_S;
+
+       WRITE4(sc, SDMMC_FIFOTH, reg);
+       wmb();
+
+       reg = READ4(sc, SDMMC_CTRL);
+       reg |= (SDMMC_CTRL_USE_IDMAC | SDMMC_CTRL_DMA_ENABLE);
+       WRITE4(sc, SDMMC_CTRL, reg);
+       wmb();
+
+       reg = READ4(sc, SDMMC_BMOD);
+       reg |= (SDMMC_BMOD_DE | SDMMC_BMOD_FB);
+       WRITE4(sc, SDMMC_BMOD, reg);
+
+       /* Start */
+       WRITE4(sc, SDMMC_PLDMND, 1);
+
+       return (0);
+}
+
+static void
+dwmmc_start_cmd(struct dwmmc_softc *sc, struct mmc_command *cmd)
+{
+       struct mmc_data *data;
+       uint32_t blksz;
+       uint32_t cmdr;
+
+       sc->curcmd = cmd;
+       data = cmd->data;
+
+       /* XXX Upper layers don't always set this */
+       cmd->mrq = sc->req;
+
+       /* Begin setting up command register. */

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to