#include <ao_dma_samd21.h>
static uint8_t ao_spi_mutex[SAMD21_NUM_SERCOM];
-static uint8_t ao_spi_pin_config[SAMD21_NUM_SERCOM];
+static uint16_t ao_spi_pin_config[SAMD21_NUM_SERCOM];
+
+#define SPI_DEBUG 0
+#define SPI_USE_DMA 1
struct ao_spi_samd21_info {
struct samd21_sercom *sercom;
static uint8_t spi_dev_null;
-#define USE_DMA 1
-
-#if USE_DMA
-
-#define AO_SAMD21_SPI_MISO_DMA_ID 0
-#define AO_SAMD21_SPI_MOSI_DMA_ID 1
+#if SPI_USE_DMA
static uint8_t ao_spi_done[SAMD21_NUM_SERCOM];
ao_spi_done[id] = 1;
ao_wakeup(&ao_spi_done[id]);
}
-#endif
static inline uint32_t
dma_chctrlb(uint8_t id, bool tx)
}
static inline uint16_t
-dma_btctrl(bool tx, bool step)
+dma_btctrl(bool step, bool tx)
{
uint16_t btctrl = 0;
return btctrl;
}
-void
-ao_spi_send(const void *block, uint16_t len, uint8_t spi_index)
+static void
+spi_run(const void *out, void *in, uint16_t len, uint16_t spi_index, bool step_out, bool step_in)
{
+ const uint8_t *o = out;
+ uint8_t *i = in;
uint8_t id = AO_SPI_INDEX(spi_index);
struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
-#if USE_DMA
ao_arch_block_interrupts();
ao_spi_done[id] = 0;
- _ao_dma_start_transfer(AO_SAMD21_SPI_MISO_DMA_ID,
+ /*
+ * Stepped addresses to the DMA engine point past the end of
+ * the block
+ */
+ if (step_out)
+ o += len;
+ if (step_in)
+ i += len;
+
+ /* read any stuck data */
+ (void) sercom->data;
+
+ _ao_dma_start_transfer(AO_SERCOM_INPUT_DMA_ID(id),
(void *) &sercom->data,
- &spi_dev_null,
+ i,
len,
dma_chctrlb(id, false),
- dma_btctrl(false, false),
+ dma_btctrl(step_in, false),
_ao_spi_recv_dma_done,
(void *) (uintptr_t) id
);
- _ao_dma_start_transfer(AO_SAMD21_SPI_MOSI_DMA_ID,
- (uint8_t *) block + len, /* must point past the end of the block */
+ _ao_dma_start_transfer(AO_SERCOM_OUTPUT_DMA_ID(id),
+ o,
(void *) &sercom->data,
len,
dma_chctrlb(id, true),
- dma_btctrl(true, true),
+ dma_btctrl(step_out, true),
NULL,
NULL
);
while (ao_spi_done[id] == 0)
ao_sleep(&ao_spi_done[id]);
- _ao_dma_done_transfer(AO_SAMD21_SPI_MOSI_DMA_ID);
- _ao_dma_done_transfer(AO_SAMD21_SPI_MISO_DMA_ID);
+ _ao_dma_done_transfer(AO_SERCOM_OUTPUT_DMA_ID(id));
+ _ao_dma_done_transfer(AO_SERCOM_INPUT_DMA_ID(id));
ao_arch_release_interrupts();
-#else
- const uint8_t *b = block;
-
- while (len--) {
- sercom->data = *b++;
- while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
- ;
- (void) sercom->data;
- }
-#endif
}
+#else
-void
-ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
+static void
+spi_run(const void *out, void *in, uint16_t len, uint16_t spi_index, bool step_out, bool step_in)
{
uint8_t id = AO_SPI_INDEX(spi_index);
struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
-
- uint8_t *b = block;
+ const uint8_t *o = out;
+ uint8_t *i = in;
while (len--) {
- sercom->data = 0xff;
+#if SPI_DEBUG
+ printf("%02x", *o);
+#endif
+ sercom->data = *o;
while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
;
- *b++ = (uint8_t) sercom->data;
+ *i = (uint8_t) sercom->data;
+#if SPI_DEBUG
+ printf("\t%02x\n", *i);
+#endif
+ if (step_out)
+ o++;
+ if (step_in)
+ i++;
}
}
+#endif
void
-ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index)
+ao_spi_send(const void *block, uint16_t len, uint16_t spi_index)
{
- uint8_t id = AO_SPI_INDEX(spi_index);
- struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
+ spi_run(block, &spi_dev_null, len, spi_index, true, false);
+}
- const uint8_t *o = out;
- uint8_t *i = in;
+void
+ao_spi_send_fixed(uint8_t data, uint16_t len, uint16_t spi_index)
+{
+ spi_run(&data, &spi_dev_null, len, spi_index, false, false);
+}
- while (len--) {
- sercom->data = *o++;
- while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
- ;
- *i++ = (uint8_t) sercom->data;
- }
+void
+ao_spi_recv(void *block, uint16_t len, uint16_t spi_index)
+{
+ spi_dev_null = 0xff;
+ spi_run(&spi_dev_null, block, len, spi_index, false, true);
+}
+
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint16_t spi_index)
+{
+ spi_run(out, in, len, spi_index, true, true);
}
static void
-ao_spi_disable_pin_config(uint8_t spi_pin_config)
+ao_spi_disable_pin_config(uint16_t spi_pin_config)
{
switch (spi_pin_config) {
#if HAS_SPI_0
- case AO_SPI_0_PA08_PA09_PA10:
+ case AO_SPI_PIN_CONFIG(AO_SPI_0_PA08_PA09_PA10):
samd21_port_pmux_clr(&samd21_port_a, 8); /* MOSI */
samd21_port_pmux_clr(&samd21_port_a, 9); /* SCLK */
samd21_port_pmux_clr(&samd21_port_a, 10); /* MISO */
break;
- case AO_SPI_0_PA04_PA05_PA06:
+ case AO_SPI_PIN_CONFIG(AO_SPI_0_PA04_PA05_PA06):
samd21_port_pmux_clr(&samd21_port_a, 4); /* MOSI */
samd21_port_pmux_clr(&samd21_port_a, 5); /* SCLK */
samd21_port_pmux_clr(&samd21_port_a, 6); /* MISO */
break;
#endif
+#if HAS_SPI_3
+ case AO_SPI_PIN_CONFIG(AO_SPI_3_PA22_PA23_PA20):
+ samd21_port_pmux_clr(&samd21_port_a, 22); /* MOSI */
+ samd21_port_pmux_clr(&samd21_port_a, 23); /* SCLK */
+ samd21_port_pmux_clr(&samd21_port_a, 20); /* MISO */
+ break;
+#endif
+#if HAS_SPI_4
+ case AO_SPI_PIN_CONFIG(AO_SPI_4_PB10_PB11_PA12):
+ samd21_port_pmux_clr(&samd21_port_b, 10); /* MOSI */
+ samd21_port_pmux_clr(&samd21_port_b, 11); /* SCLK */
+ samd21_port_pmux_clr(&samd21_port_a, 12); /* MISO */
+ break;
+#endif
+#if HAS_SPI_5
+ case AO_SPI_PIN_CONFIG(AO_SPI_5_PB22_PB23_PB03):
+ samd21_port_pmux_clr(&samd21_port_b, 22); /* MOSI */
+ samd21_port_pmux_clr(&samd21_port_b, 23); /* SCLK */
+ samd21_port_pmux_clr(&samd21_port_b, 3); /* MISO */
+ break;
+#endif
+ case 0xffff:
+ break;
}
}
static void
-ao_spi_enable_pin_config(uint8_t spi_pin_config)
+ao_spi_enable_pin_config(uint16_t spi_pin_config)
{
switch (spi_pin_config) {
#if HAS_SPI_0
- case AO_SPI_0_PA08_PA09_PA10:
+ case AO_SPI_PIN_CONFIG(AO_SPI_0_PA08_PA09_PA10):
ao_enable_output(&samd21_port_a, 8, 1);
ao_enable_output(&samd21_port_a, 9, 1);
ao_enable_input(&samd21_port_a, 10, AO_MODE_PULL_NONE);
+
samd21_port_pmux_set(&samd21_port_a, 8, SAMD21_PORT_PMUX_FUNC_C); /* MOSI */
samd21_port_pmux_set(&samd21_port_a, 9, SAMD21_PORT_PMUX_FUNC_C); /* SCLK */
samd21_port_pmux_set(&samd21_port_a, 10, SAMD21_PORT_PMUX_FUNC_C); /* MISO */
break;
- case AO_SPI_0_PA04_PA05_PA06:
+ case AO_SPI_PIN_CONFIG(AO_SPI_0_PA04_PA05_PA06):
ao_enable_output(&samd21_port_a, 4, 1);
ao_enable_output(&samd21_port_a, 5, 1);
ao_enable_input(&samd21_port_a, 6, AO_MODE_PULL_NONE);
- samd21_port_pmux_set(&samd21_port_a, 4, SAMD21_PORT_PMUX_FUNC_C); /* MOSI */
- samd21_port_pmux_set(&samd21_port_a, 5, SAMD21_PORT_PMUX_FUNC_C); /* SCLK */
- samd21_port_pmux_set(&samd21_port_a, 6, SAMD21_PORT_PMUX_FUNC_C); /* MISO */
+
+ samd21_port_pmux_set(&samd21_port_a, 4, SAMD21_PORT_PMUX_FUNC_D); /* MOSI */
+ samd21_port_pmux_set(&samd21_port_a, 5, SAMD21_PORT_PMUX_FUNC_D); /* SCLK */
+ samd21_port_pmux_set(&samd21_port_a, 6, SAMD21_PORT_PMUX_FUNC_D); /* MISO */
+ break;
+#endif
+#if HAS_SPI_3
+ case AO_SPI_PIN_CONFIG(AO_SPI_3_PA22_PA23_PA20):
+ ao_enable_output(&samd21_port_a, 22, 1);
+ ao_enable_output(&samd21_port_a, 23, 1);
+ ao_enable_input(&samd21_port_a, 20, AO_MODE_PULL_NONE);
+
+ samd21_port_pmux_set(&samd21_port_a, 22, SAMD21_PORT_PMUX_FUNC_C); /* MOSI */
+ samd21_port_pmux_set(&samd21_port_a, 23, SAMD21_PORT_PMUX_FUNC_C); /* SCLK */
+ samd21_port_pmux_set(&samd21_port_a, 20, SAMD21_PORT_PMUX_FUNC_D); /* MISO */
+ break;
+#endif
+#if HAS_SPI_4
+ case AO_SPI_PIN_CONFIG(AO_SPI_4_PB10_PB11_PA12):
+ ao_enable_output(&samd21_port_b, 10, 1);
+ ao_enable_output(&samd21_port_b, 11, 1);
+ ao_enable_input(&samd21_port_a, 12, AO_MODE_PULL_NONE);
+
+ samd21_port_pmux_set(&samd21_port_b, 10, SAMD21_PORT_PMUX_FUNC_D); /* MOSI */
+ samd21_port_pmux_set(&samd21_port_b, 11, SAMD21_PORT_PMUX_FUNC_D); /* SCLK */
+ samd21_port_pmux_set(&samd21_port_a, 12, SAMD21_PORT_PMUX_FUNC_D); /* MISO */
+ break;
+#endif
+#if HAS_SPI_5
+ case AO_SPI_PIN_CONFIG(AO_SPI_5_PB22_PB23_PB03):
+ ao_enable_output(&samd21_port_b, 22, 1);
+ ao_enable_output(&samd21_port_b, 23, 1);
+ ao_enable_input(&samd21_port_b, 3, AO_MODE_PULL_NONE);
+
+ samd21_port_pmux_set(&samd21_port_b, 22, SAMD21_PORT_PMUX_FUNC_D); /* 5.2 MOSI */
+ samd21_port_pmux_set(&samd21_port_b, 23, SAMD21_PORT_PMUX_FUNC_D); /* 5.3 SCLK */
+ samd21_port_pmux_set(&samd21_port_b, 3, SAMD21_PORT_PMUX_FUNC_D); /* 5.1 MISO */
break;
#endif
+ default:
+ ao_panic(AO_PANIC_SPI);
+ break;
}
}
static void
-ao_spi_config(uint8_t spi_index, uint32_t baud)
+ao_spi_config(uint16_t spi_index, uint32_t baud)
{
- uint8_t spi_pin_config = AO_SPI_PIN_CONFIG(spi_index);
+ uint16_t spi_pin_config = AO_SPI_PIN_CONFIG(spi_index);
uint8_t id = AO_SPI_INDEX(spi_index);
struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
/* Set spi mode */
uint32_t ctrla = sercom->ctrla;
ctrla &= ~((1UL << SAMD21_SERCOM_CTRLA_CPOL) |
- (1UL << SAMD21_SERCOM_CTRLA_CPHA));
+ (1UL << SAMD21_SERCOM_CTRLA_CPHA) |
+ (SAMD21_SERCOM_CTRLA_DOPO_MASK << SAMD21_SERCOM_CTRLA_DOPO) |
+ (SAMD21_SERCOM_CTRLA_DIPO_MASK << SAMD21_SERCOM_CTRLA_DIPO));
ctrla |= ((AO_SPI_CPOL(spi_index) << SAMD21_SERCOM_CTRLA_CPOL) |
- (AO_SPI_CPHA(spi_index) << SAMD21_SERCOM_CTRLA_CPHA));
+ (AO_SPI_CPHA(spi_index) << SAMD21_SERCOM_CTRLA_CPHA) |
+ (AO_SPI_DOPO(spi_index) << SAMD21_SERCOM_CTRLA_DOPO) |
+ (AO_SPI_DIPO(spi_index) << SAMD21_SERCOM_CTRLA_DIPO));
/* finish setup and enable the hardware */
ctrla |= (1 << SAMD21_SERCOM_CTRLA_ENABLE);
+#if SPI_DEBUG
+ printf("ctrla %08lx\n", ctrla);
+#endif
+
sercom->ctrla = ctrla;
while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
}
void
-ao_spi_get(uint8_t spi_index, uint32_t speed)
+ao_spi_get(uint16_t spi_index, uint32_t speed)
{
uint8_t id = AO_SPI_INDEX(spi_index);
}
void
-ao_spi_put(uint8_t spi_index)
+ao_spi_put(uint16_t spi_index)
{
uint8_t id = AO_SPI_INDEX(spi_index);
struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
(0 << SAMD21_SERCOM_CTRLB_PLOADEN) |
(SAMD21_SERCOM_CTRLB_CHSIZE_8 << SAMD21_SERCOM_CTRLB_CHSIZE));
-
- ao_spi_enable_pin_config(id);
+ ao_spi_pin_config[id] = 0xffff;
}
void