From 9481e33348f098f3df73006641b9a18a04f2c482 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 6 Nov 2022 17:46:56 -0800 Subject: [PATCH] samd21: Add ability to use arbitrary pin configs with SPI This embeds the DOPO and DIPO configs in the address Signed-off-by: Keith Packard --- src/samd21/ao_arch_funcs.h | 96 ++++++++++++++----- src/samd21/ao_spi_samd21.c | 189 +++++++++++++++++++++++++------------ src/samd21/samd21.h | 12 +++ 3 files changed, 215 insertions(+), 82 deletions(-) diff --git a/src/samd21/ao_arch_funcs.h b/src/samd21/ao_arch_funcs.h index e9fd3216..47656551 100644 --- a/src/samd21/ao_arch_funcs.h +++ b/src/samd21/ao_arch_funcs.h @@ -125,35 +125,77 @@ ao_enable_cs(struct samd21_port *port, uint8_t pin) /* ao_spi_samd21.c */ +#define AO_SPI_INDEX_BIT 0 +#define AO_SPI_INDEX_MASK 0x07 + +#define AO_SPI_CONFIG_BIT 4 +#define AO_SPI_CONFIG_MASK (3 << AO_SPI_CONFIG_BIT) + #define AO_SPI_CPOL_BIT 6 #define AO_SPI_CPHA_BIT 7 -#define AO_SPI_CONFIG_1 0x00 +#define AO_SPI_DOPO_BIT 8 +#define AO_SPI_DOPO_MOSI_0_SCLK_1 (0 << AO_SPI_DOPO_BIT) +#define AO_SPI_DOPO_MOSI_2_SCLK_3 (1 << AO_SPI_DOPO_BIT) +#define AO_SPI_DOPO_MOSI_3_SCLK_1 (2 << AO_SPI_DOPO_BIT) +#define AO_SPI_DOPO_MOSI_0_SCLK_3 (3 << AO_SPI_DOPO_BIT) +#define AO_SPI_DOPO_MASK (3 << AO_SPI_DOPO_BIT) + +#define AO_SPI_DIPO_BIT 10 +#define AO_SPI_DIPO_MISO_0 (0 << AO_SPI_DIPO_BIT) +#define AO_SPI_DIPO_MISO_1 (1 << AO_SPI_DIPO_BIT) +#define AO_SPI_DIPO_MISO_2 (2 << AO_SPI_DIPO_BIT) +#define AO_SPI_DIPO_MISO_3 (3 << AO_SPI_DIPO_BIT) +#define AO_SPI_DIPO_MASK (3 << AO_SPI_DIPO_MASK) + +#define AO_SPI_CONFIG_0 (0 << AO_SPI_CONFIG_BIT) +#define AO_SPI_CONFIG_1 (1 << AO_SPI_CONFIG_BIT) +#define AO_SPI_CONFIG_2 (2 << AO_SPI_CONFIG_BIT) +#define AO_SPI_CONFIG_3 (3 << AO_SPI_CONFIG_BIT) + /* * PA08 SERCOM0.0 -> MOSI (DOPO 0) * PA09 SERCOM0.1 -> SCLK (DOPO 0) * PA10 SERCOM0.2 -> MISO (DIPO 2) */ -#define AO_SPI_0_CONFIG_PA08_PA09_PA10 AO_SPI_CONFIG_1 +#define AO_SPI_0_CONFIG_PA08_PA09_PA10 (AO_SPI_CONFIG_0 | \ + AO_SPI_DOPO_MOSI_0_SCLK_1 | \ + AO_SPI_DIPO_MISO_2) -#define AO_SPI_CONFIG_2 0x08 /* * PA04 SERCOM0.0 -> MOSI (DOPO 0) * PA05 SERCOM0.1 -> SCLK (DOPO 0) * PA16 SERCOM0.2 -> MISO (DIPO 2) */ -#define AO_SPI_0_CONFIG_PA04_PA05_PA06 AO_SPI_CONFIG_2 +#define AO_SPI_0_CONFIG_PA04_PA05_PA06 (AO_SPI_CONFIG_1 | \ + AO_SPI_DOPO_MOSI_0_SCLK_1 | \ + AO_SPI_DIPO_MISO_2) -#define AO_SPI_CONFIG_NONE 0x0c +/* + * PB10 SERCOM4.2 -> MOSI (DOPO 1) + * PB11 SERCOM4.3 -> SCLK (DOPO 1) + * PA12 SERCOM4.0 -> MISO (DIPO 0) + */ +#define AO_SPI_4_CONFIG_PB10_PB11_PA12 (AO_SPI_CONFIG_0 | \ + AO_SPI_DOPO_MOSI_2_SCLK_3 | \ + AO_SPI_DIPO_MISO_0) -#define AO_SPI_INDEX_MASK 0x07 -#define AO_SPI_CONFIG_MASK 0x18 +/* + * PB22 SERCOM5.2 -> MOSI (DOPO 1) + * PB23 SERCOM5.3 -> SCLK (DOPO 1) + * PB03 SERCOM5.1 -> MISO (DIPO 1) + */ +#define AO_SPI_5_CONFIG_PB22_PB23_PB03 (AO_SPI_CONFIG_3 | \ + AO_SPI_DOPO_MOSI_2_SCLK_3 | \ + AO_SPI_DIPO_MISO_1) -#define AO_SPI_INDEX(id) ((id) & AO_SPI_INDEX_MASK) +#define AO_SPI_INDEX(id) ((uint8_t) ((id) & AO_SPI_INDEX_MASK)) #define AO_SPI_CONFIG(id) ((id) & AO_SPI_CONFIG_MASK) #define AO_SPI_PIN_CONFIG(id) ((id) & (AO_SPI_INDEX_MASK | AO_SPI_CONFIG_MASK)) #define AO_SPI_CPOL(id) ((uint32_t) (((id) >> AO_SPI_CPOL_BIT) & 1)) #define AO_SPI_CPHA(id) ((uint32_t) (((id) >> AO_SPI_CPHA_BIT) & 1)) +#define AO_SPI_DOPO(id) ((uint32_t) (((id) >> AO_SPI_DOPO_BIT) & 3)) +#define AO_SPI_DIPO(id) ((uint32_t) (((id) >> AO_SPI_DIPO_BIT) & 3)) /* * We're not going to do any fancy SPI pin remapping, just use the first @@ -167,46 +209,58 @@ ao_enable_cs(struct samd21_port *port, uint8_t pin) #define AO_SPI_0_PA08_PA09_PA10 (0 | AO_SPI_0_CONFIG_PA08_PA09_PA10) #define AO_SPI_0_PA04_PA05_PA06 (0 | AO_SPI_0_CONFIG_PA04_PA05_PA06) +#define AO_SPI_4_PB10_PB11_PA12 (4 | AO_SPI_4_CONFIG_PB10_PB11_PA12) + +#define AO_SPI_5_PB22_PB23_PB03 (5 | AO_SPI_5_CONFIG_PB22_PB23_PB03) + void -ao_spi_send(const void *block, uint16_t len, uint8_t spi_index); +ao_spi_send(const void *block, uint16_t len, uint16_t spi_index); void -ao_spi_recv(void *block, uint16_t len, uint8_t spi_index); +ao_spi_recv(void *block, uint16_t len, uint16_t spi_index); void -ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index); +ao_spi_duplex(const void *out, void *in, uint16_t len, uint16_t spi_index); void -ao_spi_get(uint8_t spi_index, uint32_t speed); +ao_spi_get(uint16_t spi_index, uint32_t speed); void -ao_spi_put(uint8_t spi_index); +ao_spi_put(uint16_t spi_index); void ao_spi_init(void); -#define ao_spi_get_mask(reg,mask,bus, speed) do { \ - ao_spi_get(bus, speed); \ +#define ao_spi_set_cs(reg,mask) do { \ + reg->outclr = mask; \ + } while(0) + +#define ao_spi_clr_cs(reg,mask) do { \ + reg->outset = mask; \ + } while(0) + +#define ao_spi_get_mask(reg,mask,spi_index, speed) do { \ + ao_spi_get(spi_index, speed); \ ao_spi_set_cs(reg,mask); \ } while (0) -#define ao_spi_put_mask(reg,mask,bus) do { \ +#define ao_spi_put_mask(reg,mask,spi_index) do { \ ao_spi_clr_cs(reg,mask); \ - ao_spi_put(bus); \ + ao_spi_put(spi_index); \ } while (0) static inline void -ao_spi_get_bit(struct samd21_port *port, uint8_t bit, uint8_t bus, uint32_t speed) +ao_spi_get_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index, uint32_t speed) { - ao_spi_get(bus, speed); + ao_spi_get(spi_index, speed); ao_gpio_set(port, bit, 0); } static inline void -ao_spi_put_bit(struct samd21_port *port, uint8_t bit, uint8_t bus) +ao_spi_put_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index) { ao_gpio_set(port, bit, 1); - ao_spi_put(bus); + ao_spi_put(spi_index); } static inline uint8_t diff --git a/src/samd21/ao_spi_samd21.c b/src/samd21/ao_spi_samd21.c index 6e49884b..f6242649 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,146 @@ 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; - 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_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_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 +354,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 +376,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 +385,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 +437,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 diff --git a/src/samd21/samd21.h b/src/samd21/samd21.h index f65c117f..cd9d2d34 100644 --- a/src/samd21/samd21.h +++ b/src/samd21/samd21.h @@ -1467,7 +1467,19 @@ extern struct samd21_sercom samd21_sercom5; /* SPI controller mode */ #define SAMD21_SERCOM_CTRLA_DOPO 16 +#define SAMD21_SERCOM_CTRLA_DOPO_MOSI_0_SCLK_1 0UL +#define SAMD21_SERCOM_CTRLA_DOPO_MOSI_2_SCLK_3 1UL +#define SAMD21_SERCOM_CTRLA_DOPO_MOSI_3_SCLK_1 2UL +#define SAMD21_SERCOM_CTRLA_DOPO_MOSI_0_SCLK_3 3UL +#define SAMD21_SERCOM_CTRLA_DOPO_MASK 3UL + #define SAMD21_SERCOM_CTRLA_DIPO 20 +#define SAMD21_SERCOM_CTRLA_DIPO_MISO_0 0UL +#define SAMD21_SERCOM_CTRLA_DIPO_MISO_1 1UL +#define SAMD21_SERCOM_CTRLA_DIPO_MISO_2 2UL +#define SAMD21_SERCOM_CTRLA_DIPO_MISO_3 3UL +#define SAMD21_SERCOM_CTRLA_DIPO_MASK 3UL + #define SAMD21_SERCOM_CTRLA_FORM 24 #define SAMD21_SERCOM_CTRLA_CPHA 28 #define SAMD21_SERCOM_CTRLA_CPOL 29 -- 2.30.2