URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [devs/] [spi/] [cortexm/] [stm32/] [current/] [src/] [spi_stm32.c] - Rev 786
Compare with Previous | Blame | View Log
//============================================================================= // // spi_stm32.c // // SPI driver implementation for STM32 // //============================================================================= // ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 2008, 2009 Free Software Foundation, Inc. // // eCos 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 or (at your option) any later // version. // // eCos 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 eCos; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // As a special exception, if other files instantiate templates or use // macros or inline functions from this file, or you compile this file // and link it with other works to produce a work based on this file, // this file does not by itself cause the resulting work to be covered by // the GNU General Public License. However the source code for this file // must still be made available in accordance with section (3) of the GNU // General Public License v2. // // This exception does not invalidate any other reasons why a work based // on this file might be covered by the GNU General Public License. // ------------------------------------------- // ####ECOSGPLCOPYRIGHTEND#### //============================================================================= //#####DESCRIPTIONBEGIN#### // // Author(s): Chris Holgate // Date: 2008-11-27 // Purpose: STM32 SPI driver implementation // //####DESCRIPTIONEND#### // //============================================================================= #include <cyg/hal/hal_io.h> #include <cyg/hal/hal_if.h> #include <cyg/hal/hal_intr.h> #include <cyg/hal/drv_api.h> #include <cyg/infra/cyg_type.h> #include <cyg/infra/cyg_ass.h> #include <cyg/infra/diag.h> #include <cyg/io/spi.h> #include <cyg/io/spi_stm32.h> #include <pkgconf/devs_spi_cortexm_stm32.h> #include <string.h> //----------------------------------------------------------------------------- // Work out the bus clock frequencies. #define APB1_FREQ ((CYGARC_HAL_CORTEXM_STM32_INPUT_CLOCK * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PLL_MUL) / \ (CYGHWR_HAL_CORTEXM_STM32_CLOCK_HCLK_DIV * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PCLK1_DIV)) #define APB2_FREQ ((CYGARC_HAL_CORTEXM_STM32_INPUT_CLOCK * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PLL_MUL) / \ (CYGHWR_HAL_CORTEXM_STM32_CLOCK_HCLK_DIV * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PCLK2_DIV)) //----------------------------------------------------------------------------- // API function call forward references. static void stm32_transaction_begin (cyg_spi_device*); static void stm32_transaction_transfer (cyg_spi_device*, cyg_bool, cyg_uint32, const cyg_uint8*, cyg_uint8*, cyg_bool); static void stm32_transaction_tick (cyg_spi_device*, cyg_bool, cyg_uint32); static void stm32_transaction_end (cyg_spi_device*); static int stm32_get_config (cyg_spi_device*, cyg_uint32, void*, cyg_uint32*); static int stm32_set_config (cyg_spi_device*, cyg_uint32, const void*, cyg_uint32*); //----------------------------------------------------------------------------- // Null data source and sink must be placed in the on-chip SRAM. This is // either done explicitly (bounce buffers instantiated) or implicitly (no // bounce buffers implies that the data area is already on SRAM). #if (defined (CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1) && (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE > 0)) || \ (defined (CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2) && (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE > 0)) || \ (defined (CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3) && (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE > 0)) static cyg_uint16 dma_tx_null __attribute__((section (".sram"))) = 0xFFFF; static cyg_uint16 dma_rx_null __attribute__((section (".sram"))) = 0xFFFF; #else static cyg_uint16 dma_tx_null = 0xFFFF; static cyg_uint16 dma_rx_null = 0xFFFF; #endif //----------------------------------------------------------------------------- // Instantiate the bus state data structures. #ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1 static const cyg_uint8 bus1_cs_gpio_list[] = { CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1_CS_GPIOS }; static const cyg_uint8 bus1_spi_gpio_list[] = { 0x05, 0x06, 0x07 }; #if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE > 0) static cyg_uint8 bus1_tx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE] __attribute__((aligned (2), section (".sram"))) = { 0 }; static cyg_uint8 bus1_rx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE] __attribute__((aligned (2), section (".sram"))) = { 0 }; #endif static const cyg_spi_cortexm_stm32_bus_setup_t bus1_setup = { .apb_freq = APB2_FREQ, .spi_reg_base = CYGHWR_HAL_STM32_SPI1, .dma_reg_base = CYGHWR_HAL_STM32_DMA1, .dma_tx_channel = 3, .dma_rx_channel = 2, .cs_gpio_num = sizeof (bus1_cs_gpio_list), .cs_gpio_list = bus1_cs_gpio_list, .spi_gpio_list = bus1_spi_gpio_list, .dma_tx_intr = CYGNUM_HAL_INTERRUPT_DMA1_CH3, .dma_rx_intr = CYGNUM_HAL_INTERRUPT_DMA1_CH2, .dma_tx_intr_pri = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_TXINTR_PRI, .dma_rx_intr_pri = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_RXINTR_PRI, #if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE > 0) .bbuf_size = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE, .bbuf_tx = bus1_tx_bbuf, .bbuf_rx = bus1_rx_bbuf, #else .bbuf_size = 0, #endif }; cyg_spi_cortexm_stm32_bus_t cyg_spi_stm32_bus1 = { .spi_bus.spi_transaction_begin = stm32_transaction_begin, .spi_bus.spi_transaction_transfer = stm32_transaction_transfer, .spi_bus.spi_transaction_tick = stm32_transaction_tick, .spi_bus.spi_transaction_end = stm32_transaction_end, .spi_bus.spi_get_config = stm32_get_config, .spi_bus.spi_set_config = stm32_set_config, .setup = &bus1_setup, .cs_up = false }; #endif #ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2 static const cyg_uint8 bus2_cs_gpio_list[] = { CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2_CS_GPIOS }; static const cyg_uint8 bus2_spi_gpio_list[] = { 0x1D, 0x1E, 0x1F }; #if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE > 0) static cyg_uint8 bus2_tx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE] __attribute__((aligned (2), section (".sram"))) = { 0 }; static cyg_uint8 bus2_rx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE] __attribute__((aligned (2), section (".sram"))) = { 0 }; #endif static const cyg_spi_cortexm_stm32_bus_setup_t bus2_setup = { .apb_freq = APB1_FREQ, .spi_reg_base = CYGHWR_HAL_STM32_SPI2, .dma_reg_base = CYGHWR_HAL_STM32_DMA1, .dma_tx_channel = 5, .dma_rx_channel = 4, .cs_gpio_num = sizeof (bus2_cs_gpio_list), .cs_gpio_list = bus2_cs_gpio_list, .spi_gpio_list = bus2_spi_gpio_list, .dma_tx_intr = CYGNUM_HAL_INTERRUPT_DMA1_CH5, .dma_rx_intr = CYGNUM_HAL_INTERRUPT_DMA1_CH4, .dma_tx_intr_pri = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_TXINTR_PRI, .dma_rx_intr_pri = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_RXINTR_PRI, #if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE > 0) .bbuf_size = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE, .bbuf_tx = bus2_tx_bbuf, .bbuf_rx = bus2_rx_bbuf, #else .bbuf_size = 0, #endif }; cyg_spi_cortexm_stm32_bus_t cyg_spi_stm32_bus2 = { .spi_bus.spi_transaction_begin = stm32_transaction_begin, .spi_bus.spi_transaction_transfer = stm32_transaction_transfer, .spi_bus.spi_transaction_tick = stm32_transaction_tick, .spi_bus.spi_transaction_end = stm32_transaction_end, .spi_bus.spi_get_config = stm32_get_config, .spi_bus.spi_set_config = stm32_set_config, .setup = &bus2_setup, .cs_up = false }; #endif #ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3 static const cyg_uint8 bus3_cs_gpio_list[] = { CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3_CS_GPIOS }; static const cyg_uint8 bus3_spi_gpio_list[] = { 0x13, 0x14, 0x15 }; #if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE > 0) static cyg_uint8 bus3_tx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE] __attribute__((aligned (2), section (".sram"))) = { 0 }; static cyg_uint8 bus3_rx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE] __attribute__((alugned (2), section (".sram"))) = { 0 }; #endif static const cyg_spi_cortexm_stm32_bus_setup_t bus3_setup = { .apb_freq = APB1_FREQ, .spi_reg_base = CYGHWR_HAL_STM32_SPI3, .dma_reg_base = CYGHWR_HAL_STM32_DMA2, .dma_tx_channel = 2, .dma_rx_channel = 1, .cs_gpio_num = sizeof (bus3_cs_gpio_list), .cs_gpio_list = bus3_cs_gpio_list, .spi_gpio_list = bus3_spi_gpio_list, .dma_tx_intr = CYGNUM_HAL_INTERRUPT_DMA2_CH2, .dma_rx_intr = CYGNUM_HAL_INTERRUPT_DMA2_CH1, .dma_tx_intr_pri = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_TXINTR_PRI, .dma_rx_intr_pri = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_RXINTR_PRI, #if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE > 0) .bbuf_size = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE, .bbuf_tx = bus3_tx_bbuf, .bbuf_rx = bus3_rx_bbuf, #else .bbuf_size = 0, #endif }; cyg_spi_cortexm_stm32_bus_t cyg_spi_stm32_bus3 = { .spi_bus.spi_transaction_begin = stm32_transaction_begin, .spi_bus.spi_transaction_transfer = stm32_transaction_transfer, .spi_bus.spi_transaction_tick = stm32_transaction_tick, .spi_bus.spi_transaction_end = stm32_transaction_end, .spi_bus.spi_get_config = stm32_get_config, .spi_bus.spi_set_config = stm32_set_config, .setup = &bus3_setup, .cs_up = false }; #endif //----------------------------------------------------------------------------- // Useful GPIO macros for 'dynamic' pin setup. static const cyg_uint32 stm32_gpio_port_offsets[] = { CYGHWR_HAL_STM32_GPIOA - CYGHWR_HAL_STM32_GPIOA, CYGHWR_HAL_STM32_GPIOB - CYGHWR_HAL_STM32_GPIOA, CYGHWR_HAL_STM32_GPIOC - CYGHWR_HAL_STM32_GPIOA, CYGHWR_HAL_STM32_GPIOD - CYGHWR_HAL_STM32_GPIOA, CYGHWR_HAL_STM32_GPIOE - CYGHWR_HAL_STM32_GPIOA, CYGHWR_HAL_STM32_GPIOF - CYGHWR_HAL_STM32_GPIOA, CYGHWR_HAL_STM32_GPIOG - CYGHWR_HAL_STM32_GPIOA }; #define STM32_GPIO_PINSPEC(__port, __bit, __mode, __cnf ) \ (stm32_gpio_port_offsets[__port] | (__bit<<16) | \ (CYGHWR_HAL_STM32_GPIO_MODE_##__mode) | \ (CYGHWR_HAL_STM32_GPIO_CNF_##__cnf)) #if (CYGNUM_DEVS_SPI_CORTEXM_STM32_PIN_TOGGLE_RATE == 2) #define CYGHWR_HAL_STM32_GPIO_MODE_OUT_SPI CYGHWR_HAL_STM32_GPIO_MODE_OUT_2MHZ #elif (CYGNUM_DEVS_SPI_CORTEXM_STM32_PIN_TOGGLE_RATE == 10) #define CYGHWR_HAL_STM32_GPIO_MODE_OUT_SPI CYGHWR_HAL_STM32_GPIO_MODE_OUT_10MHZ #elif (CYGNUM_DEVS_SPI_CORTEXM_STM32_PIN_TOGGLE_RATE == 50) #define CYGHWR_HAL_STM32_GPIO_MODE_OUT_SPI CYGHWR_HAL_STM32_GPIO_MODE_OUT_50MHZ #else #error "Invalid SPI bus toggle rate." #endif //----------------------------------------------------------------------------- // Configure a GPIO pin as a SPI chip select line. static inline void stm32_spi_gpio_cs_setup (cyg_uint32 gpio_num) { cyg_uint32 port, pin, pinspec; // Check that the pin number is in range (16 pins per port). pin = gpio_num & 0xF; port = gpio_num >> 4; CYG_ASSERT (port < 7, "STM32 SPI : Invalid GPIO number in chip select list."); // Generate the pin setup specification and configure it. pinspec = STM32_GPIO_PINSPEC (port, pin, OUT_SPI, OUT_PUSHPULL); CYGHWR_HAL_STM32_GPIO_SET (pinspec); CYGHWR_HAL_STM32_GPIO_OUT (pinspec, 1); } //----------------------------------------------------------------------------- // Drive a GPIO pin as a SPI chip select line. static inline void stm32_spi_chip_select (cyg_uint32 gpio_num, cyg_bool assert) { cyg_uint32 port, pin, pinspec; // Check that the pin number is in range (16 pins per port). pin = gpio_num & 0xF; port = gpio_num >> 4; CYG_ASSERT (port < 7, "STM32 SPI : Invalid GPIO number in chip select list."); // Generate the pin setup specification and drive it. pinspec = STM32_GPIO_PINSPEC (port, pin, OUT_SPI, OUT_PUSHPULL); CYGHWR_HAL_STM32_GPIO_OUT (pinspec, assert ? 0 : 1); } //----------------------------------------------------------------------------- // Implement DMA ISRs. These disable the DMA channel, mask the interrupt // condition and schedule their respective DSRs. static cyg_uint32 stm32_tx_ISR (cyg_vector_t vector, cyg_addrword_t data) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) data; cyg_uint32 chan = stm32_bus->setup->dma_tx_channel; cyg_haladdress reg_addr; // Disable the DMA channel. cyg_drv_isr_lock (); reg_addr = stm32_bus->setup->dma_reg_base + CYGHWR_HAL_STM32_DMA_CCR (chan); HAL_WRITE_UINT32 (reg_addr, 0); // Clear down the interrupts. reg_addr = stm32_bus->setup->dma_reg_base + CYGHWR_HAL_STM32_DMA_IFCR; HAL_WRITE_UINT32 (reg_addr, CYGHWR_HAL_STM32_DMA_IFCR_MASK (chan)); cyg_drv_interrupt_acknowledge (vector); cyg_drv_interrupt_mask (vector); cyg_drv_isr_unlock (); return (CYG_ISR_CALL_DSR | CYG_ISR_HANDLED); } static cyg_uint32 stm32_rx_ISR (cyg_vector_t vector, cyg_addrword_t data) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) data; cyg_uint32 chan = stm32_bus->setup->dma_rx_channel; cyg_haladdress reg_addr; // Disable the DMA channel. cyg_drv_isr_lock (); reg_addr = stm32_bus->setup->dma_reg_base + CYGHWR_HAL_STM32_DMA_CCR (chan); HAL_WRITE_UINT32 (reg_addr, 0); // Clear down the interrupts. reg_addr = stm32_bus->setup->dma_reg_base + CYGHWR_HAL_STM32_DMA_IFCR; HAL_WRITE_UINT32 (reg_addr, CYGHWR_HAL_STM32_DMA_IFCR_MASK (chan)); cyg_drv_interrupt_acknowledge (vector); cyg_drv_interrupt_mask (vector); cyg_drv_isr_unlock (); return (CYG_ISR_CALL_DSR | CYG_ISR_HANDLED); } //----------------------------------------------------------------------------- // Implement DMA DSRs. These clear down the interrupt conditions and assert // their respective 'transaction complete' flags. static void stm32_tx_DSR (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) data; cyg_drv_dsr_lock (); stm32_bus->tx_dma_done = true; cyg_drv_cond_signal (&stm32_bus->condvar); cyg_drv_dsr_unlock (); } static void stm32_rx_DSR (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) data; cyg_drv_dsr_lock (); stm32_bus->rx_dma_done = true; cyg_drv_cond_signal (&stm32_bus->condvar); cyg_drv_dsr_unlock (); } //----------------------------------------------------------------------------- // Set up a new SPI bus on initialisation. static void stm32_spi_bus_setup (cyg_spi_cortexm_stm32_bus_t* stm32_bus) { int i; cyg_haladdress reg_addr; cyg_uint32 pin, pinspec, reg_data; // Set up the GPIOs for use as chip select lines. for (i = 0; i < stm32_bus->setup->cs_gpio_num; i ++) { stm32_spi_gpio_cs_setup (stm32_bus->setup->cs_gpio_list[i]); } // Configure the SPI clock output pin. pin = stm32_bus->setup->spi_gpio_list[0]; pinspec = STM32_GPIO_PINSPEC ((pin >> 4), (pin & 0xF), OUT_SPI, ALT_PUSHPULL); CYGHWR_HAL_STM32_GPIO_SET (pinspec); // Configure the SPI MISO input. pin = stm32_bus->setup->spi_gpio_list[1]; pinspec = STM32_GPIO_PINSPEC ((pin >> 4), (pin & 0xF), IN, PULLUP); CYGHWR_HAL_STM32_GPIO_SET (pinspec); // Configure the SPI MOSI output. pin = stm32_bus->setup->spi_gpio_list[2]; pinspec = STM32_GPIO_PINSPEC ((pin >> 4), (pin & 0xF), OUT_SPI, ALT_PUSHPULL); CYGHWR_HAL_STM32_GPIO_SET (pinspec); // Set up SPI default configuration. reg_addr = stm32_bus->setup->spi_reg_base + CYGHWR_HAL_STM32_SPI_CR2; reg_data = CYGHWR_HAL_STM32_SPI_CR2_TXDMAEN | CYGHWR_HAL_STM32_SPI_CR2_RXDMAEN; HAL_WRITE_UINT32 (reg_addr, reg_data); // Ensure that the DMA clocks are enabled. reg_addr = CYGHWR_HAL_STM32_RCC + CYGHWR_HAL_STM32_RCC_AHBENR; HAL_READ_UINT32 (reg_addr, reg_data); if (stm32_bus->setup->dma_reg_base == CYGHWR_HAL_STM32_DMA1) reg_data |= CYGHWR_HAL_STM32_RCC_AHBENR_DMA1; else reg_data |= CYGHWR_HAL_STM32_RCC_AHBENR_DMA2; HAL_WRITE_UINT32 (reg_addr, reg_data); // Initialise the synchronisation primitivies. cyg_drv_mutex_init (&stm32_bus->mutex); cyg_drv_cond_init (&stm32_bus->condvar, &stm32_bus->mutex); // Hook up the ISRs and DSRs. cyg_drv_interrupt_create (stm32_bus->setup->dma_tx_intr, stm32_bus->setup->dma_tx_intr_pri, (cyg_addrword_t) stm32_bus, stm32_tx_ISR, stm32_tx_DSR, &stm32_bus->tx_intr_handle, &stm32_bus->tx_intr_data); cyg_drv_interrupt_attach (stm32_bus->tx_intr_handle); cyg_drv_interrupt_create (stm32_bus->setup->dma_rx_intr, stm32_bus->setup->dma_rx_intr_pri, (cyg_addrword_t) stm32_bus, stm32_rx_ISR, stm32_rx_DSR, &stm32_bus->rx_intr_handle, &stm32_bus->rx_intr_data); cyg_drv_interrupt_attach (stm32_bus->rx_intr_handle); // Call upper layer bus init. CYG_SPI_BUS_COMMON_INIT(&stm32_bus->spi_bus); } //----------------------------------------------------------------------------- // Set up a DMA channel. static void dma_channel_setup (cyg_spi_device* device, cyg_uint8* data_buf, cyg_uint32 count, cyg_bool is_tx, cyg_bool polled) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus; cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device; cyg_uint32 chan, reg_data; cyg_haladdress dma_reg_base, spi_reg_base, dma_addr, reg_addr; // Extract address and channel information. dma_reg_base = stm32_bus->setup->dma_reg_base; spi_reg_base = stm32_bus->setup->spi_reg_base; chan = is_tx ? stm32_bus->setup->dma_tx_channel : stm32_bus->setup->dma_rx_channel; // Default options for the DMA channel. reg_data = CYGHWR_HAL_STM32_DMA_CCR_EN; // Do not enable interrupts in polled mode. if (!polled) reg_data |= CYGHWR_HAL_STM32_DMA_CCR_TCIE | CYGHWR_HAL_STM32_DMA_CCR_TEIE; // Set DMA channel direction and priority level. The receive channel has higher // priority so that the inbound buffer is always cleared first. if (is_tx) reg_data |= CYGHWR_HAL_STM32_DMA_CCR_DIR | CYGHWR_HAL_STM32_DMA_CCR_PLMEDIUM; else reg_data |= CYGHWR_HAL_STM32_DMA_CCR_PLHIGH; // Set the correct transfer size. if (stm32_device->bus_16bit) reg_data |= CYGHWR_HAL_STM32_DMA_CCR_PSIZE16 | CYGHWR_HAL_STM32_DMA_CCR_MSIZE16; else reg_data |= CYGHWR_HAL_STM32_DMA_CCR_PSIZE8 | CYGHWR_HAL_STM32_DMA_CCR_MSIZE8; // Do not use memory address incrementing for dummy data. if (data_buf != NULL) { reg_data |= CYGHWR_HAL_STM32_DMA_CCR_MINC; dma_addr = (cyg_haladdress) data_buf; } else dma_addr = (cyg_haladdress) (is_tx ? &dma_tx_null : &dma_rx_null); // Program up the DMA memory address. reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CMAR (chan); HAL_WRITE_UINT32 (reg_addr, dma_addr); // Program up the peripheral memory address. dma_addr = spi_reg_base + CYGHWR_HAL_STM32_SPI_DR; reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CPAR (chan); HAL_WRITE_UINT32 (reg_addr, dma_addr); // Program up the data buffer size. reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CNDTR (chan); HAL_WRITE_UINT32 (reg_addr, count); // Enable the DMA via the configuration register. reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CCR (chan); HAL_WRITE_UINT32 (reg_addr, reg_data); } //----------------------------------------------------------------------------- // Initiate a DMA transfer over the SPI interface. static void spi_transaction_dma (cyg_spi_device* device, cyg_bool tick_only, cyg_bool polled, cyg_uint32 count, const cyg_uint8* tx_data, cyg_uint8* rx_data, cyg_bool drop_cs) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus; cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device; cyg_haladdress reg_addr; cyg_uint32 chan, reg_data; cyg_haladdress dma_reg_base = stm32_bus->setup->dma_reg_base; cyg_haladdress spi_reg_base = stm32_bus->setup->spi_reg_base; // Ensure the chip select is asserted, inserting inter-transaction guard // time if required. Note that when ticking the device we do not touch the CS. if (!stm32_bus->cs_up && !tick_only) { CYGACC_CALL_IF_DELAY_US (stm32_device->tr_bt_udly); stm32_spi_chip_select (stm32_bus->setup->cs_gpio_list[stm32_device->dev_num], true); stm32_bus->cs_up = true; CYGACC_CALL_IF_DELAY_US (stm32_device->cs_up_udly); } // Set up the DMA channels. dma_channel_setup (device, (cyg_uint8*) tx_data, count, true, polled); dma_channel_setup (device, rx_data, count, false, polled); // Run the DMA (polling for completion). if (polled) { cyg_bool transfer_done = false; // Enable the SPI controller. reg_addr = spi_reg_base + CYGHWR_HAL_STM32_SPI_CR1; HAL_WRITE_UINT32 (reg_addr, stm32_device->spi_cr1_val | CYGHWR_HAL_STM32_SPI_CR1_SPE); // Spin waiting on both DMA status flags. Trap bus errors and assert in // debug builds or return garbage in production builds. reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_ISR; do { HAL_READ_UINT32 (reg_addr, reg_data); if ((reg_data & CYGHWR_HAL_STM32_DMA_ISR_TEIF (stm32_bus->setup->dma_tx_channel)) || (reg_data & CYGHWR_HAL_STM32_DMA_ISR_TEIF (stm32_bus->setup->dma_rx_channel))) { CYG_ASSERT (false, "STM32 SPI : DMA bus fault - enable bounce buffers."); transfer_done = true; } if ((reg_data & CYGHWR_HAL_STM32_DMA_ISR_TCIF (stm32_bus->setup->dma_tx_channel)) && (reg_data & CYGHWR_HAL_STM32_DMA_ISR_TCIF (stm32_bus->setup->dma_rx_channel))) { transfer_done = true; } } while (!transfer_done); // Disable the DMA channels on completion and clear the status flags. chan = stm32_bus->setup->dma_tx_channel; reg_data = CYGHWR_HAL_STM32_DMA_IFCR_MASK (chan); reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CCR (chan); HAL_WRITE_UINT32 (reg_addr, 0); chan = stm32_bus->setup->dma_rx_channel; reg_data |= CYGHWR_HAL_STM32_DMA_IFCR_MASK (chan); reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CCR (chan); HAL_WRITE_UINT32 (reg_addr, 0); reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_IFCR; HAL_WRITE_UINT32 (reg_addr, reg_data); } // Run the DMA (interrupt driven). else { cyg_drv_mutex_lock (&stm32_bus->mutex); cyg_drv_dsr_lock (); stm32_bus->tx_dma_done = false; stm32_bus->rx_dma_done = false; cyg_drv_interrupt_unmask (stm32_bus->setup->dma_tx_intr); cyg_drv_interrupt_unmask (stm32_bus->setup->dma_rx_intr); // Enable the SPI controller. reg_addr = spi_reg_base + CYGHWR_HAL_STM32_SPI_CR1; HAL_WRITE_UINT32 (reg_addr, stm32_device->spi_cr1_val | CYGHWR_HAL_STM32_SPI_CR1_SPE); // Sit back and wait for the ISR/DSRs to signal completion. do { cyg_drv_cond_wait (&stm32_bus->condvar); } while (!(stm32_bus->tx_dma_done && stm32_bus->rx_dma_done)); cyg_drv_dsr_unlock (); cyg_drv_mutex_unlock (&stm32_bus->mutex); } // Disable the SPI controller. reg_addr = spi_reg_base + CYGHWR_HAL_STM32_SPI_CR1; HAL_WRITE_UINT32 (reg_addr, stm32_device->spi_cr1_val); // Deassert the chip select. if (drop_cs && !tick_only) { CYGACC_CALL_IF_DELAY_US (stm32_device->cs_dw_udly); stm32_spi_chip_select (stm32_bus->setup->cs_gpio_list[stm32_device->dev_num], false); stm32_bus->cs_up = false; } } //----------------------------------------------------------------------------- // Calculate BR bits for SPI_CR1. static int calculate_br_bits (cyg_spi_cortexm_stm32_bus_t* bus, cyg_uint32 *target_clockrate, cyg_uint32 *br) { cyg_uint32 divided_clk; // Calculate the maximum viable bus speed. divided_clk = bus->setup->apb_freq / 2; for (*br = 0; (*br < 7) && (divided_clk > *target_clockrate); (*br)++) divided_clk >>= 1; if ( divided_clk <= *target_clockrate ) return 0; return -1; } //----------------------------------------------------------------------------- // Initialise SPI interfaces on startup. static void CYGBLD_ATTRIB_C_INIT_PRI(CYG_INIT_BUS_SPI) stm32_spi_init(void) { #if defined(CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3) && \ defined(CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3_DISABLE_DEBUG_PORT) // Disable debug port, freeing up SPI bus 3 pins. cyg_uint32 reg_val; HAL_READ_UINT32 (CYGHWR_HAL_STM32_AFIO + CYGHWR_HAL_STM32_AFIO_MAPR, reg_val); reg_val &= ~((cyg_uint32) CYGHWR_HAL_STM32_AFIO_MAPR_SWJ_MASK); reg_val |= CYGHWR_HAL_STM32_AFIO_MAPR_SWJ_SWDPDIS; HAL_WRITE_UINT32 (CYGHWR_HAL_STM32_AFIO + CYGHWR_HAL_STM32_AFIO_MAPR, reg_val); #endif #ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1 stm32_spi_bus_setup (&cyg_spi_stm32_bus1); #endif #ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2 stm32_spi_bus_setup (&cyg_spi_stm32_bus2); #endif #ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3 stm32_spi_bus_setup (&cyg_spi_stm32_bus3); #endif } //----------------------------------------------------------------------------- // Start a SPI transaction. static void stm32_transaction_begin (cyg_spi_device* device) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus; cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device; cyg_haladdress reg_addr; cyg_uint32 reg_data, br; // On the first transaction, generate the values to be programmed into the // SPI configuration registers for this device and cache them. This avoids // having to recalculate the prescaler for every transaction. if (!stm32_device->spi_cr1_val) { reg_data = CYGHWR_HAL_STM32_SPI_CR1_MSTR | CYGHWR_HAL_STM32_SPI_CR1_SSI | CYGHWR_HAL_STM32_SPI_CR1_SSM; if (stm32_device->cl_pol) reg_data |= CYGHWR_HAL_STM32_SPI_CR1_CPOL; if (stm32_device->cl_pha) reg_data |= CYGHWR_HAL_STM32_SPI_CR1_CPHA; if (stm32_device->bus_16bit) reg_data |= CYGHWR_HAL_STM32_SPI_CR1_DFF; // Get divider bits if ( 0 != calculate_br_bits(stm32_bus, (cyg_uint32 *)&(stm32_device->cl_brate), &br) ) CYG_ASSERT (false, "STM32 SPI : Cannot run bus slowly enough for peripheral."); reg_data |= CYGHWR_HAL_STM32_SPI_CR1_BR (br); // Cache the configuration register settings. stm32_device->spi_cr1_val = reg_data; } // Set up the SPI controller. reg_addr = stm32_bus->setup->spi_reg_base + CYGHWR_HAL_STM32_SPI_CR1; HAL_WRITE_UINT32 (reg_addr, stm32_device->spi_cr1_val); } //----------------------------------------------------------------------------- // Run a transaction transfer. static void stm32_transaction_transfer (cyg_spi_device* device, cyg_bool polled, cyg_uint32 count, const cyg_uint8* tx_data, cyg_uint8* rx_data, cyg_bool drop_cs) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus; cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device; // Check for unsupported transactions. CYG_ASSERT (count > 0, "STM32 SPI : Null transfer requested."); // We check that the buffers are half-word aligned and that count is a // multiple of two in order to carry out the 16-bit transfer. if (stm32_device->bus_16bit) { CYG_ASSERT (!(count & 1) && !((cyg_uint32) tx_data & 1) && !((cyg_uint32) rx_data & 1), "STM32 SPI : Misaligned data in 16-bit transfer."); } // Perform transfer via the bounce buffers. if (stm32_bus->setup->bbuf_size != 0) { cyg_uint8* tx_local = NULL; cyg_uint8* rx_local = NULL; // If the requested transfer is too large for the bounce buffer we assert // in debug builds and truncate in production builds. if (count > stm32_bus->setup->bbuf_size) { CYG_ASSERT (false, "STM32 SPI : Transfer exceeds bounce buffer size."); count = stm32_bus->setup->bbuf_size; } if (tx_data != NULL) { tx_local = stm32_bus->setup->bbuf_tx; memcpy (tx_local, tx_data, count); } if (rx_data != NULL) { rx_local = stm32_bus->setup->bbuf_rx; } spi_transaction_dma (device, false, polled, count, tx_local, rx_local, drop_cs); if (rx_data != NULL) { memcpy (rx_data, rx_local, count); } } // Perform conventional transfer. else { spi_transaction_dma (device, false, polled, count, tx_data, rx_data, drop_cs); } } //----------------------------------------------------------------------------- // Carry out a bus tick operation - this just pushes the required number of // zeros onto the bus, leaving the chip select in its current state. static void stm32_transaction_tick (cyg_spi_device* device, cyg_bool polled, cyg_uint32 count) { cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device; // Check for unsupported transactions. CYG_ASSERT (count > 0, "STM32 SPI : Null transfer requested."); // We check that count is a multiple of two in order to carry out the 16-bit transfer. if (stm32_device->bus_16bit) { CYG_ASSERT (!(count & 1), "STM32 SPI : Misaligned data in 16-bit transfer."); } // Perform null transfer. spi_transaction_dma (device, true, polled, count, NULL, NULL, false); } //----------------------------------------------------------------------------- // Terminate a SPI transaction, disabling the SPI controller. static void stm32_transaction_end (cyg_spi_device* device) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus; cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device; cyg_haladdress reg_addr; // Ensure that the chip select is deasserted. if (stm32_bus->cs_up) { CYGACC_CALL_IF_DELAY_US (stm32_device->cs_dw_udly); stm32_spi_chip_select (stm32_bus->setup->cs_gpio_list[stm32_device->dev_num], false); stm32_bus->cs_up = false; } // Ensure the SPI controller is disabled. reg_addr = stm32_bus->setup->spi_reg_base + CYGHWR_HAL_STM32_SPI_CR1; HAL_WRITE_UINT32 (reg_addr, stm32_device->spi_cr1_val); } //----------------------------------------------------------------------------- static int stm32_get_config (cyg_spi_device* device, cyg_uint32 key, void* buf, cyg_uint32* len) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus; cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device; cyg_uint32* data_p = buf; switch (key) { case CYG_IO_GET_CONFIG_SPI_CLOCKRATE : // Sanity check if (NULL == len) { CYG_ASSERT (false, "STM32 SPI : Null pointer as len argument for stm32_get_config()."); return -1; } if (sizeof(cyg_uint32) != *len) { CYG_ASSERT (false, "STM32 SPI : Invalid length with stm32_get_config()."); return -1; } if (NULL == buf) { CYG_ASSERT (false, "STM32 SPI : Null poiter as buf argument for stm32_get_config()."); return -1; } *data_p = stm32_bus->setup->apb_freq >> ((( stm32_device->spi_cr1_val >> 3 ) & 7) + 1 ) ; return 0; default : break; } return -1; } //----------------------------------------------------------------------------- static int stm32_set_config (cyg_spi_device* device, cyg_uint32 key, const void* buf, cyg_uint32* len) { cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus; cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device; cyg_uint32 br; switch (key) { case CYG_IO_SET_CONFIG_SPI_CLOCKRATE : // Sanity check if (NULL == len) { CYG_ASSERT (false, "STM32 SPI : Null pointer as len argument for stm32_set_config()."); return -1; } if (sizeof(cyg_uint32) != *len) { CYG_ASSERT (false, "STM32 SPI : Invalid length with stm32_set_config()."); return -1; } if (NULL == buf) { CYG_ASSERT (false, "STM32 SPI : Null pointer as buf argument for stm32_set_config()."); return -1; } // Get divider bits if ( 0 != calculate_br_bits(stm32_bus, (cyg_uint32 *)buf, &br) ) { CYG_ASSERT (false, "STM32 SPI : Cannot run bus as slowly as requested."); return -1; } // Update the cache of the configuration register settings. stm32_device->spi_cr1_val &= ~CYGHWR_HAL_STM32_SPI_CR1_BR(7); stm32_device->spi_cr1_val |= CYGHWR_HAL_STM32_SPI_CR1_BR(br); return 0; default : break; } return -1; } //=============================================================================