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