svn commit: r283253 - in head/sys: arm/allwinner arm/conf boot/fdt/dts/arm dev/mmc

Luiz Otavio O Souza loos at FreeBSD.org
Thu May 21 17:39:44 UTC 2015


Author: loos
Date: Thu May 21 17:39:42 2015
New Revision: 283253
URL: https://svnweb.freebsd.org/changeset/base/283253

Log:
  Add the MMC/SD driver for Allwinner SoCs.
  
  This is based on the patch sent by Alexander Fedorov with the following
  fixes/improvements:
  
   - Better error handling;
   - Clock is derived from PLL6 (obtained from netbsd);
   - No more unnecessary busy loops on interrupt handler;
   - style(9) fixes and code cleanup.
  
  I also want to thanks Martin Galvan who has sent an alternative
  implementation with some interesting fixes.
  
  Tested on CubieBoard2, Banana-Pi (thanks to netgate!) and Cubieboard1
  (Pratik Singhal).
  
  This is intended to pave the way for the upcoming GSoC work (and make
  easier the build of images for the supported boards).
  
  PR:		196081
  Submitted by:	Alexander Fedorov <alexander.fedorov at rtlservice.com>

Added:
  head/sys/arm/allwinner/a10_mmc.c   (contents, props changed)
  head/sys/arm/allwinner/a10_mmc.h   (contents, props changed)
Modified:
  head/sys/arm/allwinner/a10_clk.c
  head/sys/arm/allwinner/a10_clk.h
  head/sys/arm/allwinner/files.allwinner
  head/sys/arm/conf/CUBIEBOARD
  head/sys/arm/conf/CUBIEBOARD2
  head/sys/boot/fdt/dts/arm/cubieboard.dts
  head/sys/boot/fdt/dts/arm/cubieboard2.dts
  head/sys/boot/fdt/dts/arm/sun4i-a10.dtsi
  head/sys/boot/fdt/dts/arm/sun7i-a20.dtsi
  head/sys/dev/mmc/mmc.c

Modified: head/sys/arm/allwinner/a10_clk.c
==============================================================================
--- head/sys/arm/allwinner/a10_clk.c	Thu May 21 17:39:42 2015	(r283252)
+++ head/sys/arm/allwinner/a10_clk.c	Thu May 21 17:39:42 2015	(r283253)
@@ -174,7 +174,8 @@ a10_clk_usb_deactivate(void)
 }
 
 int
-a10_clk_emac_activate(void) {
+a10_clk_emac_activate(void)
+{
 	struct a10_ccm_softc *sc = a10_ccm_sc;
 	uint32_t reg_value;
 
@@ -189,3 +190,110 @@ a10_clk_emac_activate(void) {
 	return (0);
 }
 
+static void
+a10_clk_pll6_enable(void)
+{
+	struct a10_ccm_softc *sc;
+	uint32_t reg_value;
+
+	/*
+	 * SATA needs PLL6 to be a 100MHz clock.
+	 * The SATA output frequency is 24MHz * n * k / m / 6.
+	 * To get to 100MHz, k & m must be equal and n must be 25.
+	 * For other uses the output frequency is 24MHz * n * k / 2.
+	 */
+	sc = a10_ccm_sc;
+	reg_value = ccm_read_4(sc, CCM_PLL6_CFG);
+	reg_value &= ~CCM_PLL_CFG_BYPASS;
+	reg_value &= ~(CCM_PLL_CFG_FACTOR_K | CCM_PLL_CFG_FACTOR_M |
+	    CCM_PLL_CFG_FACTOR_N);
+	reg_value |= (25 << CCM_PLL_CFG_FACTOR_N_SHIFT);
+	reg_value |= CCM_PLL6_CFG_SATA_CLKEN;
+	reg_value |= CCM_PLL_CFG_ENABLE;
+	ccm_write_4(sc, CCM_PLL6_CFG, reg_value);
+}
+
+static unsigned int
+a10_clk_pll6_get_rate(void)
+{
+	struct a10_ccm_softc *sc;
+	uint32_t k, n, reg_value;
+
+	sc = a10_ccm_sc;
+	reg_value = ccm_read_4(sc, CCM_PLL6_CFG);
+	n = ((reg_value & CCM_PLL_CFG_FACTOR_N) >> CCM_PLL_CFG_FACTOR_N_SHIFT);
+	k = ((reg_value & CCM_PLL_CFG_FACTOR_K) >> CCM_PLL_CFG_FACTOR_K_SHIFT) +
+	    1;
+
+	return ((CCM_CLK_REF_FREQ * n * k) / 2);
+}
+
+int
+a10_clk_mmc_activate(int devid)
+{
+	struct a10_ccm_softc *sc;
+	uint32_t reg_value;
+
+	sc = a10_ccm_sc;
+	if (sc == NULL)
+		return (ENXIO);
+
+	a10_clk_pll6_enable();
+
+	/* Gating AHB clock for SD/MMC */
+	reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
+	reg_value |= CCM_AHB_GATING_SDMMC0 << devid;
+	ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
+
+	return (0);
+}
+
+int
+a10_clk_mmc_cfg(int devid, int freq)
+{
+	struct a10_ccm_softc *sc;
+	uint32_t clksrc, m, n, ophase, phase, reg_value;
+	unsigned int pll_freq;
+
+	sc = a10_ccm_sc;
+	if (sc == NULL)
+		return (ENXIO);
+
+	freq /= 1000;
+	if (freq <= 400) {
+		pll_freq = CCM_CLK_REF_FREQ / 1000;
+		clksrc = CCM_SD_CLK_SRC_SEL_OSC24M;
+		ophase = 0;
+		phase = 0;
+		n = 2;
+	} else if (freq <= 25000) {
+		pll_freq = a10_clk_pll6_get_rate() / 1000;
+		clksrc = CCM_SD_CLK_SRC_SEL_PLL6;
+		ophase = 0;
+		phase = 5;
+		n = 2;
+	} else if (freq <= 50000) {
+		pll_freq = a10_clk_pll6_get_rate() / 1000;
+		clksrc = CCM_SD_CLK_SRC_SEL_PLL6;
+		ophase = 3;
+		phase = 5;
+		n = 0;
+	} else
+		return (EINVAL);
+	m = ((pll_freq / (1 << n)) / (freq)) - 1;
+	reg_value = ccm_read_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4));
+	reg_value &= ~CCM_SD_CLK_SRC_SEL;
+	reg_value |= (clksrc << CCM_SD_CLK_SRC_SEL_SHIFT);
+	reg_value &= ~CCM_SD_CLK_PHASE_CTR;
+	reg_value |= (phase << CCM_SD_CLK_PHASE_CTR_SHIFT);
+	reg_value &= ~CCM_SD_CLK_DIV_RATIO_N;
+	reg_value |= (n << CCM_SD_CLK_DIV_RATIO_N_SHIFT);
+	reg_value &= ~CCM_SD_CLK_OPHASE_CTR;
+	reg_value |= (ophase << CCM_SD_CLK_OPHASE_CTR_SHIFT);
+	reg_value &= ~CCM_SD_CLK_DIV_RATIO_M;
+	reg_value |= m;
+	reg_value |= CCM_PLL_CFG_ENABLE;
+	ccm_write_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4), reg_value);
+
+	return (0);
+}

Modified: head/sys/arm/allwinner/a10_clk.h
==============================================================================
--- head/sys/arm/allwinner/a10_clk.h	Thu May 21 17:39:42 2015	(r283252)
+++ head/sys/arm/allwinner/a10_clk.h	Thu May 21 17:39:42 2015	(r283253)
@@ -103,6 +103,7 @@
 #define	CCM_AHB_GATING_USB0	(1 << 0)
 #define	CCM_AHB_GATING_EHCI0	(1 << 1)
 #define	CCM_AHB_GATING_EHCI1	(1 << 3)
+#define	CCM_AHB_GATING_SDMMC0	(1 << 8)
 #define	CCM_AHB_GATING_EMAC	(1 << 17)
 
 #define	CCM_USB_PHY		(1 << 8)
@@ -110,8 +111,36 @@
 #define	CCM_USB1_RESET		(1 << 1)
 #define	CCM_USB2_RESET		(1 << 2)
 
+#define	CCM_PLL_CFG_ENABLE	(1U << 31)
+#define	CCM_PLL_CFG_BYPASS	(1U << 30)
+#define	CCM_PLL_CFG_PLL5	(1U << 25)
+#define	CCM_PLL_CFG_PLL6	(1U << 24)
+#define	CCM_PLL_CFG_FACTOR_N		0x1f00
+#define	CCM_PLL_CFG_FACTOR_N_SHIFT	8
+#define	CCM_PLL_CFG_FACTOR_K		0x30
+#define	CCM_PLL_CFG_FACTOR_K_SHIFT	4
+#define	CCM_PLL_CFG_FACTOR_M		0x3
+
+#define	CCM_PLL6_CFG_SATA_CLKEN	(1U << 14)
+
+#define	CCM_SD_CLK_SRC_SEL		0x3000000
+#define	CCM_SD_CLK_SRC_SEL_SHIFT	24
+#define	CCM_SD_CLK_SRC_SEL_OSC24M	0
+#define	CCM_SD_CLK_SRC_SEL_PLL6		1
+#define	CCM_SD_CLK_PHASE_CTR		0x700000
+#define	CCM_SD_CLK_PHASE_CTR_SHIFT	20
+#define	CCM_SD_CLK_DIV_RATIO_N		0x30000
+#define	CCM_SD_CLK_DIV_RATIO_N_SHIFT	16
+#define	CCM_SD_CLK_OPHASE_CTR		0x700
+#define	CCM_SD_CLK_OPHASE_CTR_SHIFT	8
+#define	CCM_SD_CLK_DIV_RATIO_M		0xf
+
+#define	CCM_CLK_REF_FREQ	24000000U
+
 int a10_clk_usb_activate(void);
 int a10_clk_usb_deactivate(void);
 int a10_clk_emac_activate(void);
+int a10_clk_mmc_activate(int);
+int a10_clk_mmc_cfg(int, int);
 
 #endif /* _A10_CLK_H_ */

Added: head/sys/arm/allwinner/a10_mmc.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/allwinner/a10_mmc.c	Thu May 21 17:39:42 2015	(r283253)
@@ -0,0 +1,689 @@
+/*-
+ * Copyright (c) 2013 Alexander Fedorov
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#include <arm/allwinner/a10_clk.h>
+#include <arm/allwinner/a10_mmc.h>
+
+#define	A10_MMC_MEMRES		0
+#define	A10_MMC_IRQRES		1
+#define	A10_MMC_RESSZ		2
+
+struct a10_mmc_softc {
+	bus_space_handle_t	a10_bsh;
+	bus_space_tag_t		a10_bst;
+	device_t		a10_dev;
+	int			a10_bus_busy;
+	int			a10_id;
+	int			a10_resid;
+	int			a10_timeout;
+	struct callout		a10_timeoutc;
+	struct mmc_host		a10_host;
+	struct mmc_request *	a10_req;
+	struct mtx		a10_mtx;
+	struct resource *	a10_res[A10_MMC_RESSZ];
+	uint32_t		a10_intr;
+	uint32_t		a10_intr_wait;
+	void *			a10_intrhand;
+};
+
+static struct resource_spec a10_mmc_res_spec[] = {
+	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
+	{ SYS_RES_IRQ,		0,	RF_ACTIVE | RF_SHAREABLE },
+	{ -1,			0,	0 }
+};
+
+static int a10_mmc_probe(device_t);
+static int a10_mmc_attach(device_t);
+static int a10_mmc_detach(device_t);
+static int a10_mmc_reset(struct a10_mmc_softc *);
+static void a10_mmc_intr(void *);
+static int a10_mmc_update_clock(struct a10_mmc_softc *);
+
+static int a10_mmc_update_ios(device_t, device_t);
+static int a10_mmc_request(device_t, device_t, struct mmc_request *);
+static int a10_mmc_get_ro(device_t, device_t);
+static int a10_mmc_acquire_host(device_t, device_t);
+static int a10_mmc_release_host(device_t, device_t);
+
+#define	A10_MMC_LOCK(_sc)	mtx_lock(&(_sc)->a10_mtx)
+#define	A10_MMC_UNLOCK(_sc)	mtx_unlock(&(_sc)->a10_mtx)
+#define	A10_MMC_READ_4(_sc, _reg)					\
+	bus_space_read_4((_sc)->a10_bst, (_sc)->a10_bsh, _reg)
+#define	A10_MMC_WRITE_4(_sc, _reg, _value)				\
+	bus_space_write_4((_sc)->a10_bst, (_sc)->a10_bsh, _reg, _value)
+
+static int
+a10_mmc_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+	if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-mmc"))
+		return (ENXIO);
+	device_set_desc(dev, "Allwinner Integrated MMC/SD controller");
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10_mmc_attach(device_t dev)
+{
+	device_t child;
+	struct a10_mmc_softc *sc;
+	struct sysctl_ctx_list *ctx;
+	struct sysctl_oid_list *tree;
+
+	sc = device_get_softc(dev);
+	sc->a10_dev = dev;
+	sc->a10_req = NULL;
+	sc->a10_id = device_get_unit(dev);
+	if (sc->a10_id > 3) {
+		device_printf(dev, "only 4 hosts are supported (0-3)\n");
+		return (ENXIO);
+	}
+	if (bus_alloc_resources(dev, a10_mmc_res_spec, sc->a10_res) != 0) {
+		device_printf(dev, "cannot allocate device resources\n");
+		return (ENXIO);
+	}
+	sc->a10_bst = rman_get_bustag(sc->a10_res[A10_MMC_MEMRES]);
+	sc->a10_bsh = rman_get_bushandle(sc->a10_res[A10_MMC_MEMRES]);
+	if (bus_setup_intr(dev, sc->a10_res[A10_MMC_IRQRES],
+	    INTR_TYPE_MISC | INTR_MPSAFE, NULL, a10_mmc_intr, sc,
+	    &sc->a10_intrhand)) {
+		bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res);
+		device_printf(dev, "cannot setup interrupt handler\n");
+		return (ENXIO);
+	}
+
+	/* Activate the module clock. */
+	if (a10_clk_mmc_activate(sc->a10_id) != 0) {
+		bus_teardown_intr(dev, sc->a10_res[A10_MMC_IRQRES],
+		    sc->a10_intrhand);
+		bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res);
+		device_printf(dev, "cannot activate mmc clock\n");
+		return (ENXIO);
+	}
+
+	sc->a10_timeout = 10;
+	ctx = device_get_sysctl_ctx(dev);
+	tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+	SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW,
+	    &sc->a10_timeout, 0, "Request timeout in seconds");
+	mtx_init(&sc->a10_mtx, device_get_nameunit(sc->a10_dev), "a10_mmc",
+	    MTX_DEF);
+	callout_init_mtx(&sc->a10_timeoutc, &sc->a10_mtx, 0);
+
+	/* Reset controller. */
+	if (a10_mmc_reset(sc) != 0) {
+		device_printf(dev, "cannot reset the controller\n");
+		goto fail;
+	}
+
+	sc->a10_host.f_min = 400000;
+	sc->a10_host.f_max = 52000000;
+	sc->a10_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
+	sc->a10_host.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_HSPEED;
+	sc->a10_host.mode = mode_sd;
+
+	child = device_add_child(dev, "mmc", -1);
+	if (child == NULL) {
+		device_printf(dev, "attaching MMC bus failed!\n");
+		goto fail;
+	}
+	if (device_probe_and_attach(child) != 0) {
+		device_printf(dev, "attaching MMC child failed!\n");
+		device_delete_child(dev, child);
+		goto fail;
+	}
+
+	return (0);
+
+fail:
+	callout_drain(&sc->a10_timeoutc);
+	mtx_destroy(&sc->a10_mtx);
+	bus_teardown_intr(dev, sc->a10_res[A10_MMC_IRQRES], sc->a10_intrhand);
+	bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res);
+
+	return (ENXIO);
+}
+
+static int
+a10_mmc_detach(device_t dev)
+{
+
+	return (EBUSY);
+}
+
+static int
+a10_mmc_reset(struct a10_mmc_softc *sc)
+{
+	int timeout;
+
+	A10_MMC_WRITE_4(sc, A10_MMC_GCTRL,
+	    A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_RESET);
+	timeout = 1000;
+	while (--timeout > 0) {
+		if ((A10_MMC_READ_4(sc, A10_MMC_GCTRL) & A10_MMC_RESET) == 0)
+			break;
+		DELAY(100);
+	}
+	if (timeout == 0)
+		return (ETIMEDOUT);
+
+	/* Set the timeout. */
+	A10_MMC_WRITE_4(sc, A10_MMC_TIMEOUT, 0xffffffff);
+
+	/* Clear pending interrupts. */
+	A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff);
+	/* Unmask interrupts. */
+	A10_MMC_WRITE_4(sc, A10_MMC_IMASK,
+	    A10_MMC_CMD_DONE | A10_MMC_INT_ERR_BIT |
+	    A10_MMC_DATA_OVER | A10_MMC_AUTOCMD_DONE |
+	    A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ);
+	/* Enable interrupts and AHB access. */
+	A10_MMC_WRITE_4(sc, A10_MMC_GCTRL,
+	    A10_MMC_READ_4(sc, A10_MMC_GCTRL) |
+	    A10_MMC_INT_ENABLE | A10_MMC_ACCESS_BY_AHB);
+
+	return (0);
+}
+
+static void
+a10_mmc_req_done(struct a10_mmc_softc *sc)
+{
+	struct mmc_command *cmd;
+	struct mmc_request *req;
+
+	cmd = sc->a10_req->cmd;
+	if (cmd->error != MMC_ERR_NONE) {
+		/* Reset the controller. */
+		a10_mmc_reset(sc);
+		a10_mmc_update_clock(sc);
+	}
+	/* Reset the FIFO. */
+	A10_MMC_WRITE_4(sc, A10_MMC_GCTRL,
+	    A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_FIFO_RESET);
+
+	req = sc->a10_req;
+	callout_stop(&sc->a10_timeoutc);
+	sc->a10_req = NULL;
+	sc->a10_intr = 0;
+	sc->a10_resid = 0;
+	sc->a10_intr_wait = 0;
+	req->done(req);
+}
+
+static void
+a10_mmc_req_ok(struct a10_mmc_softc *sc)
+{
+	int timeout;
+	struct mmc_command *cmd;
+	uint32_t status;
+
+	timeout = 1000;
+	while (--timeout > 0) {
+		status = A10_MMC_READ_4(sc, A10_MMC_STAS);
+		if ((status & A10_MMC_CARD_DATA_BUSY) == 0)
+			break;
+		DELAY(1000);
+	}
+	cmd = sc->a10_req->cmd;
+	if (timeout == 0) {
+		cmd->error = MMC_ERR_FAILED;
+		a10_mmc_req_done(sc);
+		return;
+	}
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			cmd->resp[0] = A10_MMC_READ_4(sc, A10_MMC_RESP3);
+			cmd->resp[1] = A10_MMC_READ_4(sc, A10_MMC_RESP2);
+			cmd->resp[2] = A10_MMC_READ_4(sc, A10_MMC_RESP1);
+			cmd->resp[3] = A10_MMC_READ_4(sc, A10_MMC_RESP0);
+		} else
+			cmd->resp[0] = A10_MMC_READ_4(sc, A10_MMC_RESP0);
+	}
+	/* All data has been transferred ? */
+	if (cmd->data != NULL && (sc->a10_resid << 2) < cmd->data->len)
+		cmd->error = MMC_ERR_FAILED;
+	a10_mmc_req_done(sc);
+}
+
+static void 
+a10_mmc_timeout(void *arg)
+{
+	struct a10_mmc_softc *sc;
+
+	sc = (struct a10_mmc_softc *)arg;
+	if (sc->a10_req != NULL) {
+		device_printf(sc->a10_dev, "controller timeout\n");
+		sc->a10_req->cmd->error = MMC_ERR_TIMEOUT;
+		a10_mmc_req_done(sc);
+	} else
+		device_printf(sc->a10_dev,
+		    "Spurious timeout - no active request\n");
+}
+
+static int
+a10_mmc_pio_transfer(struct a10_mmc_softc *sc, struct mmc_data *data)
+{
+	int i, write;
+	uint32_t bit, *buf;
+
+	buf = (uint32_t *)data->data;
+	write = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
+	bit = write ? A10_MMC_FIFO_FULL : A10_MMC_FIFO_EMPTY;
+	for (i = sc->a10_resid; i < (data->len >> 2); i++) {
+		if ((A10_MMC_READ_4(sc, A10_MMC_STAS) & bit))
+			return (1);
+		if (write)
+			A10_MMC_WRITE_4(sc, A10_MMC_FIFO, buf[i]);
+		else
+			buf[i] = A10_MMC_READ_4(sc, A10_MMC_FIFO);
+		sc->a10_resid = i + 1;
+	}
+
+	return (0);
+}
+
+static void
+a10_mmc_intr(void *arg)
+{
+	struct a10_mmc_softc *sc;
+	struct mmc_data *data;
+	uint32_t imask, rint;
+
+	sc = (struct a10_mmc_softc *)arg;
+	A10_MMC_LOCK(sc);
+	rint = A10_MMC_READ_4(sc, A10_MMC_RINTR);
+	imask = A10_MMC_READ_4(sc, A10_MMC_IMASK);
+	if (imask == 0 && rint == 0) {
+		A10_MMC_UNLOCK(sc);
+		return;
+	}
+#ifdef DEBUG
+	device_printf(sc->a10_dev, "imask: %#x, rint: %#x\n", imask, rint);
+#endif
+	if (sc->a10_req == NULL) {
+		device_printf(sc->a10_dev,
+		    "Spurious interrupt - no active request, rint: 0x%08X\n",
+		    rint);
+		A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint);
+		A10_MMC_UNLOCK(sc);
+		return;
+	}
+	if (rint & A10_MMC_INT_ERR_BIT) {
+		device_printf(sc->a10_dev, "error rint: 0x%08X\n", rint);
+		if (rint & A10_MMC_RESP_TIMEOUT)
+			sc->a10_req->cmd->error = MMC_ERR_TIMEOUT;
+		else
+			sc->a10_req->cmd->error = MMC_ERR_FAILED;
+		A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint);
+		a10_mmc_req_done(sc);
+		A10_MMC_UNLOCK(sc);
+		return;
+	}
+
+	sc->a10_intr |= rint;
+	data = sc->a10_req->cmd->data;
+	if (data != NULL && (rint & (A10_MMC_DATA_OVER |
+	    A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ)) != 0)
+		a10_mmc_pio_transfer(sc, data);
+	if ((sc->a10_intr & sc->a10_intr_wait) == sc->a10_intr_wait)
+		a10_mmc_req_ok(sc);
+
+	A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint);
+	A10_MMC_UNLOCK(sc);
+}
+
+static int
+a10_mmc_request(device_t bus, device_t child, struct mmc_request *req)
+{
+	int blksz;
+	struct a10_mmc_softc *sc;
+	struct mmc_command *cmd;
+	uint32_t cmdreg;
+
+	sc = device_get_softc(bus);
+	A10_MMC_LOCK(sc);
+	if (sc->a10_req) {
+		A10_MMC_UNLOCK(sc);
+		return (EBUSY);
+	}
+	sc->a10_req = req;
+	cmd = req->cmd;
+	cmdreg = A10_MMC_START;
+	if (cmd->opcode == MMC_GO_IDLE_STATE)
+		cmdreg |= A10_MMC_SEND_INIT_SEQ;
+	if (cmd->flags & MMC_RSP_PRESENT)
+		cmdreg |= A10_MMC_RESP_EXP;
+	if (cmd->flags & MMC_RSP_136)
+		cmdreg |= A10_MMC_LONG_RESP;
+	if (cmd->flags & MMC_RSP_CRC)
+		cmdreg |= A10_MMC_CHECK_RESP_CRC;
+
+	sc->a10_intr = 0;
+	sc->a10_resid = 0;
+	sc->a10_intr_wait = A10_MMC_CMD_DONE;
+	cmd->error = MMC_ERR_NONE;
+	if (cmd->data != NULL) {
+		sc->a10_intr_wait |= A10_MMC_DATA_OVER;
+		cmdreg |= A10_MMC_DATA_EXP | A10_MMC_WAIT_PREOVER;
+		if (cmd->data->flags & MMC_DATA_MULTI) {
+			cmdreg |= A10_MMC_SEND_AUTOSTOP;
+			sc->a10_intr_wait |= A10_MMC_AUTOCMD_DONE;
+		}
+		if (cmd->data->flags & MMC_DATA_WRITE)
+			cmdreg |= A10_MMC_WRITE;
+		blksz = min(cmd->data->len, MMC_SECTOR_SIZE);
+		A10_MMC_WRITE_4(sc, A10_MMC_BLKSZ, blksz);
+		A10_MMC_WRITE_4(sc, A10_MMC_BCNTR, cmd->data->len);
+	}
+
+	A10_MMC_WRITE_4(sc, A10_MMC_CARG, cmd->arg);
+	A10_MMC_WRITE_4(sc, A10_MMC_CMDR, cmdreg | cmd->opcode);
+	callout_reset(&sc->a10_timeoutc, sc->a10_timeout * hz,
+	    a10_mmc_timeout, sc);
+	A10_MMC_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+a10_mmc_read_ivar(device_t bus, device_t child, int which, 
+    uintptr_t *result)
+{
+	struct a10_mmc_softc *sc;
+
+	sc = device_get_softc(bus);
+	switch (which) {
+	default:
+		return (EINVAL);
+	case MMCBR_IVAR_BUS_MODE:
+		*(int *)result = sc->a10_host.ios.bus_mode;
+		break;
+	case MMCBR_IVAR_BUS_WIDTH:
+		*(int *)result = sc->a10_host.ios.bus_width;
+		break;
+	case MMCBR_IVAR_CHIP_SELECT:
+		*(int *)result = sc->a10_host.ios.chip_select;
+		break;
+	case MMCBR_IVAR_CLOCK:
+		*(int *)result = sc->a10_host.ios.clock;
+		break;
+	case MMCBR_IVAR_F_MIN:
+		*(int *)result = sc->a10_host.f_min;
+		break;
+	case MMCBR_IVAR_F_MAX:
+		*(int *)result = sc->a10_host.f_max;
+		break;
+	case MMCBR_IVAR_HOST_OCR:
+		*(int *)result = sc->a10_host.host_ocr;
+		break;
+	case MMCBR_IVAR_MODE:
+		*(int *)result = sc->a10_host.mode;
+		break;
+	case MMCBR_IVAR_OCR:
+		*(int *)result = sc->a10_host.ocr;
+		break;
+	case MMCBR_IVAR_POWER_MODE:
+		*(int *)result = sc->a10_host.ios.power_mode;
+		break;
+	case MMCBR_IVAR_VDD:
+		*(int *)result = sc->a10_host.ios.vdd;
+		break;
+	case MMCBR_IVAR_CAPS:
+		*(int *)result = sc->a10_host.caps;
+		break;
+	case MMCBR_IVAR_MAX_DATA:
+		*(int *)result = 65535;
+		break;
+	}
+
+	return (0);
+}
+
+static int
+a10_mmc_write_ivar(device_t bus, device_t child, int which,
+    uintptr_t value)
+{
+	struct a10_mmc_softc *sc;
+
+	sc = device_get_softc(bus);
+	switch (which) {
+	default:
+		return (EINVAL);
+	case MMCBR_IVAR_BUS_MODE:
+		sc->a10_host.ios.bus_mode = value;
+		break;
+	case MMCBR_IVAR_BUS_WIDTH:
+		sc->a10_host.ios.bus_width = value;
+		break;
+	case MMCBR_IVAR_CHIP_SELECT:
+		sc->a10_host.ios.chip_select = value;
+		break;
+	case MMCBR_IVAR_CLOCK:
+		sc->a10_host.ios.clock = value;
+		break;
+	case MMCBR_IVAR_MODE:
+		sc->a10_host.mode = value;
+		break;
+	case MMCBR_IVAR_OCR:
+		sc->a10_host.ocr = value;
+		break;
+	case MMCBR_IVAR_POWER_MODE:
+		sc->a10_host.ios.power_mode = value;
+		break;
+	case MMCBR_IVAR_VDD:
+		sc->a10_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 int
+a10_mmc_update_clock(struct a10_mmc_softc *sc)
+{
+	uint32_t cmdreg;
+	int retry;
+
+	cmdreg = A10_MMC_START | A10_MMC_UPCLK_ONLY |
+	    A10_MMC_WAIT_PREOVER;
+	A10_MMC_WRITE_4(sc, A10_MMC_CMDR, cmdreg);
+	retry = 0xfffff;
+	while (--retry > 0) {
+		if ((A10_MMC_READ_4(sc, A10_MMC_CMDR) & A10_MMC_START) == 0) {
+			A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff);
+			return (0);
+		}
+		DELAY(10);
+	}
+	A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff);
+	device_printf(sc->a10_dev, "timeout updating clock\n");
+
+	return (ETIMEDOUT);
+}
+
+static int
+a10_mmc_update_ios(device_t bus, device_t child)
+{
+	int error;
+	struct a10_mmc_softc *sc;
+	struct mmc_ios *ios;
+	uint32_t clkcr;
+
+	sc = device_get_softc(bus);
+	clkcr = A10_MMC_READ_4(sc, A10_MMC_CLKCR);
+	if (clkcr & A10_MMC_CARD_CLK_ON) {
+		/* Disable clock. */
+		clkcr &= ~A10_MMC_CARD_CLK_ON;
+		A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr);
+		error = a10_mmc_update_clock(sc);
+		if (error != 0)
+			return (error);
+	}
+
+	ios = &sc->a10_host.ios;
+	if (ios->clock) {
+		/* Reset the divider. */
+		clkcr &= ~A10_MMC_CLKCR_DIV;
+		A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr);
+		error = a10_mmc_update_clock(sc);
+		if (error != 0)
+			return (error);
+
+		/* Set the MMC clock. */
+		error = a10_clk_mmc_cfg(sc->a10_id, ios->clock);
+		if (error != 0)
+			return (error);
+
+		/* Enable clock. */
+		clkcr |= A10_MMC_CARD_CLK_ON;
+		A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr);
+		error = a10_mmc_update_clock(sc);
+		if (error != 0)
+			return (error);
+	}
+
+	/* Set the bus width. */
+	switch (ios->bus_width) {
+	case bus_width_1:
+		A10_MMC_WRITE_4(sc, A10_MMC_WIDTH, A10_MMC_WIDTH1);
+		break;
+	case bus_width_4:
+		A10_MMC_WRITE_4(sc, A10_MMC_WIDTH, A10_MMC_WIDTH4);
+		break;
+	case bus_width_8:
+		A10_MMC_WRITE_4(sc, A10_MMC_WIDTH, A10_MMC_WIDTH8);
+		break;
+	}
+
+	return (0);
+}
+
+static int
+a10_mmc_get_ro(device_t bus, device_t child)
+{
+
+	return (0);
+}
+
+static int
+a10_mmc_acquire_host(device_t bus, device_t child)
+{
+	struct a10_mmc_softc *sc;
+	int error;
+
+	sc = device_get_softc(bus);
+	A10_MMC_LOCK(sc);
+	while (sc->a10_bus_busy) {
+		error = msleep(sc, &sc->a10_mtx, PCATCH, "mmchw", 0);
+		if (error != 0) {
+			A10_MMC_UNLOCK(sc);
+			return (error);
+		}
+	}
+	sc->a10_bus_busy++;
+	A10_MMC_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+a10_mmc_release_host(device_t bus, device_t child)
+{
+	struct a10_mmc_softc *sc;
+
+	sc = device_get_softc(bus);
+	A10_MMC_LOCK(sc);
+	sc->a10_bus_busy--;
+	wakeup(sc);
+	A10_MMC_UNLOCK(sc);
+
+	return (0);
+}
+
+static device_method_t a10_mmc_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		a10_mmc_probe),
+	DEVMETHOD(device_attach,	a10_mmc_attach),
+	DEVMETHOD(device_detach,	a10_mmc_detach),
+
+	/* Bus interface */
+	DEVMETHOD(bus_read_ivar,	a10_mmc_read_ivar),
+	DEVMETHOD(bus_write_ivar,	a10_mmc_write_ivar),
+	DEVMETHOD(bus_print_child,	bus_generic_print_child),
+
+	/* MMC bridge interface */
+	DEVMETHOD(mmcbr_update_ios,	a10_mmc_update_ios),
+	DEVMETHOD(mmcbr_request,	a10_mmc_request),
+	DEVMETHOD(mmcbr_get_ro,		a10_mmc_get_ro),
+	DEVMETHOD(mmcbr_acquire_host,	a10_mmc_acquire_host),
+	DEVMETHOD(mmcbr_release_host,	a10_mmc_release_host),
+
+	DEVMETHOD_END
+};
+
+static devclass_t a10_mmc_devclass;
+
+static driver_t a10_mmc_driver = {
+	"a10_mmc",
+	a10_mmc_methods,
+	sizeof(struct a10_mmc_softc),
+};
+
+DRIVER_MODULE(a10_mmc, simplebus, a10_mmc_driver, a10_mmc_devclass, 0, 0);

Added: head/sys/arm/allwinner/a10_mmc.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/allwinner/a10_mmc.h	Thu May 21 17:39:42 2015	(r283253)
@@ -0,0 +1,179 @@
+/*-
+ * Copyright (c) 2013 Alexander Fedorov <alexander.fedorov at rtlservice.com>
+ * All rights reserved.
+ *
+ * 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$
+ */
+
+#ifndef	_A10_MMC_H_
+#define	_A10_MMC_H_
+
+#define	A10_MMC_GCTRL		0x00	/* Global Control Register */
+#define	A10_MMC_CLKCR		0x04	/* Clock Control Register */
+#define	A10_MMC_TIMEOUT		0x08	/* Timeout Register */
+#define	A10_MMC_WIDTH		0x0C	/* Bus Width Register */
+#define	A10_MMC_BLKSZ		0x10	/* Block Size Register */
+#define	A10_MMC_BCNTR		0x14	/* Byte Count Register */
+#define	A10_MMC_CMDR		0x18	/* Command Register */
+#define	A10_MMC_CARG		0x1C	/* Argument Register */
+#define	A10_MMC_RESP0		0x20	/* Response Register 0 */
+#define	A10_MMC_RESP1		0x24	/* Response Register 1 */
+#define	A10_MMC_RESP2		0x28	/* Response Register 2 */
+#define	A10_MMC_RESP3		0x2C	/* Response Register 3 */
+#define	A10_MMC_IMASK		0x30	/* Interrupt Mask Register */
+#define	A10_MMC_MISTA		0x34	/* Masked Interrupt Status Register */
+#define	A10_MMC_RINTR		0x38	/* Raw Interrupt Status Register */
+#define	A10_MMC_STAS		0x3C	/* Status Register */
+#define	A10_MMC_FTRGL		0x40	/* FIFO Threshold Watermark Register */
+#define	A10_MMC_FUNS		0x44	/* Function Select Register */
+#define	A10_MMC_CBCR		0x48	/* CIU Byte Count Register */
+#define	A10_MMC_BBCR		0x4C	/* BIU Byte Count Register */
+#define	A10_MMC_DBGC		0x50	/* Debug Enable Register */
+#define	A10_MMC_DMAC		0x80	/* IDMAC Control Register */
+#define	A10_MMC_DLBA		0x84	/* IDMAC Desc List Base Address Reg */
+#define	A10_MMC_IDST		0x88	/* IDMAC Status Register */
+#define	A10_MMC_IDIE		0x8C	/* IDMAC Interrupt Enable Register */
+#define	A10_MMC_CHDA		0x90
+#define	A10_MMC_CBDA		0x94
+#define	A10_MMC_FIFO		0x100	/* FIFO Access Address */
+
+/* A10_MMC_GCTRL */
+#define	A10_MMC_SOFT_RESET		(1U << 0)
+#define	A10_MMC_FIFO_RESET		(1U << 1)
+#define	A10_MMC_DMA_RESET		(1U << 2)
+#define	A10_MMC_INT_ENABLE		(1U << 4)
+#define	A10_MMC_DMA_ENABLE		(1U << 5)
+#define	A10_MMC_DEBOUNCE_ENABLE		(1U << 8)
+#define	A10_MMC_DDR_MODE		(1U << 10)
+#define	A10_MMC_ACCESS_BY_DMA		(1U << 30)
+#define	A10_MMC_ACCESS_BY_AHB		(1U << 31)
+#define	A10_MMC_RESET					\
+	(A10_MMC_SOFT_RESET | A10_MMC_FIFO_RESET | A10_MMC_DMA_RESET)
+
+/* A10_MMC_CLKCR */
+#define	A10_MMC_CARD_CLK_ON		(1U << 16)
+#define	A10_MMC_LOW_POWER_ON		(1U << 17)
+#define	A10_MMC_CLKCR_DIV		0xff
+
+/* A10_MMC_WIDTH */
+#define	A10_MMC_WIDTH1			0
+#define	A10_MMC_WIDTH4			1
+#define	A10_MMC_WIDTH8			2
+
+/* A10_MMC_CMDR */
+#define	A10_MMC_RESP_EXP		(1U << 6)
+#define	A10_MMC_LONG_RESP		(1U << 7)
+#define	A10_MMC_CHECK_RESP_CRC		(1U << 8)
+#define	A10_MMC_DATA_EXP		(1U << 9)
+#define	A10_MMC_WRITE			(1U << 10)
+#define	A10_MMC_SEQ_MODE		(1U << 11)
+#define	A10_MMC_SEND_AUTOSTOP		(1U << 12)
+#define	A10_MMC_WAIT_PREOVER		(1U << 13)
+#define	A10_MMC_STOP_ABORT_CMD		(1U << 14)
+#define	A10_MMC_SEND_INIT_SEQ		(1U << 15)
+#define	A10_MMC_UPCLK_ONLY		(1U << 21)
+#define	A10_MMC_RDCEATADEV		(1U << 22)
+#define	A10_MMC_CCS_EXP			(1U << 23)
+#define	A10_MMC_ENB_BOOT		(1U << 24)
+#define	A10_MMC_ALT_BOOT_OPT		(1U << 25)
+#define	A10_MMC_BOOT_ACK_EXP		(1U << 26)
+#define	A10_MMC_DISABLE_BOOT		(1U << 27)
+#define	A10_MMC_VOL_SWITCH		(1U << 28)
+#define	A10_MMC_START			(1U << 31)
+
+/* A10_MMC_IMASK and A10_MMC_RINTR */
+#define	A10_MMC_RESP_ERR		(1U << 1)
+#define	A10_MMC_CMD_DONE		(1U << 2)
+#define	A10_MMC_DATA_OVER		(1U << 3)
+#define	A10_MMC_TX_DATA_REQ		(1U << 4)
+#define	A10_MMC_RX_DATA_REQ		(1U << 5)
+#define	A10_MMC_RESP_CRC_ERR		(1U << 6)
+#define	A10_MMC_DATA_CRC_ERR		(1U << 7)
+#define	A10_MMC_RESP_TIMEOUT		(1U << 8)
+#define	A10_MMC_ACK_RECV		(1U << 8)
+#define	A10_MMC_DATA_TIMEOUT		(1U << 9)
+#define	A10_MMC_BOOT_START		(1U << 9)
+#define	A10_MMC_DATA_STARVE		(1U << 10)
+#define	A10_MMC_VOL_CHG_DONE		(1U << 10)
+#define	A10_MMC_FIFO_RUN_ERR		(1U << 11)
+#define	A10_MMC_HARDW_LOCKED		(1U << 12)
+#define	A10_MMC_START_BIT_ERR		(1U << 13)
+#define	A10_MMC_AUTOCMD_DONE		(1U << 14)
+#define	A10_MMC_END_BIT_ERR		(1U << 15)
+#define	A10_MMC_SDIO_INT		(1U << 16)
+#define	A10_MMC_CARD_INSERT		(1U << 30)
+#define	A10_MMC_CARD_REMOVE		(1U << 31)
+#define	A10_MMC_INT_ERR_BIT				\
+	(A10_MMC_RESP_ERR | A10_MMC_RESP_CRC_ERR |	\
+	 A10_MMC_DATA_CRC_ERR | A10_MMC_RESP_TIMEOUT |	\
+	 A10_MMC_FIFO_RUN_ERR |	A10_MMC_HARDW_LOCKED |	\
+	 A10_MMC_START_BIT_ERR | A10_MMC_END_BIT_ERR)
+
+/* A10_MMC_STAS */
+#define	A10_MMC_RX_WLFLAG		(1U << 0)
+#define	A10_MMC_TX_WLFLAG		(1U << 1)

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


More information about the svn-src-all mailing list