Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 7 Oct 2014 17:39:30 +0000 (UTC)
From:      Ruslan Bukin <br@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r272712 - in head/sys: arm/altera/socfpga arm/conf arm/samsung/exynos boot/fdt/dts/arm dev/mmc dev/mmc/host
Message-ID:  <201410071739.s97HdUGO012318@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 <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. */

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201410071739.s97HdUGO012318>