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