Module Name:    src
Committed By:   jkunz
Date:           Sun Mar  3 10:33:56 UTC 2013

Modified Files:
        src/sys/arch/arm/imx: imx23_apbdma.c imx23_apbdmareg.h
            imx23_apbhdmareg.h imx23_apbxdmareg.h imx23_icollreg.h imx23_ssp.c
        src/sys/arch/evbarm/conf: IMX23_OLINUXINO
Added Files:
        src/sys/arch/arm/imx: imx23_apbdmavar.h

Log Message:
Contribution from Petri Laakso:
- DMA driver stub code replaced with working code.
- Add support to multi block DMA in ssp driver.


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/imx/imx23_apbdma.c \
    src/sys/arch/arm/imx/imx23_icollreg.h src/sys/arch/arm/imx/imx23_ssp.c
cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/imx/imx23_apbdmareg.h \
    src/sys/arch/arm/imx/imx23_apbhdmareg.h \
    src/sys/arch/arm/imx/imx23_apbxdmareg.h
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/imx/imx23_apbdmavar.h
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/evbarm/conf/IMX23_OLINUXINO

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/imx/imx23_apbdma.c
diff -u src/sys/arch/arm/imx/imx23_apbdma.c:1.2 src/sys/arch/arm/imx/imx23_apbdma.c:1.3
--- src/sys/arch/arm/imx/imx23_apbdma.c:1.2	Sun Dec 16 19:40:00 2012
+++ src/sys/arch/arm/imx/imx23_apbdma.c	Sun Mar  3 10:33:56 2013
@@ -1,4 +1,4 @@
-/* $Id: imx23_apbdma.c,v 1.2 2012/12/16 19:40:00 jkunz Exp $ */
+/* $Id: imx23_apbdma.c,v 1.3 2013/03/03 10:33:56 jkunz Exp $ */
 
 /*
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -30,53 +30,25 @@
  */
 
 #include <sys/param.h>
+#include <sys/types.h>
 #include <sys/bus.h>
 #include <sys/device.h>
 #include <sys/errno.h>
+#include <sys/mutex.h>
 #include <sys/kmem.h>
-#include <sys/queue.h>
 #include <sys/systm.h>
 
+#include <arm/imx/imx23_apbdma.h>
 #include <arm/imx/imx23_apbdmareg.h>
+#include <arm/imx/imx23_apbdmavar.h>
 #include <arm/imx/imx23_apbhdmareg.h>
 #include <arm/imx/imx23_apbxdmareg.h>
-#include <arm/imx/imx23_apbdma.h>
 #include <arm/imx/imx23var.h>
 
 static int	apbdma_match(device_t, cfdata_t, void *);
 static void	apbdma_attach(device_t, device_t, void *);
 static int	apbdma_activate(device_t, enum devact);
 
-#define APBDMA_SOFT_RST_LOOP 455 /* At least 1 us ... */
-#define DMACTRL_RD(sc, reg)						\
-		bus_space_read_4(sc->sc_iot, sc->sc_hdl, (reg))
-#define DMACTRL_WR(sc, reg, val)					\
-		bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val))
-
-struct apbdma_softc {
-	device_t sc_dev;
-	bus_space_tag_t sc_iot;
-	bus_space_handle_t sc_hdl;
-	bus_dma_tag_t sc_dmat;
-	bus_dmamap_t sc_dmamp;
-	struct imx23_dma_channel *sc_channel;
-	int n_channel;
-};
-
-struct imx23_dma_cmd {
-	uint32_t next_cmd;
-	uint32_t cmd;
-	uint32_t buffer;
-	uint32_t pio[CMDPIOWORDS_MAX];
-	SIMPLEQ_ENTRY(imx23_dma_cmd) entries;
-};
-
-struct imx23_dma_channel {
-	SIMPLEQ_HEAD(simplehead, imx23_dma_cmd) head;
-	struct simplehead *headp;
-	struct apbdma_softc *sc;
-};
-
 CFATTACH_DECL3_NEW(apbdma,
 	sizeof(struct apbdma_softc),
 	apbdma_match,
@@ -88,6 +60,14 @@ CFATTACH_DECL3_NEW(apbdma,
 	0);
 
 static void	apbdma_reset(struct apbdma_softc *);
+static void	apbdma_init(struct apbdma_softc *);
+
+#define DMA_RD(sc, reg)							\
+		bus_space_read_4(sc->sc_iot, sc->sc_ioh, (reg))
+#define DMA_WR(sc, reg, val)						\
+		bus_space_write_4(sc->sc_iot, sc->sc_ioh, (reg), (val))
+
+#define APBDMA_SOFT_RST_LOOP 455 /* At least 1 us ... */
 
 static int
 apbdma_match(device_t parent, cfdata_t match, void *aux)
@@ -108,67 +88,44 @@ apbdma_attach(device_t parent, device_t 
 {
 	struct apb_attach_args *aa = aux;
 	struct apbdma_softc *sc = device_private(self);
-	//struct apb_softc *scp = device_private(parent);
-
-//	static int apbdma_attached = 0;
-//	struct imx23_dma_channel *chan;
-//	int i;
-	int error;
+	struct apb_softc *sc_parent = device_private(parent);
+	static u_int apbdma_attached = 0;
 
-//	if (apbdma_attached)
-//		return;
+	if ((strncmp(device_xname(parent), "apbh", 4) == 0) &&
+	    (apbdma_attached & F_AHBH_DMA))
+		return;
+	if ((strncmp(device_xname(parent), "apbx", 4) == 0) &&
+	    (apbdma_attached & F_AHBX_DMA))
+		return;
 
 	sc->sc_dev = self;
 	sc->sc_iot = aa->aa_iot;
 	sc->sc_dmat = aa->aa_dmat;
 
-	/*
- 	 * Parent bus softc has a pointer to DMA controller device_t for
-	 * specific bus. As different busses need different instances of the
-	 * DMA driver. The apb_softc.dmac is set up here. Now device drivers
-	 * which use DMA can pass apb_softc.dmac from their parent to apbdma
-	 * functions.
-	 */
 	if (bus_space_map(sc->sc_iot,
-	    aa->aa_addr, aa->aa_size, 0, &(sc->sc_hdl))) {
+	    aa->aa_addr, aa->aa_size, 0, &sc->sc_ioh)) {
 		aprint_error_dev(sc->sc_dev, "unable to map bus space\n");
 		return;
 	}
 
-	error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1,
-	    PAGE_SIZE, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamp);
-	if (error) {
-		aprint_error_dev(sc->sc_dev,
-			"couldn't create dma map. (error=%d)\n", error);
-		return;
-	}
-#ifdef notyet	
-	if (aa->aa_addr == HW_APBHDMA_BASE && aa->aa_size == HW_APBHDMA_SIZE) {
-		sc->sc_channel = kmem_alloc(sizeof(struct imx23_dma_channel)
-		    * APBH_DMA_N_CHANNELS, KM_SLEEP);
-		sc->n_channel = APBH_DMA_N_CHANNELS;
-	}
+	if (strncmp(device_xname(parent), "apbh", 4) == 0)
+		sc->flags = F_AHBH_DMA;
 
-	if (aa->aa_addr == HW_APBXDMA_BASE && aa->aa_size == HW_APBXDMA_SIZE) {
-		sc->sc_channel = kmem_alloc(sizeof(struct imx23_dma_channel)
-		    * APBX_DMA_N_CHANNELS, KM_SLEEP);
-		sc->n_channel = APBX_DMA_N_CHANNELS;
-	}
-
-	if (sc->sc_channel == NULL) {
-		aprint_error_dev(sc->sc_dev, "unable to allocate memory for"
-		    " DMA channel structures\n");
-		return;
-	}
+	if (strncmp(device_xname(parent), "apbx", 4) == 0)
+		sc->flags = F_AHBX_DMA;
 
-	for (i=0; i < sc->n_channel; i++) {
-		chan = (struct imx23_dma_channel *)sc->sc_channel+i;
-		chan->sc = sc;
-		SIMPLEQ_INIT(&chan->head);
-	}
-#endif
 	apbdma_reset(sc);
-//	apbdma_attached = 1;
+	apbdma_init(sc);
+
+	if (sc->flags & F_AHBH_DMA)
+		apbdma_attached |= F_AHBH_DMA;
+	if (sc->flags & F_AHBX_DMA)
+		apbdma_attached |= F_AHBX_DMA;
+
+	sc_parent->dmac = self;
+
+	/* Initialize mutex to control concurrent access from the drivers. */
+	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH);
 
 	aprint_normal("\n");
 
@@ -195,85 +152,242 @@ apbdma_reset(struct apbdma_softc *sc)
 	 * Prepare for soft-reset by making sure that SFTRST is not currently
 	 * asserted. Also clear CLKGATE so we can wait for its assertion below.
 	 */
-	DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
+	DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
 
 	/* Wait at least a microsecond for SFTRST to deassert. */
 	loop = 0;
-	while ((DMACTRL_RD(sc, HW_APB_CTRL0) &  HW_APB_CTRL0_SFTRST) ||
-		(loop < APBDMA_SOFT_RST_LOOP))
-	{
+	while ((DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
+	    (loop < APBDMA_SOFT_RST_LOOP))
 		loop++;
-	}
 
 	/* Clear CLKGATE so we can wait for its assertion below. */
-	DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
+	DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
 
 	/* Soft-reset the block. */
-	DMACTRL_WR(sc, HW_APB_CTRL0_SET, HW_APB_CTRL0_SFTRST);
+	DMA_WR(sc, HW_APB_CTRL0_SET, HW_APB_CTRL0_SFTRST);
 
 	/* Wait until clock is in the gated state. */
-	while (!(DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE));
+	while (!(DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE));
 
 	/* Bring block out of reset. */
-	DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
+	DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
 
 	loop = 0;
-	while ((DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
-		(loop < APBDMA_SOFT_RST_LOOP))
-	{
+	while ((DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
+	    (loop < APBDMA_SOFT_RST_LOOP))
 		loop++;
+
+	DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
+
+	/* Wait until clock is in the NON-gated state. */
+	while (DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE);
+
+	return;
+}
+
+/*
+ * Initialize APB{H,X}DMA block.
+ */
+static void
+apbdma_init(struct apbdma_softc *sc)
+{
+
+	if (sc->flags & F_AHBH_DMA) {
+		DMA_WR(sc, HW_APBH_CTRL0_SET, HW_APBH_CTRL0_AHB_BURST8_EN);
+		DMA_WR(sc, HW_APBH_CTRL0_SET, HW_APBH_CTRL0_APB_BURST4_EN);
+	}
+	return;
+}
+
+/* 
+ * Chain DMA commands together.
+ *
+ * Set src->next point to trg's physical DMA mapped address.
+ */
+void
+apbdma_cmd_chain(apbdma_command_t src, apbdma_command_t trg, void *buf,
+    bus_dmamap_t dmap)
+{
+	int i; 
+	bus_size_t daddr;
+	bus_addr_t trg_offset;
+
+	trg_offset = (bus_addr_t)trg - (bus_addr_t)buf;
+	daddr = 0;
+
+	for (i = 0; i < dmap->dm_nsegs; i++) {
+		daddr += dmap->dm_segs[i].ds_len;
+		if (trg_offset < daddr) {
+			src->next = (void *)(dmap->dm_segs[i].ds_addr +
+			    (trg_offset - (daddr - dmap->dm_segs[i].ds_len)));
+			break;
+		}
+	}
+
+	return;
+}
+
+/*
+ * Set DMA command buffer.
+ *
+ * Set cmd->buffer point to physical DMA address at offset in DMA map.
+ */
+void
+apbdma_cmd_buf(apbdma_command_t cmd, bus_addr_t offset, bus_dmamap_t dmap)
+{
+	int i;
+	bus_size_t daddr;
+
+	daddr = 0;
+
+	for (i = 0; i < dmap->dm_nsegs; i++) {
+		daddr += dmap->dm_segs[i].ds_len;
+		if (offset < daddr) {
+			cmd->buffer = (void *)(dmap->dm_segs[i].ds_addr +
+			    (offset - (daddr - dmap->dm_segs[i].ds_len)));
+			break;
+		}
+	}
+
+	return;
+}
+
+/*
+ * Initialize DMA channel.
+ */
+void
+apbdma_chan_init(struct apbdma_softc *sc, unsigned int channel)
+{
+
+	mutex_enter(&sc->sc_lock);
+
+	/* Enable CMDCMPLT_IRQ. */
+	DMA_WR(sc, HW_APB_CTRL1_SET, (1<<channel)<<16);
+
+	mutex_exit(&sc->sc_lock);
+
+	return;
+}
+
+/*
+ * Set command chain for DMA channel.
+ */
+#define HW_APB_CHN_NXTCMDAR(base, channel)	(base + (0x70 * channel))
+void
+apbdma_chan_set_chain(struct apbdma_softc *sc, unsigned int channel,
+	bus_dmamap_t dmap)
+{
+	uint32_t reg;
+
+	if (sc->flags & F_AHBH_DMA)
+		reg = HW_APB_CHN_NXTCMDAR(HW_APBH_CH0_NXTCMDAR, channel);
+	else
+		reg = HW_APB_CHN_NXTCMDAR(HW_APBX_CH0_NXTCMDAR, channel);
+	
+	mutex_enter(&sc->sc_lock);
+	DMA_WR(sc, reg, dmap->dm_segs[0].ds_addr);
+	mutex_exit(&sc->sc_lock);
+
+	return;
+}
+
+/*
+ * Initiate DMA transfer.
+ */
+#define HW_APB_CHN_SEMA(base, channel)	(base + (0x70 * channel))
+void
+apbdma_run(struct apbdma_softc *sc, unsigned int channel)
+{
+	uint32_t reg;
+	uint8_t val;
+
+	if (sc->flags & F_AHBH_DMA) {
+		reg = HW_APB_CHN_SEMA(HW_APBH_CH0_SEMA, channel);
+		val = __SHIFTIN(1, HW_APBH_CH0_SEMA_INCREMENT_SEMA);
+	 } else {
+		reg = HW_APB_CHN_SEMA(HW_APBX_CH0_SEMA, channel);
+		val = __SHIFTIN(1, HW_APBX_CH0_SEMA_INCREMENT_SEMA);
 	}
-         
-	DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
 
-        /* Wait until clock is in the NON-gated state. */
-	while (DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE);
+	mutex_enter(&sc->sc_lock);
+	DMA_WR(sc, reg, val);
+	mutex_exit(&sc->sc_lock);
 
-        return;
+	return;
 }
 
 /*
- * Allocate DMA safe memory for commands.
- */
-void *
-apbdma_dmamem_alloc(device_t dmac, int channel, bus_size_t size)
-{
-	struct apbdma_softc *sc = device_private(dmac);
-	bus_dma_segment_t segs[1];	/* bus_dmamem_free needs. */
-	int rsegs;
-	int error;
-	void *ptr = NULL;	/* bus_dmamem_unmap needs (size also) */
-
-	if (size > PAGE_SIZE)
-		return NULL;
-
-	error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, segs, 1,
-			&rsegs, BUS_DMA_NOWAIT);
-	if (error)
-		goto out;
-//XXX:
-	printf("segs[0].ds_addr=%lx, segs[0].ds_len=%lx, rsegs=%d\n", segs[0].ds_addr, segs[0].ds_len, rsegs);
-
-	error = bus_dmamem_map(sc->sc_dmat, segs, 1, size, &ptr,
-			BUS_DMA_NOWAIT);
-	if (error)
-		goto free;
-//XXX:
-	printf("segs[0].ds_addr=%lx, segs[0].ds_len=%lx, ptr=%p\n", segs[0].ds_addr, segs[0].ds_len, ptr);
-
-	error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, ptr, size, NULL,
-			BUS_DMA_NOWAIT | BUS_DMA_WRITE);
-	if (error)
-		goto unmap;
-
-	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, size,
-		BUS_DMASYNC_PREWRITE);
-
-	// return usable memory
-unmap:
-	bus_dmamem_unmap(sc->sc_dmat, ptr, size);
-free:
-	bus_dmamem_free(sc->sc_dmat, segs, 1);
-out:
-	return NULL;
+ * Acknowledge command complete IRQ.
+ */
+void
+apbdma_ack_intr(struct apbdma_softc *sc, unsigned int channel)
+{
+
+	mutex_enter(&sc->sc_lock);
+	DMA_WR(sc, HW_APB_CTRL1_CLR, (1<<channel));
+	mutex_exit(&sc->sc_lock);
+
+	return;
+}
+
+/*
+ * Acknowledge error IRQ.
+ */
+void
+apbdma_ack_error_intr(struct apbdma_softc *sc, unsigned int channel)
+{
+
+	mutex_enter(&sc->sc_lock);
+	DMA_WR(sc, HW_APB_CTRL2_CLR, (1<<channel));
+	mutex_exit(&sc->sc_lock);
+
+	return;
+}
+
+/*
+ * Return reason for the IRQ.
+ */
+unsigned int
+apbdma_intr_status(struct apbdma_softc *sc, unsigned int channel)
+{
+	unsigned int reason;
+
+	reason = 0;
+
+	mutex_enter(&sc->sc_lock);
+
+	/* Check if this was command complete IRQ. */
+	if (DMA_RD(sc, HW_APB_CTRL1) & (1<<channel))
+		reason = DMA_IRQ_CMDCMPLT;
+
+	/* Check if error was set. */
+	if (DMA_RD(sc, HW_APB_CTRL2) & (1<<channel)) {
+		if (DMA_RD(sc, HW_APB_CTRL2) & (1<<channel)<<16)
+			reason = DMA_IRQ_BUS_ERROR;
+		else
+			reason = DMA_IRQ_TERM;
+	}
+
+	mutex_exit(&sc->sc_lock);
+
+	return reason;
+}
+
+/*
+ * Reset DMA channel.
+ * Use only for devices on APBH bus.
+ */
+void
+apbdma_chan_reset(struct apbdma_softc *sc, unsigned int channel)
+{
+	
+	mutex_enter(&sc->sc_lock);
+
+	DMA_WR(sc, HW_APB_CTRL0_SET,
+	    __SHIFTIN((1<<channel), HW_APBH_CTRL0_RESET_CHANNEL));
+	while(DMA_RD(sc, HW_APB_CTRL0) & HW_APBH_CTRL0_RESET_CHANNEL);
+
+	mutex_exit(&sc->sc_lock);
+
+	return;
 }
Index: src/sys/arch/arm/imx/imx23_icollreg.h
diff -u src/sys/arch/arm/imx/imx23_icollreg.h:1.2 src/sys/arch/arm/imx/imx23_icollreg.h:1.3
--- src/sys/arch/arm/imx/imx23_icollreg.h:1.2	Sun Dec 16 19:40:00 2012
+++ src/sys/arch/arm/imx/imx23_icollreg.h	Sun Mar  3 10:33:56 2013
@@ -1,4 +1,4 @@
-/* $Id: imx23_icollreg.h,v 1.2 2012/12/16 19:40:00 jkunz Exp $ */
+/* $Id: imx23_icollreg.h,v 1.3 2013/03/03 10:33:56 jkunz Exp $ */
 
 /*
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -63,7 +63,7 @@
 				    * suspended. */
 #define IRQ_GPMI_DMA		13 /* From DMA channel for GPMI */
 #define IRQ_SSP1_DMA		14 /* From DMA channel for SSP1 */
-#define IRQ_SSP_ERROR		15 /* SSP1 device-level error and status */
+#define IRQ_SSP1_ERROR		15 /* SSP1 device-level error and status */
 #define IRQ_GPIO0		16 /* GPIO bank 0 interrupt */
 #define IRQ_GPIO1		17 /* GPIO bank 1 interrupt */
 #define IRQ_GPIO2		18 /* GPIO bank 2 interrupt */
Index: src/sys/arch/arm/imx/imx23_ssp.c
diff -u src/sys/arch/arm/imx/imx23_ssp.c:1.2 src/sys/arch/arm/imx/imx23_ssp.c:1.3
--- src/sys/arch/arm/imx/imx23_ssp.c:1.2	Sun Dec 16 19:45:52 2012
+++ src/sys/arch/arm/imx/imx23_ssp.c	Sun Mar  3 10:33:56 2013
@@ -1,4 +1,4 @@
-/* $Id: imx23_ssp.c,v 1.2 2012/12/16 19:45:52 jkunz Exp $ */
+/* $Id: imx23_ssp.c,v 1.3 2013/03/03 10:33:56 jkunz Exp $ */
 
 /*
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -33,10 +33,16 @@
 #include <sys/types.h>
 #include <sys/bus.h>
 #include <sys/cdefs.h>
+#include <sys/condvar.h>
 #include <sys/device.h>
 #include <sys/errno.h>
+#include <sys/mutex.h>
 #include <sys/systm.h>
 
+#include <arm/pic/picvar.h>
+
+#include <arm/imx/imx23_apbdmavar.h>
+#include <arm/imx/imx23_icollreg.h>
 #include <arm/imx/imx23_sspreg.h>
 #include <arm/imx/imx23var.h>
 
@@ -46,15 +52,33 @@
 
 /*
  * SD/MMC host controller driver for i.MX23.
+ *
+ * TODO:
+ *
+ * - Add support for SMC_CAPS_AUTO_STOP. 
  */
 
-struct issp_softc {
+#define DMA_MAXNSEGS ((MAXPHYS / PAGE_SIZE) + 1)
+
+typedef struct issp_softc {
 	device_t sc_dev;
-	bus_space_tag_t sc_iot;
+	apbdma_softc_t sc_dmac;
+	bus_dma_tag_t sc_dmat; 
+	bus_dmamap_t sc_dmamp;
+	bus_size_t sc_chnsiz;
+	bus_dma_segment_t sc_ds[1];
+	int sc_rseg;
 	bus_space_handle_t sc_hdl;
+	bus_space_tag_t sc_iot;
 	device_t sc_sdmmc;
-	device_t dmac;
-};
+	kmutex_t sc_lock;
+	struct kcondvar sc_intr_cv;
+	unsigned int dma_channel;
+	uint32_t sc_dma_error;
+	uint32_t sc_irq_error;
+	uint8_t sc_state;
+	uint8_t sc_bus_width;
+} *issp_softc_t;
 
 static int	issp_match(device_t, cfdata_t, void *);
 static void	issp_attach(device_t, device_t, void *);
@@ -62,7 +86,16 @@ static int	issp_activate(device_t, enum 
 
 static void	issp_reset(struct issp_softc *);
 static void	issp_init(struct issp_softc *);
-static uint32_t	issp_set_sck(struct issp_softc *, uint32_t target);
+static uint32_t	issp_set_sck(struct issp_softc *, uint32_t);
+static int	issp_dma_intr(void *);
+static int	issp_error_intr(void *);
+static void	issp_ack_intr(struct issp_softc *);
+static void	issp_create_dma_cmd_list_multi(issp_softc_t, void *,
+    struct sdmmc_command *);
+static void	issp_create_dma_cmd_list_single(issp_softc_t, void *,
+    struct sdmmc_command *);
+static void	issp_create_dma_cmd_list(issp_softc_t, void *,
+    struct sdmmc_command *);
 
 /* sdmmc(4) driver chip function prototypes. */
 static int	issp_host_reset(sdmmc_chipset_handle_t);
@@ -75,7 +108,7 @@ static int	issp_bus_clock(sdmmc_chipset_
 static int	issp_bus_width(sdmmc_chipset_handle_t, int);
 static int	issp_bus_rod(sdmmc_chipset_handle_t, int);
 static void	issp_exec_command(sdmmc_chipset_handle_t,
-			struct sdmmc_command *);
+		struct sdmmc_command *);
 static void	issp_card_enable_intr(sdmmc_chipset_handle_t, int);
 static void	issp_card_intr_ack(sdmmc_chipset_handle_t);
 
@@ -102,7 +135,8 @@ CFATTACH_DECL3_NEW(ssp,
 	issp_activate,
 	NULL,
 	NULL,
-	0);
+	0
+);
 
 #define SSP_SOFT_RST_LOOP 455	/* At least 1 us ... */
 
@@ -115,24 +149,37 @@ CFATTACH_DECL3_NEW(ssp,
 #define SSP_CLK_MIN	400		/* 400 kHz */
 #define SSP_CLK_MAX	48000		/* 48 MHz */
 
-#define SSP_BUSY (HW_SSP_STATUS_CMD_BUSY |				\
-			HW_SSP_STATUS_DATA_BUSY |			\
-			HW_SSP_STATUS_BUSY)
-
-#define SSP_RUN_ERR (HW_SSP_STATUS_RESP_CRC_ERR |			\
-			HW_SSP_STATUS_RESP_ERR |			\
-			HW_SSP_STATUS_RESP_TIMEOUT |			\
-			HW_SSP_STATUS_DATA_CRC_ERR |			\
-			HW_SSP_STATUS_TIMEOUT)
-
-#define BLKIO_NONE 0
-#define BLKIO_RD 1
-#define BLKIO_WR 2
+/* DATA_TIMEOUT is calculated as: * (1 / SSP_CLK) * (DATA_TIMEOUT * 4096) */
+#define DATA_TIMEOUT 0x4240	/* 723ms */
 
 #define BUS_WIDTH_1_BIT 0x0
 #define BUS_WIDTH_4_BIT 0x1
 #define BUS_WIDTH_8_BIT 0x2
 
+#define SSP1_ATTACHED	1
+#define SSP2_ATTACHED	2
+
+/* Flags for sc_state. */
+#define SSP_STATE_IDLE	0
+#define SSP_STATE_DMA	1
+
+#define PIO_WORD_CTRL0	0
+#define PIO_WORD_CMD0	1
+#define PIO_WORD_CMD1	2
+
+#define HW_SSP_CTRL1_IRQ_MASK (						\
+    HW_SSP_CTRL1_SDIO_IRQ |						\
+    HW_SSP_CTRL1_RESP_ERR_IRQ |						\
+    HW_SSP_CTRL1_RESP_TIMEOUT_IRQ |					\
+    HW_SSP_CTRL1_DATA_TIMEOUT_IRQ |					\
+    HW_SSP_CTRL1_DATA_CRC_IRQ |						\
+    HW_SSP_CTRL1_FIFO_UNDERRUN_IRQ |					\
+    HW_SSP_CTRL1_RECV_TIMEOUT_IRQ |					\
+    HW_SSP_CTRL1_FIFO_OVERRUN_IRQ)
+
+/* SSP does not support over 64k transfer size. */
+#define MAX_TRANSFER_SIZE 65536
+
 static int
 issp_match(device_t parent, cfdata_t match, void *aux)
 {
@@ -154,18 +201,108 @@ issp_attach(device_t parent, device_t se
 	struct apb_softc *sc_parent = device_private(parent);
 	struct apb_attach_args *aa = aux;
 	struct sdmmcbus_attach_args saa;
-	static int issp_attached = 0;
-
-	if (issp_attached)
-		return;
+	static int ssp_attached = 0;
+	int error;
+	void *intr;
 
 	sc->sc_dev = self;
 	sc->sc_iot = aa->aa_iot;
-	sc->dmac = sc_parent->dmac;
+	sc->sc_dmat = aa->aa_dmat;
+
+	/* Test if device instance is already attached. */
+	if (aa->aa_addr == HW_SSP1_BASE && ISSET(ssp_attached, SSP1_ATTACHED)) {
+		aprint_error_dev(sc->sc_dev, "SSP1 already attached\n");
+		return;
+	}
+	if (aa->aa_addr == HW_SSP2_BASE && ISSET(ssp_attached, SSP2_ATTACHED)) {
+		aprint_error_dev(sc->sc_dev, "SSP2 already attached\n");
+		return;
+	}
+
+	if (aa->aa_addr == HW_SSP1_BASE) {
+		sc->dma_channel = APBH_DMA_CHANNEL_SSP1;
+	}
+	if (aa->aa_addr == HW_SSP2_BASE) {
+		sc->dma_channel = APBH_DMA_CHANNEL_SSP2;
+	}
+
+	/* This driver requires DMA functionality from the bus.
+	 * Parent bus passes handle to the DMA controller instance. */
+	if (sc_parent->dmac == NULL) {
+		aprint_error_dev(sc->sc_dev, "DMA functionality missing\n");
+		return;
+	}
+	sc->sc_dmac = device_private(sc_parent->dmac);
+
+	/* Initialize lock. */
+	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SDMMC);
+
+	/* Condvar to wait interrupt complete. */
+	cv_init(&sc->sc_intr_cv, "ssp_intr");
 
-	if (bus_space_map(sc->sc_iot,
-	    aa->aa_addr, aa->aa_size, 0, &(sc->sc_hdl))) {
-		aprint_error_dev(sc->sc_dev, "unable to map bus space\n");
+	/* Establish interrupt handlers for SSP errors and SSP DMA. */
+	if (aa->aa_addr == HW_SSP1_BASE) {
+		intr = intr_establish(IRQ_SSP1_DMA, IPL_SDMMC, IST_LEVEL,
+		    issp_dma_intr, sc);
+		if (intr == NULL) {
+			aprint_error_dev(sc->sc_dev, "Unable to establish "
+			    "interrupt for SSP1 DMA\n");
+			return;
+		}
+		intr = intr_establish(IRQ_SSP1_ERROR, IPL_SDMMC, IST_LEVEL,
+		    issp_error_intr, sc);
+		if (intr == NULL) {
+			aprint_error_dev(sc->sc_dev, "Unable to establish "
+			    "interrupt for SSP1 ERROR\n");
+			return;
+		}
+	}
+
+	if (aa->aa_addr == HW_SSP2_BASE) {
+		intr = intr_establish(IRQ_SSP2_DMA, IPL_SDMMC, IST_LEVEL,
+		    issp_dma_intr, sc);
+		if (intr == NULL) {
+			aprint_error_dev(sc->sc_dev, "Unable to establish "
+			    "interrupt for SSP2 DMA\n");
+			return;
+		}
+		intr = intr_establish(IRQ_SSP2_ERROR, IPL_SDMMC, IST_LEVEL,
+		    issp_error_intr, sc);
+		if (intr == NULL) {
+			aprint_error_dev(sc->sc_dev, "Unable to establish "
+			    "interrupt for SSP2 ERROR\n");
+			return;
+		}
+	}
+
+	/* Allocate DMA handle. */
+	error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, 1, MAXPHYS,
+	    0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamp);
+	if (error) {
+		aprint_error_dev(sc->sc_dev,
+		    "Unable to allocate DMA handle\n");
+		return;
+	}
+
+	/* Allocate memory for DMA command chain. */
+	sc->sc_chnsiz = sizeof(struct apbdma_command) *
+	    (MAX_TRANSFER_SIZE / SDMMC_SECTOR_SIZE);
+
+	error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_chnsiz, PAGE_SIZE, 0,
+	    sc->sc_ds, 1, &sc->sc_rseg, BUS_DMA_NOWAIT);
+	if (error) {
+		aprint_error_dev(sc->sc_dev,
+		    "Unable to allocate DMA memory\n");
+		return;
+	}
+
+	/* Initialize DMA channel. */
+	apbdma_chan_init(sc->sc_dmac, sc->dma_channel);
+
+	/* Map SSP bus space. */
+	if (bus_space_map(sc->sc_iot, aa->aa_addr, aa->aa_size, 0,
+	    &sc->sc_hdl)) {
+		aprint_error_dev(sc->sc_dev, "Unable to map SSP bus space\n");
 		return;
 	}
 
@@ -177,6 +314,7 @@ issp_attach(device_t parent, device_t se
 	    __SHIFTOUT(issp_vers, HW_SSP_VERSION_MAJOR),
 	    __SHIFTOUT(issp_vers, HW_SSP_VERSION_MINOR));
 
+	/* Attach sdmmc to ssp bus. */
 	saa.saa_busname = "sdmmc";
 	saa.saa_sct	= &issp_functions;
 	saa.saa_spi_sct	= NULL;
@@ -184,8 +322,8 @@ issp_attach(device_t parent, device_t se
 	saa.saa_dmat	= aa->aa_dmat;
 	saa.saa_clkmin	= SSP_CLK_MIN;
 	saa.saa_clkmax	= SSP_CLK_MAX;
-	/* Add SMC_CAPS_DMA capability when DMA funtionality is implemented. */
-	saa.saa_caps	= SMC_CAPS_4BIT_MODE | SMC_CAPS_SINGLE_ONLY;
+	saa.saa_caps	= SMC_CAPS_DMA | SMC_CAPS_4BIT_MODE |
+	    SMC_CAPS_MULTI_SEG_DMA;
 
 	sc->sc_sdmmc = config_found(sc->sc_dev, &saa, NULL);
 	if (sc->sc_sdmmc == NULL) {
@@ -193,7 +331,11 @@ issp_attach(device_t parent, device_t se
 		return;
 	}
 
-	issp_attached = 1;
+	/* Device instance was succesfully attached. */
+	if (aa->aa_addr == HW_SSP1_BASE)
+		ssp_attached |= SSP1_ATTACHED;
+	if (aa->aa_addr == HW_SSP2_BASE)
+		ssp_attached |= SSP2_ATTACHED;
 
 	return;
 }
@@ -211,16 +353,14 @@ static int
 issp_host_reset(sdmmc_chipset_handle_t sch)
 {
 	struct issp_softc *sc = sch;
-
 	issp_reset(sc);
-
 	return 0;
 }
 
 static uint32_t
 issp_host_ocr(sdmmc_chipset_handle_t sch)
 {
-	/* SSP supports at least 3.2-3.3v */
+	/* SSP supports at least 3.2 - 3.3v */
 	return MMC_OCR_3_2V_3_3V;
 }
 
@@ -237,29 +377,6 @@ issp_host_maxblklen(sdmmc_chipset_handle
 static int
 issp_card_detect(sdmmc_chipset_handle_t sch)
 {
-	/* struct issp_softc *sc = sch;
-	 *
-	 * In the perfect world I'll just:
-	 * 	return SSP_RD(sc, HW_SSP_STATUS) & HW_SSP_STATUS_CARD_DETECT;
-	 * and call it a day.
-	 *
-	 * But on i.MX23 OLinuXino MAXI, SSP1_DETECT is not used for the SD
-	 * card detection but SSP1_DATA3 is, as Tsvetan put it:
-	 *
-	 * < Tsvetan> if you want to know if SD card is inserted watch
-	 * 		CD/DAT3/CS port
-	 * < Tsvetan> without card there is R20 weak pulldown
-	 * < Tsvetan> all cards have 40K pullup to this pin
-	 * < Tsvetan> so when card is inserted you will read it high
-	 *
-	 * Which means I should to do something like this:
-	 * 	#if BOARDTYPE == MAXI (Possibly MINI & MICRO)
-	 * 		return GPIO_READ(PIN_125) & PIN_125
-	 * 	#else
-	 * 		return SSP_RD(sc, STATUS) & CARD_DETECT;
-	 * 	#endif
-	 * Until GPIO functionality is not present I am just going to */
-
 	return 1;
 }
 
@@ -283,13 +400,22 @@ issp_bus_clock(sdmmc_chipset_handle_t sc
 	struct issp_softc *sc = sch;
 	uint32_t sck;
 
-	sck = issp_set_sck(sc, clock * 1000);
+	if (clock < SSP_CLK_MIN)
+		sck = issp_set_sck(sc, SSP_CLK_MIN * 1000);
+	else
+		sck = issp_set_sck(sc, clock * 1000);
 
-	/* Notify user if we didn't get exact clock rate from SSP that was
-	 * requested. */
-	if (sck != clock * 1000)
-		aprint_normal_dev(sc->sc_dev, "requested clock %dHz, "
-		    "but got %dHz\n", clock * 1000, sck);
+	/* Notify user if we didn't get the exact clock rate from SSP that was
+	 * requested from the SDMMC subsystem. */
+	if (sck != clock * 1000) {
+		sck = sck / 1000;
+		if (((sck) / 1000) != 0)
+			aprint_normal_dev(sc->sc_dev, "bus clock @ %u.%03u "
+			    "MHz\n", sck / 1000, sck % 1000);
+		else
+			aprint_normal_dev(sc->sc_dev, "bus clock @ %u KHz\n",
+			    sck % 1000);
+	}
 
 	return 0;
 }
@@ -298,27 +424,21 @@ static int
 issp_bus_width(sdmmc_chipset_handle_t sch, int width)
 {
 	struct issp_softc *sc = sch;
-	uint32_t reg;
-
-	reg = SSP_RD(sc, HW_SSP_CTRL0);
-	reg &= ~(HW_SSP_CTRL0_BUS_WIDTH);
 
 	switch(width) {
 	case(1):
-		reg |= __SHIFTIN(BUS_WIDTH_1_BIT, HW_SSP_CTRL0_BUS_WIDTH);
+		sc->sc_bus_width = BUS_WIDTH_1_BIT;
 		break;
 	case(4):
-		reg |= __SHIFTIN(BUS_WIDTH_4_BIT, HW_SSP_CTRL0_BUS_WIDTH);
+		sc->sc_bus_width = BUS_WIDTH_4_BIT;
 		break;
 	case(8):
-		reg |= __SHIFTIN(BUS_WIDTH_8_BIT, HW_SSP_CTRL0_BUS_WIDTH);
+		sc->sc_bus_width = BUS_WIDTH_8_BIT;
 		break;
 	default:
 		return 1;
 	}
 
-	SSP_WR(sc, HW_SSP_CTRL0, reg);
-
 	return 0;
 }
 
@@ -332,112 +452,104 @@ issp_bus_rod(sdmmc_chipset_handle_t sch,
 static void
 issp_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
 {
-	struct issp_softc *sc = sch;
-	uint32_t reg;
-	uint32_t do_blkio;
-	uint32_t i;
+	issp_softc_t sc = sch;
+	void *dma_chain;
+	int error;
+
+	/* SSP does not support over 64k transfer size. */
+	if (cmd->c_data != NULL && cmd->c_datalen > MAX_TRANSFER_SIZE) {
+		aprint_error_dev(sc->sc_dev, "transfer size over %d: %d\n",
+		    MAX_TRANSFER_SIZE, cmd->c_datalen);
+		cmd->c_error = ENODEV;
+		return;
+	}
+
+	/* Map dma_chain to point allocated previously allocated DMA chain. */
+	error = bus_dmamem_map(sc->sc_dmat, sc->sc_ds, 1, sc->sc_chnsiz,
+	    &dma_chain, BUS_DMA_NOWAIT);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "bus_dmamem_map: %d\n", error);
+		cmd->c_error = error;
+		goto out;
+	}
 
-	do_blkio = 0;
+	error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, dma_chain,
+	    sc->sc_chnsiz, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "bus_dmamap_load: %d\n", error);
+		cmd->c_error = error;
+		goto dmamem_unmap;
+	}
 
-	/* Wait until SSP done. (data I/O error + retry...) */
-	while (SSP_RD(sc, HW_SSP_STATUS) & SSP_BUSY)
-                       ;
-
-	/* Set expected response type. */
-	SSP_WR(sc, HW_SSP_CTRL0_CLR,
-	    HW_SSP_CTRL0_GET_RESP | HW_SSP_CTRL0_LONG_RESP);
+	memset(dma_chain, 0, sc->sc_chnsiz);
 
-	if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
-		SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_GET_RESP);
-		if (ISSET(cmd->c_flags, SCF_RSP_136))
-			SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_LONG_RESP);
+	/* Setup DMA command chain.*/
+	if (cmd->c_data != NULL && (cmd->c_datalen / cmd->c_blklen) > 1) {
+		/* Multi block transfer. */
+		issp_create_dma_cmd_list_multi(sc, dma_chain, cmd);
+	} else if (cmd->c_data != NULL && cmd->c_datalen) {
+		/* Single block transfer. */
+		issp_create_dma_cmd_list_single(sc, dma_chain, cmd);
+	} else {
+		/* Only command, no data. */
+		issp_create_dma_cmd_list(sc, dma_chain, cmd);
 	}
 
-	/* If CMD does not need CRC validation, tell it to SSP. */
-	if (ISSET(cmd->c_flags, SCF_RSP_CRC))
-		SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_IGNORE_CRC);
-	else
-		SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_IGNORE_CRC);
+	/* Tell DMA controller where it can find just initialized DMA chain. */
+	apbdma_chan_set_chain(sc->sc_dmac, sc->dma_channel, sc->sc_dmamp);
 
-	/* Set command. */
-	SSP_WR(sc, HW_SSP_CMD0_CLR, HW_SSP_CMD0_CMD);
-	SSP_WR(sc, HW_SSP_CMD0_SET,
-	    __SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD));
-
-	/* Set command argument. */
-	SSP_WR(sc, HW_SSP_CMD1, cmd->c_arg);
-
-	/* Is data to be transferred? */
-	if (cmd->c_datalen > 0 && cmd->c_data != NULL) {
-		SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_DATA_XFER);
-		/* Transfer XFER_COUNT of 8-bit words. */
-		SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_XFER_COUNT);
-		SSP_WR(sc, HW_SSP_CTRL0_SET,
-		    __SHIFTIN(cmd->c_datalen, HW_SSP_CTRL0_XFER_COUNT));
+	/* Synchronize command chain before DMA controller accesses it. */
+	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, sc->sc_chnsiz,
+	    BUS_DMASYNC_PREWRITE);
 
-		/* XXX: why 8CYC? Bit is never cleaned. */
-		SSP_WR(sc, HW_SSP_CMD0_SET, HW_SSP_CMD0_APPEND_8CYC);
+	sc->sc_state = SSP_STATE_DMA;
+	sc->sc_irq_error = 0;
+	sc->sc_dma_error = 0;
+	cmd->c_error = 0;
 
-		if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
-			/* Read mode. */
-			do_blkio |= BLKIO_RD;
-			SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_READ);
-		} else {
-			/* Write mode. */
-			do_blkio |= BLKIO_WR;
-			SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_READ);
-		}
-	} else {
-		/* No data to be transferred. */
-		SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_DATA_XFER);
-	}
+	mutex_enter(&sc->sc_lock);
 
-	/* Run the command. */
-	SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_RUN);
+	/* Run DMA command chain. */
+	apbdma_run(sc->sc_dmac, sc->dma_channel);
 
-	if (ISSET(do_blkio, BLKIO_RD)) {
-		for (i = 0; i < cmd->c_datalen / 4; i++) {
-			/* Wait until data arrives to FIFO. */
-			while (SSP_RD(sc, HW_SSP_STATUS)
-			    & HW_SSP_STATUS_FIFO_EMPTY) {
-				/* Abort if error while waiting. */
-				if (SSP_RD(sc, HW_SSP_STATUS) & SSP_RUN_ERR) {
-					aprint_normal_dev(sc->sc_dev,
-					    "RD_ERR: %x\n",
-					    SSP_RD(sc, HW_SSP_STATUS));
-					cmd->c_error = 1;
-					goto pioerr;
-				}
-			}
-			*((uint32_t *)cmd->c_data+i) = SSP_RD(sc, HW_SSP_DATA);
+	/* Wait DMA to complete. */
+	while (sc->sc_state == SSP_STATE_DMA)
+		cv_wait(&sc->sc_intr_cv, &sc->sc_lock);
+
+	mutex_exit(&sc->sc_lock);
+
+	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, sc->sc_chnsiz,
+	    BUS_DMASYNC_POSTWRITE);
+
+	if (sc->sc_dma_error) {
+		if (sc->sc_dma_error == DMA_IRQ_TERM) {
+			apbdma_chan_reset(sc->sc_dmac, sc->dma_channel);
+			cmd->c_error = sc->sc_dma_error;
 		}
-	} else if (ISSET(do_blkio, BLKIO_WR)) {
-		for (i = 0; i < (cmd->c_datalen / 4); i++) {
-			while (SSP_RD(sc, HW_SSP_STATUS)
-			    & HW_SSP_STATUS_FIFO_FULL) {
-				/* Abort if error while waiting. */
-				if (SSP_RD(sc, HW_SSP_STATUS) & SSP_RUN_ERR) {
-					aprint_normal_dev(sc->sc_dev,
-					    "WR_ERR: %x\n",
-					    SSP_RD(sc, HW_SSP_STATUS));
-					cmd->c_error = 1;
-					goto pioerr;
-				}
-			}
-			SSP_WR(sc, HW_SSP_DATA, *((uint32_t *)cmd->c_data+i));
+		else if (sc->sc_dma_error == DMA_IRQ_BUS_ERROR) {
+			aprint_error_dev(sc->sc_dev, "DMA_IRQ_BUS_ERROR: %d\n",
+			    sc->sc_irq_error);
+			cmd->c_error = sc->sc_dma_error;
 		}
 	}
 
-	/* Wait until SSP is done. */
-	while (SSP_RD(sc, HW_SSP_STATUS) & SSP_BUSY)
-                       ;
+	if (sc->sc_irq_error) {
+		/* Do not log RESP_TIMEOUT_IRQ error if bus width is 0 as it is
+		 * expected during SD card initialization phase. */
+		if (sc->sc_bus_width) {
+			aprint_error_dev(sc->sc_dev, "SSP_ERROR_IRQ: %d\n",
+			    sc->sc_irq_error);
+		}
+		else if(!(sc->sc_irq_error & HW_SSP_CTRL1_RESP_TIMEOUT_IRQ)) {
+			aprint_error_dev(sc->sc_dev, "SSP_ERROR_IRQ: %d\n",
+			    sc->sc_irq_error);
+		}
 
-	/* Check if the command ran successfully. */
-	reg = SSP_RD(sc, HW_SSP_STATUS);
-	if (reg & SSP_RUN_ERR)
-		cmd->c_error = reg & SSP_RUN_ERR;
+		/* Shift unsigned error code so it fits nicely to signed int. */
+		cmd->c_error = sc->sc_irq_error >> 8;
+	}
 
-	/* Read response if such was requested. */
+	/* Check reponse from the card if such was requested. */
 	if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
 		cmd->c_resp[0] = SSP_RD(sc, HW_SSP_SDRESP0);
 		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
@@ -457,7 +569,12 @@ issp_exec_command(sdmmc_chipset_handle_t
 			cmd->c_resp[3] >>= 8;
 		}
 	}
-pioerr:
+
+	bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamp);
+dmamem_unmap:
+	bus_dmamem_unmap(sc->sc_dmat, dma_chain, sc->sc_chnsiz);
+out:
+
 	return;
 }
 
@@ -465,10 +582,7 @@ static void
 issp_card_enable_intr(sdmmc_chipset_handle_t sch, int irq)
 {
 	struct issp_softc *sc = sch;
-
-	aprint_normal_dev(sc->sc_dev,
-	    "issp_card_enable_intr NOT IMPLEMENTED!\n");
-
+	aprint_error_dev(sc->sc_dev, "issp_card_enable_intr not implemented\n");
 	return;
 }
 
@@ -476,9 +590,7 @@ static void
 issp_card_intr_ack(sdmmc_chipset_handle_t sch)
 {
 	struct issp_softc *sc = sch;
-
-	aprint_normal_dev(sc->sc_dev, "issp_card_intr_ack NOT IMPLEMENTED!\n");
-
+	aprint_error_dev(sc->sc_dev, "issp_card_intr_ack not implemented\n");
 	return;
 }
 
@@ -528,13 +640,6 @@ issp_reset(struct issp_softc *sc)
 	return;
 }
 
-
-/*
- * DATA_TIMEOUT is calculated as:
- * (1 / SSP_CLK) * (DATA_TIMEOUT * 4096)
- */
-#define DATA_TIMEOUT 0x4240	/* 723ms */
-
 /*
  * Initialize SSP controller to SD/MMC mode.
  */
@@ -543,27 +648,39 @@ issp_init(struct issp_softc *sc)
 {
 	uint32_t reg;
 
-	/* Initial data bus width is 1-bit. */
 	reg = SSP_RD(sc, HW_SSP_CTRL0);
+	reg |= HW_SSP_CTRL0_ENABLE;
+
+	/* Initial data bus width is 1-bit. */
 	reg &= ~(HW_SSP_CTRL0_BUS_WIDTH);
 	reg |= __SHIFTIN(BUS_WIDTH_1_BIT, HW_SSP_CTRL0_BUS_WIDTH) |
 	    HW_SSP_CTRL0_WAIT_FOR_IRQ | HW_SSP_CTRL0_ENABLE;
 	SSP_WR(sc, HW_SSP_CTRL0, reg);
+	sc->sc_bus_width = BUS_WIDTH_1_BIT;
 
 	/* Set data timeout. */
 	reg = SSP_RD(sc, HW_SSP_TIMING);
 	reg &= ~(HW_SSP_TIMING_TIMEOUT);
 	reg |= __SHIFTIN(DATA_TIMEOUT, HW_SSP_TIMING_TIMEOUT);
+	SSP_WR(sc, HW_SSP_TIMING, reg);
 
 	/* Set initial clock rate to minimum. */
 	issp_set_sck(sc, SSP_CLK_MIN * 1000);
 
-	SSP_WR(sc, HW_SSP_TIMING, reg);
-	/* Enable SD/MMC mode and use use 8-bits per word. */
 	reg = SSP_RD(sc, HW_SSP_CTRL1);
+	/* Enable all but SDIO IRQ's. */
+	reg |= HW_SSP_CTRL1_RESP_ERR_IRQ_EN |
+	    HW_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN |
+	    HW_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |
+	    HW_SSP_CTRL1_DATA_CRC_IRQ_EN |
+	    HW_SSP_CTRL1_FIFO_UNDERRUN_EN |
+	    HW_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
+	    HW_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN;
+	reg |= HW_SSP_CTRL1_DMA_ENABLE;
+	reg |= HW_SSP_CTRL1_POLARITY;
+	/* Set SD/MMC mode and use use 8-bits per word. */
 	reg &= ~(HW_SSP_CTRL1_WORD_LENGTH | HW_SSP_CTRL1_SSP_MODE);
-	reg |= HW_SSP_CTRL1_POLARITY |
-	    __SHIFTIN(0x7, HW_SSP_CTRL1_WORD_LENGTH) |
+	reg |= __SHIFTIN(0x7, HW_SSP_CTRL1_WORD_LENGTH) |
 	    __SHIFTIN(0x3, HW_SSP_CTRL1_SSP_MODE);
 	SSP_WR(sc, HW_SSP_CTRL1, reg);
 
@@ -575,7 +692,7 @@ issp_init(struct issp_softc *sc)
  *
  * SSP_SCK is calculated as: SSP_CLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE))
  *
- * issp_set_sck find the most suitable CLOCK_DIVIDE and CLOCK_RATE register
+ * issp_set_sck finds the most suitable CLOCK_DIVIDE and CLOCK_RATE register
  * values for the target clock rate by iterating through all possible register
  * values.
  */
@@ -612,3 +729,280 @@ out:
 
 	return SSP_CLK / (div * (1 + rate));
 }
+
+/*
+ * IRQ from DMA.
+ */
+static int
+issp_dma_intr(void *arg)
+{
+	issp_softc_t sc = arg;
+	unsigned int dma_err;
+
+	dma_err = apbdma_intr_status(sc->sc_dmac, sc->dma_channel);
+
+	if (dma_err) {
+		apbdma_ack_error_intr(sc->sc_dmac, sc->dma_channel);
+	} else {
+		apbdma_ack_intr(sc->sc_dmac, sc->dma_channel);
+	}
+
+	mutex_enter(&sc->sc_lock);
+
+	sc->sc_dma_error = dma_err;
+	sc->sc_state = SSP_STATE_IDLE;
+
+	/* Signal thread that interrupt was handled. */
+	cv_signal(&sc->sc_intr_cv);
+
+	mutex_exit(&sc->sc_lock);
+
+	/* Return 1 to acknowledge IRQ. */
+	return 1;
+}
+
+/*
+ * IRQ from SSP block.
+ *
+ * When SSP receives IRQ it terminates ongoing DMA transfer by issuing DMATERM
+ * signal to DMA block.
+ */
+static int
+issp_error_intr(void *arg)
+{
+	issp_softc_t sc = arg;
+
+	mutex_enter(&sc->sc_lock);
+
+	sc->sc_irq_error =
+	    SSP_RD(sc, HW_SSP_CTRL1) & HW_SSP_CTRL1_IRQ_MASK;
+
+	issp_ack_intr(sc);
+
+	mutex_exit(&sc->sc_lock);
+
+	/* Return 1 to acknowledge IRQ. */
+	return 1;
+}
+
+/*
+ * Acknowledge SSP error IRQ.
+ */
+static void
+issp_ack_intr(struct issp_softc *sc)
+{
+
+	/* Acknowledge all IRQ's. */
+	SSP_WR(sc, HW_SSP_CTRL1_CLR, HW_SSP_CTRL1_IRQ_MASK);
+
+	return;
+}
+
+/*
+ * Set up multi block DMA transfer.
+ */
+static void
+issp_create_dma_cmd_list_multi(issp_softc_t sc, void *dma_chain,
+    struct sdmmc_command *cmd)
+{
+	apbdma_command_t dma_cmd;
+	int blocks;
+	int nblk;
+
+	blocks = cmd->c_datalen / cmd->c_blklen;
+	nblk = 0;
+	dma_cmd = dma_chain;
+
+	/* HEAD */
+	apbdma_cmd_buf(&dma_cmd[nblk], cmd->c_blklen * nblk, cmd->c_dmamap);
+	apbdma_cmd_chain(&dma_cmd[nblk], &dma_cmd[nblk+1], dma_chain,
+	    sc->sc_dmamp);
+
+	dma_cmd[nblk].control =
+	    __SHIFTIN(cmd->c_blklen, APBDMA_CMD_XFER_COUNT) |
+	    __SHIFTIN(3, APBDMA_CMD_CMDPIOWORDS) | APBDMA_CMD_HALTONTERMINATE |
+	    APBDMA_CMD_CHAIN;
+
+	if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
+		dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |=
+		    HW_SSP_CTRL0_IGNORE_CRC;
+	}
+
+	dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_DATA_XFER |
+	    __SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
+	    HW_SSP_CTRL0_WAIT_FOR_IRQ | 
+	    __SHIFTIN(cmd->c_datalen, HW_SSP_CTRL0_XFER_COUNT);
+
+	if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
+		dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |=
+		    HW_SSP_CTRL0_GET_RESP;
+		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
+			dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |=
+			    HW_SSP_CTRL0_LONG_RESP;
+		}
+	}
+
+	dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_ENABLE;
+
+	dma_cmd[nblk].pio_words[PIO_WORD_CMD0] =
+	    __SHIFTIN(ffs(cmd->c_blklen) - 1, HW_SSP_CMD0_BLOCK_SIZE) |
+	    __SHIFTIN(blocks - 1, HW_SSP_CMD0_BLOCK_COUNT) |
+	    __SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
+
+	dma_cmd[nblk].pio_words[PIO_WORD_CMD1] = cmd->c_arg;
+
+	if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
+		dma_cmd[nblk].control |=
+		    __SHIFTIN(APBDMA_CMD_DMA_WRITE, APBDMA_CMD_COMMAND);
+		dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_READ;
+	} else {
+		dma_cmd[nblk].control |=
+		    __SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND);
+	}
+
+	nblk++;
+
+	/* BODY: Build commands for blocks between head and tail, if any. */
+	for (; nblk < blocks - 1; nblk++) {
+
+		apbdma_cmd_buf(&dma_cmd[nblk], cmd->c_blklen * nblk,
+		    cmd->c_dmamap);
+
+		apbdma_cmd_chain(&dma_cmd[nblk], &dma_cmd[nblk+1], dma_chain,
+		    sc->sc_dmamp);
+
+		dma_cmd[nblk].control =
+		    __SHIFTIN(cmd->c_blklen, APBDMA_CMD_XFER_COUNT) |
+		    APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_CHAIN;
+
+		if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
+			dma_cmd[nblk].control |=
+			    __SHIFTIN(APBDMA_CMD_DMA_WRITE,
+				APBDMA_CMD_COMMAND);
+		} else {
+			dma_cmd[nblk].control |=
+			    __SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND);
+		}
+	}
+
+	/* TAIL
+	 *
+	 * TODO: Send CMD12/STOP with last DMA command to support
+	 * SMC_CAPS_AUTO_STOP.
+	 */
+	apbdma_cmd_buf(&dma_cmd[nblk], cmd->c_blklen * nblk, cmd->c_dmamap);
+	/* next = NULL */
+	dma_cmd[nblk].control =
+	    __SHIFTIN(cmd->c_blklen, APBDMA_CMD_XFER_COUNT) |
+	    APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_WAIT4ENDCMD |
+	    APBDMA_CMD_SEMAPHORE | APBDMA_CMD_IRQONCMPLT;
+
+	if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
+		dma_cmd[nblk].control |= __SHIFTIN(APBDMA_CMD_DMA_WRITE,
+		    APBDMA_CMD_COMMAND);
+	} else {
+		dma_cmd[nblk].control |= __SHIFTIN(APBDMA_CMD_DMA_READ,
+		    APBDMA_CMD_COMMAND);
+	}
+
+	return;
+}
+
+/*
+ * Set up single block DMA transfer.
+ */
+static void
+issp_create_dma_cmd_list_single(issp_softc_t sc, void *dma_chain,
+    struct sdmmc_command *cmd)
+{
+	apbdma_command_t dma_cmd;
+
+	dma_cmd = dma_chain;
+
+	dma_cmd[0].control = __SHIFTIN(cmd->c_datalen, APBDMA_CMD_XFER_COUNT) |
+	    __SHIFTIN(3, APBDMA_CMD_CMDPIOWORDS) |
+	    APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_WAIT4ENDCMD |
+	    APBDMA_CMD_SEMAPHORE | APBDMA_CMD_IRQONCMPLT;
+
+	/* Transfer single block to the beginning of the DMA buffer. */
+	apbdma_cmd_buf(&dma_cmd[0], 0, cmd->c_dmamap);
+
+	if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
+		dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
+		    HW_SSP_CTRL0_IGNORE_CRC;
+	}
+
+	dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
+	    HW_SSP_CTRL0_DATA_XFER |
+	    __SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
+	    HW_SSP_CTRL0_WAIT_FOR_IRQ |
+	    __SHIFTIN(cmd->c_datalen, HW_SSP_CTRL0_XFER_COUNT);
+
+	if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
+		dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_GET_RESP;
+		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
+			dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
+			    HW_SSP_CTRL0_LONG_RESP;
+		}
+	}
+
+	dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_ENABLE;
+	
+	dma_cmd[0].pio_words[PIO_WORD_CMD0] =
+	    HW_SSP_CMD0_APPEND_8CYC |
+	    __SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
+	dma_cmd[0].pio_words[PIO_WORD_CMD1] = cmd->c_arg;
+
+	if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
+		dma_cmd[0].control |=
+		    __SHIFTIN(APBDMA_CMD_DMA_WRITE, APBDMA_CMD_COMMAND);
+		dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_READ;
+	} else {
+		dma_cmd[0].control |=
+		    __SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND);
+	}
+	
+	return;
+}
+
+/*
+ * Do DMA PIO (issue CMD). No block transfers.
+ */
+static void
+issp_create_dma_cmd_list(issp_softc_t sc, void *dma_chain,
+    struct sdmmc_command *cmd)
+{
+	apbdma_command_t dma_cmd;
+
+	dma_cmd = dma_chain;
+
+	dma_cmd[0].control = __SHIFTIN(3, APBDMA_CMD_CMDPIOWORDS) |
+	    APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_WAIT4ENDCMD |
+	    APBDMA_CMD_SEMAPHORE | APBDMA_CMD_IRQONCMPLT |
+	    __SHIFTIN(APBDMA_CMD_NO_DMA_XFER, APBDMA_CMD_COMMAND);
+	
+	if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
+		dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
+		    HW_SSP_CTRL0_IGNORE_CRC;
+	}
+
+	dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
+	    __SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
+	    HW_SSP_CTRL0_WAIT_FOR_IRQ;
+	
+	if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
+		dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_GET_RESP;
+		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
+			dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
+			    HW_SSP_CTRL0_LONG_RESP;
+		}
+	}
+
+	dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_ENABLE;
+
+	dma_cmd[0].pio_words[PIO_WORD_CMD0] =
+		__SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
+	dma_cmd[0].pio_words[PIO_WORD_CMD1] = cmd->c_arg;
+
+	return;
+}

Index: src/sys/arch/arm/imx/imx23_apbdmareg.h
diff -u src/sys/arch/arm/imx/imx23_apbdmareg.h:1.1 src/sys/arch/arm/imx/imx23_apbdmareg.h:1.2
--- src/sys/arch/arm/imx/imx23_apbdmareg.h:1.1	Tue Nov 20 19:06:12 2012
+++ src/sys/arch/arm/imx/imx23_apbdmareg.h	Sun Mar  3 10:33:56 2013
@@ -1,4 +1,4 @@
-/* $Id: imx23_apbdmareg.h,v 1.1 2012/11/20 19:06:12 jkunz Exp $ */
+/* $Id: imx23_apbdmareg.h,v 1.2 2013/03/03 10:33:56 jkunz Exp $ */
 
 /*
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -50,4 +50,20 @@
 #define HW_APB_CTRL0_CLKGATE	__BIT(30)
 #define HW_APB_CTRL0_RSVD0	__BITS(29, 0)
 
+/*
+ * AHB to APB{H,X} Bridge Control Register 1.
+ */
+#define HW_APB_CTRL1		0x010
+#define HW_APB_CTRL1_SET	0x014
+#define HW_APB_CTRL1_CLR	0x018
+#define HW_APB_CTRL1_TOG	0x01C
+
+/*
+ * AHB to APB{H,X} Bridge Control and Status Register 2.
+ */
+#define HW_APB_CTRL2		0x020
+#define HW_APB_CTRL2_SET	0x024
+#define HW_APB_CTRL2_CLR	0x028
+#define HW_APB_CTRL2_TOG	0x02C
+
 #endif /* !_ARM_IMX_IMX23_APBDMAREG_H_ */
Index: src/sys/arch/arm/imx/imx23_apbhdmareg.h
diff -u src/sys/arch/arm/imx/imx23_apbhdmareg.h:1.1 src/sys/arch/arm/imx/imx23_apbhdmareg.h:1.2
--- src/sys/arch/arm/imx/imx23_apbhdmareg.h:1.1	Tue Nov 20 19:06:13 2012
+++ src/sys/arch/arm/imx/imx23_apbhdmareg.h	Sun Mar  3 10:33:56 2013
@@ -1,4 +1,4 @@
-/* $Id: imx23_apbhdmareg.h,v 1.1 2012/11/20 19:06:13 jkunz Exp $ */
+/* $Id: imx23_apbhdmareg.h,v 1.2 2013/03/03 10:33:56 jkunz Exp $ */
 
 /*
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -123,6 +123,30 @@
 #define HW_APBH_DEVSEL_CH0	__BITS(3, 0)
 
 /*
+ * APBH DMA Channel 0 Current Command Address Register.
+ */
+#define HW_APBH_CH0_CURCMDAR	0x040
+
+#define HW_APBH_CH0_CURCMDAR_CMD_ADDR	__BITS(31, 0)
+
+/*
+ * APBH DMA Channel 0 Next Command Address.
+ */
+#define HW_APBH_CH0_NXTCMDAR	0x050
+
+#define HW_APBH_CH0_NXTCMDAR_CMD_ADDR	__BITS(31, 0)
+
+/*
+ * APBH DMA Channel 0 Semaphore Register.
+ */
+#define HW_APBH_CH0_SEMA	0x080
+
+#define HW_APBH_CH0_SEMA_RSVD2		__BITS(31, 24)
+#define HW_APBH_CH0_SEMA_PHORE		__BITS(23, 16)
+#define HW_APBH_CH0_SEMA_RSVD1		__BITS(15, 8)
+#define HW_APBH_CH0_SEMA_INCREMENT_SEMA	__BITS(7, 0)
+
+/*
  * APBH DMA Channel 1 Current Command Address Register.
  */
 #define HW_APBH_CH1_CURCMDAR	0x0B0
Index: src/sys/arch/arm/imx/imx23_apbxdmareg.h
diff -u src/sys/arch/arm/imx/imx23_apbxdmareg.h:1.1 src/sys/arch/arm/imx/imx23_apbxdmareg.h:1.2
--- src/sys/arch/arm/imx/imx23_apbxdmareg.h:1.1	Tue Nov 20 19:06:13 2012
+++ src/sys/arch/arm/imx/imx23_apbxdmareg.h	Sun Mar  3 10:33:56 2013
@@ -1,4 +1,4 @@
-/* $Id: imx23_apbxdmareg.h,v 1.1 2012/11/20 19:06:13 jkunz Exp $ */
+/* $Id: imx23_apbxdmareg.h,v 1.2 2013/03/03 10:33:56 jkunz Exp $ */
 
 /*
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -34,7 +34,31 @@
 
 #include <sys/cdefs.h>
 
-#define HW_APBXDMA_BASE 0x80024000
-#define HW_APBXDMA_SIZE 0x2000 /* 8 kB */
+#define HW_APBXDMA_BASE 		0x80024000
+#define HW_APBXDMA_SIZE 		0x2000 /* 8 kB */
+
+/*
+ * APBX DMA Channel 0 Current Command Address Register.
+ */
+#define HW_APBX_CH0_CURCMDAR		0x100
+
+#define HW_APBX_CH0_CURCMDAR_CMD_ADDR	__BITS(31, 0)
+
+/*
+ * APBX DMA Channel 0 Next Command Address Register.
+ */
+#define HW_APBX_CH0_NXTCMDAR		0x110
+
+#define HW_APBX_CH0_NXTCMDAR_CMD_ADDR	__BITS(31, 0)
+
+/*
+ * APBX DMA Channel 0 Semaphore Register.
+ */
+#define HW_APBX_CH0_SEMA		0x140
+
+#define HW_APBX_CH0_SEMA_RSVD2		__BITS(31, 24)
+#define HW_APBX_CH0_SEMA_PHORE		__BITS(23, 16)
+#define HW_APBX_CH0_SEMA_RSVD1		__BITS(15, 8)
+#define HW_APBX_CH0_SEMA_INCREMENT_SEMA	__BITS(7, 0)
 
 #endif /* !_ARM_IMX_IMX23_APBXDMAREG_H_ */

Index: src/sys/arch/evbarm/conf/IMX23_OLINUXINO
diff -u src/sys/arch/evbarm/conf/IMX23_OLINUXINO:1.2 src/sys/arch/evbarm/conf/IMX23_OLINUXINO:1.3
--- src/sys/arch/evbarm/conf/IMX23_OLINUXINO:1.2	Sun Dec 16 19:45:52 2012
+++ src/sys/arch/evbarm/conf/IMX23_OLINUXINO	Sun Mar  3 10:33:56 2013
@@ -1,4 +1,4 @@
-# $Id: IMX23_OLINUXINO,v 1.2 2012/12/16 19:45:52 jkunz Exp $
+# $Id: IMX23_OLINUXINO,v 1.3 2013/03/03 10:33:56 jkunz Exp $
 #
 # IMX23_OLINUXINO -- Olimex i.MX23 OLinuXino kernel configuration file.
 #
@@ -7,7 +7,7 @@ include "arch/evbarm/conf/std.imx23_olin
 
 maxusers	8
 
-config netbsd root on ld0a type ?
+config netbsd root on ? type ?
 
 # The main bus device
 mainbus0	at root
@@ -16,10 +16,10 @@ mainbus0	at root
 cpu0		at mainbus?
 
 # APBH bus
-apbh0		at mainbus? base 0x80000000 size 0x00040000
+apbh0		at mainbus? base 0x80000000 size 0x40000
 
 # APBH DMA
-#apbdma0	at apbh? addr 0x80004000 size 0x2000 irq -1
+apbdma0		at apbh? addr 0x80004000 size 0x2000 irq -1
 
 # Interrupt controller
 icoll0		at apbh? addr 0x80000000 size 0x2000 irq -1
@@ -27,13 +27,13 @@ icoll0		at apbh? addr 0x80000000 size 0x
 # Synchronous serial port for SD/MMC
 ssp0		at apbh? addr 0x80010000 size 0x2000 irq 15
 sdmmc*		at ssp?
-ld*			at sdmmc?
+ld*		at sdmmc?
 
 # APBX bus
-apbx0		at mainbus? base 0x80040000 size 0x00040000
+apbx0		at mainbus? base 0x80040000 size 0x40000
 
 # APBX DMA
-#apbdma1	at apbx? addr 0x80024000 size 0x2000 irq -1
+apbdma1		at apbx? addr 0x80024000 size 0x2000 irq -1
 
 # Timers and rotary decoder
 timrot0		at apbx? addr 0x80068020 size 0x20 irq 28
@@ -50,4 +50,8 @@ options		HZ=100
 file-system	FFS
 file-system	EXT2FS
 file-system	MSDOSFS
+file-system	KERNFS
+file-system	PROCFS
+file-system	PTYFS
 
+pseudo-device	pty	# pseudo-terminals

Added files:

Index: src/sys/arch/arm/imx/imx23_apbdmavar.h
diff -u /dev/null src/sys/arch/arm/imx/imx23_apbdmavar.h:1.1
--- /dev/null	Sun Mar  3 10:33:56 2013
+++ src/sys/arch/arm/imx/imx23_apbdmavar.h	Sun Mar  3 10:33:56 2013
@@ -0,0 +1,137 @@
+/* $Id: imx23_apbdmavar.h,v 1.1 2013/03/03 10:33:56 jkunz Exp $ */
+
+/*
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Petri Laakso.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _ARM_IMX_IMX23_APBDMAVAR_H_
+#define _ARM_IMX_IMX23_APBDMAVAR_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/mutex.h>
+
+/* DMA command control register bits. */
+#define APBDMA_CMD_XFER_COUNT		__BITS(31, 16)
+#define APBDMA_CMD_CMDPIOWORDS		__BITS(15, 12)
+#define APBDMA_CMD_RESERVED		__BITS(11, 9)
+#define APBDMA_CMD_HALTONTERMINATE	__BIT(8)
+#define APBDMA_CMD_WAIT4ENDCMD		__BIT(7)
+#define APBDMA_CMD_SEMAPHORE		__BIT(6)
+#define APBDMA_CMD_NANDWAIT4READY	__BIT(5)
+#define APBDMA_CMD_NANDLOCK		__BIT(4)
+#define APBDMA_CMD_IRQONCMPLT		__BIT(3)
+#define APBDMA_CMD_CHAIN		__BIT(2)
+#define APBDMA_CMD_COMMAND		__BITS(1, 0)
+
+/* DMA command types. */
+#define APBDMA_CMD_NO_DMA_XFER		0
+#define APBDMA_CMD_DMA_WRITE		1
+#define APBDMA_CMD_DMA_READ		2
+#define APBDMA_CMD_DMA_SENSE		3
+
+/* Flags. */
+#define F_AHBH_DMA			__BIT(0)
+#define F_AHBX_DMA			__BIT(1)
+
+/* Number of channels. */
+#define AHBH_DMA_CHANNELS		8
+#define AHBX_DMA_CHANNELS		16
+
+/* APBH DMA channel assignments. */
+#define APBH_DMA_CHANNEL_RES0		0	/* Reserved. */
+#define APBH_DMA_CHANNEL_SSP1		1	/* SSP1. */
+#define APBH_DMA_CHANNEL_SSP2		2	/* SSP2. */
+#define APBH_DMA_CHANNEL_RES1		3	/* Reserved. */
+#define APBH_DMA_CHANNEL_NAND_DEVICE0	4	/* NAND_DEVICE0. */
+#define APBH_DMA_CHANNEL_NAND_DEVICE1	5	/* NAND_DEVICE1. */
+#define APBH_DMA_CHANNEL_NAND_DEVICE2	6	/* NAND_DEVICE2. */
+#define APBH_DMA_CHANNEL_NAND_DEVICE3	7	/* NAND_DEVICE3. */
+
+/* APBX DMA channel assignments. */
+#define APBX_DMA_CHANNEL_AUDIO_ADC	0	/* Audio ADCs. */
+#define APBX_DMA_CHANNEL_AUDIO_DAC	1	/* Audio DACs. */
+#define APBX_DMA_CHANNEL_SPDIF_TX	2	/* SPDIF TX. */
+#define APBX_DMA_CHANNEL_I2C		3	/* I2C. */
+#define APBX_DMA_CHANNEL_SAIF1		4	/* SAIF1. */
+#define APBX_DMA_CHANNEL_RES0		5	/* Reserved. */
+#define APBX_DMA_CHANNEL_UART1_RX	6	/* UART1 RX, IrDA RX. */
+#define APBX_DMA_CHANNEL_UART1_TX	7	/* UART1 TX, IrDA TX. */
+#define APBX_DMA_CHANNEL_UART2_RX	8	/* UART2 RX. */
+#define APBX_DMA_CHANNEL_UART2_TX	9	/* UART2 TX. */
+#define APBX_DMA_CHANNEL_SAIF2		10	/* SAIF2. */
+#define APBX_DMA_CHANNEL_RES1		11	/* Reserved. */
+#define APBX_DMA_CHANNEL_RES2		12	/* Reserved. */
+#define APBX_DMA_CHANNEL_RES3		13	/* Reserved. */
+#define APBX_DMA_CHANNEL_RES4		14	/* Reserved. */
+#define APBX_DMA_CHANNEL_RES5		15	/* Reserved. */
+
+/* Return codes for apbdma_intr_status() */
+#define DMA_IRQ_CMDCMPLT		0
+#define DMA_IRQ_TERM			1
+#define DMA_IRQ_BUS_ERROR		2
+
+#define PIO_WORDS_MAX			15
+
+/*
+ * How many PIO words apbdma_command structure has.
+ *
+ * XXX: If you change this value, make sure drivers are prepared for that.
+ * That means you have to allocate enough DMA memory for command chains.
+ */
+#define PIO_WORDS			3
+
+typedef struct apbdma_softc {
+	device_t sc_dev;
+	bus_dma_tag_t sc_dmat;
+	bus_space_handle_t sc_ioh;
+	bus_space_tag_t sc_iot;
+	kmutex_t sc_lock;
+	u_int flags;
+} *apbdma_softc_t;
+
+typedef struct apbdma_command {
+	void *next;		/* Physical address. */
+	uint32_t control;
+	void *buffer;		/* Physical address. */
+	uint32_t pio_words[PIO_WORDS];
+} *apbdma_command_t;
+
+void apbdma_cmd_chain(apbdma_command_t, apbdma_command_t, void *, bus_dmamap_t);
+void apbdma_cmd_buf(apbdma_command_t, bus_addr_t, bus_dmamap_t);
+void apbdma_chan_init(struct apbdma_softc *, unsigned int);
+void apbdma_chan_set_chain(struct apbdma_softc *, unsigned int, bus_dmamap_t);
+void apbdma_run(struct apbdma_softc *, unsigned int);
+void apbdma_ack_intr(struct apbdma_softc *, unsigned int);
+void apbdma_ack_error_intr(struct apbdma_softc *, unsigned int);
+unsigned int apbdma_intr_status(struct apbdma_softc *, unsigned int);
+void apbdma_chan_reset(struct apbdma_softc *, unsigned int);
+
+#endif /* !_ARM_IMX_IMX23_APBDMAVAR_H_ */

Reply via email to