#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <sys/queue.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <machine/cpu.h>
#include <sys/bus.h>
#include <arch/arm/s3c2xx0/s3c2440_dma.h>
#include <arm/s3c2xx0/s3c2440var.h>
#include <arch/arm/s3c2xx0/s3c2440reg.h>
#include <uvm/uvm_extern.h>
#include <machine/pmap.h>
#ifdef S3C2440_DMA_DEBUG
#define DPRINTF(s) do {printf s; } while (0)
#else
#define DPRINTF(s) do {} while (0)
#endif
#define DMAC_N_CHANNELS 4
struct dmac_desc_segs {
bus_dma_segment_t *ds_curseg;
uint8_t ds_nsegs;
};
SIMPLEQ_HEAD(dmac_xfer_state_head, dmac_xfer_state);
struct dmac_xfer_state {
struct dmac_xfer dxs_xfer;
SIMPLEQ_ENTRY(dmac_xfer_state) dxs_link;
uint8_t dxs_channel;
#define DMAC_NO_CHANNEL (~0)
uint8_t dxs_width;
bool dxs_complete;
struct dmac_desc_segs dxs_segs[2];
uint32_t dxs_options;
};
struct s3c2440_dmac_peripheral {
uint8_t dp_id;
uint8_t dp_channel_order[DMAC_N_CHANNELS+1];
#define PERPH_LAST DMAC_N_CHANNELS+1
uint8_t dp_channel_source[DMAC_N_CHANNELS];
#define PERPH_NA 7
};
struct s3c2440_dmac_channel {
struct dmac_xfer_state *dc_active;
struct dmac_xfer_state_head dc_queue;
};
struct s3c2440_dmac_softc {
bus_space_tag_t sc_iot;
bus_space_handle_t sc_dmach;
bus_dma_tag_t sc_dmat;
struct kmutex sc_mutex;
struct s3c2440_dmac_channel sc_channels[DMAC_N_CHANNELS];
struct kmutex sc_intr_mutex;
struct kcondvar sc_intr_cv;
};
static struct s3c2440_dmac_softc _s3c2440_dmac_sc;
static struct s3c2440_dmac_softc *s3c2440_dmac_sc = &_s3c2440_dmac_sc;
static struct s3c2440_dmac_peripheral s3c2440_peripherals[] = {
{DMAC_PERIPH_NONE, {0,1,2,3}, {0, 0, 0, 0}},
{DMAC_PERIPH_XDREQ0, {0,PERPH_LAST}, {0, PERPH_NA, PERPH_NA, PERPH_NA}},
{DMAC_PERIPH_XDREQ1, {1,PERPH_LAST}, {PERPH_NA, 0, PERPH_NA, PERPH_NA}},
{DMAC_PERIPH_UART0, {0,PERPH_LAST}, {1, PERPH_NA, PERPH_NA, PERPH_NA}},
{DMAC_PERIPH_UART1, {1,PERPH_LAST}, {PERPH_NA, 1, PERPH_NA, PERPH_NA}},
{DMAC_PERIPH_UART2, {3,PERPH_LAST}, {PERPH_NA, PERPH_NA, PERPH_NA, 0}},
{DMAC_PERIPH_I2SSDO, {0, 2, PERPH_LAST}, {5, PERPH_NA, 0, PERPH_NA}},
{DMAC_PERIPH_I2SSDI, {1, 2, PERPH_LAST}, {PERPH_NA, 2, 1, PERPH_NA}},
{DMAC_PERIPH_SDI, {3, 2, 1, PERPH_LAST}, {2, 6, 2, 1}},
{DMAC_PERIPH_SPI0, {1, PERPH_LAST}, {PERPH_NA, 3, PERPH_NA, PERPH_NA}},
{DMAC_PERIPH_SPI1, {3, PERPH_LAST}, {PERPH_NA, PERPH_NA, PERPH_NA, 2}},
{DMAC_PERIPH_PCMIN, {0, 2, PERPH_LAST}, {6, PERPH_NA, 5, PERPH_NA}},
{DMAC_PERIPH_PCMOUT, {1, 3, PERPH_LAST}, {PERPH_NA, 5, PERPH_NA, 6}},
{DMAC_PERIPH_MICIN, {2, 3, PERPH_LAST}, {PERPH_NA, PERPH_NA, 6, 5}},
{DMAC_PERIPH_MICOUT, {2, 3, PERPH_LAST}, {PERPH_NA, PERPH_NA, 6, 5}},
{DMAC_PERIPH_TIMER, {0, 2, 3, PERPH_LAST}, {3, PERPH_NA, 3, 3}},
{DMAC_PERIPH_USBEP1, {0, PERPH_LAST}, {4, PERPH_NA, PERPH_NA, PERPH_NA}},
{DMAC_PERIPH_USBEP2, {1, PERPH_LAST}, {PERPH_NA, 4, PERPH_NA, PERPH_NA}},
{DMAC_PERIPH_USBEP3, {2, PERPH_LAST}, {PERPH_NA, PERPH_NA, 4, PERPH_NA}},
{DMAC_PERIPH_USBEP4, {3, PERPH_LAST}, {PERPH_NA, PERPH_NA, PERPH_NA, 4}}
};
static void dmac_start(uint8_t channel_no, struct dmac_xfer_state*);
static void dmac_transfer_segment(uint8_t channel_no, struct dmac_xfer_state*);
static void dmac_channel_done(uint8_t channel_no);
void
s3c2440_dma_init(void)
{
struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
int i;
sc->sc_iot = s3c2xx0_softc->sc_iot;
sc->sc_dmach = s3c2xx0_softc->sc_dmach;
sc->sc_dmat = s3c2xx0_softc->sc_dmat;
for(i = 0; i<DMAC_N_CHANNELS; i++) {
sc->sc_channels[i].dc_active = NULL;
SIMPLEQ_INIT(&sc->sc_channels[i].dc_queue);
}
mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_BIO);
mutex_init(&sc->sc_intr_mutex, MUTEX_DEFAULT, IPL_BIO);
cv_init(&sc->sc_intr_cv, "s3c2440_dmaintr");
s3c24x0_intr_establish(S3C24X0_INT_DMA0, IPL_BIO,
IST_EDGE_RISING, s3c2440_dma_intr, (void*)1);
s3c24x0_intr_establish(S3C24X0_INT_DMA1, IPL_BIO,
IST_EDGE_RISING, s3c2440_dma_intr, (void*)2);
s3c24x0_intr_establish(S3C24X0_INT_DMA2, IPL_BIO,
IST_EDGE_RISING, s3c2440_dma_intr, (void*)3);
s3c24x0_intr_establish(S3C24X0_INT_DMA3, IPL_BIO,
IST_EDGE_RISING, s3c2440_dma_intr, (void*)4);
}
int
s3c2440_dma_intr(void *arg)
{
struct s3c2440_dmac_softc *sc;
uint32_t status;
int channel;
struct s3c2440_dmac_channel *dc;
sc = s3c2440_dmac_sc;
channel = (int)arg - 1;
dc = &sc->sc_channels[channel];
DPRINTF(("s3c2440_dma_intr\n"));
DPRINTF(("Channel %d\n", channel));
status = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_STAT(channel));
DPRINTF(("Channel %d status: %d\n", channel, status));
if ( !(status & DMASTAT_BUSY) ) {
struct dmac_xfer_state *dxs;
struct dmac_xfer *dx;
dxs = dc->dc_active;
KASSERT(dxs != NULL);
dx = &dxs->dxs_xfer;
if (dx->dx_desc[DMAC_DESC_SRC].xd_increment) {
dxs->dxs_segs[DMAC_DESC_SRC].ds_nsegs--;
if (dxs->dxs_segs[DMAC_DESC_SRC].ds_nsegs == 0) {
dxs->dxs_complete = TRUE;
} else {
dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg++;
}
}
if (dx->dx_desc[DMAC_DESC_DST].xd_increment) {
dxs->dxs_segs[DMAC_DESC_DST].ds_nsegs--;
if (dxs->dxs_segs[DMAC_DESC_DST].ds_nsegs == 0) {
dxs->dxs_complete = TRUE;
} else {
dxs->dxs_segs[DMAC_DESC_DST].ds_curseg++;
}
}
if (dxs->dxs_complete) {
dxs->dxs_channel = DMAC_NO_CHANNEL;
mutex_enter(&sc->sc_mutex);
dmac_channel_done(channel);
mutex_exit(&sc->sc_mutex);
DPRINTF(("dx_done: %p\n", (void*)dx->dx_done));
if (dx->dx_done != NULL) {
(dx->dx_done)(dx, dx->dx_cookie);
}
} else {
dmac_transfer_segment(channel, dxs);
}
}
#if 0
if ( !(status & DMASTAT_BUSY) ) {
s3c2440_dma_xfer_t xfer;
xfer = dma_channel_xfer[channel];
dma_channel_xfer[channel] = NULL;
DPRINTF((" Channel %d completed transfer\n", channel));
if (xfer->dx_remaining > 0 &&
xfer->dx_aborted == FALSE) {
DPRINTF(("Preparing next transfer\n"));
s3c2440_dma_xfer_start(xfer);
} else {
if (!xfer->dx_aborted && xfer->dx_callback != NULL)
(xfer->dx_callback)(xfer->dx_callback_arg);
xfer->dx_complete = TRUE;
}
}
#endif
#ifdef S3C2440_DMA_DEBUG
status = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_CSRC(channel));
printf("Current source for channel %d: 0x%x\n", channel, status);
status = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_CDST(channel));
printf("Current dest for channel %d: 0x%x\n", channel, status);
#endif
mutex_enter(&sc->sc_intr_mutex);
DPRINTF(("cv_broadcast\n"));
cv_broadcast(&sc->sc_intr_cv);
DPRINTF(("cv_broadcast done\n"));
mutex_exit(&sc->sc_intr_mutex);
return 1;
}
dmac_xfer_t
s3c2440_dmac_allocate_xfer(void) {
struct dmac_xfer_state *dxs;
dxs = kmem_alloc(sizeof(struct dmac_xfer_state), KM_SLEEP);
dxs->dxs_xfer.dx_done = NULL;
dxs->dxs_xfer.dx_sync_bus = DMAC_SYNC_BUS_AUTO;
dxs->dxs_xfer.dx_xfer_mode = DMAC_XFER_MODE_DEMAND;
dxs->dxs_channel = DMAC_NO_CHANNEL;
return ((dmac_xfer_t)dxs);
}
void
s3c2440_dmac_free_xfer(dmac_xfer_t dx) {
kmem_free(dx, sizeof(struct dmac_xfer_state));
}
int
s3c2440_dmac_start_xfer(dmac_xfer_t dx) {
struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
struct dmac_xfer_state *dxs = (struct dmac_xfer_state*)dx;
struct s3c2440_dmac_peripheral *perph;
int i;
bool transfer_started = FALSE;
if (dxs->dxs_xfer.dx_peripheral != DMAC_PERIPH_NONE &&
dxs->dxs_xfer.dx_peripheral >= DMAC_N_PERIPH)
return EINVAL;
dxs->dxs_complete = FALSE;
perph = &s3c2440_peripherals[dxs->dxs_xfer.dx_peripheral];
#ifdef DIAGNOSTIC
DPRINTF(("dp_id: %d, dx_peripheral: %d\n", perph->dp_id, dxs->dxs_xfer.dx_peripheral));
KASSERT(perph->dp_id == dxs->dxs_xfer.dx_peripheral);
#endif
mutex_enter(&sc->sc_mutex);
for(i=0;
perph->dp_channel_order[i] != PERPH_LAST;
i++) {
uint8_t channel_no = perph->dp_channel_order[i];
#ifdef DIAGNOSTIC
KASSERT(perph->dp_channel_source[channel_no] != PERPH_NA);
#endif
if (sc->sc_channels[channel_no].dc_active == NULL) {
dmac_start(channel_no, dxs);
transfer_started = TRUE;
break;
}
}
if (transfer_started == FALSE) {
uint8_t channel_no = perph->dp_channel_order[0];
dxs->dxs_channel = channel_no;
SIMPLEQ_INSERT_TAIL(&sc->sc_channels[channel_no].dc_queue, dxs, dxs_link);
DPRINTF(("Enqueued transfer on channel %d\n", channel_no));
}
mutex_exit(&sc->sc_mutex);
return 0;
}
static void
dmac_start(uint8_t channel_no, struct dmac_xfer_state *dxs) {
struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
struct s3c2440_dmac_channel *dc = &sc->sc_channels[channel_no];
uint32_t options;
#ifdef DIAGNOSTIC
uint32_t reg;
#endif
dmac_sync_bus_t sync_bus;
struct dmac_xfer *dx = &dxs->dxs_xfer;
DPRINTF(("Starting DMA transfer (%p) on channel %d\n", dxs, channel_no));
KASSERT(dc->dc_active == NULL);
#ifdef DIAGNOSTIC
reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_STAT(channel_no));
if (reg & DMASTAT_BUSY)
panic("DMA channel is busy, cannot start new transfer!");
#endif
dc->dc_active = dxs;
dxs->dxs_channel = channel_no;
dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg = dx->dx_desc[DMAC_DESC_SRC].xd_dma_segs;
dxs->dxs_segs[DMAC_DESC_SRC].ds_nsegs = dx->dx_desc[DMAC_DESC_SRC].xd_nsegs;
dxs->dxs_segs[DMAC_DESC_DST].ds_curseg = dx->dx_desc[DMAC_DESC_DST].xd_dma_segs;
dxs->dxs_segs[DMAC_DESC_DST].ds_nsegs = dx->dx_desc[DMAC_DESC_DST].xd_nsegs;
options = DMACON_INT_INT |
DMACON_RELOAD_NO_AUTO;
if (dxs->dxs_xfer.dx_peripheral == DMAC_PERIPH_NONE) {
options |= DMACON_SERVMODE_WHOLE;
} else {
options |= DMACON_SERVMODE_SINGLE;
}
switch (dxs->dxs_xfer.dx_xfer_mode) {
case DMAC_XFER_MODE_DEMAND:
options |= DMACON_DEMAND;
break;
case DMAC_XFER_MODE_HANDSHAKE:
options |= DMACON_HANDSHAKE;
break;
default:
panic("Unknown dx_xfer_mode");
}
sync_bus = dxs->dxs_xfer.dx_sync_bus;
switch (dxs->dxs_xfer.dx_xfer_width) {
case DMAC_XFER_WIDTH_8BIT:
DPRINTF(("8-Bit (BYTE) transfer width\n"));
options |= DMACON_DSZ_B;
dxs->dxs_width = 1;
break;
case DMAC_XFER_WIDTH_16BIT:
DPRINTF(("16-Bit (HALF-WORD) transfer width\n"));
options |= DMACON_DSZ_HW;
dxs->dxs_width = 2;
break;
case DMAC_XFER_WIDTH_32BIT:
DPRINTF(("32-Bit (WORD) transfer width\n"));
options |= DMACON_DSZ_W;
dxs->dxs_width = 4;
break;
default:
panic("Unknown transfer width");
}
if (dxs->dxs_xfer.dx_peripheral == DMAC_PERIPH_NONE) {
options |= DMACON_SW_REQ;
if (sync_bus == DMAC_SYNC_BUS_AUTO)
sync_bus = DMAC_SYNC_BUS_SYSTEM;
} else {
uint8_t source = s3c2440_peripherals[dxs->dxs_xfer.dx_peripheral].dp_channel_source[channel_no];
DPRINTF(("Hw request source: %d, channel: %d\n", source, channel_no));
options |= DMACON_HW_REQ | DMACON_HW_SRCSEL(source);
if (sync_bus == DMAC_SYNC_BUS_AUTO)
sync_bus = DMAC_SYNC_BUS_PERIPHERAL;
}
if (sync_bus == DMAC_SYNC_BUS_SYSTEM) {
DPRINTF(("Syncing with system bus\n"));
options |= DMACON_SYNC_AHB;
} else if (sync_bus == DMAC_SYNC_BUS_PERIPHERAL) {
DPRINTF(("Syncing with peripheral bus\n"));
options |= DMACON_SYNC_APB;
} else {
panic("No sync bus given");
}
dxs->dxs_options = options;
dmac_transfer_segment(channel_no, dxs);
}
static void
dmac_transfer_segment(uint8_t channel_no, struct dmac_xfer_state *dxs)
{
struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
uint32_t reg, transfer_size;
struct dmac_xfer *dx = &dxs->dxs_xfer;
DPRINTF(("dmac_transfer_segment\n"));
bus_space_write_4(sc->sc_iot, sc->sc_dmach,
DMA_DISRC(channel_no),
dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg->ds_addr);
DPRINTF(("Source address: 0x%x\n", (unsigned)dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg->ds_addr));
DPRINTF(("Dest. address: 0x%x\n", (unsigned)dxs->dxs_segs[DMAC_DESC_DST].ds_curseg->ds_addr));
reg = 0;
if (dx->dx_desc[DMAC_DESC_SRC].xd_bus_type == DMAC_BUS_TYPE_PERIPHERAL) {
reg |= DISRCC_LOC_APB;
} else {
reg |= DISRCC_LOC_AHB;
}
if (dx->dx_desc[DMAC_DESC_SRC].xd_increment) {
reg |= DISRCC_INC_INC;
} else {
reg |= DISRCC_INC_FIXED;
}
bus_space_write_4(sc->sc_iot, sc->sc_dmach, DMA_DISRCC(channel_no), reg);
bus_space_write_4(sc->sc_iot, sc->sc_dmach,
DMA_DIDST(channel_no),
dxs->dxs_segs[DMAC_DESC_DST].ds_curseg->ds_addr);
reg = 0;
if (dx->dx_desc[DMAC_DESC_DST].xd_bus_type == DMAC_BUS_TYPE_PERIPHERAL) {
reg |= DIDSTC_LOC_APB;
} else {
reg |= DIDSTC_LOC_AHB;
}
if (dx->dx_desc[DMAC_DESC_DST].xd_increment) {
reg |= DIDSTC_INC_INC;
} else {
reg |= DIDSTC_INC_FIXED;
}
bus_space_write_4(sc->sc_iot, sc->sc_dmach, DMA_DIDSTC(channel_no), reg);
if (dx->dx_desc[DMAC_DESC_SRC].xd_increment) {
if (!dx->dx_desc[DMAC_DESC_DST].xd_increment) {
transfer_size = dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg->ds_len;
} else {
transfer_size = uimin(dxs->dxs_segs[DMAC_DESC_DST].ds_curseg->ds_len,
dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg->ds_len);
}
} else {
if (dx->dx_desc[DMAC_DESC_DST].xd_increment) {
transfer_size = dxs->dxs_segs[DMAC_DESC_DST].ds_curseg->ds_len;
} else {
panic("S3C2440 DMA code does not support both source and destination being non-incrementing");
}
}
bus_space_write_4(sc->sc_iot, sc->sc_dmach, DMA_CON(channel_no),
dxs->dxs_options |
DMACON_TC(((transfer_size/dxs->dxs_width)+
uimin((transfer_size % dxs->dxs_width), 1))));
DPRINTF(("Transfer size: %d (%d)\n", transfer_size, transfer_size/dxs->dxs_width));
reg = DMAMASKTRIG_ON;
if (dxs->dxs_xfer.dx_peripheral == DMAC_PERIPH_NONE) {
reg |= DMAMASKTRIG_SW_TRIG;
}
bus_space_write_4(sc->sc_iot, sc->sc_dmach, DMA_MASKTRIG(channel_no),
reg);
#if defined(S3C2440_DMA_DEBUG)
reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_DISRC(channel_no));
printf("DMA_DISRC: 0x%X\n", reg);
reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_DISRCC(channel_no));
printf("DMA_DISRCC: 0x%X\n", reg);
reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_DIDST(channel_no));
printf("DMA_DIDST: 0x%X\n", reg);
reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_DIDSTC(channel_no));
printf("DMA_DIDSTC: 0x%X\n", reg);
reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_CON(channel_no));
printf("DMA_CON: 0x%X\n", reg);
reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_MASKTRIG(channel_no));
printf("DMA_MASKTRIG: 0x%X\n", reg);
reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_STAT(channel_no));
printf("DMA_STAT: 0x%X\n", reg);
#endif
}
static void
dmac_channel_done(uint8_t channel_no)
{
struct s3c2440_dmac_softc *sc;
struct s3c2440_dmac_channel *dc;
sc = s3c2440_dmac_sc;
dc = &sc->sc_channels[channel_no];
dc->dc_active = NULL;
if ( SIMPLEQ_EMPTY(&dc->dc_queue) ) {
DPRINTF(("DMA Queue empty for channel %d\n", channel_no));
} else {
struct dmac_xfer_state *dxs;
DPRINTF(("Took a transfer from the queue\n"));
dxs = SIMPLEQ_FIRST(&dc->dc_queue);
SIMPLEQ_REMOVE_HEAD(&dc->dc_queue, dxs_link);
dmac_start(channel_no, dxs);
}
}
int
s3c2440_dmac_wait_xfer(dmac_xfer_t dx, int timeout) {
uint32_t complete;
int err = 0;
struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
struct dmac_xfer_state *dxs = (struct dmac_xfer_state*)dx;
mutex_enter(&sc->sc_intr_mutex);
complete = dxs->dxs_complete;
while(complete == 0) {
int status;
DPRINTF(("s3c2440_dma_xfer_wait: Complete: %x\n", complete));
if ( (status = cv_timedwait(&sc->sc_intr_cv,
&sc->sc_intr_mutex, timeout)) ==
EWOULDBLOCK ) {
DPRINTF(("s3c2440_dma_xfer_wait: Timed out\n"));
complete = 1;
err = ETIMEDOUT;
break;
}
complete = dxs->dxs_complete;
}
mutex_exit(&sc->sc_intr_mutex);
#if 0
if (err == 0 && dxs->dxs_aborted == 1) {
err = EIO;
}
#endif
return err;
}
void
s3c2440_dmac_abort_xfer(dmac_xfer_t dx) {
struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
struct dmac_xfer_state *dxs = (struct dmac_xfer_state*)dx;
struct s3c2440_dmac_channel *dc;
bool wait = FALSE;
KASSERT(dxs->dxs_channel != (uint8_t)DMAC_NO_CHANNEL);
dc = &sc->sc_channels[dxs->dxs_channel];
mutex_enter(&sc->sc_mutex);
if (dc->dc_active == dxs) {
uint32_t reg;
bus_space_write_4(sc->sc_iot, sc->sc_dmach,
DMA_MASKTRIG(dxs->dxs_channel),
DMAMASKTRIG_STOP);
reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach,
DMA_MASKTRIG(dxs->dxs_channel));
DPRINTF(("s3c2440_dma: channel %d mask trigger %x\n", dxs->dxs_channel, reg));
if ( !(reg & DMAMASKTRIG_ON) ) {
DPRINTF(("No wait for abort"));
dmac_channel_done(dxs->dxs_channel);
} else {
wait = TRUE;
}
} else {
DPRINTF(("Removed transfer from queue\n"));
SIMPLEQ_REMOVE(&dc->dc_queue, dxs, dmac_xfer_state, dxs_link);
}
mutex_exit(&sc->sc_mutex);
if (wait == TRUE) {
DPRINTF(("Abort: Wait for transfer to complete\n"));
s3c2440_dmac_wait_xfer(dx, 0);
}
}