Add eeprom driver and command loop
[fw/altos] / ao_dma.c
diff --git a/ao_dma.c b/ao_dma.c
new file mode 100644 (file)
index 0000000..a109242
--- /dev/null
+++ b/ao_dma.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * 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();
+       DMAARM = mask;
+       nop(); nop(); nop(); nop();
+       nop(); nop(); nop(); nop();
+       nop();
+       *(ao_dma_done[id]) = 0;
+}
+
+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;
+}
+
+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]) = 1;
+                       ao_wakeup(ao_dma_done[id]);
+                       break;
+               }
+               mask <<= 1;
+       }
+}