aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/platforms/LPC214x/spi_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/hal/platforms/LPC214x/spi_lld.c')
-rw-r--r--os/hal/platforms/LPC214x/spi_lld.c173
1 files changed, 131 insertions, 42 deletions
diff --git a/os/hal/platforms/LPC214x/spi_lld.c b/os/hal/platforms/LPC214x/spi_lld.c
index d829c3d62..04dafb93d 100644
--- a/os/hal/platforms/LPC214x/spi_lld.c
+++ b/os/hal/platforms/LPC214x/spi_lld.c
@@ -28,13 +28,13 @@
#include "ch.h"
#include "hal.h"
-#if CH_HAL_USE_SPI || defined(__DOXYGEN__)
+#if LPC214x_SPI_USE_SSP || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
-#if USE_LPC214x_SPI1 || defined(__DOXYGEN__)
+#if LPC214x_SPI_USE_SSP || defined(__DOXYGEN__)
/** @brief SPI1 driver identifier.*/
SPIDriver SPID1;
#endif
@@ -48,38 +48,92 @@ SPIDriver SPID1;
/*===========================================================================*/
/**
- * @brief Synchronous SSP transfer.
+ * @brief Preloads the transmit FIFO.
*
- * @param[in] n number of bytes to be exchanged
- * @param[in] txbuf the pointer to the transmit buffer or @p NULL
- * @param[out] rxbuf the pointer to the receive buffer or @p NULL
+ * @param[in] spip pointer to the @p SPIDriver object
*/
-static void rw8(size_t n, const uint8_t *txbuf, uint8_t *rxbuf) {
- size_t ntx = n;
-
- while (n > 0) {
- uint32_t sr = SSPBase->SSP_SR;
- if (sr & SR_RNE) {
- uint8_t w = SSPBase->SSP_DR;
- if (rxbuf != NULL)
- *rxbuf++ = w;
- n--;
- continue; /* Priority over transmission. */
+static void ssp_fifo_preload(SPIDriver *spip) {
+ SSP *ssp = spip->spd_ssp;
+ uint32_t n = spip->spd_txcnt > LPC214x_SSP_FIFO_DEPTH ?
+ LPC214x_SSP_FIFO_DEPTH : spip->spd_txcnt;
+
+ while(((ssp->SSP_SR & SR_TNF) != 0) && (n > 0)) {
+ if (spip->spd_txptr != NULL) {
+ if ((ssp->SSP_CR0 & CR0_DSSMASK) > CR0_DSS8BIT)
+ ssp->SSP_DR = *(uint16_t *)spip->spd_txptr++;
+ else
+ ssp->SSP_DR = *(uint8_t *)spip->spd_txptr++;
}
- if ((ntx > 0) && (sr & SR_TNF)) {
- if (txbuf != NULL)
- SSPBase->SSP_DR = *txbuf++;
+ else
+ ssp->SSP_DR = 0xFFFFFFFF;
+ n--;
+ spip->spd_txcnt--;
+ }
+}
+
+#if defined(__GNUC__)
+__attribute__((noinline))
+#endif
+/**
+ * @brief Common IRQ handler.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ */
+static void serve_interrupt(SPIDriver *spip) {
+ SSP *ssp = spip->spd_ssp;
+
+ if ((ssp->SSP_MIS & MIS_ROR) != 0) {
+ /* The overflow condition should never happen because priority is given
+ to receive but a hook macro is provided anyway...*/
+ LPC214x_SPI_SSP_ERROR_HOOK();
+ }
+ ssp->SSP_ICR = ICR_RT | ICR_ROR;
+ while ((ssp->SSP_SR & SR_RNE) != 0) {
+ if (spip->spd_rxptr != NULL) {
+ if ((ssp->SSP_CR0 & CR0_DSSMASK) > CR0_DSS8BIT)
+ *(uint16_t *)spip->spd_rxptr++ = ssp->SSP_DR;
else
- SSPBase->SSP_DR = 0xFFFFFFFF;
- ntx--;
+ *(uint8_t *)spip->spd_rxptr++ = ssp->SSP_DR;
+ }
+ else
+ (void)ssp->SSP_DR;
+ if (--spip->spd_rxcnt == 0) {
+ chDbgAssert(spip->spd_txcnt == 0,
+ "chSemResetI(), #1", "counter out of synch");
+ /* Stops the IRQ sources.*/
+ ssp->SSP_IMSC = 0;
+ /* Portable SPI ISR code defined in the high level driver, note, it is
+ a macro.*/
+ _spi_isr_code(spip);
+ return;
}
}
+ ssp_fifo_preload(spip);
+ if (spip->spd_txcnt == 0)
+ ssp->SSP_IMSC = IMSC_ROR | IMSC_RT | IMSC_RX;
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
+#if LPC214x_SPI_USE_SSP || defined(__DOXYGEN__)
+/**
+ * @brief SPI1 interrupt handler.
+ *
+ * @isr
+ */
+CH_IRQ_HANDLER(SPI1IrqHandler) {
+
+ CH_IRQ_PROLOGUE();
+
+ serve_interrupt(&SPID1);
+ VICVectAddr = 0;
+
+ CH_IRQ_EPILOGUE();
+}
+#endif
+
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
@@ -91,8 +145,10 @@ static void rw8(size_t n, const uint8_t *txbuf, uint8_t *rxbuf) {
*/
void spi_lld_init(void) {
-#if USE_LPC214x_SPI1
+#if LPC214x_SPI_USE_SSP
spiObjectInit(&SPID1);
+ SPID1.spd_ssp = SSPBase;
+ SetVICVector(SPI1IrqHandler, LPC214x_SPI_SSP_IRQ_PRIORITY, SOURCE_SPI1);
#endif
}
@@ -107,16 +163,22 @@ void spi_lld_start(SPIDriver *spip) {
if (spip->spd_state == SPI_STOP) {
/* Clock activation.*/
- PCONP = (PCONP & PCALL) | PCSPI1;
+#if LPC214x_SPI_USE_SSP
+ if (&SPID1 == spip) {
+ PCONP = (PCONP & PCALL) | PCSPI1;
+ VICIntEnable = INTMASK(SOURCE_SPI1);
+ }
+#endif
}
/* Configuration.*/
SSPBase->SSP_CR1 = 0;
/* Emptying the receive FIFO, it happens to not be empty while debugging.*/
- while (SSPBase->SSP_SR & SR_RNE)
- (void) SSPBase->SSP_DR;
- SSPBase->SSP_CR0 = spip->spd_config->spc_cr0;
- SSPBase->SSP_CPSR = spip->spd_config->spc_cpsr;
- SSPBase->SSP_CR1 = spip->spd_config->spc_cr1 | CR1_SSE;
+ while (spip->spd_ssp->SSP_SR & SR_RNE)
+ (void) spip->spd_ssp->SSP_DR;
+ spip->spd_ssp->SSP_ICR = ICR_RT | ICR_ROR;
+ spip->spd_ssp->SSP_CR0 = spip->spd_config->spc_cr0;
+ spip->spd_ssp->SSP_CPSR = spip->spd_config->spc_cpsr;
+ spip->spd_ssp->SSP_CR1 = spip->spd_config->spc_cr1 | CR1_SSE;
}
/**
@@ -129,6 +191,12 @@ void spi_lld_start(SPIDriver *spip) {
void spi_lld_stop(SPIDriver *spip) {
if (spip->spd_state != SPI_STOP) {
+#if LPC214x_SPI_USE_SSP
+ if (&SPID1 == spip) {
+ PCONP = (PCONP & PCALL) & ~PCSPI1;
+ VICIntEnClear = INTMASK(SOURCE_SPI1);
+ }
+#endif
SSPBase->SSP_CR1 = 0;
SSPBase->SSP_CR0 = 0;
SSPBase->SSP_CPSR = 0;
@@ -174,14 +242,20 @@ void spi_lld_unselect(SPIDriver *spip) {
*/
void spi_lld_ignore(SPIDriver *spip, size_t n) {
- (void)spip;
- rw8(n, NULL, NULL);
+ spip->spd_rxptr = NULL;
+ spip->spd_txptr = NULL;
+ spip->spd_rxcnt = spip->spd_txcnt = n;
+ ssp_fifo_preload(spip);
+ spip->spd_ssp->SSP_IMSC = IMSC_ROR | IMSC_RT | IMSC_TX | IMSC_RX;
}
/**
* @brief Exchanges data on the SPI bus.
- * @details This function performs a simultaneous transmit/receive operation.
- * @note The buffers are organized as uint8_t arrays.
+ * @details This asynchronous function starts a simultaneous transmit/receive
+ * operation.
+ * @post At the end of the operation the configured callback is invoked.
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] n number of words to be exchanged
@@ -193,13 +267,19 @@ void spi_lld_ignore(SPIDriver *spip, size_t n) {
void spi_lld_exchange(SPIDriver *spip, size_t n,
const void *txbuf, void *rxbuf) {
- (void)spip;
- rw8(n, txbuf, rxbuf);
+ spip->spd_rxptr = rxbuf;
+ spip->spd_txptr = txbuf;
+ spip->spd_rxcnt = spip->spd_txcnt = n;
+ ssp_fifo_preload(spip);
+ spip->spd_ssp->SSP_IMSC = IMSC_ROR | IMSC_RT | IMSC_TX | IMSC_RX;
}
/**
- * @brief Sends data ever the SPI bus.
- * @note The buffers are organized as uint8_t arrays.
+ * @brief Sends data over the SPI bus.
+ * @details This asynchronous function starts a transmit operation.
+ * @post At the end of the operation the configured callback is invoked.
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] n number of words to send
@@ -209,13 +289,19 @@ void spi_lld_exchange(SPIDriver *spip, size_t n,
*/
void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
- (void)spip;
- rw8(n, txbuf, NULL);
+ spip->spd_rxptr = NULL;
+ spip->spd_txptr = txbuf;
+ spip->spd_rxcnt = spip->spd_txcnt = n;
+ ssp_fifo_preload(spip);
+ spip->spd_ssp->SSP_IMSC = IMSC_ROR | IMSC_RT | IMSC_TX | IMSC_RX;
}
/**
* @brief Receives data from the SPI bus.
- * @note The buffers are organized as uint8_t arrays.
+ * @details This asynchronous function starts a receive operation.
+ * @post At the end of the operation the configured callback is invoked.
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] n number of words to receive
@@ -225,8 +311,11 @@ void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
*/
void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
- (void)spip;
- rw8(n, NULL, rxbuf);
+ spip->spd_rxptr = rxbuf;
+ spip->spd_txptr = NULL;
+ spip->spd_rxcnt = spip->spd_txcnt = n;
+ ssp_fifo_preload(spip);
+ spip->spd_ssp->SSP_IMSC = IMSC_ROR | IMSC_RT | IMSC_TX | IMSC_RX;
}
#endif /* CH_HAL_USE_SPI */