X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fsamd21%2Fao_spi_samd21.c;h=639ba97fc7fb3416c99a6671a27800ce47e05cfa;hb=a06c84a27bd760039c522460f79bfe242d2d22c8;hp=6e49884b69248f9f8299fc9454d7d720abfd5b96;hpb=5d18ef0a25248f0192c3b539cad36d3de60a6f32;p=fw%2Faltos diff --git a/src/samd21/ao_spi_samd21.c b/src/samd21/ao_spi_samd21.c index 6e49884b..639ba97f 100644 --- a/src/samd21/ao_spi_samd21.c +++ b/src/samd21/ao_spi_samd21.c @@ -16,7 +16,18 @@ #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 + +/* + * DMA is only used for USARTs in altos, which makes assigning DMA IDs + * pretty easy + */ + +#define MISO_DMA_ID(id) ((uint8_t) ((id) * 2U + 0U)) +#define MOSI_DMA_ID(id) ((uint8_t) ((id) * 2U + 1U)) struct ao_spi_samd21_info { struct samd21_sercom *sercom; @@ -45,12 +56,7 @@ static const struct ao_spi_samd21_info ao_spi_samd21_info[SAMD21_NUM_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]; @@ -63,7 +69,6 @@ _ao_spi_recv_dma_done(uint8_t dma_id, void *closure) ao_spi_done[id] = 1; ao_wakeup(&ao_spi_done[id]); } -#endif static inline uint32_t dma_chctrlb(uint8_t id, bool tx) @@ -99,7 +104,7 @@ 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; @@ -148,33 +153,46 @@ dma_btctrl(bool tx, bool step) 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(MISO_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(MOSI_DMA_ID(id), + o, (void *) &sercom->data, len, dma_chctrlb(id, true), - dma_btctrl(true, true), + dma_btctrl(step_out, true), NULL, NULL ); @@ -182,104 +200,176 @@ ao_spi_send(const void *block, uint16_t len, uint8_t spi_index) 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(MOSI_DMA_ID(id)); + _ao_dma_done_transfer(MISO_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 */ 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; @@ -294,13 +384,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)) @@ -308,7 +406,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); @@ -317,7 +415,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; @@ -369,8 +467,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