mbed library sources. Supersedes mbed-src. Fixed broken STM32F1xx RTC on rtc_api.c
Dependents: Nucleo_F103RB_RTC_battery_bkup_pwr_off_okay
Fork of mbed-dev by
Diff: targets/TARGET_Maxim/TARGET_MAX32625/mxc/spim.c
- Revision:
- 150:02e0a0aed4ec
diff -r 156823d33999 -r 02e0a0aed4ec targets/TARGET_Maxim/TARGET_MAX32625/mxc/spim.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/TARGET_Maxim/TARGET_MAX32625/mxc/spim.c Tue Nov 08 17:45:16 2016 +0000 @@ -0,0 +1,720 @@ +/******************************************************************************* + * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + * $Date: 2016-07-26 08:41:27 -0500 (Tue, 26 Jul 2016) $ + * $Revision: 23789 $ + * + ******************************************************************************/ + +/** + * @file spim.c + * @brief SPI Master driver source. + */ + +/***** Includes *****/ +#include <stddef.h> +#include <string.h> +#include "mxc_config.h" +#include "mxc_assert.h" +#include "mxc_lock.h" +#include "spim.h" + +/***** Definitions *****/ +#define SPIM_MAX_BYTE_LEN 32 +#define SPIM_MAX_PAGE_LEN 32 + +/***** Globals *****/ + +// Saves the state of the non-blocking requests +typedef struct { + spim_req_t *req; + unsigned head_rem; +} spim_req_head_t; + +static spim_req_head_t states[MXC_CFG_SPIM_INSTANCES]; + +/***** Functions *****/ + +static unsigned SPIM_ReadRXFIFO(mxc_spim_regs_t *spim, mxc_spim_fifo_regs_t *fifo, + uint8_t *data, unsigned len); + +static uint32_t SPIM_TransHandler(mxc_spim_regs_t *spim, spim_req_t *req, int spim_num); + +/******************************************************************************/ +int SPIM_Init(mxc_spim_regs_t *spim, const spim_cfg_t *cfg, const sys_cfg_spim_t *sys_cfg) +{ + int err, spim_num; + uint32_t spim_clk, clocks; + + spim_num = MXC_SPIM_GET_IDX(spim); + MXC_ASSERT(spim_num >= 0); + + // Check the input parameters + if(cfg == NULL) + return E_NULL_PTR; + + if(cfg->baud == 0) + return E_BAD_PARAM; + + // Set system level configurations + if ((err = SYS_SPIM_Init(spim, cfg, sys_cfg)) != E_NO_ERROR) { + return err; + } + + /* Configure the baud, make sure the SPIM clk is enabled and the baud + is less than the maximum */ + spim_clk = SYS_SPIM_GetFreq(spim); + if((spim_clk == 0) || ((spim_clk == SystemCoreClock) && ((spim_clk/2) < cfg->baud))) { + return E_BAD_PARAM; + } + + // Initialize state pointers + states[spim_num].req = NULL; + states[spim_num].head_rem = 0; + + // Drain the FIFOs, enable SPIM, enable SCK Feedback + spim->gen_ctrl = 0; + spim->gen_ctrl = (MXC_F_SPIM_GEN_CTRL_SPI_MSTR_EN | MXC_F_SPIM_GEN_CTRL_TX_FIFO_EN | + MXC_F_SPIM_GEN_CTRL_RX_FIFO_EN | MXC_F_SPIM_GEN_CTRL_ENABLE_SCK_FB_MODE); + + // Set mode and page size + spim->mstr_cfg = (((cfg->mode << MXC_F_SPIM_MSTR_CFG_SPI_MODE_POS) & MXC_F_SPIM_MSTR_CFG_SPI_MODE) | + MXC_S_SPIM_MSTR_CFG_PAGE_32B | (0x2 << MXC_F_SPIM_MSTR_CFG_ACT_DELAY_POS)); + + // Configure the SSEL polarity + spim->ss_sr_polarity = ((cfg->ssel_pol << MXC_F_SPIM_SS_SR_POLARITY_SS_POLARITY_POS) & + MXC_F_SPIM_SS_SR_POLARITY_SS_POLARITY); + +#if(MXC_SPIM_REV == 0) + // Disable the feedback clock in modes 1 and 2 + if((cfg->mode == 1) || (cfg->mode == 2)) { + spim->gen_ctrl &= ~MXC_F_SPIM_GEN_CTRL_ENABLE_SCK_FB_MODE; + spim->mstr_cfg |= (0x1 << MXC_F_SPIM_MSTR_CFG_SDIO_SAMPLE_POINT_POS); + } +#else + // Increase the RX FIFO margin + MXC_SPIM1->spcl_ctrl = ((MXC_SPIM1->spcl_ctrl & ~(MXC_F_SPIM_SPCL_CTRL_RX_FIFO_MARGIN)) | + (0x3 << MXC_F_SPIM_SPCL_CTRL_RX_FIFO_MARGIN_POS)); +#endif + + // Calculate the hi/lo clock setting + if(spim_clk/2 > cfg->baud) { + + /* Disable the feedback mode and use the sample mode with an appropriate hi/lo clk + to achieve the lower baud rate */ + spim->gen_ctrl &= ~MXC_F_SPIM_GEN_CTRL_ENABLE_SCK_FB_MODE; + + clocks = (spim_clk / (2*cfg->baud)); + + if(clocks == 0 || clocks > 0x10) { + return E_BAD_PARAM; + } + + // 0 => 16 in the 4 bit field for HI_CLK and LO_CLK + if(clocks == 0x10) { + clocks = 0; + } + + } else { + // Continue to use feedback mode and set hi/lo clk to 1 + clocks = 1; + } + + spim->mstr_cfg |= (((clocks << MXC_F_SPIM_MSTR_CFG_SCK_HI_CLK_POS) & MXC_F_SPIM_MSTR_CFG_SCK_HI_CLK) | + ((clocks << MXC_F_SPIM_MSTR_CFG_SCK_LO_CLK_POS) & MXC_F_SPIM_MSTR_CFG_SCK_LO_CLK)); + + return E_NO_ERROR; +} + +/******************************************************************************/ +int SPIM_Shutdown(mxc_spim_regs_t *spim) +{ + int spim_num, err; + spim_req_t *temp_req; + + // Disable and clear interrupts + spim->inten = 0; + spim->intfl = spim->intfl; + + // Disable SPIM and FIFOS + spim->gen_ctrl &= ~(MXC_F_SPIM_GEN_CTRL_SPI_MSTR_EN | MXC_F_SPIM_GEN_CTRL_TX_FIFO_EN | + MXC_F_SPIM_GEN_CTRL_RX_FIFO_EN); + + // Call all of the pending callbacks for this SPIM + spim_num = MXC_SPIM_GET_IDX(spim); + if(states[spim_num].req != NULL) { + + // Save the request + temp_req = states[spim_num].req; + + // Unlock this SPIM + mxc_free_lock((uint32_t*)&states[spim_num].req); + + // Callback if not NULL + if(temp_req->callback != NULL) { + temp_req->callback(temp_req, E_SHUTDOWN); + } + } + + // Clear system level configurations + if ((err = SYS_SPIM_Shutdown(spim)) != E_NO_ERROR) { + return err; + } + + return E_NO_ERROR; +} + +/******************************************************************************/ +int SPIM_Clocks(mxc_spim_regs_t *spim, uint32_t len, uint8_t ssel, uint8_t deass) +{ + int spim_num; + mxc_spim_fifo_regs_t *fifo; + uint16_t header = 0x1; + uint32_t num = len; + + // Make sure the SPIM has been initialized + if((spim->gen_ctrl & MXC_F_SPIM_GEN_CTRL_SPI_MSTR_EN) == 0) + return E_UNINITIALIZED; + + if(!(len > 0)) { + return E_NO_ERROR; + } + + // Check the previous transaction if we're switching the slave select + if((ssel != ((spim->mstr_cfg & MXC_F_SPIM_MSTR_CFG_SLAVE_SEL) >> + MXC_F_SPIM_MSTR_CFG_SLAVE_SEL_POS)) && (spim->gen_ctrl & MXC_F_SPIM_GEN_CTRL_BB_SS_IN_OUT)) { + + // Return E_BUSY if the slave select is still asserted + return E_BUSY; + } + + // Attempt to lock this SPIM + spim_num = MXC_SPIM_GET_IDX(spim); + if(mxc_get_lock((uint32_t*)&states[spim_num].req, 1) != E_NO_ERROR) { + return E_BUSY; + } + + // Set which slave select we are using + spim->mstr_cfg = ((spim->mstr_cfg & ~MXC_F_SPIM_MSTR_CFG_SLAVE_SEL) | + ((ssel << MXC_F_SPIM_MSTR_CFG_SLAVE_SEL_POS) & MXC_F_SPIM_MSTR_CFG_SLAVE_SEL)); + + //force deass to a 1 or 0 + deass = !!deass; + +#if(MXC_SPIM_REV == 0) + // Wait for all of the data to transmit + while(spim->fifo_ctrl & MXC_F_SPIM_FIFO_CTRL_TX_FIFO_USED) {} + + // Disable the feedback clock, save state + uint32_t gen_ctrl = spim->gen_ctrl; + spim->gen_ctrl &= ~MXC_F_SPIM_GEN_CTRL_ENABLE_SCK_FB_MODE; +#endif + + // Get the TX and RX FIFO for this SPIM + fifo = MXC_SPIM_GET_SPIM_FIFO(spim_num); + + // Send the headers to transmit the clocks + while(len > 32) { + fifo->trans_16[0] = header; + fifo->trans_16[0] = 0xF000; + fifo->trans_16[0] = 0xF000; + len -= 32; + } + + if(len) { + if(len < 32) { + header |= (len << 4); + } + header |= (deass << 13); + + fifo->trans_16[0] = header; + + if(len > 16) { + fifo->trans_16[0] = 0xF000; + } + fifo->trans_16[0] = 0xF000; + } + +#if(MXC_SPIM_REV == 0) + // Wait for all of the data to transmit + while(spim->fifo_ctrl & MXC_F_SPIM_FIFO_CTRL_TX_FIFO_USED) {} + + // Restore feedback clock setting + spim->gen_ctrl |= (gen_ctrl & MXC_F_SPIM_GEN_CTRL_ENABLE_SCK_FB_MODE); +#endif + + // Unlock this SPIM + mxc_free_lock((uint32_t*)&states[spim_num].req); + + return num; +} + + +/******************************************************************************/ +int SPIM_Trans(mxc_spim_regs_t *spim, spim_req_t *req) +{ + int spim_num; + + // Make sure the SPIM has been initialized + if((spim->gen_ctrl & MXC_F_SPIM_GEN_CTRL_SPI_MSTR_EN) == 0) + return E_UNINITIALIZED; + + // Check the input parameters + if(req == NULL) + return E_NULL_PTR; + + if((req->rx_data == NULL) && (req->tx_data == NULL)) + return E_NULL_PTR; + + if(!(req->len > 0)) { + return E_NO_ERROR; + } + + // Check the previous transaction if we're switching the slave select + if((req->ssel != ((spim->mstr_cfg & MXC_F_SPIM_MSTR_CFG_SLAVE_SEL) >> + MXC_F_SPIM_MSTR_CFG_SLAVE_SEL_POS)) && (spim->gen_ctrl & MXC_F_SPIM_GEN_CTRL_BB_SS_IN_OUT)) { + + // Return E_BUSY if the slave select is still asserted + return E_BUSY; + } + + // Attempt to register this write request + spim_num = MXC_SPIM_GET_IDX(spim); + if(mxc_get_lock((uint32_t*)&states[spim_num].req, (uint32_t)req) != E_NO_ERROR) { + return E_BUSY; + } + + // Set which slave select we are using + spim->mstr_cfg = ((spim->mstr_cfg & ~MXC_F_SPIM_MSTR_CFG_SLAVE_SEL) | + ((req->ssel << MXC_F_SPIM_MSTR_CFG_SLAVE_SEL_POS) & MXC_F_SPIM_MSTR_CFG_SLAVE_SEL)); + + //force deass to a 1 or 0 + req->deass = !!req->deass; + + // Clear the number of bytes counter + req->read_num = 0; + req->write_num = 0; + req->callback = NULL; + states[spim_num].head_rem = 0; + + // Start the transaction, keep calling the handler until complete + while(SPIM_TransHandler(spim, req, spim_num) != 0); + + if(req->tx_data == NULL) { + return req->read_num; + } + return req->write_num; +} + +/******************************************************************************/ +int SPIM_TransAsync(mxc_spim_regs_t *spim, spim_req_t *req) +{ + int spim_num; + + // Make sure the SPIM has been initialized + if((spim->gen_ctrl & MXC_F_SPIM_GEN_CTRL_SPI_MSTR_EN) == 0) + return E_UNINITIALIZED; + + // Check the input parameters + if(req == NULL) + return E_NULL_PTR; + + if((req->rx_data == NULL) && (req->tx_data == NULL)) + return E_NULL_PTR; + + if(!(req->len > 0)) { + return E_NO_ERROR; + } + + + // Check the previous transaction if we're switching the slave select + if((req->ssel != ((spim->mstr_cfg & MXC_F_SPIM_MSTR_CFG_SLAVE_SEL) >> + MXC_F_SPIM_MSTR_CFG_SLAVE_SEL_POS)) && (spim->gen_ctrl & MXC_F_SPIM_GEN_CTRL_BB_SS_IN_OUT)) { + + // Return E_BUSY if the slave select is still asserted + return E_BUSY; + } + + // Attempt to register this write request + spim_num = MXC_SPIM_GET_IDX(spim); + if(mxc_get_lock((uint32_t*)&states[spim_num].req, (uint32_t)req) != E_NO_ERROR) { + return E_BUSY; + } + + // Set which slave select we are using + spim->mstr_cfg = ((spim->mstr_cfg & ~MXC_F_SPIM_MSTR_CFG_SLAVE_SEL) | + ((req->ssel << MXC_F_SPIM_MSTR_CFG_SLAVE_SEL_POS) & MXC_F_SPIM_MSTR_CFG_SLAVE_SEL)); + + //force deass to a 1 or 0 + req->deass = !!req->deass; + + // Clear the number of bytes counter + req->read_num = 0; + req->write_num = 0; + states[spim_num].head_rem = 0; + + // Start the transaction, enable the interrupts + spim->inten = SPIM_TransHandler(spim, req, spim_num); + + return E_NO_ERROR; +} + +/******************************************************************************/ +int SPIM_AbortAsync(spim_req_t *req) +{ + int spim_num; + mxc_spim_regs_t *spim; + + // Check the input parameters + if(req == NULL) { + return E_BAD_PARAM; + } + + // Find the request, set to NULL + for(spim_num = 0; spim_num < MXC_CFG_SPIM_INSTANCES; spim_num++) { + if(req == states[spim_num].req) { + + spim = MXC_SPIM_GET_SPIM(spim_num); + + // Disable interrupts, clear the flags + spim->inten = 0; + spim->intfl = spim->intfl; + + // Reset the SPIM to cancel the on ongoing transaction + spim->gen_ctrl &= ~(MXC_F_SPIM_GEN_CTRL_SPI_MSTR_EN); + spim->gen_ctrl |= (MXC_F_SPIM_GEN_CTRL_SPI_MSTR_EN); + + // Unlock this SPIM + mxc_free_lock((uint32_t*)&states[spim_num].req); + + // Callback if not NULL + if(req->callback != NULL) { + req->callback(req, E_ABORT); + } + + return E_NO_ERROR; + } + } + + return E_BAD_PARAM; +} + +/******************************************************************************/ +void SPIM_Handler(mxc_spim_regs_t *spim) +{ + int spim_num; + uint32_t flags; + + // Clear the interrupt flags + spim->inten = 0; + flags = spim->intfl; + spim->intfl = flags; + + spim_num = MXC_SPIM_GET_IDX(spim); + + // Figure out if this SPIM has an active request + if((states[spim_num].req != NULL) && (flags)) { + spim->inten = SPIM_TransHandler(spim, states[spim_num].req, spim_num); + } +} + +/******************************************************************************/ +int SPIM_Busy(mxc_spim_regs_t *spim) +{ + // Check to see if there are any ongoing transactions + if((states[MXC_SPIM_GET_IDX(spim)].req == NULL) && + !(spim->fifo_ctrl & MXC_F_SPIM_FIFO_CTRL_TX_FIFO_USED)) { + + return E_NO_ERROR; + } + + return E_BUSY; +} + +/******************************************************************************/ +int SPIM_PrepForSleep(mxc_spim_regs_t *spim) +{ + if(SPIM_Busy(spim) != E_NO_ERROR) { + return E_BUSY; + } + + // Disable interrupts + spim->inten = 0; + return E_NO_ERROR; +} + +/******************************************************************************/ +static unsigned SPIM_ReadRXFIFO(mxc_spim_regs_t *spim, mxc_spim_fifo_regs_t *fifo, + uint8_t *data, unsigned len) +{ + unsigned num = 0; + unsigned avail = ((spim->fifo_ctrl & MXC_F_SPIM_FIFO_CTRL_RX_FIFO_USED) >> + MXC_F_SPIM_FIFO_CTRL_RX_FIFO_USED_POS); + + // Get data from the RXFIFO + while(avail && (len - num)) { + + if((avail >= 4) && ((len-num) >= 4)) { + // Save data from the RXFIFO + uint32_t temp = fifo->rslts_32[0]; + data[num+0] = ((temp & 0x000000FF) >> 0); + data[num+1] = ((temp & 0x0000FF00) >> 8); + data[num+2] = ((temp & 0x00FF0000) >> 16); + data[num+3] = ((temp & 0xFF000000) >> 24); + num+=4; + avail-=4; + } else if ((avail >= 2) && ((len-num) >= 2)) { + // Save data from the RXFIFO + uint16_t temp = fifo->rslts_16[0]; + data[num+0] = ((temp & 0x00FF) >> 0); + data[num+1] = ((temp & 0xFF00) >> 8); + num+=2; + avail-=2; + } else { + // Save data from the RXFIFO + data[num] = fifo->rslts_8[0]; + num+=1; + avail-=1; + } + + // Check to see if there is more data in the FIFO + if(avail == 0) { + avail = ((spim->fifo_ctrl & MXC_F_SPIM_FIFO_CTRL_RX_FIFO_USED) >> + MXC_F_SPIM_FIFO_CTRL_RX_FIFO_USED_POS); + } + } + + return num; +} + +uint16_t header_save; + + +/******************************************************************************/ +static uint32_t SPIM_TransHandler(mxc_spim_regs_t *spim, spim_req_t *req, int spim_num) +{ + uint8_t read, write; + uint16_t header; + uint32_t pages, bytes, inten; + unsigned remain, bytes_read, head_rem_temp, avail; + mxc_spim_fifo_regs_t *fifo; + + inten = 0; + + // Get the FIFOS for this UART + fifo = MXC_SPIM_GET_SPIM_FIFO(spim_num); + + // Figure out if we're reading + if(req->rx_data != NULL) { + read = 1; + } else { + read = 0; + } + + // Figure out if we're writing + if(req->tx_data != NULL) { + write = 1; + } else { + write = 0; + } + + // Read byte from the FIFO if we are reading + if(read) { + + // Read all of the data in the RXFIFO, or until we don't need anymore + bytes_read = SPIM_ReadRXFIFO(spim, fifo, &req->rx_data[req->read_num], + (req->len - req->read_num)); + + req->read_num += bytes_read; + + // Adjust head_rem if we are only reading + if(!write && (states[spim_num].head_rem > 0)) { + states[spim_num].head_rem -= bytes_read; + } + + // Figure out how many byte we have left to read + if(states[spim_num].head_rem > 0) { + remain = states[spim_num].head_rem; + } else { + remain = req->len - req->read_num; + } + + if(remain) { + + // Set the RX interrupts + if (remain > MXC_CFG_SPIM_FIFO_DEPTH) { + spim->fifo_ctrl = ((spim->fifo_ctrl & ~MXC_F_SPIM_FIFO_CTRL_RX_FIFO_AF_LVL) | + ((MXC_CFG_SPIM_FIFO_DEPTH - 2) << + MXC_F_SPIM_FIFO_CTRL_RX_FIFO_AF_LVL_POS)); + + } else { + spim->fifo_ctrl = ((spim->fifo_ctrl & ~MXC_F_SPIM_FIFO_CTRL_RX_FIFO_AF_LVL) | + ((remain - 1) << MXC_F_SPIM_FIFO_CTRL_RX_FIFO_AF_LVL_POS)); + } + + inten |= MXC_F_SPIM_INTEN_RX_FIFO_AF; + } + } + + // Figure out how many bytes we have left to send headers for + if(write) { + remain = req->len - req->write_num; + } else { + remain = req->len - req->read_num; + } + + // See if we need to send a new header + if(states[spim_num].head_rem <= 0 && remain) { + + // Set the transaction configuration in the header + header = ((write << 0) | (read << 1) | (req->width << 9)); + + if(remain >= SPIM_MAX_BYTE_LEN) { + + // Send a 32 byte header + if(remain == SPIM_MAX_BYTE_LEN) { + + header |= ((0x1 << 2) | (req->deass << 13)); + + // Save the number of bytes we need to write to the FIFO + bytes = SPIM_MAX_BYTE_LEN; + + } else { + // Send in increments of 32 byte pages + header |= (0x2 << 2); + pages = remain / SPIM_MAX_PAGE_LEN; + + if(pages >= 32) { + // 0 maps to 32 in the header + bytes = 32 * SPIM_MAX_PAGE_LEN; + } else { + header |= (pages << 4); + bytes = pages * SPIM_MAX_PAGE_LEN; + } + + // Check if this is the last header we will send + if((remain - bytes) == 0) { + header |= (req->deass << 13); + } + } + + header_save = header; + fifo->trans_16[0] = header; + + // Save the number of bytes we need to write to the FIFO + states[spim_num].head_rem = bytes; + + } else { + + // Send final header with the number of bytes remaining and if + // we want to de-assert the SS at the end of the transaction + header |= ((0x1 << 2) | (remain << 4) | (req->deass << 13)); + fifo->trans_16[0] = header; + states[spim_num].head_rem = remain; + } + } + + // Put data into the FIFO if we are writing + remain = req->len - req->write_num; + head_rem_temp = states[spim_num].head_rem; + if(write && head_rem_temp) { + + // Fill the FIFO + avail = (MXC_CFG_SPIM_FIFO_DEPTH - ((spim->fifo_ctrl & MXC_F_SPIM_FIFO_CTRL_TX_FIFO_USED) >> + MXC_F_SPIM_FIFO_CTRL_TX_FIFO_USED_POS)); + + // Use memcpy for everything except the last byte in odd length transactions + while((avail >= 2) && (head_rem_temp >= 2)) { + + unsigned length; + if(head_rem_temp < avail) { + length = head_rem_temp; + } else { + length = avail; + } + + // Only memcpy even numbers + length = ((length / 2) * 2); + + memcpy((void*)fifo->trans_32, &(req->tx_data[req->write_num]), length); + + head_rem_temp -= length; + req->write_num += length; + + avail = (MXC_CFG_SPIM_FIFO_DEPTH - ((spim->fifo_ctrl & MXC_F_SPIM_FIFO_CTRL_TX_FIFO_USED) >> + MXC_F_SPIM_FIFO_CTRL_TX_FIFO_USED_POS)); + } + + // Copy the last byte and pad with 0xF0 to not get confused as header + if((avail >= 1) && (head_rem_temp == 1)) { + + // Write the last byte + fifo->trans_16[0] = (0xF000 | req->tx_data[req->write_num]); + + avail -= 1; + req->write_num += 1; + head_rem_temp -= 1; + } + + states[spim_num].head_rem = head_rem_temp; + remain = req->len - req->write_num; + + // Set the TX interrupts + if(remain) { + + // Set the TX FIFO almost empty interrupt if we have to refill + spim->fifo_ctrl = ((spim->fifo_ctrl & ~MXC_F_SPIM_FIFO_CTRL_TX_FIFO_AE_LVL) | + ((MXC_CFG_SPIM_FIFO_DEPTH - 2) << MXC_F_SPIM_FIFO_CTRL_TX_FIFO_AE_LVL_POS)); + + inten |= MXC_F_SPIM_INTEN_TX_FIFO_AE; + + } + } + + // Check to see if we've finished reading and writing + if(((read && (req->read_num == req->len)) || !read) && + ((req->write_num == req->len) || !write)) { + + // Disable interrupts + spim->inten = 0; + + // Unlock this SPIM + mxc_free_lock((uint32_t*)&states[spim_num].req); + + // Callback if not NULL + if(req->callback != NULL) { + req->callback(req, E_NO_ERROR); + } + } + + // Enable the SPIM interrupts + return inten; +}