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_ */