mbed library sources. Supersedes mbed-src.

Dependents:   Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more

Revision:
149:156823d33999
Parent:
144:ef7eb2e8f9f7
Child:
150:02e0a0aed4ec
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/spi_api.c	Fri Oct 28 11:17:30 2016 +0100
@@ -0,0 +1,1343 @@
+/***************************************************************************//**
+ * @file spi_api.c
+ *******************************************************************************
+ * @section License
+ * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
+ *******************************************************************************
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#include "device.h"
+#include "clocking.h"
+#if DEVICE_SPI
+
+#include "mbed_assert.h"
+#include "PeripheralPins.h"
+#include "pinmap.h"
+#include "pinmap_function.h"
+#include "error.h"
+
+#include "dma_api.h"
+#include "dma_api_HAL.h"
+#include "serial_api_HAL.h"
+#include "spi_api.h"
+#include "em_usart.h"
+#include "em_cmu.h"
+#include "em_dma.h"
+#include "sleep_api.h"
+#include "sleepmodes.h"
+
+static uint16_t fill_word = SPI_FILL_WORD;
+
+#define SPI_LEAST_ACTIVE_SLEEPMODE EM1
+
+static inline CMU_Clock_TypeDef spi_get_clock_tree(spi_t *obj)
+{
+    switch ((int)obj->spi.spi) {
+#ifdef USART0
+        case SPI_0:
+            return cmuClock_USART0;
+#endif
+#ifdef USART1
+        case SPI_1:
+            return cmuClock_USART1;
+#endif
+#ifdef USART2
+        case SPI_2:
+            return cmuClock_USART2;
+#endif
+        default:
+            error("Spi module not available.. Out of bound access.");
+            return cmuClock_HFPER;
+    }
+}
+
+static inline uint8_t spi_get_index(spi_t *obj)
+{
+    uint8_t index = 0;
+    switch ((int)obj->spi.spi) {
+#ifdef USART0
+        case SPI_0:
+            index = 0;
+            break;
+#endif
+#ifdef USART1
+        case SPI_1:
+            index = 1;
+            break;
+#endif
+#ifdef USART2
+        case SPI_2:
+            index = 2;
+            break;
+#endif
+        default:
+            error("Spi module not available.. Out of bound access.");
+            break;
+    }
+    return index;
+}
+
+uint8_t spi_get_module(spi_t *obj)
+{
+    return spi_get_index(obj);
+}
+
+static void usart_init(spi_t *obj, uint32_t baudrate, USART_Databits_TypeDef databits, bool master, USART_ClockMode_TypeDef clockMode )
+{
+    USART_InitSync_TypeDef init = USART_INITSYNC_DEFAULT;
+    init.enable = usartDisable;
+    init.baudrate = baudrate;
+    init.databits = databits;
+    init.master = master;
+    init.msbf   = 1;
+    init.clockMode = clockMode;
+
+    /* Determine the reference clock, because the correct clock may not be set up at init time (e.g. before main()) */
+    init.refFreq = REFERENCE_FREQUENCY;
+
+    USART_InitSync(obj->spi.spi, &init);
+}
+
+void spi_preinit(spi_t *obj, PinName mosi, PinName miso, PinName clk, PinName cs)
+{
+    SPIName spi_mosi = (SPIName) pinmap_peripheral(mosi, PinMap_SPI_MOSI);
+    SPIName spi_miso = (SPIName) pinmap_peripheral(miso, PinMap_SPI_MISO);
+    SPIName spi_clk = (SPIName) pinmap_peripheral(clk, PinMap_SPI_CLK);
+    SPIName spi_cs = (SPIName) pinmap_peripheral(cs, PinMap_SPI_CS);
+    SPIName spi_data = (SPIName) pinmap_merge(spi_mosi, spi_miso);
+    SPIName spi_ctrl = (SPIName) pinmap_merge(spi_clk, spi_cs);
+
+    obj->spi.spi = (USART_TypeDef *) pinmap_merge(spi_data, spi_ctrl);
+    MBED_ASSERT((int) obj->spi.spi != NC);
+
+    if (cs != NC) { /* Slave mode */
+        obj->spi.master = false;
+    } else {
+        obj->spi.master = true;
+    }
+
+#if defined(_SILICON_LABS_32B_PLATFORM_1)
+    // On P1, we need to ensure all pins are on same location
+    uint32_t loc_mosi = pin_location(mosi, PinMap_SPI_MOSI);
+    uint32_t loc_miso = pin_location(miso, PinMap_SPI_MISO);
+    uint32_t loc_clk = pin_location(clk, PinMap_SPI_CLK);
+    uint32_t loc_cs = pin_location(cs, PinMap_SPI_CS);
+    uint32_t loc_data = pinmap_merge(loc_mosi, loc_miso);
+    uint32_t loc_ctrl = pinmap_merge(loc_clk, loc_cs);
+    obj->spi.location = pinmap_merge(loc_data, loc_ctrl);
+    MBED_ASSERT(obj->spi.location != NC);
+#endif
+
+    obj->spi.dmaOptionsTX.dmaUsageState = DMA_USAGE_OPPORTUNISTIC;
+}
+
+void spi_enable_pins(spi_t *obj, uint8_t enable, PinName mosi, PinName miso, PinName clk, PinName cs)
+{
+    if (enable) {
+        if (obj->spi.master) { /* Master mode */
+            /* Either mosi or miso can be NC */
+            if (mosi != NC) {
+                pin_mode(mosi, PushPull);
+            }
+            if (miso != NC) {
+                pin_mode(miso, Input);
+            }
+            pin_mode(clk, PushPull);
+            /* Don't set cs pin, since we toggle it manually */
+        } else { /* Slave mode */
+            if (mosi != NC) {
+                pin_mode(mosi, Input);
+            }
+            if (miso != NC) {
+                pin_mode(miso, PushPull);
+            }
+            pin_mode(clk, Input);
+            pin_mode(cs, Input);
+        }
+    } else {
+        // TODO_LP return PinMode to the previous state
+        if (obj->spi.master) { /* Master mode */
+            /* Either mosi or miso can be NC */
+            if (mosi != NC) {
+                pin_mode(mosi, Disabled);
+            }
+            if (miso != NC) {
+                pin_mode(miso, Disabled);
+            }
+            pin_mode(clk, Disabled);
+            /* Don't set cs pin, since we toggle it manually */
+        } else { /* Slave mode */
+            if (mosi != NC) {
+                pin_mode(mosi, Disabled);
+            }
+            if (miso != NC) {
+                pin_mode(miso, Disabled);
+            }
+            pin_mode(clk, Disabled);
+            pin_mode(cs, Disabled);
+        }
+    }
+
+    /* Enabling pins and setting location */
+#ifdef _USART_ROUTEPEN_RESETVALUE
+    uint32_t route = USART_ROUTEPEN_CLKPEN;
+    obj->spi.spi->ROUTELOC0 &= ~_USART_ROUTELOC0_CLKLOC_MASK;
+    obj->spi.spi->ROUTELOC0 |= pin_location(clk, PinMap_SPI_CLK)<<_USART_ROUTELOC0_CLKLOC_SHIFT;
+    if (mosi != NC) {
+        route |= USART_ROUTEPEN_TXPEN;
+        obj->spi.spi->ROUTELOC0 &= ~_USART_ROUTELOC0_TXLOC_MASK;
+        obj->spi.spi->ROUTELOC0 |= pin_location(mosi, PinMap_SPI_MOSI)<<_USART_ROUTELOC0_TXLOC_SHIFT;
+    }
+    if (miso != NC) {
+        route |= USART_ROUTEPEN_RXPEN;
+        obj->spi.spi->ROUTELOC0 &= ~_USART_ROUTELOC0_RXLOC_MASK;
+        obj->spi.spi->ROUTELOC0 |= pin_location(miso, PinMap_SPI_MOSI)<<_USART_ROUTELOC0_RXLOC_SHIFT;
+    }
+    if (!obj->spi.master) {
+        route |= USART_ROUTEPEN_CSPEN;
+        obj->spi.spi->ROUTELOC0 &= ~_USART_ROUTELOC0_CSLOC_MASK;
+        obj->spi.spi->ROUTELOC0 |= pin_location(cs, PinMap_SPI_MOSI)<<_USART_ROUTELOC0_CSLOC_SHIFT;
+    }
+    obj->spi.spi->ROUTEPEN = route;
+}
+#else
+    uint32_t route = USART_ROUTE_CLKPEN | (obj->spi.location << _USART_ROUTE_LOCATION_SHIFT);
+
+    if (mosi != NC) {
+        route |= USART_ROUTE_TXPEN;
+    }
+    if (miso != NC) {
+        route |= USART_ROUTE_RXPEN;
+    }
+    if (!obj->spi.master) {
+        route |= USART_ROUTE_CSPEN;
+    }
+    obj->spi.spi->ROUTE = route;
+}
+#endif
+void spi_enable(spi_t *obj, uint8_t enable)
+{
+    USART_Enable(obj->spi.spi, (enable ? usartEnable : usartDisable));
+}
+
+void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName clk, PinName cs)
+{
+    CMU_ClockEnable(cmuClock_HFPER, true);
+    spi_preinit(obj, mosi, miso, clk, cs);
+    CMU_ClockEnable(spi_get_clock_tree(obj), true);
+    usart_init(obj, 100000, usartDatabits8, true, usartClockMode0);
+
+    spi_enable_pins(obj, true, mosi, miso, clk, cs);
+    spi_enable(obj, true);
+}
+
+void spi_enable_event(spi_t *obj, uint32_t event, uint8_t enable)
+{
+    if(enable) obj->spi.event |= event;
+    else obj->spi.event &= ~event;
+}
+
+/****************************************************************************
+* void spi_enable_interrupt(spi_t *obj, uint32_t handler, uint8_t enable)
+*
+* This will enable the interrupt in NVIC for the associated USART RX channel
+*
+*   * obj: pointer to spi object
+*   * handler: pointer to interrupt handler for this channel
+*   * enable: Whether to enable (true) or disable (false) the interrupt
+*
+****************************************************************************/
+void spi_enable_interrupt(spi_t *obj, uint32_t handler, uint8_t enable)
+{
+    IRQn_Type IRQvector;
+
+    switch ((uint32_t)obj->spi.spi) {
+#ifdef USART0
+        case USART_0:
+            IRQvector = USART0_RX_IRQn;
+            break;
+#endif
+#ifdef USART1
+        case USART_1:
+            IRQvector = USART1_RX_IRQn;
+            break;
+#endif
+#ifdef USART2
+        case USART_2:
+            IRQvector = USART2_RX_IRQn;
+            break;
+#endif
+        default:
+            error("Undefined SPI peripheral");
+            return;
+    }
+
+    if (enable == true) {
+        NVIC_SetVector(IRQvector, handler);
+        USART_IntEnable(obj->spi.spi, USART_IEN_RXDATAV);
+        NVIC_EnableIRQ(IRQvector);
+    } else {
+        NVIC_SetVector(IRQvector, handler);
+        USART_IntDisable(obj->spi.spi, USART_IEN_RXDATAV);
+        NVIC_DisableIRQ(IRQvector);
+    }
+}
+
+void spi_format(spi_t *obj, int bits, int mode, int slave)
+{
+    /* Bits: values between 4 and 16 are valid */
+    MBED_ASSERT(bits >= 4 && bits <= 16);
+    obj->spi.bits = bits;
+    /* 0x01 = usartDatabits4, etc, up to 0x0D = usartDatabits16 */
+    USART_Databits_TypeDef databits = (USART_Databits_TypeDef) (bits - 3);
+
+    USART_ClockMode_TypeDef clockMode;
+    MBED_ASSERT(mode >= 0 && mode <= 3);
+    switch (mode) {
+        case 0:
+            clockMode = usartClockMode0;
+            break;
+        case 1:
+            clockMode = usartClockMode1;
+            break;
+        case 2:
+            clockMode = usartClockMode2;
+            break;
+        case 3:
+            clockMode = usartClockMode3;
+            break;
+        default:
+            clockMode = usartClockMode0;
+    }
+
+    //save state
+#ifdef _USART_ROUTEPEN_RESETVALUE
+    uint32_t route = obj->spi.spi->ROUTEPEN;
+    uint32_t loc = obj->spi.spi->ROUTELOC0;
+#else
+    uint32_t route = obj->spi.spi->ROUTE;
+#endif
+    uint32_t iflags = obj->spi.spi->IEN;
+    bool enabled = (obj->spi.spi->STATUS & (USART_STATUS_RXENS | USART_STATUS_TXENS)) != 0;
+
+    usart_init(obj, 100000, databits, (slave ? false : true), clockMode);
+
+    //restore state
+#ifdef _USART_ROUTEPEN_RESETVALUE
+    obj->spi.spi->ROUTEPEN = route;
+    obj->spi.spi->ROUTELOC0 = loc;
+#else
+    obj->spi.spi->ROUTE = route;
+#endif
+    obj->spi.spi->IEN = iflags;
+
+    if(enabled) spi_enable(obj, enabled);
+}
+
+void spi_frequency(spi_t *obj, int hz)
+{
+    USART_BaudrateSyncSet(obj->spi.spi, REFERENCE_FREQUENCY, hz);
+}
+
+/* Read/Write */
+
+void spi_write(spi_t *obj, int value)
+{
+    if (obj->spi.bits <= 8) {
+        USART_Tx(obj->spi.spi, (uint8_t) value);
+    } else if (obj->spi.bits == 9) {
+        USART_TxExt(obj->spi.spi, (uint16_t) value & 0x1FF);
+    } else {
+        USART_TxDouble(obj->spi.spi, (uint16_t) value);
+    }
+}
+
+int spi_read(spi_t *obj)
+{
+    if (obj->spi.bits <= 8) {
+        return (int) obj->spi.spi->RXDATA;
+    } else if (obj->spi.bits == 9) {
+        return (int) obj->spi.spi->RXDATAX & 0x1FF;
+    } else {
+        return (int) obj->spi.spi->RXDOUBLE;
+    }
+}
+
+int spi_read_asynch(spi_t *obj)
+{
+    return spi_read(obj);
+}
+
+int spi_master_write(spi_t *obj, int value)
+{
+    spi_write(obj, value);
+
+    /* Wait for transmission of last byte */
+    while (!(obj->spi.spi->STATUS & USART_STATUS_TXC)) {
+    }
+
+    return spi_read(obj);
+}
+
+inline uint8_t spi_master_tx_ready(spi_t *obj)
+{
+    return (obj->spi.spi->STATUS & USART_STATUS_TXBL) ? true : false;
+}
+
+uint8_t spi_master_rx_ready(spi_t *obj)
+{
+    return (obj->spi.spi->STATUS & USART_STATUS_RXDATAV) ? true : false;
+}
+
+uint8_t spi_master_tx_int_flag(spi_t *obj)
+{
+    return (obj->spi.spi->IF & USART_IF_TXBL) ? true : false;
+}
+
+uint8_t spi_master_rx_int_flag(spi_t *obj)
+{
+    return (obj->spi.spi->IF & (USART_IF_RXDATAV | USART_IF_RXFULL)) ? true : false;
+}
+
+void spi_master_read_asynch_complete(spi_t *obj)
+{
+    obj->spi.spi->IFC = USART_IFC_RXFULL; // in case it got full
+}
+
+void spi_master_write_asynch_complete(spi_t *obj)
+{
+    obj->spi.spi->IFC = USART_IFC_TXC;
+}
+
+void spi_irq_handler(spi_t *obj)
+{
+    spi_read(obj); //TODO_LP store data to the object?
+}
+
+uint8_t spi_active(spi_t *obj)
+{
+    switch(obj->spi.dmaOptionsTX.dmaUsageState) {
+        case DMA_USAGE_TEMPORARY_ALLOCATED:
+            return true;
+        case DMA_USAGE_ALLOCATED:
+            /* Check whether the allocated DMA channel is active */
+#ifdef LDMA_PRESENT
+            return(LDMAx_ChannelEnabled(obj->spi.dmaOptionsTX.dmaChannel) || LDMAx_ChannelEnabled(obj->spi.dmaOptionsRX.dmaChannel));
+#else
+            return(DMA_ChannelEnabled(obj->spi.dmaOptionsTX.dmaChannel) || DMA_ChannelEnabled(obj->spi.dmaOptionsRX.dmaChannel));
+#endif
+        default:
+            /* Check whether interrupt for spi is enabled */
+            return (obj->spi.spi->IEN & (USART_IEN_RXDATAV | USART_IEN_TXBL)) ? true : false;
+    }
+}
+
+void spi_buffer_set(spi_t *obj, const void *tx, uint32_t tx_length, void *rx, uint32_t rx_length, uint8_t bit_width)
+{
+    uint32_t i;
+    uint16_t *tx_ptr = (uint16_t *) tx;
+
+    obj->tx_buff.buffer = (void *)tx;
+    obj->rx_buff.buffer = rx;
+    obj->tx_buff.length = tx_length;
+    obj->rx_buff.length = rx_length;
+    obj->tx_buff.pos = 0;
+    obj->rx_buff.pos = 0;
+    obj->tx_buff.width = bit_width;
+    obj->rx_buff.width = bit_width;
+
+    if((obj->spi.bits == 9) && (tx != 0)) {
+        // Make sure we don't have inadvertent non-zero bits outside 9-bit frames which could trigger unwanted operation
+        for(i = 0; i < (tx_length / 2); i++) {
+            tx_ptr[i] &= 0x1FF;
+        }
+    }
+}
+
+static void spi_buffer_tx_write(spi_t *obj)
+{
+    uint32_t data = 0;
+
+    // Interpret buffer according to declared width
+    if (!obj->tx_buff.buffer) {
+        data = SPI_FILL_WORD;
+    } else if (obj->tx_buff.width == 32) {
+        uint32_t * tx = (uint32_t *)obj->tx_buff.buffer;
+        data = tx[obj->tx_buff.pos];
+    } else if (obj->tx_buff.width == 16) {
+        uint16_t * tx = (uint16_t *)obj->tx_buff.buffer;
+        data = tx[obj->tx_buff.pos];
+    } else {
+        uint8_t * tx = (uint8_t *)obj->tx_buff.buffer;
+        data = tx[obj->tx_buff.pos];
+    }
+    obj->tx_buff.pos++;
+
+    // Send buffer
+    if (obj->spi.bits > 9) {
+        obj->spi.spi->TXDOUBLE = data;
+    } else if (obj->spi.bits == 9) {
+        obj->spi.spi->TXDATAX = data;
+    } else {
+        obj->spi.spi->TXDATA = data;
+    }
+}
+
+static void spi_buffer_rx_read(spi_t *obj)
+{
+    uint32_t data;
+
+    if (obj->spi.spi->STATUS & USART_STATUS_RXDATAV) {
+        // Read from the FIFO
+        if (obj->spi.bits > 9) {
+            data = obj->spi.spi->RXDOUBLE;
+        } else if (obj->spi.bits == 9) {
+            data = obj->spi.spi->RXDATAX;
+        } else {
+            data = obj->spi.spi->RXDATA;
+        }
+
+        // If there is room in the buffer, store the data
+        if (obj->rx_buff.buffer && obj->rx_buff.pos < obj->rx_buff.length) {
+            if (obj->rx_buff.width == 32) {
+                uint32_t * rx = (uint32_t *)(obj->rx_buff.buffer);
+                rx[obj->rx_buff.pos] = data;
+            } else if (obj->rx_buff.width == 16) {
+                uint16_t * rx = (uint16_t *)(obj->rx_buff.buffer);
+                rx[obj->rx_buff.pos] = data;
+            } else {
+                uint8_t * rx = (uint8_t *)(obj->rx_buff.buffer);
+                rx[obj->rx_buff.pos] = data;
+            }
+            obj->rx_buff.pos++;
+        }
+    }
+}
+
+int spi_master_write_asynch(spi_t *obj)
+{
+    int ndata = 0;
+    while ((obj->tx_buff.pos < obj->tx_buff.length) && (obj->spi.spi->STATUS & USART_STATUS_TXBL)) {
+        spi_buffer_tx_write(obj);
+        ndata++;
+    }
+    return ndata;
+}
+
+int spi_master_read_asynch(spi_t *obj)
+{
+    int ndata = 0;
+    while ((obj->rx_buff.pos < obj->rx_buff.length) && (obj->spi.spi->STATUS & (USART_STATUS_RXDATAV | USART_STATUS_RXFULL))) {
+        spi_buffer_rx_read(obj);
+        ndata++;
+    }
+    // all sent but still more to receive? need to align tx buffer
+    if ((obj->tx_buff.pos >= obj->tx_buff.length) && (obj->rx_buff.pos < obj->rx_buff.length)) {
+        obj->tx_buff.buffer = (void *)0;
+        obj->tx_buff.length = obj->rx_buff.length;
+    }
+
+    return ndata;
+}
+
+uint8_t spi_buffer_rx_empty(spi_t *obj)
+{
+    return (obj->rx_buff.pos >= obj->rx_buff.length ? true : false );
+}
+
+uint8_t spi_buffer_tx_empty(spi_t *obj)
+{
+    return (obj->tx_buff.pos >= obj->tx_buff.length ? true : false );
+}
+
+//TODO_LP implement slave
+
+int spi_slave_receive(spi_t *obj)
+{
+    if (obj->spi.bits <= 9) {
+        return (obj->spi.spi->STATUS & USART_STATUS_RXDATAV) ? 1 : 0;
+    } else {
+        return (obj->spi.spi->STATUS & USART_STATUS_RXFULL) ? 1 : 0;
+    }
+}
+
+int spi_slave_read(spi_t *obj)
+{
+    return spi_read(obj);
+}
+
+void spi_slave_write(spi_t *obj, int value)
+{
+    spi_write(obj, value);
+}
+
+uint32_t spi_event_check(spi_t *obj)
+{
+    uint32_t requestedEvent = obj->spi.event;
+    uint32_t event = 0;
+    uint8_t quit = spi_buffer_rx_empty(obj) & spi_buffer_tx_empty(obj);
+    if (((requestedEvent & SPI_EVENT_COMPLETE) != 0) && (quit == true)) {
+        event |= SPI_EVENT_COMPLETE;
+    }
+
+    if(quit == true) {
+        event |= SPI_EVENT_INTERNAL_TRANSFER_COMPLETE;
+    }
+
+    return event;
+}
+/******************************************
+* void transferComplete(uint channel, bool primary, void* user)
+*
+* Callback function which gets called upon DMA transfer completion
+* the user-defined pointer is pointing to the CPP-land thunk
+******************************************/
+void transferComplete(unsigned int channel, bool primary, void *user)
+{
+    (void) channel;
+    (void) primary;
+
+    /* User pointer should be a thunk to CPP land */
+    if (user != NULL) {
+        ((DMACallback)user)();
+    }
+}
+
+/******************************************
+* bool spi_allocate_dma(spi_t *obj);
+* (helper function for spi_enable_dma)
+*
+* This function will request two DMA channels from the DMA API if needed
+* by the hint provided. They will be allocated to the SPI object pointed to.
+*
+* return value: whether the channels were acquired successfully (true) or not.
+******************************************/
+bool spi_allocate_dma(spi_t *obj)
+{
+    int dmaChannelIn, dmaChannelOut;
+    dmaChannelIn = dma_channel_allocate(DMA_CAP_NONE);
+    if (dmaChannelIn == DMA_ERROR_OUT_OF_CHANNELS) {
+        return false;
+    }
+    dmaChannelOut = dma_channel_allocate(DMA_CAP_NONE);
+    if (dmaChannelOut == DMA_ERROR_OUT_OF_CHANNELS) {
+        dma_channel_free(dmaChannelIn);
+        return false;
+    }
+
+    obj->spi.dmaOptionsTX.dmaChannel = dmaChannelOut;
+    obj->spi.dmaOptionsRX.dmaChannel = dmaChannelIn;
+    return true;
+}
+
+/******************************************
+* void spi_enable_dma(spi_t *obj, DMAUsage state)
+*
+* This function tries to allocate DMA as indicated by the hint (state).
+* There are three possibilities:
+*   * state = NEVER:
+*       if there were channels allocated by state = ALWAYS, they will be released
+*   * state = OPPORTUNITIC:
+*       if there are channels available, they will get used, but freed upon transfer completion
+*   * state = ALWAYS
+*       if there are channels available, they will get allocated and not be freed until state changes
+******************************************/
+void spi_enable_dma(spi_t *obj, DMAUsage state)
+{
+    if (state == DMA_USAGE_ALWAYS && obj->spi.dmaOptionsTX.dmaUsageState != DMA_USAGE_ALLOCATED) {
+        /* Try to allocate channels */
+        if (spi_allocate_dma(obj)) {
+            obj->spi.dmaOptionsTX.dmaUsageState = DMA_USAGE_ALLOCATED;
+        } else {
+            obj->spi.dmaOptionsTX.dmaUsageState = state;
+        }
+    } else if (state == DMA_USAGE_OPPORTUNISTIC) {
+        if (obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_ALLOCATED) {
+            /* Channels have already been allocated previously by an ALWAYS state, so after this transfer, we will release them */
+            obj->spi.dmaOptionsTX.dmaUsageState = DMA_USAGE_TEMPORARY_ALLOCATED;
+        } else {
+            /* Try to allocate channels */
+            if (spi_allocate_dma(obj)) {
+                obj->spi.dmaOptionsTX.dmaUsageState = DMA_USAGE_TEMPORARY_ALLOCATED;
+            } else {
+                obj->spi.dmaOptionsTX.dmaUsageState = state;
+            }
+        }
+    } else if (state == DMA_USAGE_NEVER) {
+        /* If channels are allocated, get rid of them */
+        if (obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_ALLOCATED) {
+            dma_channel_free(obj->spi.dmaOptionsTX.dmaChannel);
+            dma_channel_free(obj->spi.dmaOptionsRX.dmaChannel);
+        }
+        obj->spi.dmaOptionsTX.dmaUsageState = DMA_USAGE_NEVER;
+    }
+}
+
+#ifdef LDMA_PRESENT
+/************************************************************************************
+ *          DMA helper functions                                                    *
+ ************************************************************************************/
+/******************************************
+* static void serial_dmaTransferComplete(uint channel, bool primary, void* user)
+*
+* Callback function which gets called upon DMA transfer completion
+* the user-defined pointer is pointing to the CPP-land thunk
+******************************************/
+static void serial_dmaTransferComplete(unsigned int channel, bool primary, void *user)
+{
+
+    /* User pointer should be a thunk to CPP land */
+    if (user != NULL) {
+        ((DMACallback)user)();
+    }
+}
+static void spi_master_dma_channel_setup(spi_t *obj, void* callback)
+{
+    obj->spi.dmaOptionsRX.dmaCallback.userPtr = callback;
+}
+#else
+/******************************************
+* void spi_master_dma_channel_setup(spi_t *obj)
+*
+* This function will setup the DMA configuration for SPI transfers
+*
+* The channel numbers are fetched from the SPI instance, so this function
+* should only be called when those channels have actually been allocated.
+******************************************/
+static void spi_master_dma_channel_setup(spi_t *obj, void* callback)
+{
+    DMA_CfgChannel_TypeDef  rxChnlCfg;
+    DMA_CfgChannel_TypeDef  txChnlCfg;
+
+    /* Setting up channel for rx. */
+    obj->spi.dmaOptionsRX.dmaCallback.cbFunc = transferComplete;
+    obj->spi.dmaOptionsRX.dmaCallback.userPtr = callback;
+
+    rxChnlCfg.highPri   = false;
+    rxChnlCfg.enableInt = true;
+    rxChnlCfg.cb        = &(obj->spi.dmaOptionsRX.dmaCallback);
+
+    /* Setting up channel for tx. */
+    obj->spi.dmaOptionsTX.dmaCallback.cbFunc = transferComplete;
+    obj->spi.dmaOptionsTX.dmaCallback.userPtr = callback;
+
+    txChnlCfg.highPri   = false;
+    txChnlCfg.enableInt = true;
+    txChnlCfg.cb        = &(obj->spi.dmaOptionsTX.dmaCallback);
+
+    switch ((int)obj->spi.spi) {
+#ifdef USART0
+        case SPI_0:
+            rxChnlCfg.select = DMAREQ_USART0_RXDATAV;
+            txChnlCfg.select = DMAREQ_USART0_TXEMPTY;
+            break;
+#endif
+#ifdef USART1
+        case SPI_1:
+            rxChnlCfg.select = DMAREQ_USART1_RXDATAV;
+            txChnlCfg.select = DMAREQ_USART1_TXEMPTY;
+            break;
+#endif
+#ifdef USART2
+        case SPI_2:
+            rxChnlCfg.select = DMAREQ_USART2_RXDATAV;
+            txChnlCfg.select = DMAREQ_USART2_TXEMPTY;
+            break;
+#endif
+        default:
+            error("Spi module not available.. Out of bound access.");
+            break;
+    }
+    DMA_CfgChannel(obj->spi.dmaOptionsRX.dmaChannel, &rxChnlCfg);
+    DMA_CfgChannel(obj->spi.dmaOptionsTX.dmaChannel, &txChnlCfg);
+}
+#endif // LDMA_PRESENT
+/******************************************
+* void spi_activate_dma(spi_t *obj, void* rxdata, void* txdata, int length)
+*
+* This function will start the DMA engine for SPI transfers
+*
+*   * rxdata: pointer to RX buffer, if needed.
+*   * txdata: pointer to TX buffer, if needed. Else FF's.
+*   * tx_length: how many bytes will get sent.
+*   * rx_length: how many bytes will get received. If > tx_length, TX will get padded with n lower bits of SPI_FILL_WORD.
+******************************************/
+#ifdef LDMA_PRESENT
+static void spi_activate_dma(spi_t *obj, void* rxdata, const void* txdata, int tx_length, int rx_length)
+{
+    LDMA_PeripheralSignal_t dma_periph;
+
+    if(rxdata) {
+        volatile const void *source_addr;
+        /* Select RX source address. 9 bit frame length requires to use extended register.
+           10 bit and larger frame requires to use RXDOUBLE register. */
+        switch((int)obj->spi.spi) {
+            case USART_0:
+                dma_periph = ldmaPeripheralSignal_USART0_RXDATAV;
+                break;
+            case USART_1:
+                dma_periph = ldmaPeripheralSignal_USART1_RXDATAV;
+                break;
+            default:
+                EFM_ASSERT(0);
+                while(1);
+                break;
+        }
+
+        if (obj->spi.bits <= 8) {
+            source_addr = &obj->spi.spi->RXDATA;
+        } else if (obj->spi.bits == 9) {
+            source_addr = &obj->spi.spi->RXDATAX;
+        } else {
+            source_addr = &obj->spi.spi->RXDOUBLE;
+        }
+
+        LDMA_TransferCfg_t xferConf = LDMA_TRANSFER_CFG_PERIPHERAL(dma_periph);
+        LDMA_Descriptor_t desc = LDMA_DESCRIPTOR_SINGLE_P2M_BYTE(source_addr, rxdata, rx_length);
+
+        if(obj->spi.bits >= 9){
+            desc.xfer.size = ldmaCtrlSizeHalf;
+        }
+
+        if (obj->tx_buff.width == 32) {
+            if (obj->spi.bits >= 9) {
+                desc.xfer.dstInc = ldmaCtrlDstIncTwo;
+            } else {
+                desc.xfer.dstInc = ldmaCtrlDstIncFour;
+            }
+        } else if (obj->tx_buff.width == 16) {
+            if (obj->spi.bits >= 9) {
+                desc.xfer.dstInc = ldmaCtrlDstIncOne;
+            } else {
+                desc.xfer.dstInc = ldmaCtrlDstIncTwo;
+            }
+        } else {
+            desc.xfer.dstInc = ldmaCtrlDstIncOne;
+        }
+
+        LDMAx_StartTransfer(obj->spi.dmaOptionsRX.dmaChannel, &xferConf, &desc, serial_dmaTransferComplete,obj->spi.dmaOptionsRX.dmaCallback.userPtr);
+    }
+
+    volatile void *target_addr;
+
+    /* Select TX target address. 9 bit frame length requires to use extended register.
+       10 bit and larger frame requires to use TXDOUBLE register. */
+    switch ((int)obj->spi.spi) {
+        case USART_0:
+            dma_periph = ldmaPeripheralSignal_USART0_TXBL;
+            break;
+        case USART_1:
+            dma_periph = ldmaPeripheralSignal_USART1_TXBL;
+            break;
+        default:
+            EFM_ASSERT(0);
+            while(1);
+            break;
+    }
+
+    if (obj->spi.bits <= 8) {
+        target_addr = &obj->spi.spi->TXDATA;
+    } else if (obj->spi.bits == 9) {
+        target_addr = &obj->spi.spi->TXDATAX;
+    } else {
+        target_addr = &obj->spi.spi->TXDOUBLE;
+    }
+
+    /*  Check the transmit length, and split long transfers to smaller ones */
+    int max_length = 1024;
+#ifdef _LDMA_CH_CTRL_XFERCNT_MASK
+    max_length = (_LDMA_CH_CTRL_XFERCNT_MASK>>_LDMA_CH_CTRL_XFERCNT_SHIFT)+1;
+#endif
+    if (tx_length > max_length) {
+        tx_length = max_length;
+    }
+
+    /* Save amount of TX done by DMA */
+    obj->tx_buff.pos += tx_length;
+
+    LDMA_TransferCfg_t xferConf = LDMA_TRANSFER_CFG_PERIPHERAL(dma_periph);
+    LDMA_Descriptor_t desc = LDMA_DESCRIPTOR_SINGLE_M2P_BYTE((txdata ? txdata : &fill_word), target_addr, tx_length);
+
+    if (obj->spi.bits >= 9) {
+        desc.xfer.size = ldmaCtrlSizeHalf;
+    }
+
+    if (!txdata) {
+        desc.xfer.srcInc = ldmaCtrlSrcIncNone;
+    } else if (obj->tx_buff.width == 32) {
+        if (obj->spi.bits >= 9) {
+            desc.xfer.srcInc = ldmaCtrlSrcIncTwo;
+        } else {
+            desc.xfer.srcInc = ldmaCtrlSrcIncFour;
+        }
+    } else if (obj->tx_buff.width == 16) {
+        if (obj->spi.bits >= 9) {
+            desc.xfer.srcInc = ldmaCtrlSrcIncOne;
+        } else {
+            desc.xfer.srcInc = ldmaCtrlSrcIncTwo;
+        }
+    } else {
+        desc.xfer.srcInc = ldmaCtrlSrcIncOne;
+    }
+
+    // Kick off DMA TX
+    LDMAx_StartTransfer(obj->spi.dmaOptionsTX.dmaChannel, &xferConf, &desc, serial_dmaTransferComplete,obj->spi.dmaOptionsTX.dmaCallback.userPtr);
+}
+
+#else
+/******************************************
+* void spi_activate_dma(spi_t *obj, void* rxdata, void* txdata, int length)
+*
+* This function will start the DMA engine for SPI transfers
+*
+*   * rxdata: pointer to RX buffer, if needed.
+*   * txdata: pointer to TX buffer, if needed. Else FF's.
+*   * tx_length: how many bytes will get sent.
+*   * rx_length: how many bytes will get received. If > tx_length, TX will get padded with n lower bits of SPI_FILL_WORD.
+******************************************/
+static void spi_activate_dma(spi_t *obj, void* rxdata, const void* txdata, int tx_length, int rx_length)
+{
+    /* DMA descriptors */
+    DMA_CfgDescr_TypeDef rxDescrCfg;
+    DMA_CfgDescr_TypeDef txDescrCfg;
+
+    /* Split up transfers if the length is larger than what the DMA supports. */
+    const int DMA_MAX_TRANSFER = (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT);
+
+    if (tx_length > DMA_MAX_TRANSFER) {
+        tx_length = DMA_MAX_TRANSFER;
+    }
+    if (rx_length > DMA_MAX_TRANSFER) {
+        rx_length = DMA_MAX_TRANSFER;
+    }
+
+    /* Save amount of TX done by DMA */
+    obj->tx_buff.pos += tx_length;
+    obj->rx_buff.pos += rx_length;
+
+    /* Only activate RX DMA if a receive buffer is specified */
+    if (rxdata != NULL) {
+        // Setting up channel descriptor
+        if (obj->rx_buff.width == 32) {
+            rxDescrCfg.dstInc = dmaDataInc4;
+        } else if (obj->rx_buff.width == 16) {
+            rxDescrCfg.dstInc = dmaDataInc2;
+        } else {
+            rxDescrCfg.dstInc = dmaDataInc1;
+        }
+        rxDescrCfg.srcInc = dmaDataIncNone;
+        rxDescrCfg.size = (obj->spi.bits <= 8 ? dmaDataSize1 : dmaDataSize2); //When frame size >= 9, use RXDOUBLE
+        rxDescrCfg.arbRate = dmaArbitrate1;
+        rxDescrCfg.hprot = 0;
+        DMA_CfgDescr(obj->spi.dmaOptionsRX.dmaChannel, true, &rxDescrCfg);
+
+        void * rx_reg;
+        if (obj->spi.bits > 9) {
+            rx_reg = (void *)&obj->spi.spi->RXDOUBLE;
+        } else if (obj->spi.bits == 9) {
+            rx_reg = (void *)&obj->spi.spi->RXDATAX;
+        } else {
+            rx_reg = (void *)&obj->spi.spi->RXDATA;
+        }
+
+        /* Activate RX channel */
+        DMA_ActivateBasic(obj->spi.dmaOptionsRX.dmaChannel,
+                          true,
+                          false,
+                          rxdata,
+                          rx_reg,
+                          rx_length - 1);
+    }
+
+    // buffer with all FFs.
+    /* Setting up channel descriptor */
+    txDescrCfg.dstInc = dmaDataIncNone;
+    if (txdata == 0) {
+        // Don't increment source when there is no transmit buffer
+        txDescrCfg.srcInc = dmaDataIncNone;
+    } else {
+        if (obj->tx_buff.width == 32) {
+            txDescrCfg.srcInc = dmaDataInc4;
+        } else if (obj->tx_buff.width == 16) {
+            txDescrCfg.srcInc = dmaDataInc2;
+        } else {
+            txDescrCfg.srcInc = dmaDataInc1;
+        }
+    }
+    txDescrCfg.size = (obj->spi.bits <= 8 ? dmaDataSize1 : dmaDataSize2); //When frame size >= 9, use TXDOUBLE
+    txDescrCfg.arbRate = dmaArbitrate1;
+    txDescrCfg.hprot = 0;
+    DMA_CfgDescr(obj->spi.dmaOptionsTX.dmaChannel, true, &txDescrCfg);
+
+    void * tx_reg;
+    if (obj->spi.bits > 9) {
+        tx_reg = (void *)&obj->spi.spi->TXDOUBLE;
+    } else if (obj->spi.bits == 9) {
+        tx_reg = (void *)&obj->spi.spi->TXDATAX;
+    } else {
+        tx_reg = (void *)&obj->spi.spi->TXDATA;
+    }
+
+    /* Activate TX channel */
+    DMA_ActivateBasic(obj->spi.dmaOptionsTX.dmaChannel,
+                      true,
+                      false,
+                      tx_reg,
+                      (txdata == 0 ? &fill_word : (void *)txdata), // When there is nothing to transmit, point to static fill word
+                      (tx_length - 1));
+}
+#endif //LDMA_PRESENT
+/********************************************************************
+* spi_master_transfer_dma(spi_t *obj, void *rxdata, void *txdata, int length, DMACallback cb, DMAUsage hint)
+*
+* Start an SPI transfer by using DMA and the supplied hint for DMA useage
+*
+*   * obj: pointer to specific SPI instance
+*   * rxdata: pointer to rx buffer. If null, we will assume only TX is relevant, and RX will be ignored.
+*   * txdata: pointer to TX buffer. If null, we will assume only the read is relevant, and will send FF's for reading back.
+*   * length: How many bytes should be written/read.
+*   * cb: thunk pointer into CPP-land to get the spi object
+*   * hint: hint for the requested DMA useage.
+*       * NEVER: do not use DMA, but use IRQ instead
+*       * OPPORTUNISTIC: use DMA if there are channels available, but return them after the transfer.
+*       * ALWAYS: use DMA if channels are available, and hold on to the channels after the transfer.
+*                 If the previous transfer has kept the channel, that channel will continue to get used.
+*
+********************************************************************/
+void spi_master_transfer_dma(spi_t *obj, const void *txdata, void *rxdata, int tx_length, int rx_length, void* cb, DMAUsage hint)
+{
+    /* Init DMA here to include it in the power figure */
+    dma_init();
+    /* Clear TX and RX registers */
+    obj->spi.spi->CMD = USART_CMD_CLEARTX;
+    obj->spi.spi->CMD = USART_CMD_CLEARRX;
+    /* If the DMA channels are already allocated, we can assume they have been setup already */
+    if (hint != DMA_USAGE_NEVER && obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_ALLOCATED) {
+        /* setup has already been done, so just activate the transfer */
+        spi_activate_dma(obj, rxdata, txdata, tx_length, rx_length);
+    } else if (hint == DMA_USAGE_NEVER) {
+        /* use IRQ */
+        obj->spi.spi->IFC = 0xFFFFFFFF;
+        spi_master_write_asynch(obj);
+        spi_enable_interrupt(obj, (uint32_t)cb, true);
+    } else {
+        /* try to acquire channels */
+        dma_init();
+        spi_enable_dma(obj, hint);
+
+        /* decide between DMA and IRQ */
+        if (obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_ALLOCATED || obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) {
+            /* disable the interrupts that may have been left open previously */
+            spi_enable_interrupt(obj, (uint32_t)cb, false);
+
+            /* DMA channels are allocated, so do their setup */
+            spi_master_dma_channel_setup(obj, cb);
+            /* and activate the transfer */
+            spi_activate_dma(obj, rxdata, txdata, tx_length, rx_length);
+        } else {
+            /* DMA is unavailable, so fall back to IRQ */
+            obj->spi.spi->IFC = 0xFFFFFFFF;
+            spi_master_write_asynch(obj);
+            spi_enable_interrupt(obj, (uint32_t)cb, true);
+        }
+    }
+}
+
+/** Begin the SPI transfer. Buffer pointers and lengths are specified in tx_buff and rx_buff
+ *
+ * @param[in] obj     The SPI object which holds the transfer information
+ * @param[in] tx        The buffer to send
+ * @param[in] tx_length The number of words to transmit
+ * @param[in] rx        The buffer to receive
+ * @param[in] rx_length The number of words to receive
+ * @param[in] bit_width The bit width of buffer words
+ * @param[in] event     The logical OR of events to be registered
+ * @param[in] handler   SPI interrupt handler
+ * @param[in] hint      A suggestion for how to use DMA with this transfer
+ */
+void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
+{
+    if( spi_active(obj) ) return;
+
+    /* update fill word if on 9-bit frame size */
+    if(obj->spi.bits == 9) fill_word = SPI_FILL_WORD & 0x1FF;
+    else fill_word = SPI_FILL_WORD;
+
+    /* check corner case */
+    if(tx_length == 0) {
+        tx_length = rx_length;
+        tx = (void*) 0;
+    }
+
+    /* First, set the buffer */
+    spi_buffer_set(obj, tx, tx_length, rx, rx_length, bit_width);
+
+    /* Then, enable the events */
+    spi_enable_event(obj, SPI_EVENT_ALL, false);
+    spi_enable_event(obj, event, true);
+
+    // Set the sleep mode
+    blockSleepMode(SPI_LEAST_ACTIVE_SLEEPMODE);
+
+    /* And kick off the transfer */
+    spi_master_transfer_dma(obj, tx, rx, tx_length, rx_length, (void*)handler, hint);
+}
+
+
+/********************************************************************
+* uint32_t spi_irq_handler_generic(spi_t* obj)
+*
+* handler which should get called by CPP-land when either a DMA or SPI IRQ gets fired for a SPI transaction.
+*
+*   * obj: pointer to the specific SPI instance
+*
+* return: event mask. Currently only 0 or SPI_EVENT_COMPLETE upon transfer completion.
+*
+********************************************************************/
+#ifdef LDMA_PRESENT
+uint32_t spi_irq_handler_asynch(spi_t* obj)
+{
+    if (obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_ALLOCATED || obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) {
+        /* DMA implementation */
+        /* If there is still data in the TX buffer, setup a new transfer. */
+        if (obj->tx_buff.pos < obj->tx_buff.length) {
+            /* Find position and remaining length without modifying tx_buff. */
+            void* tx_pointer = (char*)obj->tx_buff.buffer + obj->tx_buff.pos;
+            uint32_t tx_length = obj->tx_buff.length - obj->tx_buff.pos;
+
+            /* Begin transfer. Rely on spi_activate_dma to split up the transfer further. */
+            spi_activate_dma(obj, obj->rx_buff.buffer, tx_pointer, tx_length, obj->rx_buff.length);
+
+            return 0;
+        }
+        /* If there is an RX transfer ongoing, wait for it to finish */
+        if (LDMAx_ChannelEnabled(obj->spi.dmaOptionsRX.dmaChannel)) {
+            /* Check if we need to kick off TX transfer again to force more incoming data. */
+            if (LDMA_TransferDone(obj->spi.dmaOptionsTX.dmaChannel) && (obj->tx_buff.pos < obj->rx_buff.length)) {
+                void* tx_pointer = (char*)obj->tx_buff.buffer + obj->tx_buff.pos;
+                uint32_t tx_length = obj->tx_buff.length - obj->tx_buff.pos;
+                /* Begin transfer. Rely on spi_activate_dma to split up the transfer further. */
+                spi_activate_dma(obj, obj->rx_buff.buffer, tx_pointer, tx_length, obj->rx_buff.length);
+            } else return 0;
+        }
+        /* If there is still a TX transfer ongoing (tx_length > rx_length), wait for it to finish */
+        if (!LDMA_TransferDone(obj->spi.dmaOptionsTX.dmaChannel)) {
+            return 0;
+        }
+        /* Release the dma channels if they were opportunistically allocated */
+        if (obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) {
+            dma_channel_free(obj->spi.dmaOptionsTX.dmaChannel);
+            dma_channel_free(obj->spi.dmaOptionsRX.dmaChannel);
+            obj->spi.dmaOptionsTX.dmaUsageState = DMA_USAGE_OPPORTUNISTIC;
+        }
+
+        /* Wait transmit to complete, before user code is indicated*/
+        while(!(obj->spi.spi->STATUS & USART_STATUS_TXC));
+        unblockSleepMode(SPI_LEAST_ACTIVE_SLEEPMODE);
+        /* return to CPP land to say we're finished */
+        return SPI_EVENT_COMPLETE;
+    } else {
+        /* IRQ implementation */
+        if (spi_master_rx_int_flag(obj)) {
+            spi_master_read_asynch(obj);
+        }
+
+        if (spi_master_tx_int_flag(obj)) {
+            spi_master_write_asynch(obj);
+        }
+
+        uint32_t event = spi_event_check(obj);
+        if (event & SPI_EVENT_INTERNAL_TRANSFER_COMPLETE) {
+            /* disable interrupts */
+            spi_enable_interrupt(obj, (uint32_t)NULL, false);
+
+            unblockSleepMode(SPI_LEAST_ACTIVE_SLEEPMODE);
+            /* Return the event back to userland */
+            return event;
+        }
+
+        return 0;
+    }
+}
+#else
+uint32_t spi_irq_handler_asynch(spi_t* obj)
+{
+
+    /* Determine whether the current scenario is DMA or IRQ, and act accordingly */
+
+    if (obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_ALLOCATED || obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) {
+        /* DMA implementation */
+
+        /* If there is still data in the TX buffer, setup a new transfer. */
+        if (obj->tx_buff.pos < obj->tx_buff.length) {
+            /* If there is still a TX transfer ongoing, let it finish
+             * before (if necessary) kicking off a new transfer */
+            if (DMA_ChannelEnabled(obj->spi.dmaOptionsTX.dmaChannel)) {
+                return 0;
+            }
+            /* Find position and remaining length without modifying tx_buff. */
+            void * tx_pointer;
+            if (obj->tx_buff.width == 32) {
+                tx_pointer = ((uint32_t *)obj->tx_buff.buffer) + obj->tx_buff.pos;
+            } else if (obj->tx_buff.width == 16) {
+                tx_pointer = ((uint16_t *)obj->tx_buff.buffer) + obj->tx_buff.pos;
+            } else {
+                tx_pointer = ((uint8_t *)obj->tx_buff.buffer) + obj->tx_buff.pos;
+            }
+            uint32_t tx_length = obj->tx_buff.length - obj->tx_buff.pos;
+
+            /* Refresh RX transfer too if it exists */
+            void * rx_pointer = NULL;
+            if (obj->rx_buff.pos < obj->rx_buff.length) {
+                if (obj->rx_buff.width == 32) {
+                    rx_pointer = ((uint32_t *)obj->rx_buff.buffer) + obj->rx_buff.pos;
+                } else if (obj->rx_buff.width == 16) {
+                    rx_pointer = ((uint16_t *)obj->rx_buff.buffer) + obj->rx_buff.pos;
+                } else {
+                    rx_pointer = ((uint8_t *)obj->rx_buff.buffer) + obj->rx_buff.pos;
+                }                
+            }
+            uint32_t rx_length = obj->rx_buff.length - obj->rx_buff.pos;
+
+            /* Wait for the previous transfer to complete. */
+            while(!(obj->spi.spi->STATUS & USART_STATUS_TXC));
+
+            /* Begin transfer. Rely on spi_activate_dma to split up the transfer further. */
+            spi_activate_dma(obj, rx_pointer, tx_pointer, tx_length, rx_length);
+
+            return 0;
+        }
+
+        /* If an RX transfer is ongoing, continue processing RX data */
+        if (DMA_ChannelEnabled(obj->spi.dmaOptionsRX.dmaChannel)) {
+            /* Check if we need to kick off TX transfer again to force more incoming data. */
+            if (!DMA_ChannelEnabled(obj->spi.dmaOptionsTX.dmaChannel) && (obj->rx_buff.pos < obj->rx_buff.length)) {
+                //Save state of TX transfer amount
+                int length_diff = obj->rx_buff.length - obj->rx_buff.pos;
+                obj->tx_buff.pos = obj->rx_buff.length;
+
+                //Kick off a new DMA transfer
+                DMA_CfgDescr_TypeDef txDescrCfg;
+
+                fill_word = SPI_FILL_WORD;
+                /* Setting up channel descriptor */
+                txDescrCfg.dstInc = dmaDataIncNone;
+                txDescrCfg.srcInc = dmaDataIncNone; //Do not increment source pointer when there is no transmit buffer
+                txDescrCfg.size = (obj->spi.bits <= 8 ? dmaDataSize1 : dmaDataSize2); //When frame size > 9, we can use TXDOUBLE to save bandwidth
+                txDescrCfg.arbRate = dmaArbitrate1;
+                txDescrCfg.hprot = 0;
+                DMA_CfgDescr(obj->spi.dmaOptionsTX.dmaChannel, true, &txDescrCfg);
+
+                void * tx_reg;
+                if (obj->spi.bits > 9) {
+                    tx_reg = (void *)&obj->spi.spi->TXDOUBLE;
+                } else if (obj->spi.bits == 9) {
+                    tx_reg = (void *)&obj->spi.spi->TXDATAX;
+                } else {
+                    tx_reg = (void *)&obj->spi.spi->TXDATA;
+                }
+
+                /* Activate TX channel */
+                DMA_ActivateBasic(obj->spi.dmaOptionsTX.dmaChannel,
+                                  true,
+                                  false,
+                                  tx_reg, //When frame size > 9, point to TXDOUBLE
+                                  &fill_word, // When there is nothing to transmit, point to static fill word
+                                  length_diff - 1);
+            } else {
+                /* Nothing to do */
+                return 0;
+            }
+        }
+
+        /* Release the dma channels if they were opportunistically allocated */
+        if (obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) {
+            dma_channel_free(obj->spi.dmaOptionsTX.dmaChannel);
+            dma_channel_free(obj->spi.dmaOptionsRX.dmaChannel);
+            obj->spi.dmaOptionsTX.dmaUsageState = DMA_USAGE_OPPORTUNISTIC;
+        }
+
+        /* Wait for transmit to complete, before user code is indicated */
+        while(!(obj->spi.spi->STATUS & USART_STATUS_TXC));
+        unblockSleepMode(SPI_LEAST_ACTIVE_SLEEPMODE);
+
+        /* return to CPP land to say we're finished */
+        return SPI_EVENT_COMPLETE;
+    } else {
+        /* IRQ implementation */
+        if (spi_master_rx_int_flag(obj)) {
+            spi_master_read_asynch(obj);
+        }
+
+        if (spi_master_tx_int_flag(obj)) {
+            spi_master_write_asynch(obj);
+        }
+
+        uint32_t event = spi_event_check(obj);
+        if (event & SPI_EVENT_INTERNAL_TRANSFER_COMPLETE) {
+            /* disable interrupts */
+            spi_enable_interrupt(obj, (uint32_t)NULL, false);
+
+            /* Wait for transmit to complete, before user code is indicated */
+            while(!(obj->spi.spi->STATUS & USART_STATUS_TXC));
+            unblockSleepMode(SPI_LEAST_ACTIVE_SLEEPMODE);
+
+            /* Return the event back to userland */
+            return event;
+        }
+
+        return 0;
+    }
+}
+#endif // LDMA_PRESENT
+/** Abort an SPI transfer
+ *
+ * @param obj The SPI peripheral to stop
+ */
+void spi_abort_asynch(spi_t *obj)
+{
+    // If we're not currently transferring, then there's nothing to do here
+    if(spi_active(obj) != 0) return;
+
+    // Determine whether we're running DMA or interrupt
+    if (obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_ALLOCATED || obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) {
+        // Cancel the DMA transfers
+#ifdef LDMA_PRESENT
+        LDMA_StopTransfer(obj->spi.dmaOptionsTX.dmaChannel);
+        LDMA_StopTransfer(obj->spi.dmaOptionsRX.dmaChannel);
+#else
+        DMA_ChannelEnable(obj->spi.dmaOptionsTX.dmaChannel, false);
+        DMA_ChannelEnable(obj->spi.dmaOptionsRX.dmaChannel, false);
+#endif
+        /* Release the dma channels if they were opportunistically allocated */
+        if (obj->spi.dmaOptionsTX.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) {
+            dma_channel_free(obj->spi.dmaOptionsTX.dmaChannel);
+            dma_channel_free(obj->spi.dmaOptionsRX.dmaChannel);
+            obj->spi.dmaOptionsTX.dmaUsageState = DMA_USAGE_OPPORTUNISTIC;
+        }
+
+    } else {
+        // Interrupt implementation: switch off interrupts
+        spi_enable_interrupt(obj, (uint32_t)NULL, false);
+    }
+
+    // Release sleep mode block
+    unblockSleepMode(SPI_LEAST_ACTIVE_SLEEPMODE);
+}
+
+#endif