From a080a564b9b54e6b3495d30703c45ba2850b1703 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 8 Mar 2023 15:53:58 -0800 Subject: [PATCH] altos/stm32f1: Add DMA and SPI drivers These came from the code for the stm32l15 chips Signed-off-by: Keith Packard --- src/stm32f1/ao_arch_funcs.h | 203 ++++++++++++++ src/stm32f1/ao_dma_stm.c | 202 ++++++++++++++ src/stm32f1/ao_spi_stm.c | 543 ++++++++++++++++++++++++++++++++++++ src/stm32f1/stm32f1.h | 204 +++++++++++++- 4 files changed, 1151 insertions(+), 1 deletion(-) create mode 100644 src/stm32f1/ao_dma_stm.c create mode 100644 src/stm32f1/ao_spi_stm.c diff --git a/src/stm32f1/ao_arch_funcs.h b/src/stm32f1/ao_arch_funcs.h index 502fb140..ad9ab2a6 100644 --- a/src/stm32f1/ao_arch_funcs.h +++ b/src/stm32f1/ao_arch_funcs.h @@ -104,6 +104,12 @@ ao_enable_input(struct stm_gpio *port, int bit, int mode) ao_gpio_set_mode(port, bit, mode); } +static inline void +ao_enable_cs(struct stm_gpio *port, int bit) +{ + ao_enable_output(port, bit, 1); +} + #if USE_SERIAL_1_SW_FLOW || USE_SERIAL_2_SW_FLOW || USE_SERIAL_3_SW_FLOW #define HAS_SERIAL_SW_FLOW 1 #else @@ -332,4 +338,201 @@ ao_arch_wait_interrupt(void) { (stm_scb.aircr = ((STM_SCB_AIRCR_VECTKEY_KEY << STM_SCB_AIRCR_VECTKEY) | \ (1 << STM_SCB_AIRCR_SYSRESETREQ))) +/* ao_dma_stm.c + */ + +extern uint8_t ao_dma_done[STM_NUM_DMA]; + +void +ao_dma_set_transfer(uint8_t index, + volatile void *peripheral, + void *memory, + uint16_t count, + uint32_t ccr); + +void +ao_dma_set_isr(uint8_t index, void (*isr)(int index)); + +void +ao_dma_start(uint8_t index); + +void +ao_dma_done_transfer(uint8_t index); + +void +ao_dma_alloc(uint8_t index); + +void +ao_dma_init(void); + +/* ao_spi_stm.c + */ + +/* PCLK is set to 16MHz (HCLK 32MHz, APB prescaler 2) */ + +#define _AO_SPI_SPEED_8MHz STM_SPI_CR1_BR_PCLK_2 +#define _AO_SPI_SPEED_4MHz STM_SPI_CR1_BR_PCLK_4 +#define _AO_SPI_SPEED_2MHz STM_SPI_CR1_BR_PCLK_8 +#define _AO_SPI_SPEED_1MHz STM_SPI_CR1_BR_PCLK_16 +#define _AO_SPI_SPEED_500kHz STM_SPI_CR1_BR_PCLK_32 +#define _AO_SPI_SPEED_250kHz STM_SPI_CR1_BR_PCLK_64 +#define _AO_SPI_SPEED_125kHz STM_SPI_CR1_BR_PCLK_128 +#define _AO_SPI_SPEED_62500Hz STM_SPI_CR1_BR_PCLK_256 + +static inline uint32_t +ao_spi_speed(uint32_t hz) +{ + if (hz >= 8000000) return _AO_SPI_SPEED_8MHz; + if (hz >= 4000000) return _AO_SPI_SPEED_4MHz; + if (hz >= 2000000) return _AO_SPI_SPEED_2MHz; + if (hz >= 1000000) return _AO_SPI_SPEED_1MHz; + if (hz >= 500000) return _AO_SPI_SPEED_500kHz; + if (hz >= 250000) return _AO_SPI_SPEED_250kHz; + if (hz >= 125000) return _AO_SPI_SPEED_125kHz; + return _AO_SPI_SPEED_62500Hz; +} + +#define AO_SPI_CPOL_BIT 4 +#define AO_SPI_CPHA_BIT 5 + +#define AO_SPI_CONFIG_1 0x00 +#define AO_SPI_1_CONFIG_PA5_PA6_PA7 AO_SPI_CONFIG_1 +#define AO_SPI_2_CONFIG_PB13_PB14_PB15 AO_SPI_CONFIG_1 + +#define AO_SPI_CONFIG_2 0x04 +#define AO_SPI_1_CONFIG_PB3_PB4_PB5 AO_SPI_CONFIG_2 +#define AO_SPI_2_CONFIG_PD1_PD3_PD4 AO_SPI_CONFIG_2 + +#define AO_SPI_CONFIG_3 0x08 +#define AO_SPI_1_CONFIG_PE13_PE14_PE15 AO_SPI_CONFIG_3 + +#define AO_SPI_CONFIG_NONE 0x0c + +#define AO_SPI_INDEX_MASK 0x01 +#define AO_SPI_CONFIG_MASK 0x0c + +#define AO_SPI_1_PA5_PA6_PA7 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PA5_PA6_PA7) +#define AO_SPI_1_PB3_PB4_PB5 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PB3_PB4_PB5) +#define AO_SPI_1_PE13_PE14_PE15 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PE13_PE14_PE15) + +#define AO_SPI_2_PB13_PB14_PB15 (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PB13_PB14_PB15) +#define AO_SPI_2_PD1_PD3_PD4 (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PD1_PD3_PD4) + +#define AO_SPI_INDEX(id) ((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_MAKE_MODE(pol,pha) (((pol) << AO_SPI_CPOL_BIT) | ((pha) << AO_SPI_CPHA_BIT)) +#define AO_SPI_MODE_0 AO_SPI_MAKE_MODE(0,0) +#define AO_SPI_MODE_1 AO_SPI_MAKE_MODE(0,1) +#define AO_SPI_MODE_2 AO_SPI_MAKE_MODE(1,0) +#define AO_SPI_MODE_3 AO_SPI_MAKE_MODE(1,1) + +uint8_t +ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id); + +void +ao_spi_get(uint8_t spi_index, uint32_t speed); + +void +ao_spi_put(uint8_t spi_index); + +void +ao_spi_put_pins(uint8_t spi_index); + +void +ao_spi_send(const void *block, uint16_t len, uint8_t spi_index); + +void +ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index); + +void +ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index); + +void +ao_spi_start_bytes(uint8_t spi_index); + +void +ao_spi_stop_bytes(uint8_t spi_index); + +static inline void +ao_spi_send_byte(uint8_t byte, uint8_t spi_index) +{ + struct stm_spi *stm_spi; + + switch (AO_SPI_INDEX(spi_index)) { + case 0: + stm_spi = &stm_spi1; + break; + case 1: + stm_spi = &stm_spi2; + break; + } + + while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE))) + ; + stm_spi->dr = byte; + while (!(stm_spi->sr & (1 << STM_SPI_SR_RXNE))) + ; + (void) stm_spi->dr; +} + +static inline uint8_t +ao_spi_recv_byte(uint8_t spi_index) +{ + struct stm_spi *stm_spi; + + switch (AO_SPI_INDEX(spi_index)) { + case 0: + stm_spi = &stm_spi1; + break; + case 1: + stm_spi = &stm_spi2; + break; + } + + while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE))) + ; + stm_spi->dr = 0xff; + while (!(stm_spi->sr & (1 << STM_SPI_SR_RXNE))) + ; + return (uint8_t) stm_spi->dr; +} + +void +ao_spi_recv(void *block, uint16_t len, uint8_t spi_index); + +void +ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index); + +void +ao_spi_init(void); + +#define ao_spi_set_cs(reg,mask) ((reg)->bsrr = ((uint32_t) (mask)) << 16) +#define ao_spi_clr_cs(reg,mask) ((reg)->bsrr = (mask)) + +#define ao_spi_get_mask(reg,mask,bus, speed) do { \ + ao_spi_get(bus, speed); \ + ao_spi_set_cs(reg,mask); \ + } while (0) + +static inline uint8_t +ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t speed, uint8_t task_id) +{ + if (!ao_spi_try_get(bus, speed, task_id)) + return 0; + ao_spi_set_cs(reg, mask); + return 1; +} + +#define ao_spi_put_mask(reg,mask,bus) do { \ + ao_spi_clr_cs(reg,mask); \ + ao_spi_put(bus); \ + } while (0) + +#define ao_spi_get_bit(reg,bit,bus,speed) ao_spi_get_mask(reg,1<<(bit),bus,speed) +#define ao_spi_put_bit(reg,bit,bus) ao_spi_put_mask(reg,1<<(bit),bus) + #endif /* _AO_ARCH_FUNCS_H_ */ diff --git a/src/stm32f1/ao_dma_stm.c b/src/stm32f1/ao_dma_stm.c new file mode 100644 index 00000000..d3162d5b --- /dev/null +++ b/src/stm32f1/ao_dma_stm.c @@ -0,0 +1,202 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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; 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +#define NUM_DMA 7 + +struct ao_dma_config { + void (*isr)(int index); +}; + +uint8_t ao_dma_done[NUM_DMA]; + +static struct ao_dma_config ao_dma_config[NUM_DMA]; +static uint8_t ao_dma_allocated[NUM_DMA]; +static uint8_t ao_dma_mutex[NUM_DMA]; + +static void +ao_dma_isr(uint8_t index) { + /* Get channel interrupt bits */ + uint32_t isr = stm_dma.isr & (STM_DMA_ISR_MASK << + STM_DMA_ISR(index)); + + /* Ack them */ + stm_dma.ifcr = isr; + if (ao_dma_config[index].isr) + (*ao_dma_config[index].isr)(index); + else { + ao_dma_done[index] = 1; + ao_wakeup(&ao_dma_done[index]); + } +} + +void stm_dma1_channel1_isr(void) { ao_dma_isr(STM_DMA_INDEX(1)); } +void stm_dma1_channel2_isr(void) { ao_dma_isr(STM_DMA_INDEX(2)); } +#ifdef STM_DMA1_3_STOLEN +#define LEAVE_DMA_ON +#else +void stm_dma1_channel3_isr(void) { ao_dma_isr(STM_DMA_INDEX(3)); } +#endif +void stm_dma1_channel4_isr(void) { ao_dma_isr(STM_DMA_INDEX(4)); } +#ifdef STM_DMA1_5_STOLEN +#define LEAVE_DMA_ON +#else +void stm_dma1_channel5_isr(void) { ao_dma_isr(STM_DMA_INDEX(5)); } +#endif +void stm_dma1_channel6_isr(void) { ao_dma_isr(STM_DMA_INDEX(6)); } +void stm_dma1_channel7_isr(void) { ao_dma_isr(STM_DMA_INDEX(7)); } + +#ifndef LEAVE_DMA_ON +static uint8_t ao_dma_active; +#endif + +void +ao_dma_set_transfer(uint8_t index, + volatile void *peripheral, + void *memory, + uint16_t count, + uint32_t ccr) +{ + if (ao_dma_allocated[index]) { + if (ao_dma_mutex[index]) + ao_panic(AO_PANIC_DMA); + ao_dma_mutex[index] = 0xff; + } else + ao_mutex_get(&ao_dma_mutex[index]); +#ifndef LEAVE_DMA_ON + ao_arch_critical( + if (ao_dma_active++ == 0) + stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN); + ); +#endif + stm_dma.channel[index].ccr = ccr | (1 << STM_DMA_CCR_TCIE); + stm_dma.channel[index].cndtr = count; + stm_dma.channel[index].cpar = peripheral; + stm_dma.channel[index].cmar = memory; + ao_dma_config[index].isr = NULL; +} + +void +ao_dma_set_isr(uint8_t index, void (*isr)(int)) +{ + ao_dma_config[index].isr = isr; +} + +void +ao_dma_start(uint8_t index) +{ + ao_dma_done[index] = 0; + stm_dma.channel[index].ccr |= (1UL << STM_DMA_CCR_EN); +} + +void +ao_dma_done_transfer(uint8_t index) +{ + stm_dma.channel[index].ccr &= ~(1UL << STM_DMA_CCR_EN); +#ifndef LEAVE_DMA_ON + ao_arch_critical( + if (--ao_dma_active == 0) + stm_rcc.ahbenr &= ~(1UL << STM_RCC_AHBENR_DMA1EN); + ); +#endif + if (ao_dma_allocated[index]) + ao_dma_mutex[index] = 0; + else + ao_mutex_put(&ao_dma_mutex[index]); +} + +void +ao_dma_alloc(uint8_t index) +{ + if (ao_dma_allocated[index]) + ao_panic(AO_PANIC_DMA); + ao_dma_allocated[index] = 1; +} + +#if DEBUG +void +ao_dma_dump_cmd(void) +{ + int i; + +#ifndef LEAVE_DMA_ON + ao_arch_critical( + if (ao_dma_active++ == 0) + stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN); + ); +#endif + printf ("isr %08x ifcr%08x\n", stm_dma.isr, stm_dma.ifcr); + for (i = 0; i < NUM_DMA; i++) + printf("%d: done %d allocated %d mutex %2d ccr %04x cndtr %04x cpar %08x cmar %08x isr %08x\n", + i, + ao_dma_done[i], + ao_dma_allocated[i], + ao_dma_mutex[i], + stm_dma.channel[i].ccr, + stm_dma.channel[i].cndtr, + stm_dma.channel[i].cpar, + stm_dma.channel[i].cmar, + ao_dma_config[i].isr); +#ifndef LEAVE_DMA_ON + ao_arch_critical( + if (--ao_dma_active == 0) + stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMA1EN); + ); +#endif +} + +static const struct ao_cmds ao_dma_cmds[] = { + { ao_dma_dump_cmd, "D\0Dump DMA status" }, + { 0, NULL } +}; +#endif + +void +ao_dma_init(void) +{ + int index; + +#ifdef LEAVE_DMA_ON + stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN); +#endif + for (index = 0; index < STM_NUM_DMA; index++) { +#if STM_DMA1_5_STOLEN + if (index == STM_DMA_INDEX(5)) { + ao_dma_allocated[index] = 1; + ao_dma_mutex[index] = 0xff; + continue; + } +#endif +#if STM_DMA1_3_STOLEN + if (index == STM_DMA_INDEX(3)) { + ao_dma_allocated[index] = 1; + ao_dma_mutex[index] = 0xff; + continue; + } +#endif + stm_nvic_set_enable(STM_ISR_DMA1_CHANNEL1_POS + index); + stm_nvic_set_priority(STM_ISR_DMA1_CHANNEL1_POS + index, + AO_STM_NVIC_MED_PRIORITY); + ao_dma_allocated[index] = 0; + ao_dma_mutex[index] = 0; + } +#if DEBUG + ao_cmd_register(&ao_dma_cmds[0]); +#endif +} diff --git a/src/stm32f1/ao_spi_stm.c b/src/stm32f1/ao_spi_stm.c new file mode 100644 index 00000000..0ec0110d --- /dev/null +++ b/src/stm32f1/ao_spi_stm.c @@ -0,0 +1,543 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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; 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include + +struct ao_spi_stm_info { + uint8_t miso_dma_index; + uint8_t mosi_dma_index; + struct stm_spi *stm_spi; +}; + +static uint8_t ao_spi_mutex[STM_NUM_SPI]; +static uint8_t ao_spi_pin_config[STM_NUM_SPI]; + +static const struct ao_spi_stm_info ao_spi_stm_info[STM_NUM_SPI] = { + { + .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_RX), + .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX), + &stm_spi1 + }, + { + .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_RX), + .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_TX), + &stm_spi2 + } +}; + +static uint8_t spi_dev_null; + +#if DEBUG +static struct { + uint8_t task; + uint8_t which; + AO_TICK_TYPE tick; + uint16_t len; +} spi_tasks[64]; +static uint8_t spi_task_index; + +static void +validate_spi(struct stm_spi *stm_spi, int which, uint16_t len) +{ + uint32_t sr = stm_spi->sr; + + if (stm_spi != &stm_spi2) + return; + spi_tasks[spi_task_index].task = ao_cur_task ? ao_cur_task->task_id : 0; + spi_tasks[spi_task_index].which = which; + spi_tasks[spi_task_index].tick = ao_time(); + spi_tasks[spi_task_index].len = len; + spi_task_index = (spi_task_index + 1) & (63); + if (sr & (1 << STM_SPI_SR_FRE)) + ao_panic(0x40 | 1); + if (sr & (1 << STM_SPI_SR_BSY)) + ao_panic(0x40 | 2); + if (sr & (1 << STM_SPI_SR_OVR)) + ao_panic(0x40 | 3); + if (sr & (1 << STM_SPI_SR_MODF)) + ao_panic(0x40 | 4); + if (sr & (1 << STM_SPI_SR_UDR)) + ao_panic(0x40 | 5); + if ((sr & (1 << STM_SPI_SR_TXE)) == 0) + ao_panic(0x40 | 6); + if (sr & (1 << STM_SPI_SR_RXNE)) + ao_panic(0x40 | 7); + if (which != 5 && which != 6 && which != 13) + if (ao_cur_task->task_id != ao_spi_mutex[1]) + ao_panic(0x40 | 8); +} +#else +#define validate_spi(stm_spi, which, len) do { (void) (which); (void) (len); } while (0) +#endif + +static void +ao_spi_set_dma_mosi(uint8_t id, const void *data, uint16_t len, uint32_t minc) +{ + struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi; + uint8_t mosi_dma_index = ao_spi_stm_info[id].mosi_dma_index; + + ao_dma_set_transfer(mosi_dma_index, + &stm_spi->dr, + (void *) data, + len, + (0 << STM_DMA_CCR_MEM2MEM) | + (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) | + (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) | + (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) | + (minc << STM_DMA_CCR_MINC) | + (0 << STM_DMA_CCR_PINC) | + (0 << STM_DMA_CCR_CIRC) | + (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR)); +} + +static void +ao_spi_set_dma_miso(uint8_t id, void *data, uint16_t len, uint32_t minc) +{ + struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi; + uint8_t miso_dma_index = ao_spi_stm_info[id].miso_dma_index; + + ao_dma_set_transfer(miso_dma_index, + &stm_spi->dr, + data, + len, + (0 << STM_DMA_CCR_MEM2MEM) | + (STM_DMA_CCR_PL_VERY_HIGH << STM_DMA_CCR_PL) | + (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) | + (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) | + (minc << STM_DMA_CCR_MINC) | + (0 << STM_DMA_CCR_PINC) | + (0 << STM_DMA_CCR_CIRC) | + (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR)); +} + +static void +ao_spi_run(uint8_t id, uint8_t which, uint16_t len) +{ + struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi; + uint8_t mosi_dma_index = ao_spi_stm_info[id].mosi_dma_index; + uint8_t miso_dma_index = ao_spi_stm_info[id].miso_dma_index; + + validate_spi(stm_spi, which, len); + + stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) | + (0 << STM_SPI_CR2_RXNEIE) | + (0 << STM_SPI_CR2_ERRIE) | + (0 << STM_SPI_CR2_SSOE) | + (1 << STM_SPI_CR2_TXDMAEN) | + (1 << STM_SPI_CR2_RXDMAEN)); + + ao_dma_start(miso_dma_index); + ao_dma_start(mosi_dma_index); + + ao_arch_critical( + while (!ao_dma_done[miso_dma_index]) + ao_sleep(&ao_dma_done[miso_dma_index]); + ); + + while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0); + while (stm_spi->sr & (1 << STM_SPI_SR_BSY)); + + validate_spi(stm_spi, which+1, len); + + stm_spi->cr2 = 0; + + ao_dma_done_transfer(mosi_dma_index); + ao_dma_done_transfer(miso_dma_index); +} + +void +ao_spi_send(const void *block, uint16_t len, uint8_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + + /* Set up the transmit DMA to deliver data */ + ao_spi_set_dma_mosi(id, block, len, 1); + + /* Set up the receive DMA -- when this is done, we know the SPI unit + * is idle. Without this, we'd have to poll waiting for the BSY bit to + * be cleared + */ + ao_spi_set_dma_miso(id, &spi_dev_null, len, 0); + + ao_spi_run(id, 1, len); +} + +void +ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + + /* Set up the transmit DMA to deliver data */ + ao_spi_set_dma_mosi(id, &value, len, 0); + + /* Set up the receive DMA -- when this is done, we know the SPI unit + * is idle. Without this, we'd have to poll waiting for the BSY bit to + * be cleared + */ + ao_spi_set_dma_miso(id, &spi_dev_null, len, 0); + + ao_spi_run(id, 3, len); +} + +void +ao_spi_start_bytes(uint8_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi; + + stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) | + (0 << STM_SPI_CR2_RXNEIE) | + (0 << STM_SPI_CR2_ERRIE) | + (0 << STM_SPI_CR2_SSOE) | + (0 << STM_SPI_CR2_TXDMAEN) | + (0 << STM_SPI_CR2_RXDMAEN)); + validate_spi(stm_spi, 5, 0xffff); +} + +void +ao_spi_stop_bytes(uint8_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi; + + while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0) + ; + while (stm_spi->sr & (1 << STM_SPI_SR_BSY)) + ; + /* Clear the OVR flag */ + (void) stm_spi->dr; + (void) stm_spi->sr; + validate_spi(stm_spi, 6, 0xffff); + stm_spi->cr2 = 0; +} + +void +ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + const uint8_t *b = block; + struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi; + + stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) | + (0 << STM_SPI_CR2_RXNEIE) | + (0 << STM_SPI_CR2_ERRIE) | + (0 << STM_SPI_CR2_SSOE) | + (0 << STM_SPI_CR2_TXDMAEN) | + (0 << STM_SPI_CR2_RXDMAEN)); + validate_spi(stm_spi, 7, len); + while (len--) { + while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE))); + stm_spi->dr = *b++; + } + while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0) + ; + while (stm_spi->sr & (1 << STM_SPI_SR_BSY)) + ; + /* Clear the OVR flag */ + (void) stm_spi->dr; + (void) stm_spi->sr; + validate_spi(stm_spi, 8, len); +} + +void +ao_spi_recv(void *block, uint16_t len, uint8_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + + spi_dev_null = 0xff; + + /* Set up transmit DMA to make the SPI hardware actually run */ + ao_spi_set_dma_mosi(id, &spi_dev_null, len, 0); + + /* Set up the receive DMA to capture data */ + ao_spi_set_dma_miso(id, block, len, 1); + + ao_spi_run(id, 9, len); +} + +void +ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + + /* Set up transmit DMA to send data */ + ao_spi_set_dma_mosi(id, out, len, 1); + + /* Set up the receive DMA to capture data */ + ao_spi_set_dma_miso(id, in, len, 1); + + ao_spi_run(id, 11, len); +} + +#define stm_spi_input_disable(gpio, pin) do { \ + stm_gpio_conf(gpio, pin, \ + STM_GPIO_CR_MODE_INPUT, \ + STM_GPIO_CR_CNF_INPUT_FLOATING); \ + } while(0) + +#define stm_spi_output_disable(gpio, pin, mode) do { \ + ao_gpio_set(gpio, pin, 1); \ + stm_gpio_conf(gpio, pin, \ + mode, \ + STM_GPIO_CR_CNF_OUTPUT_PUSH_PULL); \ + } while(0) + +static void +ao_spi_disable_pin_config(uint8_t spi_pin_config) +{ + /* Disable current config + */ + switch (spi_pin_config) { +#if SPI_1_PA5_PA6_PA7 + case AO_SPI_1_PA5_PA6_PA7: + stm_spi_output_disable(&stm_gpioa, 5, SPI_1_MODE_OUTPUT); + stm_spi_input_disable(&stm_gpioa, 6); + stm_spi_output_disable(&stm_gpioa, 7, SPI_1_MODE_OUTPUT); + break; +#endif +#if SPI_1_PB3_PB4_PB5 + case AO_SPI_1_PB3_PB4_PB5: + stm_spi_output_disable(&stm_gpiob, 3, SPI_1_MODE_OUTPUT); + stm_spi_input_disable(&stm_gpiob, 4); + stm_spi_output_disable(&stm_gpiob, 5, SPI_1_MODE_OUTPUT); + break; +#endif +#if SPI_2_PB13_PB14_PB15 + case AO_SPI_2_PB13_PB14_PB15: + stm_spi_output_disable(&stm_gpiob, 13, SPI_2_MODE_OUTPUT); + stm_spi_input_disable(&stm_gpiob, 14); + stm_spi_output_disable(&stm_gpiob, 15, SPI_2_MODE_OUTPUT); + break; +#endif + } +} + +#define stm_spi_input_enable(gpio, pin) do { \ + stm_gpio_conf(gpio, pin, \ + STM_GPIO_CR_MODE_INPUT, \ + STM_GPIO_CR_CNF_INPUT_FLOATING); \ + } while(0) + +#define stm_spi_output_enable(gpio, pin, mode) do { \ + stm_gpio_conf(gpio, pin, \ + mode, \ + STM_GPIO_CR_CNF_OUTPUT_AF_PUSH_PULL); \ + } while(0) + +static void +ao_spi_enable_pin_config(uint8_t spi_pin_config) +{ + /* Enable new config + */ + switch (spi_pin_config) { +#if SPI_1_PA5_PA6_PA7 + case AO_SPI_1_PA5_PA6_PA7: + stm_spi_output_enable(&stm_gpioa, 5, SPI_1_MODE_OUTPUT); + stm_spi_input_enable(&stm_gpioa, 6); + stm_spi_output_enable(&stm_gpioa, 7, SPI_1_MODE_OUTPUT); + break; +#endif +#if SPI_1_PB3_PB4_PB5 + case AO_SPI_1_PB3_PB4_PB5: + stm_spi_output_enable(&stm_gpiob, 3, SPI_1_MODE_OUTPUT); + stm_spi_input_enable(&stm_gpiob, 4); + stm_spi_output_enable(&stm_gpiob, 5, SPI_1_MODE_OUTPUT); + break; +#endif +#if SPI_2_PB13_PB14_PB15 + case AO_SPI_2_PB13_PB14_PB15: + stm_spi_output_enable(&stm_gpiob, 13, SPI_2_MODE_OUTPUT); + stm_spi_input_enable(&stm_gpiob, 14); + stm_spi_output_enable(&stm_gpiob, 15, SPI_2_MODE_OUTPUT); + break; +#endif + } +} + +static void +ao_spi_config(uint8_t spi_index, uint32_t speed) +{ + uint8_t spi_pin_config = AO_SPI_PIN_CONFIG(spi_index); + uint8_t id = AO_SPI_INDEX(spi_index); + struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi; + + if (spi_pin_config != ao_spi_pin_config[id]) { + + /* Disable old config + */ + ao_spi_disable_pin_config(ao_spi_pin_config[id]); + + /* Enable new config + */ + ao_spi_enable_pin_config(spi_pin_config); + + /* Remember current config + */ + ao_spi_pin_config[id] = spi_pin_config; + } + + /* Turn the SPI transceiver on and set the mode */ + stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) | /* Three wire mode */ + (0 << STM_SPI_CR1_BIDIOE) | + (0 << STM_SPI_CR1_CRCEN) | /* CRC disabled */ + (0 << STM_SPI_CR1_CRCNEXT) | + (0 << STM_SPI_CR1_DFF) | + (0 << STM_SPI_CR1_RXONLY) | + (1 << STM_SPI_CR1_SSM) | /* Software SS handling */ + (1 << STM_SPI_CR1_SSI) | /* ... */ + (0 << STM_SPI_CR1_LSBFIRST) | /* Big endian */ + (1 << STM_SPI_CR1_SPE) | /* Enable SPI unit */ + (speed << STM_SPI_CR1_BR) | /* baud rate to pclk/4 */ + (1 << STM_SPI_CR1_MSTR) | + (AO_SPI_CPOL(spi_index) << STM_SPI_CR1_CPOL) | /* Format */ + (AO_SPI_CPHA(spi_index) << STM_SPI_CR1_CPHA)); + validate_spi(stm_spi, 13, 0); +} + +uint8_t +ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + + if (!ao_mutex_try(&ao_spi_mutex[id], task_id)) + return 0; + ao_spi_config(spi_index, speed); + return 1; +} + +void +ao_spi_get(uint8_t spi_index, uint32_t speed) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + + ao_mutex_get(&ao_spi_mutex[id]); + ao_spi_config(spi_index, speed); +} + +void +ao_spi_put(uint8_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi; + + stm_spi->cr1 = 0; + ao_mutex_put(&ao_spi_mutex[id]); +} + +void +ao_spi_put_pins(uint8_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + + ao_spi_disable_pin_config(ao_spi_pin_config[id]); + ao_spi_pin_config[id] = AO_SPI_CONFIG_NONE; + ao_spi_put(spi_index); +} + +static void +ao_spi_channel_init(uint8_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi; + + ao_spi_disable_pin_config(AO_SPI_PIN_CONFIG(spi_index)); + + stm_spi->cr1 = 0; + stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) | + (0 << STM_SPI_CR2_RXNEIE) | + (0 << STM_SPI_CR2_ERRIE) | + (0 << STM_SPI_CR2_SSOE) | + (0 << STM_SPI_CR2_TXDMAEN) | + (0 << STM_SPI_CR2_RXDMAEN)); + + /* Clear any pending data and error flags */ + (void) stm_spi->dr; + (void) stm_spi->sr; +} + +#if DEBUG +void +ao_spi_dump_cmd(void) +{ + int s; + + for (s = 0; s < 64; s++) { + int i = (spi_task_index + s) & 63; + if (spi_tasks[i].which) { + int t; + const char *name = "(none)"; + for (t = 0; t < ao_num_tasks; t++) + if (ao_tasks[t]->task_id == spi_tasks[i].task) { + name = ao_tasks[t]->name; + break; + } + printf("%2d: %5d task %2d which %2d len %5d %s\n", + s, + spi_tasks[i].tick, + spi_tasks[i].task, + spi_tasks[i].which, + spi_tasks[i].len, + name); + } + } + for (s = 0; s < STM_NUM_SPI; s++) { + struct stm_spi *spi = ao_spi_stm_info[s].stm_spi; + + printf("%1d: mutex %2d index %3d miso dma %3d mosi dma %3d", + s, ao_spi_mutex[s], ao_spi_index[s], + ao_spi_stm_info[s].miso_dma_index, + ao_spi_stm_info[s].mosi_dma_index); + printf(" cr1 %04x cr2 %02x sr %03x\n", + spi->cr1, spi->cr2, spi->sr); + } + +} + +static const struct ao_cmds ao_spi_cmds[] = { + { ao_spi_dump_cmd, "S\0Dump SPI status" }, + { 0, NULL } +}; +#endif + +void +ao_spi_init(void) +{ +#if HAS_SPI_1 +# if SPI_1_PA5_PA6_PA7 + ao_enable_port(&stm_gpioa); +# endif +# if SPI_1_PB3_PB4_PB5 + ao_enable_port(&stm_gpiob); +# endif + stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN); + ao_spi_pin_config[0] = AO_SPI_CONFIG_NONE; + ao_spi_channel_init(0); +#endif + +#if HAS_SPI_2 +# if SPI_2_PB13_PB14_PB15 + ao_enable_port(&stm_gpiob); +# endif + stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN); + ao_spi_pin_config[1] = AO_SPI_CONFIG_NONE; + ao_spi_channel_init(1); +#endif +#if DEBUG + ao_cmd_register(&ao_spi_cmds[0]); +#endif +} diff --git a/src/stm32f1/stm32f1.h b/src/stm32f1/stm32f1.h index ba6f4db3..cf7d0460 100644 --- a/src/stm32f1/stm32f1.h +++ b/src/stm32f1/stm32f1.h @@ -777,7 +777,209 @@ union stm_usb_bdt { extern uint8_t stm_usb_sram[] __attribute__ ((aligned(4))); -//#define stm_usb_sram ((uint8_t *)0x40006000); +//#define stm_usb_sram ((uint8_t *)0x40006000) + +struct stm_dma_channel { + vuint32_t ccr; + vuint32_t cndtr; + vvoid_t cpar; + vvoid_t cmar; + vuint32_t reserved; +}; + +#define STM_NUM_DMA 7 + +struct stm_dma { + vuint32_t isr; + vuint32_t ifcr; + struct stm_dma_channel channel[STM_NUM_DMA]; +}; + +extern struct stm_dma stm_dma; + +#define stm_dma (*((struct stm_dma *) 0x40020000)) + +/* DMA channels go from 1 to 7, instead of 0 to 6 (sigh) + */ + +#define STM_DMA_INDEX(channel) ((channel) - 1) + +#define STM_DMA_ISR(index) ((index) << 2) +#define STM_DMA_ISR_MASK 0xfUL +#define STM_DMA_ISR_TEIF 3 +#define STM_DMA_ISR_HTIF 2 +#define STM_DMA_ISR_TCIF 1 +#define STM_DMA_ISR_GIF 0 + +#define STM_DMA_IFCR(index) ((index) << 2) +#define STM_DMA_IFCR_MASK 0xfUL +#define STM_DMA_IFCR_CTEIF 3 +#define STM_DMA_IFCR_CHTIF 2 +#define STM_DMA_IFCR_CTCIF 1 +#define STM_DMA_IFCR_CGIF 0 + +#define STM_DMA_CCR_MEM2MEM (14) + +#define STM_DMA_CCR_PL (12) +#define STM_DMA_CCR_PL_LOW (0) +#define STM_DMA_CCR_PL_MEDIUM (1) +#define STM_DMA_CCR_PL_HIGH (2) +#define STM_DMA_CCR_PL_VERY_HIGH (3) +#define STM_DMA_CCR_PL_MASK (3) + +#define STM_DMA_CCR_MSIZE (10) +#define STM_DMA_CCR_MSIZE_8 (0) +#define STM_DMA_CCR_MSIZE_16 (1) +#define STM_DMA_CCR_MSIZE_32 (2) +#define STM_DMA_CCR_MSIZE_MASK (3) + +#define STM_DMA_CCR_PSIZE (8) +#define STM_DMA_CCR_PSIZE_8 (0) +#define STM_DMA_CCR_PSIZE_16 (1) +#define STM_DMA_CCR_PSIZE_32 (2) +#define STM_DMA_CCR_PSIZE_MASK (3) + +#define STM_DMA_CCR_MINC (7) +#define STM_DMA_CCR_PINC (6) +#define STM_DMA_CCR_CIRC (5) +#define STM_DMA_CCR_DIR (4) +#define STM_DMA_CCR_DIR_PER_TO_MEM 0 +#define STM_DMA_CCR_DIR_MEM_TO_PER 1 +#define STM_DMA_CCR_TEIE (3) +#define STM_DMA_CCR_HTIE (2) +#define STM_DMA_CCR_TCIE (1) +#define STM_DMA_CCR_EN (0) + +#define STM_DMA_CHANNEL_ADC1 1 +#define STM_DMA_CHANNEL_SPI1_RX 2 +#define STM_DMA_CHANNEL_SPI1_TX 3 +#define STM_DMA_CHANNEL_SPI2_RX 4 +#define STM_DMA_CHANNEL_SPI2_TX 5 +#define STM_DMA_CHANNEL_USART3_TX 2 +#define STM_DMA_CHANNEL_USART3_RX 3 +#define STM_DMA_CHANNEL_USART1_TX 4 +#define STM_DMA_CHANNEL_USART1_RX 5 +#define STM_DMA_CHANNEL_USART2_RX 6 +#define STM_DMA_CHANNEL_USART2_TX 7 +#define STM_DMA_CHANNEL_I2C2_TX 4 +#define STM_DMA_CHANNEL_I2C2_RX 5 +#define STM_DMA_CHANNEL_I2C1_TX 6 +#define STM_DMA_CHANNEL_I2C1_RX 7 +#define STM_DMA_CHANNEL_TIM1_CH1 2 +#define STM_DMA_CHANNEL_TIM1_CH4 4 +#define STM_DMA_CHANNEL_TIM1_TRIG 4 +#define STM_DMA_CHANNEL_TIM1_COM 4 +#define STM_DMA_CHANNEL_TIM1_UP 5 +#define STM_DMA_CHANNEL_TIM1_CH3 6 +#define STM_DMA_CHANNEL_TIM2_CH3 1 +#define STM_DMA_CHANNEL_TIM2_UP 2 +#define STM_DMA_CHANNEL_TIM2_CH1 5 +#define STM_DMA_CHANNEL_TIM2_CH2 7 +#define STM_DMA_CHANNEL_TIM2_CH4 7 +#define STM_DMA_CHANNEL_TIM3_CH3 2 +#define STM_DMA_CHANNEL_TIM3_CH4 3 +#define STM_DMA_CHANNEL_TIM3_UP 3 +#define STM_DMA_CHANNEL_TIM3_CH1 6 +#define STM_DMA_CHANNEL_TIM3_TRIG 6 +#define STM_DMA_CHANNEL_TIM4_CH1 1 +#define STM_DMA_CHANNEL_TIM4_CH2 4 +#define STM_DMA_CHANNEL_TIM4_CH3 5 +#define STM_DMA_CHANNEL_TIM4_UP 7 + +/* high density, xl-density and connectivity devices also have dma2 */ + +#define STM_DMA2_CHANNEL_ADC3 5 +#define STM_DMA2_CHANNEL_SPI3_RX 1 +#define STM_DMA2_CHANNEL_SPI3_TX 2 +#define STM_DMA2_CHANNEL_UART4_RX 3 +#define STM_DMA2_CHANNEL_UART4_TX 5 +#define STM_DMA2_CHANNEL_TIM5_CH4 1 +#define STM_DMA2_CHANNEL_TIM5_TRIG 1 +#define STM_DMA2_CHANNEL_TIM5_CH3 2 +#define STM_DMA2_CHANNEL_TIM5_UP 2 +#define STM_DMA2_CHANNEL_TIM5_CH2 4 +#define STM_DMA2_CHANNEL_TIM5_CH1 5 +#define STM_DMA2_CHANNEL_TIM6_UP 3 +#define STM_DMA2_CHANNEL_DAC_CHANNEL1 3 +#define STM_DMA2_CHANNEL_TIM7_UP 4 +#define STM_DMA2_CHANNEL_DAC_CHANNEL2 4 +#define STM_DMA2_CHANNEL_TIM8_CH3 1 +#define STM_DMA2_CHANNEL_TIM8_UP 1 +#define STM_DMA2_CHANNEL_TIM8_CH4 2 +#define STM_DMA2_CHANNEL_TIM8_TRIG 2 +#define STM_DMA2_CHANNEL_TIM8_COM 2 +#define STM_DMA2_CHANNEL_TIM8_CH1 3 +#define STM_DMA2_CHANNEL_TIM8_CH2 5 + +struct stm_spi { + vuint32_t cr1; + vuint32_t cr2; + vuint32_t sr; + vuint32_t dr; + + vuint32_t crcpr; + vuint32_t rxcrcr; + vuint32_t txcrcr; + vuint32_t i2scfgr; + + vuint32_t i2spr; +}; + +extern struct stm_spi stm_spi1, stm_spi2; + +//#define stm_spi1 (*((struct stm_spi *) 0x40013000)) +//#define stm_spi2 (*((struct stm_spi *) 0x40003800)) + +/* SPI channels go from 1 to 2, instead of 0 to 1 (sigh) + */ + +#define STM_NUM_SPI 2 + +#define STM_SPI_INDEX(channel) ((channel) - 1) + +#define STM_SPI_CR1_BIDIMODE 15 +#define STM_SPI_CR1_BIDIOE 14 +#define STM_SPI_CR1_CRCEN 13 +#define STM_SPI_CR1_CRCNEXT 12 +#define STM_SPI_CR1_DFF 11 +#define STM_SPI_CR1_RXONLY 10 +#define STM_SPI_CR1_SSM 9 +#define STM_SPI_CR1_SSI 8 +#define STM_SPI_CR1_LSBFIRST 7 +#define STM_SPI_CR1_SPE 6 +#define STM_SPI_CR1_BR 3 +#define STM_SPI_CR1_BR_PCLK_2 0 +#define STM_SPI_CR1_BR_PCLK_4 1 +#define STM_SPI_CR1_BR_PCLK_8 2 +#define STM_SPI_CR1_BR_PCLK_16 3 +#define STM_SPI_CR1_BR_PCLK_32 4 +#define STM_SPI_CR1_BR_PCLK_64 5 +#define STM_SPI_CR1_BR_PCLK_128 6 +#define STM_SPI_CR1_BR_PCLK_256 7 +#define STM_SPI_CR1_BR_MASK 7UL + +#define STM_SPI_CR1_MSTR 2 +#define STM_SPI_CR1_CPOL 1 +#define STM_SPI_CR1_CPHA 0 + +#define STM_SPI_CR2_TXEIE 7 +#define STM_SPI_CR2_RXNEIE 6 +#define STM_SPI_CR2_ERRIE 5 +#define STM_SPI_CR2_SSOE 2 +#define STM_SPI_CR2_TXDMAEN 1 +#define STM_SPI_CR2_RXDMAEN 0 + +#define STM_SPI_SR_FRE 8 +#define STM_SPI_SR_BSY 7 +#define STM_SPI_SR_OVR 6 +#define STM_SPI_SR_MODF 5 +#define STM_SPI_SR_CRCERR 4 +#define STM_SPI_SR_UDR 3 +#define STM_SPI_SR_CHSIDE 2 +#define STM_SPI_SR_TXE 1 +#define STM_SPI_SR_RXNE 0 + + #define isr_decl(name) void stm_ ## name ## _isr(void) -- 2.30.2