diff options
author | br <br@FreeBSD.org> | 2014-10-07 17:39:30 +0000 |
---|---|---|
committer | br <br@FreeBSD.org> | 2014-10-07 17:39:30 +0000 |
commit | d60731705739197535a478dc692dd3b148f8384b (patch) | |
tree | 494dcca651575667532857d47c7b8587ec0f14df /sys/dev/mmc/host | |
parent | 8cd4bd98b6e566b3556a8bed73458e7b32cbbe77 (diff) | |
download | FreeBSD-src-d60731705739197535a478dc692dd3b148f8384b.zip FreeBSD-src-d60731705739197535a478dc692dd3b148f8384b.tar.gz |
Add driver for Synopsys DesignWare Mobile Storage Host Controller.
Sponsored by: DARPA, AFRL
Diffstat (limited to 'sys/dev/mmc/host')
-rw-r--r-- | sys/dev/mmc/host/dwmmc.c | 1103 | ||||
-rw-r--r-- | sys/dev/mmc/host/dwmmc.h | 150 |
2 files changed, 1253 insertions, 0 deletions
diff --git a/sys/dev/mmc/host/dwmmc.c b/sys/dev/mmc/host/dwmmc.c new file mode 100644 index 0000000..003db4f --- /dev/null +++ b/sys/dev/mmc/host/dwmmc.c @@ -0,0 +1,1103 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin <br@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. */ + + cmdr = cmd->opcode; + + dprintf("cmd->opcode 0x%08x\n", cmd->opcode); + + if (cmd->opcode == MMC_STOP_TRANSMISSION || + cmd->opcode == MMC_GO_IDLE_STATE || + cmd->opcode == MMC_GO_INACTIVE_STATE) + cmdr |= SDMMC_CMD_STOP_ABORT; + else if (cmd->opcode != MMC_SEND_STATUS && data) + cmdr |= SDMMC_CMD_WAIT_PRVDATA; + + /* Set up response handling. */ + if (MMC_RSP(cmd->flags) != MMC_RSP_NONE) { + cmdr |= SDMMC_CMD_RESP_EXP; + if (cmd->flags & MMC_RSP_136) + cmdr |= SDMMC_CMD_RESP_LONG; + } + + if (cmd->flags & MMC_RSP_CRC) + cmdr |= SDMMC_CMD_RESP_CRC; + + /* + * XXX: Not all platforms want this. + */ + cmdr |= SDMMC_CMD_USE_HOLD_REG; + + if ((sc->flags & CARD_INIT_DONE) == 0) { + sc->flags |= (CARD_INIT_DONE); + cmdr |= SDMMC_CMD_SEND_INIT; + } + + if (data) { + if ((cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || + cmd->opcode == MMC_READ_MULTIPLE_BLOCK) && + sc->use_auto_stop) + cmdr |= SDMMC_CMD_SEND_ASTOP; + + cmdr |= SDMMC_CMD_DATA_EXP; + if (data->flags & MMC_DATA_STREAM) + cmdr |= SDMMC_CMD_MODE_STREAM; + if (data->flags & MMC_DATA_WRITE) + cmdr |= SDMMC_CMD_DATA_WRITE; + + WRITE4(sc, SDMMC_TMOUT, 0xffffffff); + WRITE4(sc, SDMMC_BYTCNT, data->len); + blksz = (data->len < MMC_SECTOR_SIZE) ? \ + data->len : MMC_SECTOR_SIZE; + WRITE4(sc, SDMMC_BLKSIZ, blksz); + + dma_prepare(sc, cmd); + wmb(); + } + + dprintf("cmdr 0x%08x\n", cmdr); + + WRITE4(sc, SDMMC_CMDARG, cmd->arg); + wmb(); + WRITE4(sc, SDMMC_CMD, cmdr | SDMMC_CMD_START); +}; + +static void +dwmmc_next_operation(struct dwmmc_softc *sc) +{ + struct mmc_request *req; + + req = sc->req; + if (req == NULL) + return; + + sc->acd_rcvd = 0; + sc->dto_rcvd = 0; + sc->cmd_done = 0; + + /* + * XXX: Wait until card is still busy. + * We do need this to prevent data timeouts, + * mostly caused by multi-block write command + * followed by single-read. + */ + while(READ4(sc, SDMMC_STATUS) & (SDMMC_STATUS_DATA_BUSY)) + continue; + + if (sc->flags & PENDING_CMD) { + sc->flags &= ~PENDING_CMD; + dwmmc_start_cmd(sc, req->cmd); + return; + } else if (sc->flags & PENDING_STOP && !sc->use_auto_stop) { + sc->flags &= ~PENDING_STOP; + dwmmc_start_cmd(sc, req->stop); + return; + } + + sc->req = NULL; + sc->curcmd = NULL; + req->done(req); +} + +static int +dwmmc_request(device_t brdev, device_t reqdev, struct mmc_request *req) +{ + struct dwmmc_softc *sc; + + sc = device_get_softc(brdev); + + dprintf("%s\n", __func__); + + DWMMC_LOCK(sc); + + if (sc->req != NULL) { + DWMMC_UNLOCK(sc); + return (EBUSY); + } + + sc->req = req; + sc->flags |= PENDING_CMD; + if (sc->req->stop) + sc->flags |= PENDING_STOP; + dwmmc_next_operation(sc); + + DWMMC_UNLOCK(sc); + return (0); +} + +static int +dwmmc_get_ro(device_t brdev, device_t reqdev) +{ + + dprintf("%s\n", __func__); + + return (0); +} + +static int +dwmmc_acquire_host(device_t brdev, device_t reqdev) +{ + struct dwmmc_softc *sc; + + sc = device_get_softc(brdev); + + DWMMC_LOCK(sc); + while (sc->bus_busy) + msleep(sc, &sc->sc_mtx, PZERO, "dwmmcah", hz / 5); + sc->bus_busy++; + DWMMC_UNLOCK(sc); + return (0); +} + +static int +dwmmc_release_host(device_t brdev, device_t reqdev) +{ + struct dwmmc_softc *sc; + + sc = device_get_softc(brdev); + + DWMMC_LOCK(sc); + sc->bus_busy--; + wakeup(sc); + DWMMC_UNLOCK(sc); + return (0); +} + +static int +dwmmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) +{ + struct dwmmc_softc *sc; + + sc = device_get_softc(bus); + + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + *(int *)result = sc->host.ios.bus_mode; + break; + case MMCBR_IVAR_BUS_WIDTH: + *(int *)result = sc->host.ios.bus_width; + break; + case MMCBR_IVAR_CHIP_SELECT: + *(int *)result = sc->host.ios.chip_select; + break; + case MMCBR_IVAR_CLOCK: + *(int *)result = sc->host.ios.clock; + break; + case MMCBR_IVAR_F_MIN: + *(int *)result = sc->host.f_min; + break; + case MMCBR_IVAR_F_MAX: + *(int *)result = sc->host.f_max; + break; + case MMCBR_IVAR_HOST_OCR: + *(int *)result = sc->host.host_ocr; + break; + case MMCBR_IVAR_MODE: + *(int *)result = sc->host.mode; + break; + case MMCBR_IVAR_OCR: + *(int *)result = sc->host.ocr; + break; + case MMCBR_IVAR_POWER_MODE: + *(int *)result = sc->host.ios.power_mode; + break; + case MMCBR_IVAR_VDD: + *(int *)result = sc->host.ios.vdd; + break; + case MMCBR_IVAR_CAPS: + sc->host.caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; + *(int *)result = sc->host.caps; + break; + case MMCBR_IVAR_MAX_DATA: + *(int *)result = DESC_COUNT; + } + return (0); +} + +static int +dwmmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) +{ + struct dwmmc_softc *sc; + + sc = device_get_softc(bus); + + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + sc->host.ios.bus_mode = value; + break; + case MMCBR_IVAR_BUS_WIDTH: + sc->host.ios.bus_width = value; + break; + case MMCBR_IVAR_CHIP_SELECT: + sc->host.ios.chip_select = value; + break; + case MMCBR_IVAR_CLOCK: + sc->host.ios.clock = value; + break; + case MMCBR_IVAR_MODE: + sc->host.mode = value; + break; + case MMCBR_IVAR_OCR: + sc->host.ocr = value; + break; + case MMCBR_IVAR_POWER_MODE: + sc->host.ios.power_mode = value; + break; + case MMCBR_IVAR_VDD: + sc->host.ios.vdd = value; + break; + /* These are read-only */ + case MMCBR_IVAR_CAPS: + case MMCBR_IVAR_HOST_OCR: + case MMCBR_IVAR_F_MIN: + case MMCBR_IVAR_F_MAX: + case MMCBR_IVAR_MAX_DATA: + return (EINVAL); + } + return (0); +} + +static device_method_t dwmmc_methods[] = { + DEVMETHOD(device_probe, dwmmc_probe), + DEVMETHOD(device_attach, dwmmc_attach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, dwmmc_read_ivar), + DEVMETHOD(bus_write_ivar, dwmmc_write_ivar), + + /* mmcbr_if */ + DEVMETHOD(mmcbr_update_ios, dwmmc_update_ios), + DEVMETHOD(mmcbr_request, dwmmc_request), + DEVMETHOD(mmcbr_get_ro, dwmmc_get_ro), + DEVMETHOD(mmcbr_acquire_host, dwmmc_acquire_host), + DEVMETHOD(mmcbr_release_host, dwmmc_release_host), + + DEVMETHOD_END +}; + +static driver_t dwmmc_driver = { + "dwmmc", + dwmmc_methods, + sizeof(struct dwmmc_softc), +}; + +static devclass_t dwmmc_devclass; + +DRIVER_MODULE(dwmmc, simplebus, dwmmc_driver, dwmmc_devclass, 0, 0); + diff --git a/sys/dev/mmc/host/dwmmc.h b/sys/dev/mmc/host/dwmmc.h new file mode 100644 index 0000000..8f763cd --- /dev/null +++ b/sys/dev/mmc/host/dwmmc.h @@ -0,0 +1,150 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin <br@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. + * + * $FreeBSD$ + */ + +#define SDMMC_CTRL 0x0 /* Control Register */ +#define SDMMC_CTRL_USE_IDMAC (1 << 25) /* Use Internal DMAC */ +#define SDMMC_CTRL_DMA_ENABLE (1 << 5) /* */ +#define SDMMC_CTRL_INT_ENABLE (1 << 4) /* Enable interrupts */ +#define SDMMC_CTRL_DMA_RESET (1 << 2) /* Reset DMA */ +#define SDMMC_CTRL_FIFO_RESET (1 << 1) /* Reset FIFO */ +#define SDMMC_CTRL_RESET (1 << 0) /* Reset SD/MMC controller */ +#define SDMMC_PWREN 0x4 /* Power Enable Register */ +#define SDMMC_PWREN_PE (1 << 0) /* Power On */ +#define SDMMC_CLKDIV 0x8 /* Clock Divider Register */ +#define SDMMC_CLKSRC 0xC /* SD Clock Source Register */ +#define SDMMC_CLKENA 0x10 /* Clock Enable Register */ +#define SDMMC_CLKENA_LP (1 << 16) /* Low-power mode */ +#define SDMMC_CLKENA_CCLK_EN (1 << 0) /* SD/MMC Enable */ +#define SDMMC_TMOUT 0x14 /* Timeout Register */ +#define SDMMC_CTYPE 0x18 /* Card Type Register */ +#define SDMMC_CTYPE_8BIT (1 << 16) +#define SDMMC_CTYPE_4BIT (1 << 0) +#define SDMMC_BLKSIZ 0x1C /* Block Size Register */ +#define SDMMC_BYTCNT 0x20 /* Byte Count Register */ +#define SDMMC_INTMASK 0x24 /* Interrupt Mask Register */ +#define SDMMC_INTMASK_SDIO (1 << 16) /* SDIO Interrupt Enable */ +#define SDMMC_INTMASK_EBE (1 << 15) /* End-bit error */ +#define SDMMC_INTMASK_ACD (1 << 14) /* Auto command done */ +#define SDMMC_INTMASK_SBE (1 << 13) /* Start-bit error */ +#define SDMMC_INTMASK_HLE (1 << 12) /* Hardware locked write err */ +#define SDMMC_INTMASK_FRUN (1 << 11) /* FIFO underrun/overrun err */ +#define SDMMC_INTMASK_HTO (1 << 10) /* Data starvation by host timeout */ +#define SDMMC_INTMASK_DRT (1 << 9) /* Data read timeout */ +#define SDMMC_INTMASK_RTO (1 << 8) /* Response timeout */ +#define SDMMC_INTMASK_DCRC (1 << 7) /* Data CRC error */ +#define SDMMC_INTMASK_RCRC (1 << 6) /* Response CRC error */ +#define SDMMC_INTMASK_RXDR (1 << 5) /* Receive FIFO data request */ +#define SDMMC_INTMASK_TXDR (1 << 4) /* Transmit FIFO data request */ +#define SDMMC_INTMASK_DTO (1 << 3) /* Data transfer over */ +#define SDMMC_INTMASK_CMD_DONE (1 << 2) /* Command done */ +#define SDMMC_INTMASK_RE (1 << 1) /* Response error */ +#define SDMMC_INTMASK_CD (1 << 0) /* Card Detected */ +#define SDMMC_CMDARG 0x28 /* Command Argument Register */ +#define SDMMC_CMD 0x2C /* Command Register */ +#define SDMMC_CMD_START (1 << 31) +#define SDMMC_CMD_USE_HOLD_REG (1 << 29) +#define SDMMC_CMD_UPD_CLK_ONLY (1 << 21) /* Update clk only */ +#define SDMMC_CMD_SEND_INIT (1 << 15) /* Send initialization */ +#define SDMMC_CMD_STOP_ABORT (1 << 14) /* stop current data transfer */ +#define SDMMC_CMD_WAIT_PRVDATA (1 << 13) /* Wait for prev data transfer completion */ +#define SDMMC_CMD_SEND_ASTOP (1 << 12) /* Send stop command at end of data tx/rx */ +#define SDMMC_CMD_MODE_STREAM (1 << 11) /* Stream data transfer */ +#define SDMMC_CMD_DATA_WRITE (1 << 10) /* Write to card */ +#define SDMMC_CMD_DATA_EXP (1 << 9) /* Data transfer expected */ +#define SDMMC_CMD_RESP_CRC (1 << 8) /* Check Response CRC */ +#define SDMMC_CMD_RESP_LONG (1 << 7) /* Long response expected */ +#define SDMMC_CMD_RESP_EXP (1 << 6) /* Response expected */ +#define SDMMC_RESP0 0x30 /* Response Register 0 */ +#define SDMMC_RESP1 0x34 /* Response Register 1 */ +#define SDMMC_RESP2 0x38 /* Response Register 2 */ +#define SDMMC_RESP3 0x3C /* Response Register 3 */ +#define SDMMC_MINTSTS 0x40 /* Masked Interrupt Status Register */ +#define SDMMC_RINTSTS 0x44 /* Raw Interrupt Status Register */ +#define SDMMC_STATUS 0x48 /* Status Register */ +#define SDMMC_STATUS_DATA_BUSY (1 << 9) /* card_data[0] */ +#define SDMMC_FIFOTH 0x4C /* FIFO Threshold Watermark Register */ +#define SDMMC_FIFOTH_MSIZE_S 28 /* Burst size of multiple transaction */ +#define SDMMC_FIFOTH_RXWMARK_S 16 /* FIFO threshold watermark level */ +#define SDMMC_FIFOTH_TXWMARK_S 0 /* FIFO threshold watermark level */ +#define SDMMC_CDETECT 0x50 /* Card Detect Register */ +#define SDMMC_WRTPRT 0x54 /* Write Protect Register */ +#define SDMMC_TCBCNT 0x5C /* Transferred CIU Card Byte Count */ +#define SDMMC_TBBCNT 0x60 /* Transferred Host to BIU-FIFO Byte Count */ +#define SDMMC_DEBNCE 0x64 /* Debounce Count Register */ +#define SDMMC_USRID 0x68 /* User ID Register */ +#define SDMMC_VERID 0x6C /* Version ID Register */ +#define SDMMC_HCON 0x70 /* Hardware Configuration Register */ +#define SDMMC_UHS_REG 0x74 /* UHS-1 Register */ +#define SDMMC_UHS_REG_DDR (1 << 16) /* DDR mode */ +#define SDMMC_RST_N 0x78 /* Hardware Reset Register */ +#define SDMMC_BMOD 0x80 /* Bus Mode Register */ +#define SDMMC_BMOD_DE (1 << 7) /* IDMAC Enable */ +#define SDMMC_BMOD_FB (1 << 1) /* AHB Master Fixed Burst */ +#define SDMMC_BMOD_SWR (1 << 0) /* Reset DMA */ +#define SDMMC_PLDMND 0x84 /* Poll Demand Register */ +#define SDMMC_DBADDR 0x88 /* Descriptor List Base Address */ +#define SDMMC_IDSTS 0x8C /* Internal DMAC Status Register */ +#define SDMMC_IDINTEN 0x90 /* Internal DMAC Interrupt Enable */ +#define SDMMC_IDINTEN_AI (1 << 9) /* Abnormal Interrupt Summary */ +#define SDMMC_IDINTEN_NI (1 << 8) /* Normal Interrupt Summary */ +#define SDMMC_IDINTEN_CES (1 << 5) /* Card Error Summary */ +#define SDMMC_IDINTEN_DU (1 << 4) /* Descriptor Unavailable */ +#define SDMMC_IDINTEN_FBE (1 << 2) /* Fatal Bus Error */ +#define SDMMC_IDINTEN_RI (1 << 1) /* Receive Interrupt */ +#define SDMMC_IDINTEN_TI (1 << 0) /* Transmit Interrupt */ +#define SDMMC_IDINTEN_MASK (SDMMC_IDINTEN_AI | SDMMC_IDINTEN_NI | SDMMC_IDINTEN_CES | \ + SDMMC_IDINTEN_DU | SDMMC_IDINTEN_FBE | SDMMC_IDINTEN_RI | \ + SDMMC_IDINTEN_TI) +#define SDMMC_DSCADDR 0x94 /* Current Host Descriptor Address */ +#define SDMMC_BUFADDR 0x98 /* Current Buffer Descriptor Address */ +#define SDMMC_CARDTHRCTL 0x100 /* Card Threshold Control Register */ +#define SDMMC_BACK_END_POWER_R 0x104 /* Back End Power Register */ +#define SDMMC_DATA 0x200 /* Data FIFO Access */ + +/* eMMC */ +#define EMMCP_MPSBEGIN0 0x1200 /* */ +#define EMMCP_SEND0 0x1204 /* */ +#define EMMCP_CTRL0 0x120C /* */ +#define MPSCTRL_SECURE_READ_BIT (1 << 7) +#define MPSCTRL_SECURE_WRITE_BIT (1 << 6) +#define MPSCTRL_NON_SECURE_READ_BIT (1 << 5) +#define MPSCTRL_NON_SECURE_WRITE_BIT (1 << 4) +#define MPSCTRL_USE_FUSE_KEY (1 << 3) +#define MPSCTRL_ECB_MODE (1 << 2) +#define MPSCTRL_ENCRYPTION (1 << 1) +#define MPSCTRL_VALID (1 << 0) + +/* Platform-specific defines */ +#define SDMMC_CLKSEL 0x9C +#define SDMMC_CLKSEL_SAMPLE_SHIFT 0 +#define SDMMC_CLKSEL_DRIVE_SHIFT 16 +#define SDMMC_CLKSEL_DIVIDER_SHIFT 24 |