*
* 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.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
static uint8_t ao_spi_mutex[LPC_NUM_SPI];
-struct ao_lpc_ssp_state {
- int tx_count;
- const uint8_t *tx;
- int tx_inc;
- int rx_count;
- uint8_t *rx;
- int rx_inc;
-};
-
-static struct ao_lpc_ssp_state ao_lpc_ssp_state[LPC_NUM_SPI];
-
static struct lpc_ssp * const ao_lpc_ssp[LPC_NUM_SPI] = { &lpc_ssp0, &lpc_ssp1 };
-static inline void
-ao_lpc_ssp_recv(struct lpc_ssp *lpc_ssp, struct ao_lpc_ssp_state *state)
-{
- while ((lpc_ssp->sr & (1 << LPC_SSP_SR_RNE)) &&
- state->rx_count)
- {
- /* RX ready, read a byte */
- *state->rx = lpc_ssp->dr;
- state->rx += state->rx_inc;
- state->rx_count--;
- }
-}
-
-static void
-ao_lpc_ssp_isr(struct lpc_ssp *lpc_ssp, struct ao_lpc_ssp_state *state)
-{
- ao_lpc_ssp_recv(lpc_ssp, state);
- while ((lpc_ssp->sr & (1 << LPC_SSP_SR_TNF)) &&
- state->tx_count)
- {
- /* TX ready, write a byte */
- lpc_ssp->dr = *state->tx;
- state->tx += state->tx_inc;
- state->tx_count--;
- ao_lpc_ssp_recv(lpc_ssp, state);
- }
- if (!state->rx_count) {
- lpc_ssp->imsc &= ~(1 << LPC_SSP_IMSC_TXIM);
- ao_wakeup(state);
- }
-}
-
-void
-lpc_ssp0_isr(void)
-{
- ao_lpc_ssp_isr(&lpc_ssp0, &ao_lpc_ssp_state[0]);
-}
-
-void
-lpc_ssp1_isr(void)
-{
- ao_lpc_ssp_isr(&lpc_ssp1, &ao_lpc_ssp_state[1]);
-}
-
-static void
-ao_spi_run(struct lpc_ssp *lpc_ssp, struct ao_lpc_ssp_state *state)
-{
- ao_arch_block_interrupts();
- lpc_ssp->imsc = (1 << LPC_SSP_IMSC_TXIM);
- while (state->rx_count)
- ao_sleep(state);
- ao_arch_release_interrupts();
-}
-
-static uint8_t ao_spi_tx_dummy = 0xff;
-static uint8_t ao_spi_rx_dummy;
+#define spi_loop(len, put, get) do { \
+ while (len--) { \
+ /* send a byte */ \
+ lpc_ssp->dr = put; \
+ /* wait for the received byte to appear */ \
+ while ((lpc_ssp->sr & (1 << LPC_SSP_SR_RNE)) == 0) \
+ ; \
+ /* receive a byte */ \
+ get (uint8_t) lpc_ssp->dr; \
+ } \
+ /* Wait for the SSP to go idle (it already should be) */ \
+ while (lpc_ssp->sr & (1 << LPC_SSP_SR_BSY)); \
+ } while (0)
void
ao_spi_send(const void *block, uint16_t len, uint8_t id)
{
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
- struct ao_lpc_ssp_state *state = &ao_lpc_ssp_state[id];
-
- state->tx_count = state->rx_count = len;
- state->tx = block;
- state->tx_inc = 1;
- state->rx = &ao_spi_rx_dummy;
- state->rx_inc = 0;
- ao_spi_run(lpc_ssp, state);
+ const uint8_t *o = block;
+
+ spi_loop(len, *o++, (void));
}
void
ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t id)
{
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
- struct ao_lpc_ssp_state *state = &ao_lpc_ssp_state[id];
-
- state->tx_count = state->rx_count = len;
- state->tx = &value;
- state->tx_inc = 0;
- state->rx = &ao_spi_rx_dummy;
- state->rx_inc = 0;
- ao_spi_run(lpc_ssp, state);
+
+ spi_loop(len, value, (void));
}
void
ao_spi_recv(void *block, uint16_t len, uint8_t id)
{
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
- struct ao_lpc_ssp_state *state = &ao_lpc_ssp_state[id];
-
- state->tx_count = state->rx_count = len;
- state->tx = &ao_spi_tx_dummy;
- state->tx_inc = 0;
- state->rx = block;
- state->rx_inc = 1;
- ao_spi_run(lpc_ssp, state);
+ uint8_t *i = block;
+
+ spi_loop(len, 0xff, *i++ =);
}
void
ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t id)
{
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
- struct ao_lpc_ssp_state *state = &ao_lpc_ssp_state[id];
-
- state->tx_count = state->rx_count = len;
- state->tx = out;
- state->tx_inc = 1;
- state->rx = in;
- state->rx_inc = 1;
- ao_spi_run(lpc_ssp, state);
+ const uint8_t *o = out;
+ uint8_t *i = in;
+
+ spi_loop(len, *o++, *i++ =);
}
void
}
static void
-ao_spi_channel_init(uint8_t id)
+ao_spi_channel_init(uint8_t id, uint8_t mode)
{
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
uint8_t d;
lpc_ssp->cr0 = ((LPC_SSP_CR0_DSS_8 << LPC_SSP_CR0_DSS) |
(LPC_SSP_CR0_FRF_SPI << LPC_SSP_CR0_FRF) |
- (0 << LPC_SSP_CR0_CPOL) |
- (0 << LPC_SSP_CR0_CPHA) |
+ mode |
(0 << LPC_SSP_CR0_SCR));
/* Enable the device */
ao_spi_init(void)
{
#if HAS_SPI_0
+#ifndef SPI_0_MODE
+#define SPI_0_MODE 0
+#endif
/* Configure pins */
#if SPI_SCK0_P0_6
lpc_ioconf.pio0_6 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_6_SCK0);
lpc_scb.ssp0clkdiv = 1;
/* Reset the device */
- lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
+ lpc_scb.presetctrl &= ~(1UL << LPC_SCB_PRESETCTRL_SSP0_RST_N);
lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
- ao_spi_channel_init(0);
-
- /* Configure NVIC */
- lpc_nvic_set_enable(LPC_ISR_SSP0_POS);
- lpc_nvic_set_priority(LPC_ISR_SSP0_POS, 0);
+ ao_spi_channel_init(0, SPI_0_MODE);
#endif
#if HAS_SPI_1
+#ifndef SPI_1_MODE
+#define SPI_1_MODE 0
+#endif
#if SPI_SCK1_P1_15
lpc_ioconf.pio1_15 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_15_SCK1);
lpc_scb.ssp1clkdiv = 1;
/* Reset the device */
- lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP1_RST_N);
+ lpc_scb.presetctrl &= ~(1UL << LPC_SCB_PRESETCTRL_SSP1_RST_N);
lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP1_RST_N);
- ao_spi_channel_init(1);
-
- /* Configure NVIC */
- lpc_nvic_set_enable(LPC_ISR_SSP1_POS);
- lpc_nvic_set_priority(LPC_ISR_SSP1_POS, 0);
-
+ ao_spi_channel_init(1, SPI_1_MODE);
#endif /* HAS_SPI_1 */
}