MMC Controller driver for PXA255

Jacques Fourie jacques.fourie at gmail.com
Sat Aug 30 07:37:34 UTC 2008


> On Fri, 29 Aug 2008 18:20:16 +0200
> "Jacques Fourie" <jacques.fourie at gmail.com> mentioned:
>
>> Hi,
>>
>> I've written a driver for the MMC controller found on the Intel Xscale
>> PXA255 (as found on the Gumstix Connex). It seems to work OK - I've
>> tested with a range of SD cards. The driver works in PIO mode (still
>> busy to debug some DMA issues) and on my Gumstix Connex I get around
>> 400kB/s. If anyone wants to review the code for inclusion let me know
>> and I'll be happy to provide it.
>>
>
> Great work!
> Upload it somewhere or post here, so we can take a look.
>
> Thanks!
> --
> Stanislav Sedov
> ST4096-RIPE
>
Hi,

I previously sent Warner some mods but forgot to cc the list. Here is
a diff against current (svn revision 182470).  I also made some minor
mods to the mmc stack that I'll post as soon as I've cleaned them up -
they contain a lot of extra printf's at the moment.

Jacques
-------------- next part --------------
diff -x '*svn*' -u -N -d -r fbsd_current_20080830/src/sys/arm/xscale/pxa/files.pxa fbsd_jf_priv/src/sys/arm/xscale/pxa/files.pxa
--- fbsd_current_20080830/src/sys/arm/xscale/pxa/files.pxa	2008-08-30 08:43:57.000000000 +0200
+++ fbsd_jf_priv/src/sys/arm/xscale/pxa/files.pxa	2008-08-25 16:31:49.000000000 +0200
@@ -1,4 +1,4 @@
-# $FreeBSD: head/sys/arm/xscale/pxa/files.pxa 179700 2008-06-10 03:44:14Z kevlo $
+# $FreeBSD$
 
 arm/arm/bus_space_generic.c		standard
 arm/arm/cpufunc_asm_xscale.S		standard
@@ -11,8 +11,12 @@
 arm/xscale/pxa/pxa_smi.c		standard
 arm/xscale/pxa/pxa_space.c		standard
 arm/xscale/pxa/pxa_timer.c		standard
+arm/xscale/pxa/pxa_clk_mgr.c		standard
+arm/xscale/pxa/pxa_dmac.c		standard
 
 arm/xscale/pxa/uart_bus_pxa.c		optional uart
 arm/xscale/pxa/uart_cpu_pxa.c		optional uart
 
 arm/xscale/pxa/if_smc_smi.c		optional smc
+
+arm/xscale/pxa/pxa_mci.c		optional pxa_mci
diff -x '*svn*' -u -N -d -r fbsd_current_20080830/src/sys/arm/xscale/pxa/pxa_clk_mgr.c fbsd_jf_priv/src/sys/arm/xscale/pxa/pxa_clk_mgr.c
--- fbsd_current_20080830/src/sys/arm/xscale/pxa/pxa_clk_mgr.c	1970-01-01 02:00:00.000000000 +0200
+++ fbsd_jf_priv/src/sys/arm/xscale/pxa/pxa_clk_mgr.c	2008-08-25 13:20:43.000000000 +0200
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 2008 Jacques Fourie.  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 ``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 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/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/resource.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+#include <arm/xscale/pxa/pxareg.h>
+#include <arm/xscale/pxa/pxavar.h>
+
+struct pxa_clk_mgr_softc {
+	device_t dev;
+	struct resource	*mem_res;	
+};
+
+static struct pxa_clk_mgr_softc *pxa_clk_mgr_softc = NULL;
+
+static int pxa_clk_mgr_activate(device_t dev);
+
+static void pxa_clk_mgr_deactivate(device_t dev);
+
+static int
+pxa_clk_mgr_probe(device_t dev)
+{
+
+	device_set_desc(dev, "PXA clock manager");
+	return (0);
+}
+
+static int
+pxa_clk_mgr_attach(device_t dev)
+{
+	struct pxa_clk_mgr_softc *sc = device_get_softc(dev);
+	int err;
+
+	sc->dev = dev;
+	pxa_clk_mgr_softc = sc;
+	err = pxa_clk_mgr_activate(dev);
+	if (err)
+		goto out;
+
+	err = bus_generic_attach(dev);
+out:;
+	if (err)
+		pxa_clk_mgr_deactivate(dev);
+	return (err);
+}
+
+static int
+pxa_clk_mgr_activate(device_t dev)
+{
+	struct pxa_clk_mgr_softc *sc;
+	int rid;
+
+	sc = device_get_softc(dev);
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL)
+		goto errout;
+	return (0);
+errout:
+	pxa_clk_mgr_deactivate(dev);
+	return (ENOMEM);
+}
+
+static void
+pxa_clk_mgr_deactivate(device_t dev)
+{
+	struct pxa_clk_mgr_softc *sc;
+
+	sc = device_get_softc(dev);
+	bus_generic_detach(sc->dev);
+	if (sc->mem_res)
+		bus_release_resource(dev, SYS_RES_MEMORY,
+		    rman_get_rid(sc->mem_res), sc->mem_res);
+	sc->mem_res = 0;
+	return;
+}
+
+void
+pxa_set_cken(uint32_t clock, uint32_t enable)
+{
+	uint32_t val;
+	struct pxa_clk_mgr_softc *sc;
+
+	sc = pxa_clk_mgr_softc;
+	/* Read current value */
+	val = bus_read_4(sc->mem_res, CLKMAN_CKEN);
+	if (enable)
+		val |= clock;
+	else	
+		val &= ~clock;
+	bus_write_4(sc->mem_res, CLKMAN_CKEN, val);
+}
+
+static device_method_t pxa_clk_mgr_methods[] = {
+	/* device_if */
+	DEVMETHOD(device_probe, pxa_clk_mgr_probe),
+	DEVMETHOD(device_attach, pxa_clk_mgr_attach),
+	{0, 0},
+};
+
+static driver_t pxa_clk_mgr_driver = {
+	"pxa_clk_mgr",
+	pxa_clk_mgr_methods,
+	sizeof(struct pxa_clk_mgr_softc),
+};
+static devclass_t pxa_clk_mgr_devclass;
+
+DRIVER_MODULE(pxa_clk_mgr, pxa, pxa_clk_mgr_driver, pxa_clk_mgr_devclass, 0, 0);
diff -x '*svn*' -u -N -d -r fbsd_current_20080830/src/sys/arm/xscale/pxa/pxa_dmac.c fbsd_jf_priv/src/sys/arm/xscale/pxa/pxa_dmac.c
--- fbsd_current_20080830/src/sys/arm/xscale/pxa/pxa_dmac.c	1970-01-01 02:00:00.000000000 +0200
+++ fbsd_jf_priv/src/sys/arm/xscale/pxa/pxa_dmac.c	2008-08-28 11:33:39.000000000 +0200
@@ -0,0 +1,345 @@
+/*-
+ * Copyright (c) 2006 Benno Rice.  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 ``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 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/module.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/timetc.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <arm/xscale/pxa/pxavar.h>
+#include <arm/xscale/pxa/pxareg.h>
+
+struct dmac_channel {
+	int	dc_id;
+	int	dc_allocated;
+	int	dc_busy;
+	int	dc_error;
+};
+
+struct pxa_dmac_softc {
+	struct resource *	pd_res[2];
+	device_t		pd_dev;
+	bus_space_tag_t		pd_bst;
+	bus_space_handle_t	pd_bsh;
+	struct mtx 		pd_mtx;
+	struct dmac_channel	pd_channels[DMAC_N_CHANNELS];
+};
+
+static struct resource_spec pxa_dmac_spec[] = {
+	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
+	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
+	{ -1, 0 }
+};
+
+static struct pxa_dmac_softc *dmac_softc = NULL;
+
+static int	pxa_dmac_probe(device_t);
+static int	pxa_dmac_attach(device_t);
+
+static driver_filter_t	pxa_dmac_intr;
+
+static int
+pxa_dmac_probe(device_t dev)
+{
+
+	device_set_desc(dev, "DMA Controller");
+	return (0);
+}
+
+static int
+pxa_dmac_attach(device_t dev)
+{
+	int	error, i;
+	void	*ihl;
+	struct	pxa_dmac_softc *sc;
+
+	sc = (struct pxa_dmac_softc *)device_get_softc(dev);
+
+	if (dmac_softc != NULL)
+		return (ENXIO);
+	dmac_softc = sc;
+
+	error = bus_alloc_resources(dev, pxa_dmac_spec, sc->pd_res);
+	if (error) {
+		device_printf(dev, "could not allocate resources\n");
+		return (ENXIO);
+	}
+
+	if (bus_setup_intr(dev, sc->pd_res[1], INTR_TYPE_MISC, pxa_dmac_intr,
+	    NULL, NULL, &ihl) != 0) {
+		bus_release_resources(dev, pxa_dmac_spec, sc->pd_res);
+		device_printf(dev, "could not set up interrupt\n");
+		return (ENXIO);
+	}
+
+	sc->pd_bst = rman_get_bustag(sc->pd_res[0]);
+	sc->pd_bsh = rman_get_bushandle(sc->pd_res[0]);
+
+	for (i = 0; i < DMAC_N_CHANNELS; i++) {
+		sc->pd_channels[i].dc_id = i;
+		sc->pd_channels[i].dc_allocated = 0;
+		sc->pd_channels[i].dc_busy = 0;
+		sc->pd_channels[i].dc_error = 0;
+	}
+
+	/*
+	 * We use a spin lock so we can lock it in the (fast) interrupt handler.
+	 */
+	mtx_init(&sc->pd_mtx, "dmac mutex", NULL, MTX_SPIN);
+
+	sc->pd_dev = dev;
+
+	return (0);
+}
+
+static int
+pxa_dmac_intr(void *arg)
+{
+	uint32_t	dint, dcsr;
+	int		i;
+	struct		pxa_dmac_softc *sc;
+
+	(void)arg;
+
+	sc = dmac_softc;
+
+	dint = bus_space_read_4(sc->pd_bst, sc->pd_bsh, DMAC_DINT);
+
+	//printf("DMAC intr : %08X\n", dint);
+	
+	for (i = 0; i < DMAC_N_CHANNELS; i++) {
+		if (dint & (1 << i)) {
+			dcsr = bus_space_read_4(sc->pd_bst, sc->pd_bsh,
+			    DMAC_DCSR(i));
+			mtx_lock_spin(&sc->pd_mtx);
+			if ((dcsr & DCSR_BUSERRINTR) != 0) {
+				sc->pd_channels[i].dc_error = 1;
+			}
+			sc->pd_channels[i].dc_busy = 0;
+			mtx_unlock_spin(&sc->pd_mtx);
+			wakeup_one(&(sc->pd_channels[i]));
+			bus_space_write_4(sc->pd_bst, sc->pd_bsh,
+			    DMAC_DCSR(i), dcsr);
+		}
+	}
+	
+	return (FILTER_HANDLED);
+}
+
+static device_method_t pxa_dmac_methods[] = {
+	DEVMETHOD(device_probe, pxa_dmac_probe),
+	DEVMETHOD(device_attach, pxa_dmac_attach),
+
+	{0, 0}
+};
+
+static driver_t pxa_dmac_driver = {
+	"dmac",
+	pxa_dmac_methods,
+	sizeof(struct pxa_dmac_softc),
+};
+
+static devclass_t pxa_dmac_devclass;
+
+DRIVER_MODULE(pxadmac, pxa, pxa_dmac_driver, pxa_dmac_devclass, 0, 0);
+
+int
+pxa_dmac_alloc(int priority, struct dmac_channel **channel, int timeout)
+{
+	int	i, start, end;
+	struct	pxa_dmac_softc *sc;
+
+	sc = dmac_softc;
+
+	if (priority < DMAC_PRIORITY_HIGHEST && priority > DMAC_PRIORITY_LOW) {
+		*channel = NULL;
+		return (EDOOFUS);
+	}
+
+	switch (priority) {
+	case DMAC_PRIORITY_HIGHEST:
+		start = 0;
+		end = 3;
+		break;
+
+	case DMAC_PRIORITY_HIGH:
+		start = 4;
+		end = 7;
+		break;
+
+	case DMAC_PRIORITY_LOW:
+	default:
+		start = 8;
+		end = 15;
+		break;
+	}
+
+	mtx_lock_spin(&sc->pd_mtx);
+
+retry:
+	for (i = start; i <= end; i++) {
+		if (sc->pd_channels[i].dc_allocated == 0) {
+			sc->pd_channels[i].dc_allocated = 1;
+			mtx_unlock_spin(&sc->pd_mtx);
+			*channel = &(sc->pd_channels[i]);
+			return (0);
+		}
+	}
+
+	if (timeout) {
+		msleep(sc, &sc->pd_mtx, 0, "dmaalc",
+		    timeout == -1 ? 0 : timeout);
+		if (timeout != -1)
+			timeout = 0;
+		goto retry;
+	}
+
+	mtx_unlock_spin(&sc->pd_mtx);
+	return (ENOSPC);
+}
+
+void
+pxa_dmac_release(struct dmac_channel *channel)
+{
+	struct	pxa_dmac_softc *sc;
+
+	sc = dmac_softc;
+
+	mtx_lock_spin(&sc->pd_mtx);
+	channel->dc_allocated = 0;
+	mtx_unlock_spin(&sc->pd_mtx);
+	wakeup_one(sc);
+}
+
+int
+pxa_dmac_transfer(struct dmac_channel *channel, bus_addr_t desc)
+{
+	struct		pxa_dmac_softc *sc;
+
+	sc = dmac_softc;
+
+	if ((desc & 0xf) != 0)
+		return (EINVAL);
+	if (channel->dc_busy == 1)
+		return (EBUSY);
+
+	mtx_lock_spin(&sc->pd_mtx);
+
+	channel->dc_error = 0;
+
+	bus_space_write_4(sc->pd_bst, sc->pd_bsh, DMAC_DDADR(channel->dc_id),
+	    desc);
+	bus_space_write_4(sc->pd_bst, sc->pd_bsh, DMAC_DCSR(channel->dc_id),
+	    DCSR_BUSERRINTR|DCSR_ENDINTR|DCSR_RUN);
+
+	channel->dc_busy = 1;
+
+	mtx_unlock_spin(&sc->pd_mtx);
+	return (0);
+}
+
+int
+pxa_dmac_transfer_single(struct dmac_channel *channel, bus_addr_t source,
+    bus_addr_t target, uint32_t command)
+{
+	struct		pxa_dmac_softc *sc;
+	uint32_t	dcsr;
+
+	sc = dmac_softc;
+
+	if (channel->dc_busy == 1)
+		return (EBUSY);
+
+	mtx_lock_spin(&sc->pd_mtx);
+
+	channel->dc_error = 0;
+
+	dcsr = bus_space_read_4(sc->pd_bst, sc->pd_bsh,
+	    DMAC_DCSR(channel->dc_id));
+	dcsr &= ~DCSR_RUN;
+	dcsr |= DCSR_NODESCFETCH;
+
+	/* Interrupt when transfer is complete */
+	command |= DCMD_ENDIRQEN;
+
+	bus_space_write_4(sc->pd_bst, sc->pd_bsh, DMAC_DCSR(channel->dc_id),
+	    dcsr);
+	bus_space_write_4(sc->pd_bst, sc->pd_bsh, DMAC_DSADR(channel->dc_id),
+	    source);
+	bus_space_write_4(sc->pd_bst, sc->pd_bsh, DMAC_DTADR(channel->dc_id),
+	    target);
+	bus_space_write_4(sc->pd_bst, sc->pd_bsh, DMAC_DCMD(channel->dc_id),
+	    command);
+	dcsr |= DCSR_RUN | DCSR_BUSERRINTR | DCSR_ENDINTR;
+	bus_space_write_4(sc->pd_bst, sc->pd_bsh, DMAC_DCSR(channel->dc_id),
+	    dcsr);
+
+	channel->dc_busy = 1;
+
+	mtx_unlock_spin(&sc->pd_mtx);
+	return (0);
+}
+
+void
+pxa_dmac_channel_map_valid(struct dmac_channel *channel, uint32_t map_reg)
+{
+	uint32_t val;
+	struct		pxa_dmac_softc *sc = dmac_softc;
+
+	val = DRCMR_MAPVLD | channel->dc_id;
+	bus_space_write_4(sc->pd_bst, sc->pd_bsh, DMAC_DRCMR(map_reg), val);
+}
+
+void
+pxa_dmac_channel_map_invalid(struct dmac_channel *channel, uint32_t map_reg)
+{
+	struct		pxa_dmac_softc *sc = dmac_softc;
+
+	bus_space_write_4(sc->pd_bst, sc->pd_bsh, DMAC_DRCMR(map_reg), 0);
+}
+
+int
+pxa_dmac_transfer_done(struct dmac_channel *channel)
+{
+
+	return (!channel->dc_busy);
+}
+
+int
+pxa_dmac_transfer_failed(struct dmac_channel *channel)
+{
+
+	return (channel->dc_error);
+}
diff -x '*svn*' -u -N -d -r fbsd_current_20080830/src/sys/arm/xscale/pxa/pxa_mci.c fbsd_jf_priv/src/sys/arm/xscale/pxa/pxa_mci.c
--- fbsd_current_20080830/src/sys/arm/xscale/pxa/pxa_mci.c	1970-01-01 02:00:00.000000000 +0200
+++ fbsd_jf_priv/src/sys/arm/xscale/pxa/pxa_mci.c	2008-08-29 18:09:13.000000000 +0200
@@ -0,0 +1,848 @@
+/*-
+ * Copyright (c) 2008 Jacques Fourie.  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 ``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 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/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/resource.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+#include <arm/xscale/pxa/pxareg.h>
+#include <arm/xscale/pxa/pxavar.h>
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#include "mmcbr_if.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define DBG(fmt, ...)
+#endif
+
+#define CLOCKRATE_MAX	20000000
+#define CLOCKRATE_MIN	312500
+#define CLOCKRATE	CLOCKRATE_MAX 
+#define BBSZ		512
+
+struct pxa_mci_softc {
+	void *intrhand;			
+	device_t dev;
+	int flags;
+#define CMD_STARTED	1
+#define STOP_STARTED	2
+	struct resource *irq_res;	
+	struct resource	*mem_res;	
+	struct mtx sc_mtx;
+	struct mmc_host host;
+	int bus_busy;
+	struct mmc_request *req;
+	struct mmc_command *curcmd;
+	char bounce_buffer[BBSZ];
+	uint32_t clockrate;
+	uint32_t imask;
+	uint32_t cmdat;
+	enum mmc_power_mode power_mode;
+	uint32_t xferred;
+};
+
+static inline uint8_t
+RD1(struct pxa_mci_softc *sc, bus_size_t off)
+{
+	return bus_read_1(sc->mem_res, off);
+}
+
+static inline void
+WR1(struct pxa_mci_softc *sc, bus_size_t off, uint8_t val)
+{
+	bus_write_1(sc->mem_res, off, val);
+}
+
+static inline uint32_t
+RD4(struct pxa_mci_softc *sc, bus_size_t off)
+{
+	return bus_read_4(sc->mem_res, off);
+}
+
+static inline void
+WR4(struct pxa_mci_softc *sc, bus_size_t off, uint32_t val)
+{
+	bus_write_4(sc->mem_res, off, val);
+}
+
+/* bus entry points */
+static int pxa_mci_probe(device_t dev);
+static int pxa_mci_attach(device_t dev);
+static void pxa_mci_intr(void *);
+
+/* helper routines */
+static int pxa_mci_activate(device_t dev);
+static void pxa_mci_deactivate(device_t dev);
+
+#define PXA_MCI_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
+#define	PXA_MCI_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
+#define PXA_MCI_LOCK_INIT(_sc) \
+	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
+	    "mci", MTX_DEF)
+#define PXA_MCI_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
+#define PXA_MCI_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
+#define PXA_MCI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+
+static void
+pxa_mci_stop_clock(struct pxa_mci_softc *sc)
+{
+	u_int32_t timeout = 10000;
+	u_int32_t val;
+
+	if ((val = RD4(sc, MMC_STAT)) & STAT_CLK_EN) {
+		/* Try to stop clock */
+		WR4(sc, MMC_STRPCL, STRPCL_STOP);	
+		/* Poll status with timeout */
+		do {
+			if (((val = RD4(sc, MMC_STAT)) & STAT_CLK_EN) == 0)
+				break;
+			DELAY(1);	
+		} while (timeout--);
+		
+		if (val & STAT_CLK_EN)
+			DBG("Failed to stop clock\n");	
+	}
+}
+
+/*
+ * Starts the MMC clock. Assumes that sc->clockrate has 
+ * been set.
+ */
+static void
+pxa_mci_start_clock(struct pxa_mci_softc *sc)
+{
+	uint32_t val;
+
+	val = RD4(sc, MMC_STAT);
+	if (val & STAT_CLK_EN)
+		DBG("clock enabled before start!\n");
+
+	DBG("Start clock, clockrate = %08X\n", sc->clockrate);
+	WR4(sc, MMC_CLKRT, sc->clockrate);	
+	WR4(sc, MMC_STRPCL, STRPCL_START);
+}
+
+/*
+ * softc lock must be held when making this call! 
+ */
+static void
+pxa_mci_irq_disable(struct pxa_mci_softc *sc, uint32_t mask)
+{
+	sc->imask |= mask;
+	WR4(sc, MMC_I_MASK, sc->imask);
+}
+
+/*
+ * softc lock must be held when making this call! 
+ */
+static void
+pxa_mci_irq_enable(struct pxa_mci_softc *sc, uint32_t mask)
+{
+	sc->imask &= ~mask;
+	WR4(sc, MMC_I_MASK, sc->imask);
+}
+
+static int
+pxa_mci_probe(device_t dev)
+{
+
+	device_set_desc(dev, "MCI mmc/sd host bridge");
+	return (0);
+}
+
+static int
+pxa_mci_attach(device_t dev)
+{
+	struct pxa_mci_softc *sc = device_get_softc(dev);
+	int err;
+	device_t child;
+
+	/* 
+	 * Program GPIO for alternate functions
+	 * used by MMC controller. Is this the
+	 * correct place?
+	 */
+	pxa_gpio_set_function(6, GPIO_ALT_FN_1_OUT);
+	pxa_gpio_set_function(53, GPIO_ALT_FN_1_OUT);
+	pxa_gpio_set_function(8, GPIO_ALT_FN_1_OUT);
+
+	sc->dev = dev;
+	sc->imask = MMC_I_MASK_ALL; 
+	
+	err = pxa_mci_activate(dev);
+	if (err)
+		goto out;
+
+	PXA_MCI_LOCK_INIT(sc);
+
+	/* Stop the clock */
+	pxa_mci_stop_clock(sc);
+	/* Init SPI and RESTO */
+	WR4(sc, MMC_SPI, 0);
+	WR4(sc, MMC_RESTO, MMC_RESTO_DEFAULT);
+	/* Mask all interrupts */
+	WR4(sc, MMC_I_MASK, sc->imask);
+
+	/*
+	 * Activate the interrupt
+	 */
+	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, pxa_mci_intr, sc, &sc->intrhand);
+	if (err) {
+		PXA_MCI_LOCK_DESTROY(sc);
+		goto out;
+	}
+	sc->host.f_min = CLOCKRATE_MIN;
+	sc->host.f_max = CLOCKRATE_MAX;
+	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);
+	device_set_ivars(dev, &sc->host);
+	err = bus_generic_attach(dev);
+out:;
+	if (err)
+		pxa_mci_deactivate(dev);
+	return (err);
+}
+
+static int
+pxa_mci_activate(device_t dev)
+{
+	struct pxa_mci_softc *sc;
+	int rid;
+
+	sc = device_get_softc(dev);
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL)
+		goto errout;
+	rid = 0;
+	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->irq_res == NULL)
+		goto errout;
+	return (0);
+errout:
+	pxa_mci_deactivate(dev);
+	return (ENOMEM);
+}
+
+static void
+pxa_mci_deactivate(device_t dev)
+{
+	struct pxa_mci_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (sc->intrhand)
+		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
+	sc->intrhand = 0;
+	bus_generic_detach(sc->dev);
+	if (sc->mem_res)
+		bus_release_resource(dev, SYS_RES_MEMORY,
+		    rman_get_rid(sc->mem_res), sc->mem_res);
+	sc->mem_res = 0;
+	if (sc->irq_res)
+		bus_release_resource(dev, SYS_RES_IRQ,
+		    rman_get_rid(sc->irq_res), sc->irq_res);
+	sc->irq_res = 0;
+	return;
+}
+
+static int
+pxa_mci_update_ios(device_t brdev, device_t reqdev)
+{
+	struct pxa_mci_softc *sc;
+	struct mmc_host *host;
+	struct mmc_ios *ios;
+	uint32_t clkdiv;
+
+	sc = device_get_softc(brdev);
+	host = &sc->host;
+	ios = &host->ios;
+	
+	if (ios->clock == 0) {
+		DBG("ios->clock == 0\n");
+		pxa_mci_stop_clock(sc);
+		/* Disable MMC clock */
+		pxa_set_cken(CKEN_MMC, 0);
+	} else {
+		DBG("ios->clock = %d\n", ios->clock);
+		/* Calculate clock rate.
+		 * Write on next command.
+		 */
+		clkdiv = CLOCKRATE / ios->clock;
+		if (clkdiv > (1<<6))
+			clkdiv = (1<<6);
+		sc->clockrate = fls(clkdiv) - 1;
+		/* Enable MMC clock */
+		pxa_set_cken(CKEN_MMC, 1);
+	}
+
+	if (sc->power_mode != ios->power_mode) {
+		sc->power_mode = ios->power_mode;
+
+		if (ios->power_mode == power_on) {
+			sc->cmdat |= CMDAT_INIT;
+			DBG("Setting INIT\n");
+		}
+	}
+
+
+	return (0);
+}
+
+static int 
+pxa_mci_setup_data(struct pxa_mci_softc *sc, struct mmc_data *data)
+{
+	unsigned int numblocks;
+	struct mmc_request *req;
+
+	req = data->mrq;
+
+	numblocks = (data->len + BBSZ - 1) / BBSZ;
+
+	if (data->flags & MMC_DATA_STREAM)
+		numblocks = 0xffff;
+	
+	WR4(sc, MMC_BLKLEN, BBSZ);
+	WR4(sc, MMC_NOB, numblocks);
+
+	if (data->flags & MMC_DATA_READ) {
+		;
+	} else {
+		if (data->len != BBSZ) 
+			panic("Multiblock write support");
+		bcopy(data->data, sc->bounce_buffer, data->len);
+	}
+
+	return 0;
+}
+
+static void
+pxa_mci_start_cmd(struct pxa_mci_softc *sc, struct mmc_command *cmd, uint32_t cmdat)
+{
+	struct mmc_data *data = NULL;
+	struct mmc_request *req = NULL;
+	int err;
+
+	sc->curcmd = cmd;
+	data = cmd->data;
+	req = sc->req;
+	/*if (data != NULL)
+		req = data->mrq;
+	else
+		req = cmd->mrq;
+	*/
+
+	/* Stop the clock */
+	pxa_mci_stop_clock(sc);
+
+#ifdef RSP_TYPE
+#undef RSP_TYPE
+#endif
+
+#define RSP_TYPE(x)	((x) & MMC_RSP_MASK & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
+
+	if (cmd->flags & MMC_RSP_BUSY)
+		cmdat |= CMDAT_BUSY;
+	if (MMC_RSP(cmd->flags) == MMC_RSP_NONE)
+		cmdat |= CMDAT_RESPONSE_FORMAT_NO;
+	switch (RSP_TYPE(cmd->flags)) {
+	case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */
+		cmdat |= CMDAT_RESPONSE_FORMAT_R1; 
+		break;
+	case RSP_TYPE(MMC_RSP_R2):
+		cmdat |= CMDAT_RESPONSE_FORMAT_R2; 
+		break;
+	case RSP_TYPE(MMC_RSP_R3):
+		cmdat |= CMDAT_RESPONSE_FORMAT_R3; 
+		break;
+	default:
+		break;
+	}
+
+	DBG("req = %p, data = %p, cmd = %p, opcode = %d\n", req, data, cmd, cmd->opcode);
+
+#undef RSP_TYPE
+
+	if (data != NULL) {
+		/* Setup of data transfer */
+		if ((err = pxa_mci_setup_data(sc, data)) != 0) {
+			DBG("Data setup failed : %d, %p, %p\n", err, req, req->cmd);
+			/* Mark request as done */
+			if (req->cmd->flags & STOP_STARTED)
+				req->stop->error = MMC_ERR_NO_MEMORY;
+			else
+				req->cmd->error = MMC_ERR_NO_MEMORY;
+			sc->req = NULL;
+			sc->curcmd = NULL;
+			req->done(req);
+			return;
+		}
+		cmdat &= ~CMDAT_BUSY; 
+		cmdat |= CMDAT_DATA_EN;
+		if (data->flags & MMC_DATA_WRITE)
+			cmdat |= CMDAT_WRITE;
+		if (data->flags & MMC_DATA_STREAM)
+			cmdat |= CMDAT_STREAM_BLOCK;
+	}
+
+	DBG("cmdat = %08X opcode = %d\n", cmdat, cmd->opcode);
+	
+	WR4(sc, MMC_CMD, cmd->opcode); 
+	WR4(sc, MMC_ARGH, cmd->arg >> 16);
+	WR4(sc, MMC_ARGL, cmd->arg & 0xffff);
+	WR4(sc, MMC_CMDAT, cmdat);
+	pxa_mci_start_clock(sc);
+	pxa_mci_irq_enable(sc, MMC_I_END_CMD_RES);
+	return;
+}
+
+static void
+pxa_mci_start(struct pxa_mci_softc *sc)
+{
+	struct mmc_request *req;
+	uint32_t cmdat;
+
+	req = sc->req;
+	if (req == NULL)
+		return;
+	
+	cmdat = sc->cmdat;
+	sc->cmdat &= ~CMDAT_INIT;
+
+	// assert locked
+	if (!(sc->flags & CMD_STARTED)) {
+		sc->flags |= CMD_STARTED;
+		DBG("Starting CMD\n");
+		pxa_mci_start_cmd(sc, req->cmd, cmdat);
+		return;
+	}
+	if (!(sc->flags & STOP_STARTED) && req->stop) {
+		DBG("Starting Stop\n");
+		sc->flags |= STOP_STARTED;
+		pxa_mci_start_cmd(sc, req->stop, cmdat);
+		return;
+	}
+	/* We must be done -- bad idea to do this while locked? */
+	sc->req = NULL;
+	sc->curcmd = NULL;
+	req->done(req);
+}
+
+static int
+pxa_mci_request(device_t brdev, device_t reqdev, struct mmc_request *req)
+{
+	struct pxa_mci_softc *sc = device_get_softc(brdev);
+
+	DBG("Request : %p\n", req);
+
+	PXA_MCI_LOCK(sc);
+	// XXX do we want to be able to queue up multiple commands?
+	// XXX sounds like a good idea, but all protocols are sync, so
+	// XXX maybe the idea is naive...
+	if (sc->req != NULL) {
+		PXA_MCI_UNLOCK(sc);
+		DBG("sc->req != NULL\n");
+		return EBUSY;
+	}
+	sc->req = req;
+	sc->flags = 0;
+	pxa_mci_start(sc);
+	PXA_MCI_UNLOCK(sc);
+	return (0);
+}
+
+static void
+pxa_mci_cmd_done(struct pxa_mci_softc *sc, uint32_t stat)
+{
+	struct mmc_command *cmd;
+	uint32_t val0, val1, val2;
+	int i;
+	static uint32_t prev_opcode = 256;
+
+	if (sc->curcmd == NULL)
+		return;
+	
+	cmd = sc->curcmd;
+	
+	pxa_mci_irq_disable(sc, MMC_I_END_CMD_RES);
+	
+	/*
+	 * Discard upper 8 bits of first 16-bit word.
+	 */
+	DBG("cmd response = ");
+	val0 = RD4(sc, MMC_RES) & 0xffff;
+	for (i = 0; i < 4; i++) {
+		val1 = RD4(sc, MMC_RES) & 0xffff;
+		val2 = RD4(sc, MMC_RES) & 0xffff;
+		cmd->resp[i] = val0 << 24 | val1 << 8 | val2 >> 8;
+		val0 = val2;
+		DBG("%08X ", cmd->resp[i]);
+	}
+	DBG("\n");
+	
+	cmd->error = MMC_ERR_NONE;
+
+	if (stat & STAT_TIMEOUT_RESPONSE) {
+		cmd->error = MMC_ERR_TIMEOUT;
+		if (cmd->opcode != 8 && cmd->opcode != 41 && cmd->opcode != 55) {
+			DBG("Response timeout : opcode = %d, prev = %d\n", 
+			    cmd->opcode, prev_opcode);
+		}
+	}
+	else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+		cmd->error = MMC_ERR_BADCRC;
+		DBG("cmd crc error\n");
+	}
+
+	prev_opcode = cmd->opcode;
+
+	if (cmd->data && cmd->error == MMC_ERR_NONE) {
+		if (cmd->data->flags & MMC_DATA_READ) {
+			sc->xferred = 0; 
+			pxa_mci_irq_enable(sc, MMC_I_RXFIFO_RD_REQ);
+		}
+		else if (cmd->data->flags & MMC_DATA_WRITE) {
+			sc->xferred = 0;
+			pxa_mci_irq_enable(sc, MMC_I_TXFIFO_WR_REQ);
+		}
+	} else {
+		pxa_mci_start(sc);
+	}
+}
+
+static void
+pxa_mci_data_done(struct pxa_mci_softc *sc, uint32_t stat)
+{
+	struct mmc_data *data;
+	struct mmc_command *cmd;
+
+	if (sc->curcmd == NULL || sc->curcmd->data == NULL)
+		return;
+	
+	cmd = sc->curcmd;
+	data = cmd->data;
+
+	if (stat & STAT_READ_TIME_OUT)
+		cmd->error = MMC_ERR_TIMEOUT;
+	else if (stat & (STAT_CRC_READ_ERROR | STAT_CRC_WRITE_ERROR)) {
+		cmd->error = MMC_ERR_BADCRC;
+		DBG("CRC error : %08X\n", stat);
+	}
+	else if (data->flags & MMC_DATA_READ &&
+	    (stat & STAT_RECV_FIFO_FULL)) {
+		DBG("RX fifo full\n");
+	}
+	else if (data->flags & MMC_DATA_WRITE &&
+	    (stat & STAT_XMIT_FIFO_EMPTY)) {
+		DBG("TX fifo empty\n");
+	}
+
+	/*
+	 * From Linux:
+	 * There appears to be a hardware design bug here.  There seems to
+	 * be no way to find out how much data was transferred to the card.
+	 * This means that if there was an error on any block, we mark all
+	 * data blocks as being in error.
+	 */
+	if (cmd->error != MMC_ERR_NONE) {
+		DBG("Setting transfer len to 0\n");
+		data->xfer_len = 0;
+	}
+
+	pxa_mci_irq_disable(sc, MMC_I_DATA_TRAN_DONE);
+
+	pxa_mci_start(sc);
+}
+
+static void
+pxa_mci_xfer_data(struct pxa_mci_softc *sc, unsigned char write, uint32_t stat)
+{
+	struct mmc_data *data = NULL;
+	struct mmc_request *req = NULL;
+	struct mmc_command *cmd = NULL;
+	int i;
+	unsigned char *dest = NULL;
+	uint32_t sr = 0;
+
+	cmd = sc->curcmd;
+	data = cmd->data;
+	req = sc->req;
+	
+	sr = RD4(sc, MMC_STAT);
+
+	if (write) {
+		pxa_mci_irq_disable(sc, MMC_I_TXFIFO_WR_REQ);
+		for (i = 0; i < 32; i++) 
+			WR1(sc, MMC_TXFIFO, sc->bounce_buffer[i + sc->xferred]);
+		sc->xferred += 32;
+		if (sc->xferred < data->len)
+			pxa_mci_irq_enable(sc, MMC_I_TXFIFO_WR_REQ);		
+	}
+	else {
+		pxa_mci_irq_disable(sc, MMC_I_RXFIFO_RD_REQ);
+		dest = (unsigned char *)(data->data);
+		for (i = 0; i < 32; i++)
+			dest[i + sc->xferred] = RD1(sc, MMC_RXFIFO);	
+		sc->xferred += 32;
+		if (sc->xferred < data->len)
+			pxa_mci_irq_enable(sc, MMC_I_RXFIFO_RD_REQ);
+	}
+
+	if (sc->xferred >= data->len) 
+		pxa_mci_irq_enable(sc, MMC_I_DATA_TRAN_DONE); 
+}
+
+static void
+pxa_mci_intr(void *arg)
+{
+	struct pxa_mci_softc *sc = (struct pxa_mci_softc*)arg;
+	uint32_t sr, ir;
+	struct mmc_data *data = NULL;
+	struct mmc_request *req = NULL;
+	struct mmc_command *cmd = NULL;
+	
+	PXA_MCI_LOCK(sc);
+
+	ir = RD4(sc, MMC_I_REG);
+	
+	cmd = sc->curcmd;
+	data = cmd->data;
+	req = sc->req;
+
+	DBG("IR = %08X\n", ir);
+
+	if (ir != 0) {
+		sr = RD4(sc, MMC_STAT);
+
+		DBG("SR = %08X\n", sr);
+
+		if (ir & MMC_I_END_CMD_RES)
+			pxa_mci_cmd_done(sc, sr);
+		if (ir & MMC_I_RXFIFO_RD_REQ)
+			pxa_mci_xfer_data(sc, 0, sr);
+		if (ir & MMC_I_TXFIFO_WR_REQ)
+			pxa_mci_xfer_data(sc, 1, sr);
+		if (ir & MMC_I_DATA_TRAN_DONE) {
+			if ((data->flags & MMC_DATA_WRITE) &&
+			    !(sr & STAT_PRG_DONE)) {
+				pxa_mci_irq_disable(sc, MMC_I_DATA_TRAN_DONE);
+				/* Wait for prg_done */
+				pxa_mci_irq_enable(sc, MMC_I_PRG_DONE); 
+			}
+			else
+				pxa_mci_data_done(sc, sr);
+		}
+		if (ir & MMC_I_PRG_DONE) {
+			pxa_mci_irq_disable(sc, MMC_I_PRG_DONE);
+			pxa_mci_data_done(sc, sr);
+		}
+	}
+
+	PXA_MCI_UNLOCK(sc);
+}
+
+static int
+pxa_mci_get_ro(device_t brdev, device_t reqdev)
+{
+	return (-1);
+}
+
+static int
+pxa_mci_acquire_host(device_t brdev, device_t reqdev)
+{
+	struct pxa_mci_softc *sc = device_get_softc(brdev);
+	int err = 0;
+
+	PXA_MCI_LOCK(sc);
+	while (sc->bus_busy)
+		msleep(sc, &sc->sc_mtx, PZERO, "mciah", hz / 5);
+	sc->bus_busy++;
+	PXA_MCI_UNLOCK(sc);
+	
+	return (err);
+}
+
+static int
+pxa_mci_release_host(device_t brdev, device_t reqdev)
+{
+	struct pxa_mci_softc *sc = device_get_softc(brdev);
+
+	PXA_MCI_LOCK(sc);
+	sc->bus_busy--;
+	wakeup(sc);
+	PXA_MCI_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+pxa_mci_read_ivar(device_t bus, device_t child, int which, u_char *result)
+{
+	struct pxa_mci_softc *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;
+	}
+	
+	return (0);
+}
+
+static int
+pxa_mci_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
+{
+	struct pxa_mci_softc *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;
+	case MMCBR_IVAR_HOST_OCR:
+	case MMCBR_IVAR_F_MIN:
+	case MMCBR_IVAR_F_MAX:
+		return (EINVAL);
+	}
+	return (0);
+}
+
+static device_method_t pxa_mci_methods[] = {
+	/* device_if */
+	DEVMETHOD(device_probe, pxa_mci_probe),
+	DEVMETHOD(device_attach, pxa_mci_attach),
+	/*DEVMETHOD(device_detach, pxa_mci_detach),*/
+
+	/* Bus interface */
+	DEVMETHOD(bus_read_ivar, pxa_mci_read_ivar),
+	DEVMETHOD(bus_write_ivar, pxa_mci_write_ivar),
+
+	/* mmcbr_if */
+	DEVMETHOD(mmcbr_update_ios, pxa_mci_update_ios),
+	DEVMETHOD(mmcbr_request, pxa_mci_request),
+	DEVMETHOD(mmcbr_get_ro, pxa_mci_get_ro),
+	DEVMETHOD(mmcbr_acquire_host, pxa_mci_acquire_host),
+	DEVMETHOD(mmcbr_release_host, pxa_mci_release_host),
+
+	{0, 0},
+};
+
+static driver_t pxa_mci_driver = {
+	"pxa_mci",
+	pxa_mci_methods,
+	sizeof(struct pxa_mci_softc),
+};
+static devclass_t pxa_mci_devclass;
+
+
+DRIVER_MODULE(pxa_mci, pxa, pxa_mci_driver, pxa_mci_devclass, 0, 0);
diff -x '*svn*' -u -N -d -r fbsd_current_20080830/src/sys/arm/xscale/pxa/pxa_obio.c fbsd_jf_priv/src/sys/arm/xscale/pxa/pxa_obio.c
--- fbsd_current_20080830/src/sys/arm/xscale/pxa/pxa_obio.c	2008-08-30 08:43:57.000000000 +0200
+++ fbsd_jf_priv/src/sys/arm/xscale/pxa/pxa_obio.c	2008-08-25 13:02:19.000000000 +0200
@@ -23,7 +23,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/arm/xscale/pxa/pxa_obio.c 179701 2008-06-10 06:06:15Z kevlo $");
+__FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -76,6 +76,8 @@
 	{ "uart", PXA2X0_BTUART_BASE, PXA2X0_BTUART_SIZE, { PXA2X0_INT_BTUART, 0 } },
 	{ "uart", PXA2X0_STUART_BASE, PXA2X0_STUART_SIZE, { PXA2X0_INT_STUART, 0 } },
 	{ "uart", PXA2X0_HWUART_BASE, PXA2X0_HWUART_SIZE, { PXA2X0_INT_HWUART, 0 } },
+	{ "pxa_clk_mgr", PXA2X0_CLKMAN_BASE, PXA2X0_CLKMAN_SIZE, { 0 } },	  	
+	{ "pxa_mci", PXA2X0_MMC_BASE, PXA2X0_MMC_SIZE, { PXA2X0_INT_MMC, 0 } },  			 
 	{ "smi", PXA2X0_CS0_START, PXA2X0_CS_SIZE * 6, { 0 } },
 	{ NULL, 0, 0, { 0 } }
 };
@@ -244,6 +246,10 @@
 			    start, end, count, flags));
 		return (NULL);
 	}
+	/* Temp hack */
+	if (rle->res != NULL)
+		return NULL;
+
 	if (rle->res != NULL)
 		panic("pxa_alloc_resource: resource is busy");
diff -x '*svn*' -u -N -d -r fbsd_current_20080830/src/sys/arm/xscale/pxa/pxareg.h fbsd_jf_priv/src/sys/arm/xscale/pxa/pxareg.h
--- fbsd_current_20080830/src/sys/arm/xscale/pxa/pxareg.h	2008-08-30 08:43:57.000000000 +0200
+++ fbsd_jf_priv/src/sys/arm/xscale/pxa/pxareg.h	2008-08-26 10:48:16.000000000 +0200
@@ -32,7 +32,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/sys/arm/xscale/pxa/pxareg.h 179595 2008-06-06 05:08:09Z benno $
+ * $FreeBSD$
  */
 
 
@@ -210,6 +210,9 @@
 #define  DCMD_INCTRGADDR  (1<<30)	/* increment target address */
 #define  DCMD_INCSRCADDR  (1<<31)	/* increment source address */
 
+#define DRCMR_MMC_RX	21
+#define DRCMR_MMC_TX	22
+
 #ifndef __ASSEMBLER__
 /* DMA descriptor */
 struct pxa_dma_desc {
@@ -582,6 +585,7 @@
 #define  CMDAT_INIT		(1<<6) /* preceede command with 80 clocks */
 #define  CMDAT_MMC_DMA_EN	(1<<7) /* DMA enable */
 #define MMC_RESTO	0x14	/* expected response time out */
+#define  MMC_RESTO_DEFAULT 0x40 /* default value */
 #define MMC_RDTO 	0x18	/* expected data read time out */
 #define MMC_BLKLEN	0x1c	/* block length of data transaction */
 #define MMC_NOB  	0x20	/* number of blocks (block mode) */
@@ -596,6 +600,7 @@
 #define  MMC_I_CLK_IS_OFF	(1<<4)
 #define  MMC_I_RXFIFO_RD_REQ	(1<<5)
 #define  MMC_I_TXFIFO_WR_REQ	(1<<6)
+#define  MMC_I_MASK_ALL		((1<<7) - 1)
 #define MMC_CMD  	0x30	/* index of current command */
 #define MMC_ARGH 	0x34	/* MSW part of the current command arg */
 #define MMC_ARGL 	0x38	/* LSW part of the current command arg */
diff -x '*svn*' -u -N -d -r fbsd_current_20080830/src/sys/arm/xscale/pxa/pxavar.h fbsd_jf_priv/src/sys/arm/xscale/pxa/pxavar.h
--- fbsd_current_20080830/src/sys/arm/xscale/pxa/pxavar.h	2008-08-30 08:43:57.000000000 +0200
+++ fbsd_jf_priv/src/sys/arm/xscale/pxa/pxavar.h	2008-08-25 16:58:46.000000000 +0200
@@ -34,7 +34,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/sys/arm/xscale/pxa/pxavar.h 179595 2008-06-06 05:08:09Z benno $
+ * $FreeBSD$
  *
  */
 
@@ -87,6 +87,9 @@
 int	pxa_dmac_transfer_done(struct dmac_channel *);
 int	pxa_dmac_transfer_failed(struct dmac_channel *);
 
+void	pxa_dmac_channel_map_valid(struct dmac_channel *channel, uint32_t map_reg);
+void	pxa_dmac_channel_map_invalid(struct dmac_channel *channel, uint32_t map_reg);
+
 enum pxa_device_ivars {
 	PXA_IVAR_BASE,
 };
@@ -109,4 +112,7 @@
 
 #undef CSR_ACCESSOR
 
+
+void pxa_set_cken(uint32_t clock, uint32_t enable);
+
 #endif /* _PXAVAR_H_ */


More information about the freebsd-arm mailing list