/* * Copyright © 2009 Keith Packard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "ao.h" #define NUM_DMA 5 /* * The config address for DMA0 is programmed * separately from that of DMA1-4, but for simplicity, * we make them all contiguous. */ static __xdata struct cc_dma_channel ao_dma_config[NUM_DMA]; static __xdata uint8_t * __xdata ao_dma_done[NUM_DMA]; static __data uint8_t ao_next_dma; uint8_t ao_dma_alloc(__xdata uint8_t *done) { uint8_t id; if (ao_next_dma == NUM_DMA) ao_panic(AO_PANIC_DMA); id = ao_next_dma++; ao_dma_done[id] = done; /* When the first dma object is allocated, set up the DMA * controller */ if (id == 0) { DMAIRQ = 0; DMAIF = 0; IEN1 |= IEN1_DMAIE; } return id; } void ao_dma_set_transfer(uint8_t id, void __xdata *srcaddr, void __xdata *dstaddr, uint16_t count, uint8_t cfg0, uint8_t cfg1) { if (DMAARM & (1 << id)) ao_panic(AO_PANIC_DMA); ao_dma_config[id].src_high = ((uint16_t) srcaddr) >> 8; ao_dma_config[id].src_low = ((uint16_t) srcaddr); ao_dma_config[id].dst_high = ((uint16_t) dstaddr) >> 8; ao_dma_config[id].dst_low = ((uint16_t) dstaddr); ao_dma_config[id].len_high = count >> 8; ao_dma_config[id].len_low = count; ao_dma_config[id].cfg0 = cfg0; ao_dma_config[id].cfg1 = cfg1 | DMA_CFG1_IRQMASK; if (id == 0) { DMA0CFGH = ((uint16_t) (&ao_dma_config[0])) >> 8; DMA0CFGL = ((uint16_t) (&ao_dma_config[0])); } else { DMA1CFGH = ((uint16_t) (&ao_dma_config[1])) >> 8; DMA1CFGL = ((uint16_t) (&ao_dma_config[1])); } } #define nop() _asm nop _endasm; void ao_dma_start(uint8_t id) { uint8_t mask = (1 << id); DMAIRQ &= ~mask; DMAARM = 0x80 | mask; nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); *(ao_dma_done[id]) = 0; DMAARM = mask; nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); } void ao_dma_trigger(uint8_t id) { DMAREQ |= (1 << id); } void ao_dma_abort(uint8_t id) { uint8_t mask = (1 << id); DMAARM = 0x80 | mask; DMAIRQ &= ~mask; *(ao_dma_done[id]) |= AO_DMA_ABORTED; ao_wakeup(ao_dma_done[id]); } void ao_dma_isr(void) interrupt 8 { uint8_t id, mask; /* Find the first DMA channel which is done */ mask = 1; for (id = 0; id < ao_next_dma; id++) { if (DMAIRQ & mask) { /* Clear CPU interrupt flag */ DMAIF = 0; /* Clear the completed ID */ DMAIRQ = ~mask; *(ao_dma_done[id]) |= AO_DMA_DONE; ao_wakeup(ao_dma_done[id]); break; } mask <<= 1; } }