X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fsamd21%2Fao_spi_samd21.c;h=aee8cb1aef33ade3a4d643faa67f78b18f611d36;hb=14f1e175af85c0ef4539316d5ce049798a878fcb;hp=487c23eb36cb768a06fb2bd6c2cfdd8c77fc36c5;hpb=a54f4fae343b7524db508dc1a29d36847a2decde;p=fw%2Faltos diff --git a/src/samd21/ao_spi_samd21.c b/src/samd21/ao_spi_samd21.c index 487c23eb..aee8cb1a 100644 --- a/src/samd21/ao_spi_samd21.c +++ b/src/samd21/ao_spi_samd21.c @@ -13,9 +13,13 @@ */ #include +#include 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; @@ -42,106 +46,322 @@ static const struct ao_spi_samd21_info ao_spi_samd21_info[SAMD21_NUM_SERCOM] = { }, }; -//static uint8_t spi_dev_null; +static uint8_t spi_dev_null; -void -ao_spi_send(const void *block, uint16_t len, uint8_t spi_index) +#if SPI_USE_DMA + +static uint8_t ao_spi_done[SAMD21_NUM_SERCOM]; + +static void +_ao_spi_recv_dma_done(uint8_t dma_id, void *closure) { - uint8_t id = AO_SPI_INDEX(spi_index); - struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom; + uint8_t id = (uint8_t) (uintptr_t) closure; - const uint8_t *b = block; + (void) dma_id; + ao_spi_done[id] = 1; + ao_wakeup(&ao_spi_done[id]); +} - while (len--) { - sercom->data = *b++; - while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0) - ; - (void) sercom->data; +static inline uint32_t +dma_chctrlb(uint8_t id, bool tx) +{ + uint32_t chctrlb = 0; + + /* No complicated actions needed */ + chctrlb |= SAMD21_DMAC_CHCTRLB_CMD_NOACT << SAMD21_DMAC_CHCTRLB_CMD; + + /* Trigger after each byte transferred */ + chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT << SAMD21_DMAC_CHCTRLB_TRIGACT; + + /* Set the trigger source */ + if (tx) + chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_TX(id) << SAMD21_DMAC_CHCTRLB_TRIGSRC; + else + chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_RX(id) << SAMD21_DMAC_CHCTRLB_TRIGSRC; + + /* RX has priority over TX so that we don't drop incoming bytes */ + if (tx) + chctrlb |= SAMD21_DMAC_CHCTRLB_LVL_LVL0 << SAMD21_DMAC_CHCTRLB_LVL; + else + chctrlb |= SAMD21_DMAC_CHCTRLB_LVL_LVL3 << SAMD21_DMAC_CHCTRLB_LVL; + + /* No events needed */ + chctrlb |= 0UL << SAMD21_DMAC_CHCTRLB_EVOE; + chctrlb |= 0UL << SAMD21_DMAC_CHCTRLB_EVIE; + + /* And no actions either */ + chctrlb |= SAMD21_DMAC_CHCTRLB_EVACT_NOACT << SAMD21_DMAC_CHCTRLB_EVACT; + + return chctrlb; +} + +static inline uint16_t +dma_btctrl(bool step, bool tx) +{ + uint16_t btctrl = 0; + + /* Always step by 1 */ + btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X1 << SAMD21_DMAC_DESC_BTCTRL_STEPSIZE; + + /* Step the source if transmit, otherwise step the dest */ + if (tx) + btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSEL_SRC << SAMD21_DMAC_DESC_BTCTRL_STEPSEL; + else + btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSEL_DST << SAMD21_DMAC_DESC_BTCTRL_STEPSEL; + + /* Set the increment if stepping */ + if (tx) { + if (step) + btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC; + else + btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC; + btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC; + } else { + btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC; + if (step) + btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC; + else + btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC; } + + /* byte at a time please */ + btctrl |= SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE << SAMD21_DMAC_DESC_BTCTRL_BEATSIZE; + + /* + * Watch for interrupts on RX -- we need to wait for the last byte to get received + * to know the SPI bus is idle + */ + if (tx) + btctrl |= SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_NOACT << SAMD21_DMAC_DESC_BTCTRL_BLOCKACT; + else + btctrl |= SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_INT << SAMD21_DMAC_DESC_BTCTRL_BLOCKACT; + + /* don't need any events */ + btctrl |= SAMD21_DMAC_DESC_BTCTRL_EVOSEL_DISABLE << SAMD21_DMAC_DESC_BTCTRL_EVOSEL; + + /* And make the descriptor valid */ + btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_VALID; + + return btctrl; } -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) { + 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; - uint8_t *b = block; + ao_arch_block_interrupts(); + ao_spi_done[id] = 0; + + /* + * 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, + i, + len, + dma_chctrlb(id, false), + dma_btctrl(step_in, false), + + _ao_spi_recv_dma_done, + (void *) (uintptr_t) id + ); + + _ao_dma_start_transfer(AO_SERCOM_OUTPUT_DMA_ID(id), + o, + (void *) &sercom->data, + len, + dma_chctrlb(id, 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_SERCOM_OUTPUT_DMA_ID(id)); + _ao_dma_done_transfer(AO_SERCOM_INPUT_DMA_ID(id)); + ao_arch_release_interrupts(); +} + +#else + +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; + 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; @@ -156,13 +376,21 @@ ao_spi_config(uint8_t spi_index, uint32_t baud) /* 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)) @@ -170,7 +398,7 @@ ao_spi_config(uint8_t spi_index, uint32_t baud) } 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); @@ -179,7 +407,7 @@ ao_spi_get(uint8_t spi_index, uint32_t speed) } 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; @@ -231,8 +459,7 @@ ao_spi_init_sercom(uint8_t id) (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