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"