aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/ports/MSP430X/hal_dma_lld.c
diff options
context:
space:
mode:
authorawygle <awygle@gmail.com>2016-05-04 23:47:33 -0700
committerawygle <awygle@gmail.com>2016-05-04 23:47:33 -0700
commitcf02c79b5aa7209542cbf1b0cefe703a2c0c60be (patch)
tree0557b4194870b710411fbddaf1c58eb432e2d0d4 /os/hal/ports/MSP430X/hal_dma_lld.c
parentfe1d3f21146b1d28bd281b3682a528042b80253c (diff)
downloadChibiOS-Contrib-cf02c79b5aa7209542cbf1b0cefe703a2c0c60be.tar.gz
ChibiOS-Contrib-cf02c79b5aa7209542cbf1b0cefe703a2c0c60be.tar.bz2
ChibiOS-Contrib-cf02c79b5aa7209542cbf1b0cefe703a2c0c60be.zip
Merge pull request #68 from awygle/msp430x
MSP430X DMA Support, EXP430FR6989 Demo + Makefile Updates
Diffstat (limited to 'os/hal/ports/MSP430X/hal_dma_lld.c')
-rw-r--r--os/hal/ports/MSP430X/hal_dma_lld.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/os/hal/ports/MSP430X/hal_dma_lld.c b/os/hal/ports/MSP430X/hal_dma_lld.c
new file mode 100644
index 0000000..58293ca
--- /dev/null
+++ b/os/hal/ports/MSP430X/hal_dma_lld.c
@@ -0,0 +1,244 @@
+/*
+ ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file MSP430X hal_dma_lld.c
+ * @brief MSP430X DMA subsystem low level driver source.
+ *
+ * @addtogroup MSP430X_DMA
+ * @{
+ */
+
+#include "hal.h"
+#include "ch.h"
+#include "hal_dma_lld.h"
+
+#if (HAL_USE_DMA == TRUE) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/* TODO make sure this is right... */
+static msp430x_dma_ch_reg_t * const dma_channels = (msp430x_dma_ch_reg_t *)&DMA0CTL;
+static uint8_t * const dma_ctls = (uint8_t *)&DMACTL0;
+
+static msp430x_dma_cb_t callbacks[MSP430X_DMA_CHANNELS];
+#if CH_CFG_USE_SEMAPHORES
+static semaphore_t dma_lock;
+#endif
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static void init_request(const msp430x_dma_req_t * request, uint8_t index) {
+
+ dma_ctls[index] = request->trigger;
+ callbacks[index] = request->callback;
+ msp430x_dma_ch_reg_t * ch = &dma_channels[index];
+ ch->sa = (uintptr_t)request->source_addr;
+ ch->da = (uintptr_t)request->dest_addr;
+ ch->sz = request->size;
+ ch->ctl = DMAREQ | DMAIE | DMAEN | request->data_mode | request->addr_mode
+ | request->transfer_mode;
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+PORT_IRQ_HANDLER(DMA_VECTOR) {
+ uint8_t index;
+ OSAL_IRQ_PROLOGUE();
+
+ index = (DMAIV >> 1) - 1;
+
+ if (index < MSP430X_DMA_CHANNELS) {
+ msp430x_dma_cb_t * cb = &callbacks[index];
+
+ /* WARNING: CALLBACKS ARE CALLED IN AN ISR CONTEXT! */
+ if (cb->callback != NULL) {
+ cb->callback(cb->args);
+ }
+
+ }
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Initialize the DMA engine.
+ *
+ * @init
+ */
+void dmaInit(void) {
+#if CH_CFG_USE_SEMAPHORES
+ chSemObjectInit(&dma_lock, MSP430X_DMA_CHANNELS);
+#endif
+}
+
+/**
+ * @brief Requests a DMA transfer operation from the DMA engine.
+ * @note The DMA engine uses unclaimed DMA channels to provide DMA services
+ * for one-off or infrequent uses. If all channels are busy, and
+ * semaphores are enabled, the calling thread will sleep until a
+ * channel is available or the request times out. If semaphores are
+ * disabled, the calling thread will busy-wait instead of sleeping.
+ */
+bool dmaRequest(msp430x_dma_req_t * request, systime_t timeout) {
+ /* Check if a DMA channel is available */
+#if CH_CFG_USE_SEMAPHORES
+ msg_t semresult = chSemWaitTimeout(&dma_lock, timeout);
+ if (semresult != MSG_OK)
+ return true;
+#endif
+
+#if !(CH_CFG_USE_SEMAPHORES)
+ systime_t start = chVTGetSystemTimeX();
+
+ do {
+#endif
+ /* Grab the correct DMA channel to use */
+ int i = 0;
+ for (i = 0; i < MSP430X_DMA_CHANNELS; i++) {
+ if (!(dma_channels[i].ctl & DMAEN)) {
+ break;
+ }
+ }
+#if !(CH_CFG_USE_SEMAPHORES)
+ while (chVTTimeElapsedSinceX(start) < timeout);
+#endif
+
+#if !(CH_CFG_USE_SEMAPHORES)
+ if (i == MSP430X_DMA_CHANNELS) {
+ return true;
+ }
+#endif
+
+ /* Make the request */
+ init_request(request, i);
+
+#if CH_CFG_USE_SEMAPHORES
+ chSemSignal(&dma_lock);
+#endif
+
+ return false;
+}
+
+/**
+ * @brief Acquires exclusive control of a DMA channel.
+ * @pre The channel must not be already acquired or an error is returned.
+ * @note If the channel is in use by the DMA engine, blocks until acquired.
+ * @post This channel must be interacted with using only the functions
+ * defined in this module.
+ *
+ * @param[out] channel The channel handle. Must be pre-allocated.
+ * @param[in] index The index of the channel (< MSP430X_DMA_CHANNELS).
+ * @return The operation status.
+ * @retval false no error, channel acquired.
+ * @retval true error, channel already acquired.
+ */
+bool dmaAcquire(msp430x_dma_ch_t * channel, uint8_t index) {
+ /* Acquire the channel in an idle mode */
+
+ /* Is the channel already acquired? */
+ osalDbgAssert(index < MSP430X_DMA_CHANNELS, "invalid channel index");
+ if (dma_channels[index].ctl & DMADT_4) {
+ return true;
+ }
+
+ /* Increment the DMA counter */
+#if CH_CFG_USE_SEMAPHORES
+ msg_t semresult = chSemWait(&dma_lock);
+ if (semresult != MSG_OK)
+ return true;
+#endif
+
+ while (dma_channels[index].ctl & DMAEN) ;
+
+ dma_ctls[index] = DMA_TRIGGER_MNEM(DMAREQ);
+ dma_channels[index].sz = 0;
+ dma_channels[index].ctl = DMAEN | DMAABORT | DMADT_4;
+
+ channel->registers = dma_channels + index;
+ channel->ctl = dma_ctls + index;
+ channel->cb = callbacks + index;
+
+ return false;
+}
+
+/**
+ * @brief Initiates a DMA transfer operation using an acquired channel.
+ * @pre The channel must have been acquired using @p dmaAcquire().
+ *
+ * @param[in] channel pointer to a DMA channel from @p dmaAcquire().
+ * @param[in] request pointer to a DMA request object.
+ */
+void dmaTransfer(msp430x_dma_ch_t * channel, msp430x_dma_req_t * request) {
+
+ *(channel->ctl) = request->trigger;
+
+ channel->cb->callback = request->callback.callback;
+ channel->cb->args = request->callback.args;
+
+ chSysLock();
+ channel->registers->ctl &= (~DMAEN);
+ channel->registers->sa = (uintptr_t)request->source_addr;
+ channel->registers->da = (uintptr_t)request->dest_addr;
+ channel->registers->sz = request->size;
+ channel->registers->ctl = DMAIE | request->data_mode | request->addr_mode
+ | request->transfer_mode | DMADT_4 | DMAEN | DMAREQ; /* repeated transfers */
+ chSysUnlock();
+}
+
+/**
+ * @brief Releases exclusive control of a DMA channel.
+ * @details The channel is released from control and returned to the DMA engine
+ * pool. Trying to release an unallocated channel is an illegal
+ * operation and is trapped if assertions are enabled.
+ * @pre The channel must have been acquired using @p dmaAcquire().
+ * @post The channel is returned to the DMA engine pool.
+ */
+void dmaRelease(msp430x_dma_ch_t * channel) {
+
+ osalDbgCheck(channel != NULL);
+ osalDbgAssert(channel->registers->ctl & DMADT_4, "not acquired");
+
+ /* Release the channel in an idle mode */
+ channel->registers->ctl = DMAABORT;
+
+ /* release the DMA counter */
+#if CH_CFG_USE_SEMAPHORES
+ chSemSignal(&dma_lock);
+#endif
+}
+
+#endif /* HAL_USE_DMA == TRUE */
+
+/** @} */