diff --git a/arch/arm/src/stm32f0l0/Make.defs b/arch/arm/src/stm32f0l0/Make.defs index bc6aa0cf5a9..05edb1cc125 100644 --- a/arch/arm/src/stm32f0l0/Make.defs +++ b/arch/arm/src/stm32f0l0/Make.defs @@ -65,7 +65,7 @@ CMN_CSRCS += up_dumpnvic.c endif CHIP_ASRCS = -CHIP_CSRCS = stm32_start.c stm32_gpio.c stm32_irq.c # stm32_dma_v1.c +CHIP_CSRCS = stm32_start.c stm32_gpio.c stm32_exti_gpio.c stm32_irq.c # stm32_dma_v1.c CHIP_CSRCS += stm32_lse.c stm32_lowputc.c stm32_serial.c # Configuration-dependent STM32F0/L0 files @@ -112,12 +112,8 @@ ifeq ($(CONFIG_STM32F0L0_I2C),y) CHIP_CSRCS += stm32_i2c.c endif -ifeq ($(CONFIG_STM32F0L0_SPI0),y) +ifeq ($(CONFIG_STM32F0L0_SPI),y) CHIP_CSRCS += stm32_spi.c -else -ifeq ($(CONFIG_STM32F0L0_SPI1),y) -CHIP_CSRCS += stm32_spi.c -endif endif ifeq ($(CONFIG_PWM),y) diff --git a/arch/arm/src/stm32f0l0/hardware/stm32l0_exti.h b/arch/arm/src/stm32f0l0/hardware/stm32l0_exti.h index 06d341dfbf7..f62df1100a4 100644 --- a/arch/arm/src/stm32f0l0/hardware/stm32l0_exti.h +++ b/arch/arm/src/stm32f0l0/hardware/stm32l0_exti.h @@ -83,14 +83,12 @@ #define EXTI_COMP1 (1 << 21) /* EXTI line 21 is connected to the COMP1 (comparator) output */ #define EXTI_COMP2 (1 << 22) /* EXTI line 22 is connected to the COMP2 (comparator) output */ #define EXTI_I2C1 (1 << 23) /* EXTI line 23 is connected to the I2C1 wakeup */ - /* EXTI line 24 is reserved (internally held low) */ +#define EXTI_I2C3 (1 << 24) /* EXTI line 24 is connected to the I2C3 wakeup */ #define EXTI_USART1 (1 << 25) /* EXTI line 25 is connected to the USART1 wakeup */ #define EXTI_USART2 (1 << 26) /* EXTI line 26 is connected to the USART2 wakeup */ -#define EXTI_CEC (1 << 27) /* EXTI line 27 is connected to the CEC wakeup */ -#define EXTI_USART3 (1 << 28) /* EXTI line 28 is connected to the USART3 wakeup */ - /* EXTI line 29 is reserved (internally held low) */ - /* EXTI line 30 is reserved (internally held low) */ -#define EXTI_VDDIO2 (1 << 31) /* EXTI line 31 is connected to the Vddio2 supply comparator */ + /* EXTI line 27 is reserved */ +#define EXTI_LPUART1 (1 << 28) /* EXTI line 28 is connected to the LPUART wakeup */ +#define EXTI_LPTIM1 (1 << 29) /* EXTI line 29 is connected to the LPTIM1 wakeup */ /* Interrupt mask register */ diff --git a/arch/arm/src/stm32f0l0/stm32.h b/arch/arm/src/stm32f0l0/stm32.h new file mode 100644 index 00000000000..20bac988fd2 --- /dev/null +++ b/arch/arm/src/stm32f0l0/stm32.h @@ -0,0 +1,66 @@ +/************************************************************************************ + * arch/arm/src/stm32f0l0/stm32.h + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Mateusz Szafoni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F0L0_STM32_H +#define __ARCH_ARM_SRC_STM32F0L0_STM32_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include +#include +#include + +#include "up_internal.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Peripherals **********************************************************************/ + +#include "chip.h" +#include "stm32_dma.h" +#include "stm32_gpio.h" +#include "stm32_i2c.h" +#include "stm32_pwr.h" +#include "stm32_rcc.h" +#include "stm32_spi.h" +#include "stm32_uart.h" +#include "stm32_lowputc.h" + +#endif /* __ARCH_ARM_SRC_STM32F0L0_STM32_H */ diff --git a/arch/arm/src/stm32f0l0/stm32_exti.h b/arch/arm/src/stm32f0l0/stm32_exti.h new file mode 100644 index 00000000000..d83e4c725d0 --- /dev/null +++ b/arch/arm/src/stm32f0l0/stm32_exti.h @@ -0,0 +1,143 @@ +/************************************************************************************ + * arch/arm/src/stm32f0l0/stm32_exti.h + * + * Copyright (C) 2009, 2012, 2015, 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F0L0_STM32_EXTI_H +#define __ARCH_ARM_SRC_STM32F0L0_STM32_EXTI_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include + +#include "chip.h" +#include "hardware/stm32_exti.h" + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Public Function Prototypes + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_gpiosetevent + * + * Description: + * Sets/clears GPIO based event and interrupt triggers. + * + * Input Parameters: + * - pinset: gpio pin configuration + * - rising/falling edge: enables + * - event: generate event when set + * - func: when non-NULL, generate interrupt + * - arg: Argument passed to the interrupt callback + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure indicating the + * nature of the failure. + * + ************************************************************************************/ + +int stm32_gpiosetevent(uint32_t pinset, bool risingedge, bool fallingedge, + bool event, xcpt_t func, void *arg); + +/************************************************************************************ + * Name: stm32_exti_alarm + * + * Description: + * Sets/clears EXTI alarm interrupt. + * + * Input Parameters: + * - rising/falling edge: enables interrupt on rising/falling edges + * - event: generate event when set + * - func: when non-NULL, generate interrupt + * - arg: Argument passed to the interrupt callback + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure indicating the + * nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int stm32_exti_alarm(bool risingedge, bool fallingedge, bool event, xcpt_t func, + void *arg); +#endif + +/**************************************************************************** + * Name: stm32_exti_wakeup + * + * Description: + * Sets/clears EXTI wakeup interrupt. + * + * Input Parameters: + * - rising/falling edge: enables interrupt on rising/falling edges + * - event: generate event when set + * - func: when non-NULL, generate interrupt + * - arg: Argument passed to the interrupt callback + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure indicating the + * nature of the failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_PERIODIC +int stm32_exti_wakeup(bool risingedge, bool fallingedge, bool event, + xcpt_t func, void *arg); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_STM32F0L0_STM32_EXTI_H */ diff --git a/arch/arm/src/stm32f0l0/stm32_exti_gpio.c b/arch/arm/src/stm32f0l0/stm32_exti_gpio.c new file mode 100644 index 00000000000..33af4edf568 --- /dev/null +++ b/arch/arm/src/stm32f0l0/stm32_exti_gpio.c @@ -0,0 +1,385 @@ +/**************************************************************************** + * arch/arm/src/stm32f0l0/stm32_exti_gpio.c + * + * Copyright (C) 2009, 2011-2012, 2015, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Author: Gregory Nutt + * Uros Platise + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "up_arch.h" +#include "chip.h" +#include "stm32_gpio.h" +#include "stm32_exti.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct gpio_callback_s +{ + xcpt_t callback; + void *arg; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Interrupt handlers attached to each EXTI */ + +static struct gpio_callback_s g_gpio_callbacks[16]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Interrupt Service Routines - Dispatchers + ****************************************************************************/ + +static int stm32_exti0_isr(int irq, void *context, void *arg) +{ + int ret = OK; + + /* Clear the pending interrupt */ + + putreg32(0x0001, STM32_EXTI_PR); + + /* And dispatch the interrupt to the handler */ + + if (g_gpio_callbacks[0].callback != NULL) + { + xcpt_t callback = g_gpio_callbacks[0].callback; + void *cbarg = g_gpio_callbacks[0].arg; + + ret = callback(irq, context, cbarg); + } + + return ret; +} + +static int stm32_exti1_isr(int irq, void *context, void *arg) +{ + int ret = OK; + + /* Clear the pending interrupt */ + + putreg32(0x0002, STM32_EXTI_PR); + + /* And dispatch the interrupt to the handler */ + + if (g_gpio_callbacks[1].callback != NULL) + { + xcpt_t callback = g_gpio_callbacks[1].callback; + void *cbarg = g_gpio_callbacks[1].arg; + + ret = callback(irq, context, cbarg); + } + + return ret; +} + +static int stm32_exti2_isr(int irq, void *context, void *arg) +{ + int ret = OK; + + /* Clear the pending interrupt */ + + putreg32(0x0004, STM32_EXTI_PR); + + /* And dispatch the interrupt to the handler */ + + if (g_gpio_callbacks[2].callback != NULL) + { + xcpt_t callback = g_gpio_callbacks[2].callback; + void *cbarg = g_gpio_callbacks[2].arg; + + ret = callback(irq, context, cbarg); + } + + return ret; +} + +static int stm32_exti3_isr(int irq, void *context, void * arg) +{ + int ret = OK; + + /* Clear the pending interrupt */ + + putreg32(0x0008, STM32_EXTI_PR); + + /* And dispatch the interrupt to the handler */ + + if (g_gpio_callbacks[3].callback != NULL) + { + xcpt_t callback = g_gpio_callbacks[3].callback; + void *cbarg = g_gpio_callbacks[3].arg; + + ret = callback(irq, context, cbarg); + } + + return ret; +} + +static int stm32_exti4_isr(int irq, void *context, void *arg) +{ + int ret = OK; + + /* Clear the pending interrupt */ + + putreg32(0x0010, STM32_EXTI_PR); + + /* And dispatch the interrupt to the handler */ + + if (g_gpio_callbacks[4].callback != NULL) + { + xcpt_t callback = g_gpio_callbacks[4].callback; + void *cbarg = g_gpio_callbacks[4].arg; + + ret = callback(irq, context, cbarg); + } + + return ret; +} + +static int stm32_exti_multiisr(int irq, void *context, void *arg, + int first, int last) +{ + uint32_t pr; + int pin; + int ret = OK; + + /* Examine the state of each pin in the group */ + + pr = getreg32(STM32_EXTI_PR); + + /* And dispatch the interrupt to the handler */ + + for (pin = first; pin <= last; pin++) + { + /* Is an interrupt pending on this pin? */ + + uint32_t mask = (1 << pin); + if ((pr & mask) != 0) + { + /* Clear the pending interrupt */ + + putreg32(mask, STM32_EXTI_PR); + + /* And dispatch the interrupt to the handler */ + + if (g_gpio_callbacks[pin].callback != NULL) + { + xcpt_t callback = g_gpio_callbacks[pin].callback; + void *cbarg = g_gpio_callbacks[pin].arg; + int tmp; + + tmp = callback(irq, context, cbarg); + if (tmp < 0) + { + ret = tmp; + } + } + } + } + + return ret; +} + +static int stm32_exti95_isr(int irq, void *context, void *arg) +{ + return stm32_exti_multiisr(irq, context, arg, 5, 9); +} + +static int stm32_exti1510_isr(int irq, void *context, void *arg) +{ + return stm32_exti_multiisr(irq, context, arg, 10, 15); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_gpiosetevent + * + * Description: + * Sets/clears GPIO based event and interrupt triggers. + * + * Input Parameters: + * - pinset: GPIO pin configuration + * - risingedge: Enables interrupt on rising edges + * - fallingedge: Enables interrupt on falling edges + * - event: Generate event when set + * - func: When non-NULL, generate interrupt + * - arg: Argument passed to the interrupt callback + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure indicating the + * nature of the failure. + * + ****************************************************************************/ + +int stm32_gpiosetevent(uint32_t pinset, bool risingedge, bool fallingedge, + bool event, xcpt_t func, void *arg) +{ + FAR struct gpio_callback_s *shared_cbs; + uint32_t pin = pinset & GPIO_PIN_MASK; + uint32_t exti = STM32_EXTI_BIT(pin); + int irq; + xcpt_t handler; + int nshared; + int i; + + /* Select the interrupt handler for this EXTI pin */ + + if (pin < 5) + { + irq = pin + STM32_IRQ_EXTI0; + nshared = 1; + shared_cbs = &g_gpio_callbacks[pin]; + switch (pin) + { + case 0: + handler = stm32_exti0_isr; + break; + + case 1: + handler = stm32_exti1_isr; + break; + + case 2: + handler = stm32_exti2_isr; + break; + + case 3: + handler = stm32_exti3_isr; + break; + + default: + handler = stm32_exti4_isr; + break; + } + } + else if (pin < 10) + { + irq = STM32_IRQ_EXTI95; + handler = stm32_exti95_isr; + shared_cbs = &g_gpio_callbacks[5]; + nshared = 5; + } + else + { + irq = STM32_IRQ_EXTI1510; + handler = stm32_exti1510_isr; + shared_cbs = &g_gpio_callbacks[10]; + nshared = 6; + } + + /* Get the previous GPIO IRQ handler; Save the new IRQ handler. */ + + g_gpio_callbacks[pin].callback = func; + g_gpio_callbacks[pin].arg = arg; + + /* Install external interrupt handlers */ + + if (func) + { + irq_attach(irq, handler, NULL); + up_enable_irq(irq); + } + else + { + /* Only disable IRQ if shared handler does not have any active + * callbacks. + */ + + for (i = 0; i < nshared; i++) + { + if (shared_cbs[i].callback != NULL) + { + break; + } + } + + if (i == nshared) + { + up_disable_irq(irq); + } + } + + /* Configure GPIO, enable EXTI line enabled if event or interrupt is + * enabled. + */ + + if (event || func) + { + pinset |= GPIO_EXTI; + } + + stm32_configgpio(pinset); + + /* Configure rising/falling edges */ + + modifyreg32(STM32_EXTI_RTSR, + risingedge ? 0 : exti, + risingedge ? exti : 0); + modifyreg32(STM32_EXTI_FTSR, + fallingedge ? 0 : exti, + fallingedge ? exti : 0); + + /* Enable Events and Interrupts */ + + modifyreg32(STM32_EXTI_EMR, + event ? 0 : exti, + event ? exti : 0); + modifyreg32(STM32_EXTI_IMR, + func ? 0 : exti, + func ? exti : 0); + + return OK; +} diff --git a/arch/arm/src/stm32f0l0/stm32_gpio.c b/arch/arm/src/stm32f0l0/stm32_gpio.c index b02eb7ea9fb..b2caf126acf 100644 --- a/arch/arm/src/stm32f0l0/stm32_gpio.c +++ b/arch/arm/src/stm32f0l0/stm32_gpio.c @@ -313,7 +313,6 @@ int stm32_configgpio(uint32_t cfgset) if ((cfgset & GPIO_EXTI) != 0) { -#if 0 /* "In STM32 F1 the selection of the EXTI line source is performed through * the EXTIx bits in the AFIO_EXTICRx registers, while in F2 series this * selection is done through the EXTIx bits in the SYSCFG_EXTICRx registers. @@ -336,7 +335,6 @@ int stm32_configgpio(uint32_t cfgset) regval |= (((uint32_t)port) << shift); putreg32(regval, regaddr); -#endif } leave_critical_section(flags); diff --git a/arch/arm/src/stm32f0l0/stm32_spi.c b/arch/arm/src/stm32f0l0/stm32_spi.c new file mode 100644 index 00000000000..454093534fa --- /dev/null +++ b/arch/arm/src/stm32f0l0/stm32_spi.c @@ -0,0 +1,1798 @@ +/************************************************************************************ + * arch/arm/src/stm32f0l0/stm32_spi.c + * copied from arch/arm/src/stm32 + * + * Copyright (C) 2009-2013, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/************************************************************************************ + * The external functions, stm32_spi1/2/3select and stm32_spi1/2/3status must be + * provided by board-specific logic. They are implementations of the select + * and status methods of the SPI interface defined by struct spi_ops_s (see + * include/nuttx/spi/spi.h). All other methods (including stm32_spibus_initialize()) + * are provided by common STM32 logic. To use this common SPI logic on your + * board: + * + * 1. Provide logic in stm32_board_initialize() to configure SPI chip select + * pins. + * 2. Provide stm32_spi1/2/3select() and stm32_spi1/2/3status() functions in your + * board-specific logic. These functions will perform chip selection and + * status operations using GPIOs in the way your board is configured. + * 3. Add a calls to stm32_spibus_initialize() in your low level application + * initialization logic + * 4. The handle returned by stm32_spibus_initialize() may then be used to bind the + * SPI driver to higher level logic (e.g., calling + * mmcsd_spislotinitialize(), for example, will bind the SPI driver to + * the SPI MMC/SD driver). + * + ************************************************************************************/ + +/* This driver is ported from the stm32 one, which only supports 8 and 16 bits + * transfers. The STM32 family supports frame size from 4 to 16 bits, but we do not + * support that yet. For the moment, we replace uses of the CR1_DFF bit with a check + * of the CR2_DS[0..3] bits. If the value is SPI_CR2_DS_16BIT it means 16 bits, else 8 bits. + */ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip.h" +#include "stm32.h" +#include "stm32_gpio.h" +#include "stm32_dma.h" +#include "stm32_spi.h" + +#include + +#ifdef CONFIG_STM32F0L0_SPI + +#ifdef ARCH_CHIP_STM32F0 +# error SPI driver not tested for F0 yet +#endif + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Configuration ********************************************************************/ +/* SPI interrupts */ + +#ifdef CONFIG_STM32F0L0_SPI_INTERRUPTS +# error "Interrupt driven SPI not yet supported" +#endif + +/* Can't have both interrupt driven SPI and SPI DMA */ + +#if defined(CONFIG_STM32F0L0_SPI_INTERRUPTS) && defined(CONFIG_STM32F0L0_SPI_DMA) +# error "Cannot enable both interrupt mode and DMA mode for SPI" +#endif + +/* SPI DMA priority */ + +#ifdef CONFIG_STM32F0L0_SPI_DMA + +# if defined(CONFIG_SPI_DMAPRIO) +# define SPI_DMA_PRIO CONFIG_SPI_DMAPRIO +# else +# define SPI_DMA_PRIO DMA_CCR_PRIMED +# endif + +# if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0 +# error "Illegal value for CONFIG_SPI_DMAPRIO" +# endif + +#endif + +/* DMA channel configuration */ + +#define SPI_RXDMA16_CONFIG (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC ) +#define SPI_RXDMA8_CONFIG (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC ) +#define SPI_RXDMA16NULL_CONFIG (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS ) +#define SPI_RXDMA8NULL_CONFIG (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS ) +#define SPI_TXDMA16_CONFIG (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR) +#define SPI_TXDMA8_CONFIG (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR) +#define SPI_TXDMA16NULL_CONFIG (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS |DMA_CCR_DIR) +#define SPI_TXDMA8NULL_CONFIG (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_DIR) + +/************************************************************************************ + * Private Types + ************************************************************************************/ + +struct stm32_spidev_s +{ + struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ + uint32_t spibase; /* SPIn base address */ + uint32_t spiclock; /* Clocking for the SPI module */ +#ifdef CONFIG_STM32F0L0_SPI_INTERRUPTS + uint8_t spiirq; /* SPI IRQ number */ +#endif +#ifdef CONFIG_STM32F0L0_SPI_DMA + volatile uint8_t rxresult; /* Result of the RX DMA */ + volatile uint8_t txresult; /* Result of the RX DMA */ +#ifdef CONFIG_SPI_TRIGGER + bool defertrig; /* Flag indicating that trigger should be deferred */ + bool trigarmed; /* Flag indicating that the trigger is armed */ +#endif + uint16_t rxch; /* The RX DMA channel number */ + uint16_t txch; /* The TX DMA channel number */ + DMA_HANDLE rxdma; /* DMA channel handle for RX transfers */ + DMA_HANDLE txdma; /* DMA channel handle for TX transfers */ + sem_t rxsem; /* Wait for RX DMA to complete */ + sem_t txsem; /* Wait for TX DMA to complete */ + uint32_t txccr; /* DMA control register for TX transfers */ + uint32_t rxccr; /* DMA control register for RX transfers */ +#endif + bool initialized; /* Has SPI interface been initialized */ + sem_t exclsem; /* Held while chip is selected for mutual exclusion */ + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + uint8_t nbits; /* Width of word in bits (4 through 16) */ + uint8_t mode; /* Mode 0,1,2,3 */ +#ifdef CONFIG_PM + struct pm_callback_s pm_cb; /* PM callbacks */ +#endif +}; + +/************************************************************************************ + * Private Function Prototypes + ************************************************************************************/ + +/* Helpers */ + +static inline uint16_t spi_getreg(FAR struct stm32_spidev_s *priv, uint8_t offset); +static inline void spi_putreg(FAR struct stm32_spidev_s *priv, uint8_t offset, + uint16_t value); +static inline uint16_t spi_readword(FAR struct stm32_spidev_s *priv); +static inline void spi_writeword(FAR struct stm32_spidev_s *priv, uint16_t byte); +static inline bool spi_16bitmode(FAR struct stm32_spidev_s *priv); + +/* DMA support */ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static void spi_dmarxwait(FAR struct stm32_spidev_s *priv); +static void spi_dmatxwait(FAR struct stm32_spidev_s *priv); +static inline void spi_dmarxwakeup(FAR struct stm32_spidev_s *priv); +static inline void spi_dmatxwakeup(FAR struct stm32_spidev_s *priv); +static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg); +static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg); +static void spi_dmarxsetup(FAR struct stm32_spidev_s *priv, + FAR void *rxbuffer, FAR void *rxdummy, size_t nwords); +static void spi_dmatxsetup(FAR struct stm32_spidev_s *priv, + FAR const void *txbuffer, FAR const void *txdummy, size_t nwords); +static inline void spi_dmarxstart(FAR struct stm32_spidev_s *priv); +static inline void spi_dmatxstart(FAR struct stm32_spidev_s *priv); +#endif + +/* SPI methods */ + +static int spi_lock(FAR struct spi_dev_s *dev, bool lock); +static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency); +static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode); +static void spi_setbits(FAR struct spi_dev_s *dev, int nbits); +#ifdef CONFIG_SPI_HWFEATURES +static int spi_hwfeatures(FAR struct spi_dev_s *dev, + spi_hwfeatures_t features); +#endif +static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd); +static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords); +#ifdef CONFIG_SPI_TRIGGER +static int spi_trigger(FAR struct spi_dev_s *dev); +#endif +#ifndef CONFIG_SPI_EXCHANGE +static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + size_t nwords); +static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *rxbuffer, + size_t nwords); +#endif + +/* Initialization */ + +static void spi_bus_initialize(FAR struct stm32_spidev_s *priv); + +/* PM interface */ + +#ifdef CONFIG_PM +static int spi_pm_prepare(FAR struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate); +#endif + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI1 +static const struct spi_ops_s g_spi1ops = +{ + .lock = spi_lock, + .select = stm32_spi1select, + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = spi_hwfeatures, +#endif + .status = stm32_spi1status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = stm32_spi1cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +#ifdef CONFIG_SPI_TRIGGER + .trigger = spi_trigger, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = stm32_spi1register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct stm32_spidev_s g_spi1dev = +{ + .spidev = { &g_spi1ops }, + .spibase = STM32_SPI1_BASE, + .spiclock = STM32_PCLK2_FREQUENCY, +#ifdef CONFIG_STM32F0L0_SPI_INTERRUPTS + .spiirq = STM32_IRQ_SPI1, +#endif +#ifdef CONFIG_STM32F0L0_SPI_DMA + /* lines must be configured in board.h */ + .rxch = DMACHAN_SPI1_RX, + .txch = DMACHAN_SPI1_TX, +#endif +#ifdef CONFIG_PM + .pm_cb.prepare = spi_pm_prepare, +#endif +}; +#endif + +#ifdef CONFIG_STM32F0L0_SPI2 +static const struct spi_ops_s g_spi2ops = +{ + .lock = spi_lock, + .select = stm32_spi2select, + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = spi_hwfeatures, +#endif + .status = stm32_spi2status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = stm32_spi2cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +#ifdef CONFIG_SPI_TRIGGER + .trigger = spi_trigger, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = stm32_spi2register, /* provided externally */ +#else + .registercallback = 0, /* not implemented */ +#endif +}; + +static struct stm32_spidev_s g_spi2dev = +{ + .spidev = { &g_spi2ops }, + .spibase = STM32_SPI2_BASE, + .spiclock = STM32_PCLK1_FREQUENCY, +#ifdef CONFIG_STM32F0L0_SPI_INTERRUPTS + .spiirq = STM32_IRQ_SPI2, +#endif +#ifdef CONFIG_STM32F0L0_SPI_DMA + .rxch = DMACHAN_SPI2_RX, + .txch = DMACHAN_SPI2_TX, +#endif +#ifdef CONFIG_PM + .pm_cb.prepare = spi_pm_prepare, +#endif +}; +#endif + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: spi_getreg + * + * Description: + * Get the contents of the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 16-bit register + * + ************************************************************************************/ + +static inline uint16_t spi_getreg(FAR struct stm32_spidev_s *priv, uint8_t offset) +{ + return getreg16(priv->spibase + offset); +} + +/************************************************************************************ + * Name: spi_putreg + * + * Description: + * Write a 16-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 16-bit value to be written + * + * Returned Value: + * The contents of the 16-bit register + * + ************************************************************************************/ + +static inline void spi_putreg(FAR struct stm32_spidev_s *priv, uint8_t offset, + uint16_t value) +{ + putreg16(value, priv->spibase + offset); +} + +/************************************************************************************ + * Name: spi_getreg8 + * + * Description: + * Get the contents of the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 8-bit register + * + ************************************************************************************/ + +static inline uint8_t spi_getreg8(FAR struct stm32_spidev_s *priv, uint8_t offset) +{ + return getreg8(priv->spibase + offset); +} + +/************************************************************************************ + * Name: spi_putreg8 + * + * Description: + * Write a 8-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 8-bit value to be written + * + ************************************************************************************/ + +static inline void spi_putreg8(FAR struct stm32_spidev_s *priv, uint8_t offset, + uint8_t value) +{ + putreg8(value, priv->spibase + offset); +} + +/************************************************************************************ + * Name: spi_readword + * + * Description: + * Read one word (TWO bytes!) from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * Word as read + * + ************************************************************************************/ + +static inline uint16_t spi_readword(FAR struct stm32_spidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0); + + /* Then return the received byte */ + + return spi_getreg(priv, STM32_SPI_DR_OFFSET); +} + +/************************************************************************************ + * Name: spi_readbyte + * + * Description: + * Read one byte from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * Byte as read + * + ************************************************************************************/ + +static inline uint8_t spi_readbyte(FAR struct stm32_spidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0); + + /* Then return the received byte */ + + return spi_getreg8(priv, STM32_SPI_DR_OFFSET); +} + +/************************************************************************************ + * Name: spi_writeword + * + * Description: + * Write one 16-bit frame to the SPI FIFO + * + * Input Parameters: + * priv - Device-specific state data + * byte - Word to send + * + * Returned Value: + * None + * + ************************************************************************************/ + +static inline void spi_writeword(FAR struct stm32_spidev_s *priv, uint16_t word) +{ + /* Wait until the transmit buffer is empty */ + + while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_TXE) == 0); + + /* Then send the byte */ + + spi_putreg(priv, STM32_SPI_DR_OFFSET, word); +} + +/************************************************************************************ + * Name: spi_writebyte + * + * Description: + * Write one 8-bit frame to the SPI FIFO + * + * Input Parameters: + * priv - Device-specific state data + * byte - Byte to send + * + * Returned Value: + * None + * + ************************************************************************************/ + +static inline void spi_writebyte(FAR struct stm32_spidev_s *priv, uint8_t byte) +{ + /* Wait until the transmit buffer is empty */ + + while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_TXE) == 0); + + /* Then send the byte */ + + spi_putreg8(priv, STM32_SPI_DR_OFFSET, byte); +} + +/************************************************************************************ + * Name: spi_16bitmode + * + * Description: + * Check if the SPI is operating in 16-bit mode + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * true: 16-bit mode, false: 8-bit mode + * + ************************************************************************************/ + +static inline bool spi_16bitmode(FAR struct stm32_spidev_s *priv) +{ + return (priv->nbits > 8); +} + +/************************************************************************************ + * Name: spi_dmarxwaitw + * + * Description: + * Wait for DMA to complete. + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static void spi_dmarxwait(FAR struct stm32_spidev_s *priv) +{ + int ret; + + /* Take the semaphore (perhaps waiting). If the result is zero, then the DMA + * must not really have completed??? + */ + + do + { + ret = nxsem_wait(&priv->rxsem); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR || priv->rxresult == 0); +} +#endif + +/************************************************************************************ + * Name: spi_dmatxwait + * + * Description: + * Wait for DMA to complete. + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static void spi_dmatxwait(FAR struct stm32_spidev_s *priv) +{ + int ret; + + /* Take the semaphore (perhaps waiting). If the result is zero, then the DMA + * must not really have completed??? + */ + + do + { + ret = nxsem_wait(&priv->txsem); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR || priv->txresult == 0); +} +#endif + +/************************************************************************************ + * Name: spi_dmarxwakeup + * + * Description: + * Signal that DMA is complete + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static inline void spi_dmarxwakeup(FAR struct stm32_spidev_s *priv) +{ + (void)nxsem_post(&priv->rxsem); +} +#endif + +/************************************************************************************ + * Name: spi_dmatxwakeup + * + * Description: + * Signal that DMA is complete + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static inline void spi_dmatxwakeup(FAR struct stm32_spidev_s *priv) +{ + (void)nxsem_post(&priv->txsem); +} +#endif + +/************************************************************************************ + * Name: spi_dmarxcallback + * + * Description: + * Called when the RX DMA completes + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg; + + /* Wake-up the SPI driver */ + + priv->rxresult = isr | 0x080; /* OR'ed with 0x80 to assure non-zero */ + spi_dmarxwakeup(priv); +} +#endif + +/************************************************************************************ + * Name: spi_dmatxcallback + * + * Description: + * Called when the RX DMA completes + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg; + + /* Wake-up the SPI driver */ + + priv->txresult = isr | 0x080; /* OR'ed with 0x80 to assure non-zero */ + spi_dmatxwakeup(priv); +} +#endif + +/************************************************************************************ + * Name: spi_dmarxsetup + * + * Description: + * Setup to perform RX DMA + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static void spi_dmarxsetup(FAR struct stm32_spidev_s *priv, FAR void *rxbuffer, + FAR void *rxdummy, size_t nwords) +{ + /* 8- or 16-bit mode? */ + + if (spi_16bitmode(priv)) + { + /* 16-bit mode -- is there a buffer to receive data in? */ + + if (rxbuffer) + { + priv->rxccr = SPI_RXDMA16_CONFIG; + } + else + { + rxbuffer = rxdummy; + priv->rxccr = SPI_RXDMA16NULL_CONFIG; + } + } + else + { + /* 8-bit mode -- is there a buffer to receive data in? */ + + if (rxbuffer) + { + priv->rxccr = SPI_RXDMA8_CONFIG; + } + else + { + rxbuffer = rxdummy; + priv->rxccr = SPI_RXDMA8NULL_CONFIG; + } + } + + /* Configure the RX DMA */ + + stm32_dmasetup(priv->rxdma, priv->spibase + STM32_SPI_DR_OFFSET, + (uint32_t)rxbuffer, nwords, priv->rxccr); +} +#endif + +/************************************************************************************ + * Name: spi_dmatxsetup + * + * Description: + * Setup to perform TX DMA + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static void spi_dmatxsetup(FAR struct stm32_spidev_s *priv, FAR const void *txbuffer, + FAR const void *txdummy, size_t nwords) +{ + /* 8- or 16-bit mode? */ + + if (spi_16bitmode(priv)) + { + /* 16-bit mode -- is there a buffer to transfer data from? */ + + if (txbuffer) + { + priv->txccr = SPI_TXDMA16_CONFIG; + } + else + { + txbuffer = txdummy; + priv->txccr = SPI_TXDMA16NULL_CONFIG; + } + } + else + { + /* 8-bit mode -- is there a buffer to transfer data from? */ + + if (txbuffer) + { + priv->txccr = SPI_TXDMA8_CONFIG; + } + else + { + txbuffer = txdummy; + priv->txccr = SPI_TXDMA8NULL_CONFIG; + } + } + + /* Setup the TX DMA */ + + stm32_dmasetup(priv->txdma, priv->spibase + STM32_SPI_DR_OFFSET, + (uint32_t)txbuffer, nwords, priv->txccr); +} +#endif + +/************************************************************************************ + * Name: spi_dmarxstart + * + * Description: + * Start RX DMA + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static inline void spi_dmarxstart(FAR struct stm32_spidev_s *priv) +{ + priv->rxresult = 0; + stm32_dmastart(priv->rxdma, spi_dmarxcallback, priv, false); +} +#endif + +/************************************************************************************ + * Name: spi_dmatxstart + * + * Description: + * Start TX DMA + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static inline void spi_dmatxstart(FAR struct stm32_spidev_s *priv) +{ + priv->txresult = 0; + stm32_dmastart(priv->txdma, spi_dmatxcallback, priv, false); +} +#endif + +/************************************************************************************ + * Name: spi_modifycr + * + * Description: + * Clear and set bits in the CR1 or CR2 register + * + * Input Parameters: + * priv - Device-specific state data + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ************************************************************************************/ + +static void spi_modifycr(uint32_t addr, FAR struct stm32_spidev_s *priv, + uint16_t setbits, uint16_t clrbits) +{ + uint16_t cr; + + cr = spi_getreg(priv, addr); + cr &= ~clrbits; + cr |= setbits; + spi_putreg(priv, addr, cr); +} + +/************************************************************************************ + * Name: spi_lock + * + * Description: + * On SPI busses where there are multiple devices, it will be necessary to + * lock SPI to have exclusive access to the busses for a sequence of + * transfers. The bus should be locked before the chip is selected. After + * locking the SPI bus, the caller should then also call the setfrequency, + * setbits, and setmode methods to make sure that the SPI is properly + * configured for the device. If the SPI buss is being shared, then it + * may have been left in an incompatible state. + * + * Input Parameters: + * dev - Device-specific state data + * lock - true: Lock spi bus, false: unlock SPI bus + * + * Returned Value: + * None + * + ************************************************************************************/ + +static int spi_lock(FAR struct spi_dev_s *dev, bool lock) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + int ret; + + if (lock) + { + /* Take the semaphore (perhaps waiting) */ + + do + { + ret = nxsem_wait(&priv->exclsem); + + /* The only case that an error should occur here is if the wait + * was awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); + } + else + { + (void)nxsem_post(&priv->exclsem); + ret = OK; + } + + return ret; +} + +/************************************************************************************ + * Name: spi_setfrequency + * + * Description: + * Set the SPI frequency. + * + * Input Parameters: + * dev - Device-specific state data + * frequency - The SPI frequency requested + * + * Returned Value: + * Returns the actual frequency selected + * + ************************************************************************************/ + +static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + uint16_t setbits; + uint32_t actual; + + /* Limit to max possible (if STM32_SPI_CLK_MAX is defined in board.h) */ + + if (frequency > STM32_SPI_CLK_MAX) + { + frequency = STM32_SPI_CLK_MAX; + } + + /* Has the frequency changed? */ + + if (frequency != priv->frequency) + { + /* Choices are limited by PCLK frequency with a set of divisors */ + + if (frequency >= priv->spiclock >> 1) + { + /* More than fPCLK/2. This is as fast as we can go */ + + setbits = SPI_CR1_FPCLCKd2; /* 000: fPCLK/2 */ + actual = priv->spiclock >> 1; + } + else if (frequency >= priv->spiclock >> 2) + { + /* Between fPCLCK/2 and fPCLCK/4, pick the slower */ + + setbits = SPI_CR1_FPCLCKd4; /* 001: fPCLK/4 */ + actual = priv->spiclock >> 2; + } + else if (frequency >= priv->spiclock >> 3) + { + /* Between fPCLCK/4 and fPCLCK/8, pick the slower */ + + setbits = SPI_CR1_FPCLCKd8; /* 010: fPCLK/8 */ + actual = priv->spiclock >> 3; + } + else if (frequency >= priv->spiclock >> 4) + { + /* Between fPCLCK/8 and fPCLCK/16, pick the slower */ + + setbits = SPI_CR1_FPCLCKd16; /* 011: fPCLK/16 */ + actual = priv->spiclock >> 4; + } + else if (frequency >= priv->spiclock >> 5) + { + /* Between fPCLCK/16 and fPCLCK/32, pick the slower */ + + setbits = SPI_CR1_FPCLCKd32; /* 100: fPCLK/32 */ + actual = priv->spiclock >> 5; + } + else if (frequency >= priv->spiclock >> 6) + { + /* Between fPCLCK/32 and fPCLCK/64, pick the slower */ + + setbits = SPI_CR1_FPCLCKd64; /* 101: fPCLK/64 */ + actual = priv->spiclock >> 6; + } + else if (frequency >= priv->spiclock >> 7) + { + /* Between fPCLCK/64 and fPCLCK/128, pick the slower */ + + setbits = SPI_CR1_FPCLCKd128; /* 110: fPCLK/128 */ + actual = priv->spiclock >> 7; + } + else + { + /* Less than fPCLK/128. This is as slow as we can go */ + + setbits = SPI_CR1_FPCLCKd256; /* 111: fPCLK/256 */ + actual = priv->spiclock >> 8; + } + + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE); + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, setbits, SPI_CR1_BR_MASK); + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0); + + /* Save the frequency selection so that subsequent reconfigurations will be + * faster. + */ + + spiinfo("Frequency %d->%d\n", frequency, actual); + + priv->frequency = frequency; + priv->actual = actual; + } + + return priv->actual; +} + +/************************************************************************************ + * Name: spi_setmode + * + * Description: + * Set the SPI mode. see enum spi_mode_e for mode definitions + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * Returns the actual frequency selected + * + ************************************************************************************/ + +static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + uint16_t setbits; + uint16_t clrbits; + + spiinfo("mode=%d\n", mode); + + /* Has the mode changed? */ + + if (mode != priv->mode) + { + /* Yes... Set CR1 appropriately */ + + switch (mode) + { + case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ + setbits = 0; + clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA; + break; + + case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ + setbits = SPI_CR1_CPHA; + clrbits = SPI_CR1_CPOL; + break; + + case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ + setbits = SPI_CR1_CPOL; + clrbits = SPI_CR1_CPHA; + break; + + case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ + setbits = SPI_CR1_CPOL | SPI_CR1_CPHA; + clrbits = 0; + break; + + default: + return; + } + + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE); + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, setbits, clrbits); + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0); + + /* Save the mode so that subsequent re-configurations will be faster */ + + priv->mode = mode; + } +} + +/************************************************************************************ + * Name: spi_setbits + * + * Description: + * Set the number of bits per word. With STM32, this is not restricted to 8 or 16, + * but can be any value between 4 and 16. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits requested, negative value means LSB first. + * + * Returned Value: + * None + * + ************************************************************************************/ + +static void spi_setbits(FAR struct spi_dev_s *dev, int nbits) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + uint16_t setbits; + uint16_t clrbits; + int savbits = nbits; + + spiinfo("nbits=%d\n", nbits); + + /* Has the number of bits changed? */ + + if (nbits != priv->nbits) + { + /* Yes... Set CR2 appropriately */ + /* Set the number of bits (valid range 4-16) */ + + if (nbits < 4 || nbits > 16) + { + spierr("ERROR: nbits out of range: %d\n", nbits); + return; + } + + clrbits = SPI_CR2_DS_MASK; + setbits = SPI_CR2_DS_VAL(nbits); + + /* If nbits is <=8, then we are in byte mode and FRXTH shall be set + * (else, transaction will not complete). + */ + + if (nbits < 9) + { + setbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 1 byte */ + } + else + { + clrbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 2 bytes */ + } + + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE); + spi_modifycr(STM32_SPI_CR2_OFFSET, priv, setbits, clrbits); + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0); + + /* Save the selection so the subsequence re-configurations will be faster */ + + priv->nbits = savbits; // nbits has been clobbered... save the signed value. + } +} + +/**************************************************************************** + * Name: spi_hwfeatures + * + * Description: + * Set hardware-specific feature flags. + * + * Input Parameters: + * dev - Device-specific state data + * features - H/W feature flags + * + * Returned Value: + * Zero (OK) if the selected H/W features are enabled; A negated errno + * value if any H/W feature is not supportable. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_HWFEATURES +static int spi_hwfeatures(FAR struct spi_dev_s *dev, spi_hwfeatures_t features) +{ +#if defined(CONFIG_SPI_BITORDER) || defined(CONFIG_SPI_TRIGGER) + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; +#endif + +#ifdef CONFIG_SPI_BITORDER + uint16_t setbits; + uint16_t clrbits; + + spiinfo("features=%08x\n", features); + + /* Transfer data LSB first? */ + + if ((features & HWFEAT_LSBFIRST) != 0) + { + setbits = SPI_CR1_LSBFIRST; + clrbits = 0; + } + else + { + setbits = 0; + clrbits = SPI_CR1_LSBFIRST; + } + + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE); + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, setbits, clrbits); + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0); + + features &= ~HWFEAT_LSBFIRST; +#endif + +#ifdef CONFIG_SPI_TRIGGER +/* Turn deferred trigger mode on or off. Only applicable for DMA mode. If a + * transfer is deferred then the DMA will not actually be triggered until a + * subsequent call to SPI_TRIGGER to set it off. The thread will be waiting + * on the transfer completing as normal. + */ + + priv->defertrig = ((features & HWFEAT_TRIGGER) != 0); + features &= ~HWFEAT_TRIGGER; +#endif + + /* Other H/W features are not supported */ + + return (features == 0) ? OK : -ENOSYS; +} +#endif + +/************************************************************************************ + * Name: spi_send + * + * Description: + * Exchange one word on SPI + * + * Input Parameters: + * dev - Device-specific state data + * wd - The word to send. the size of the data is determined by the + * number of bits selected for the SPI interface. + * + * Returned Value: + * response + * + ************************************************************************************/ + +static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + uint32_t regval; + uint16_t ret; + + DEBUGASSERT(priv && priv->spibase); + + /* According to the number of bits, access data register as word or byte + * This is absolutely required because of packing. With <=8 bit frames, + * two bytes are received by a 16-bit read of the data register! + */ + + if (spi_16bitmode(priv)) + { + spi_writeword(priv, wd); + ret = spi_readword(priv); + } + else + { + spi_writebyte(priv, (uint8_t)(wd & 0xFF)); + ret = (uint16_t)spi_readbyte(priv); + } + + /* Check and clear any error flags (Reading from the SR clears the error + * flags). + */ + + regval = spi_getreg(priv, STM32_SPI_SR_OFFSET); + + if (spi_16bitmode(priv)) + { + spiinfo("Sent: %04x Return: %04x Status: %02x\n", wd, ret, regval); + } + else + { + spiinfo("Sent: %02x Return: %02x Status: %02x\n", wd, ret, regval); + } + + UNUSED(regval); + return ret; +} + +/************************************************************************************ + * Name: spi_exchange (no DMA). aka spi_exchange_nodma + * + * Description: + * Exchange a block of data on SPI without using DMA + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to a buffer in which to receive data + * nwords - the length of data to be exchaned in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into uint16_t's + * + * Returned Value: + * None + * + ************************************************************************************/ + +#if !defined(CONFIG_STM32F0L0_SPI_DMA) || defined(CONFIG_STM32F0L0_DMACAPABLE) +#if !defined(CONFIG_STM32F0L0_SPI_DMA) +static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +#else +static void spi_exchange_nodma(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +#endif +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + DEBUGASSERT(priv && priv->spibase); + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + + /* 8- or 16-bit mode? */ + + if (spi_16bitmode(priv)) + { + /* 16-bit mode */ + + const uint16_t *src = (const uint16_t *)txbuffer; + uint16_t *dest = (uint16_t *)rxbuffer; + uint16_t word; + + while (nwords-- > 0) + { + /* Get the next word to write. Is there a source buffer? */ + + if (src) + { + word = *src++; + } + else + { + word = 0xffff; + } + + /* Exchange one word */ + + word = spi_send(dev, word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } + else + { + /* 8-bit mode */ + + const uint8_t *src = (const uint8_t *)txbuffer; + uint8_t *dest = (uint8_t *)rxbuffer; + uint8_t word; + + while (nwords-- > 0) + { + /* Get the next word to write. Is there a source buffer? */ + + if (src) + { + word = *src++; + } + else + { + word = 0xff; + } + + /* Exchange one word */ + + word = (uint8_t)spi_send(dev, (uint16_t)word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } +} +#endif /* !CONFIG_STM32F0L0_SPI_DMA || CONFIG_STM32F0L0_DMACAPABLE */ + +/**************************************************************************** + * Name: spi_exchange (with DMA capability) + * + * Description: + * Exchange a block of data on SPI using DMA + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to a buffer in which to receive data + * nwords - the length of data to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into uint16_t's + * + * Returned Value: + * None + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI_DMA +static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + +#ifdef CONFIG_STM32F0L0_DMACAPABLE + if ((txbuffer && !stm32_dmacapable((uint32_t)txbuffer, nwords, priv->txccr)) || + (rxbuffer && !stm32_dmacapable((uint32_t)rxbuffer, nwords, priv->rxccr))) + { + /* Unsupported memory region, fall back to non-DMA method. */ + + spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords); + } + else +#endif + { + static uint16_t rxdummy = 0xffff; + static const uint16_t txdummy = 0xffff; + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + DEBUGASSERT(priv && priv->spibase); + + /* Setup DMAs */ + + spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords); + spi_dmatxsetup(priv, txbuffer, &txdummy, nwords); + +#ifdef CONFIG_SPI_TRIGGER + /* Is deferred triggering in effect? */ + + if (!priv->defertrig) + { + /* No.. Start the DMAs */ + + spi_dmarxstart(priv); + spi_dmatxstart(priv); + } + else + { + /* Yes.. indicated that we are ready to be started */ + + priv->trigarmed = true; + } +#else + /* Start the DMAs */ + + spi_dmarxstart(priv); + spi_dmatxstart(priv); +#endif + + /* Then wait for each to complete */ + + spi_dmarxwait(priv); + spi_dmatxwait(priv); + +#ifdef CONFIG_SPI_TRIGGER + priv->trigarmed = false; +#endif + } +} +#endif /* CONFIG_STM32F0L0_SPI_DMA */ + +/**************************************************************************** + * Name: spi_trigger + * + * Description: + * Trigger a previously configured DMA transfer. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * OK - Trigger was fired + * ENOTSUP - Trigger not fired due to lack of DMA support + * EIO - Trigger not fired because not previously primed + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_TRIGGER +static int spi_trigger(FAR struct spi_dev_s *dev) +{ +#ifdef CONFIG_STM32F0L0_SPI_DMA + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + + if (!priv->trigarmed) + { + return -EIO; + } + + spi_dmarxstart(priv); + spi_dmatxstart(priv); + + return OK; +#else + return -ENOSYS; +#endif +} +#endif + +/**************************************************************************** + * Name: spi_sndblock + * + * Description: + * Send a block of data on SPI + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * nwords - the length of data to send from the buffer in number of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into uint16_t's + * + * Returned Value: + * None + * + ************************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer, size_t nwords) +{ + spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords); + return spi_exchange(dev, txbuffer, NULL, nwords); +} +#endif + +/************************************************************************************ + * Name: spi_recvblock + * + * Description: + * Receive a block of data from SPI + * + * Input Parameters: + * dev - Device-specific state data + * rxbuffer - A pointer to the buffer in which to recieve data + * nwords - the length of data that can be received in the buffer in number + * of words. The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into uint16_t's + * + * Returned Value: + * None + * + ************************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *rxbuffer, size_t nwords) +{ + spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords); + return spi_exchange(dev, NULL, rxbuffer, nwords); +} +#endif + +/************************************************************************************ + * Name: spi_pm_prepare + * + * Description: + * Request the driver to prepare for a new power state. This is a + * warning that the system is about to enter into a new power state. The + * driver should begin whatever operations that may be required to enter + * power state. The driver may abort the state change mode by returning + * a non-zero value from the callback function. + * + * Input Parameters: + * cb - Returned to the driver. The driver version of the callback + * structure may include additional, driver-specific state + * data at the end of the structure. + * domain - Identifies the activity domain of the state change + * pmstate - Identifies the new PM state + * + * Returned Value: + * 0 (OK) means the event was successfully processed and that the driver + * is prepared for the PM state change. Non-zero means that the driver + * is not prepared to perform the tasks needed achieve this power setting + * and will cause the state change to be aborted. NOTE: The prepare + * method will also be recalled when reverting from lower back to higher + * power consumption modes (say because another driver refused a lower + * power state change). Drivers are not permitted to return non-zero + * values when reverting back to higher power consumption modes! + * + ************************************************************************************/ + +#ifdef CONFIG_PM +static int spi_pm_prepare(FAR struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate) +{ + struct stm32_spidev_s *priv = + (struct stm32_spidev_s *)((char *)cb - + offsetof(struct stm32_spidev_s, pm_cb)); + int sval; + + /* Logic to prepare for a reduced power state goes here. */ + + switch (pmstate) + { + case PM_NORMAL: + case PM_IDLE: + break; + + case PM_STANDBY: + case PM_SLEEP: + /* Check if exclusive lock for SPI bus is held. */ + + if (nxsem_getvalue(&priv->exclsem, &sval) < 0) + { + DEBUGASSERT(false); + return -EINVAL; + } + + if (sval <= 0) + { + /* Exclusive lock is held, do not allow entry to deeper PM states. */ + + return -EBUSY; + } + + break; + + default: + /* Should not get here */ + + break; + } + + return OK; +} +#endif + +/************************************************************************************ + * Name: spi_bus_initialize + * + * Description: + * Initialize the selected SPI bus in its default state (Master, 8-bit, mode 0, etc.) + * + * Input Parameters: + * priv - private SPI device structure + * + * Returned Value: + * None + * + ************************************************************************************/ + +static void spi_bus_initialize(FAR struct stm32_spidev_s *priv) +{ + uint16_t setbits; + uint16_t clrbits; +#ifdef CONFIG_PM + int ret; +#endif + + /* Configure CR1 and CR2. Default configuration: + * Mode 0: CR1.CPHA=0 and CR1.CPOL=0 + * Master: CR1.MSTR=1 + * 8-bit: CR2.DS=7 + * MSB tranmitted first: CR1.LSBFIRST=0 + * Replace NSS with SSI & SSI=1: CR1.SSI=1 CR1.SSM=1 (prevents MODF error) + * Two lines full duplex: CR1.BIDIMODE=0 CR1.BIDIOIE=(Don't care) and CR1.RXONLY=0 + */ + + clrbits = SPI_CR1_CPHA | SPI_CR1_CPOL | SPI_CR1_BR_MASK | SPI_CR1_LSBFIRST | + SPI_CR1_RXONLY | SPI_CR1_BIDIOE | SPI_CR1_BIDIMODE; + setbits = SPI_CR1_MSTR | SPI_CR1_SSI | SPI_CR1_SSM; + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, setbits, clrbits); + + clrbits = SPI_CR2_DS_MASK; + setbits = SPI_CR2_DS_8BIT | SPI_CR2_FRXTH; /* FRXTH must be high in 8-bit mode */ + spi_modifycr(STM32_SPI_CR2_OFFSET, priv, setbits, clrbits); + + priv->frequency = 0; + priv->nbits = 8; + priv->mode = SPIDEV_MODE0; + + /* Select a default frequency of approx. 400KHz */ + + spi_setfrequency((FAR struct spi_dev_s *)priv, 400000); + + /* CRCPOLY configuration */ + + spi_putreg(priv, STM32_SPI_CRCPR_OFFSET, 7); + + /* Initialize the SPI semaphore that enforces mutually exclusive access */ + + nxsem_init(&priv->exclsem, 0, 1); + +#ifdef CONFIG_STM32F0L0_SPI_DMA + /* Initialize the SPI semaphores that is used to wait for DMA completion */ + + nxsem_init(&priv->rxsem, 0, 0); + nxsem_init(&priv->txsem, 0, 0); + + /* These semaphores are used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_setprotocol(&priv->rxsem, SEM_PRIO_NONE); + nxsem_setprotocol(&priv->txsem, SEM_PRIO_NONE); + + /* Get DMA channels. NOTE: stm32_dmachannel() will always assign the DMA channel. + * if the channel is not available, then stm32_dmachannel() will block and wait + * until the channel becomes available. WARNING: If you have another device sharing + * a DMA channel with SPI and the code never releases that channel, then the call + * to stm32_dmachannel() will hang forever in this function! Don't let your + * design do that! + */ + + priv->rxdma = stm32_dmachannel(priv->rxch); + priv->txdma = stm32_dmachannel(priv->txch); + DEBUGASSERT(priv->rxdma && priv->txdma); + + spi_modifycr(STM32_SPI_CR2_OFFSET, priv, SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN, 0); +#endif + + /* Enable spi */ + + spi_modifycr(STM32_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0); + +#ifdef CONFIG_PM + /* Register to receive power management callbacks */ + + ret = pm_register(&priv->pm_cb); + DEBUGASSERT(ret == OK); + UNUSED(ret); +#endif +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_spibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * Port number (for hardware that has mutiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on succcess; a NULL on failure + * + ************************************************************************************/ + +FAR struct spi_dev_s *stm32_spibus_initialize(int bus) +{ + FAR struct stm32_spidev_s *priv = NULL; + + irqstate_t flags = enter_critical_section(); + +#ifdef CONFIG_STM32F0L0_SPI1 + if (bus == 1) + { + /* Select SPI1 */ + + priv = &g_spi1dev; + + /* Only configure if the bus is not already configured */ + + if (!priv->initialized) + { + /* Configure SPI1 pins: SCK, MISO, and MOSI */ + + stm32_configgpio(GPIO_SPI1_SCK); + stm32_configgpio(GPIO_SPI1_MISO); + stm32_configgpio(GPIO_SPI1_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + spi_bus_initialize(priv); + priv->initialized = true; + } + } + else +#endif +#ifdef CONFIG_STM32F0L0_SPI2 + if (bus == 2) + { + /* Select SPI2 */ + + priv = &g_spi2dev; + + /* Only configure if the bus is not already configured */ + + if (!priv->initialized) + { + /* Configure SPI2 pins: SCK, MISO, and MOSI */ + + stm32_configgpio(GPIO_SPI2_SCK); + stm32_configgpio(GPIO_SPI2_MISO); + stm32_configgpio(GPIO_SPI2_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + spi_bus_initialize(priv); + priv->initialized = true; + } + } + else +#endif + { + spierr("ERROR: Unsupported SPI bus: %d\n", bus); + return NULL; + } + + leave_critical_section(flags); + return (FAR struct spi_dev_s *)priv; +} + +#endif /* CONFIG_STM32F0L0_SPI */ diff --git a/arch/arm/src/stm32f0l0/stm32_spi.h b/arch/arm/src/stm32f0l0/stm32_spi.h new file mode 100644 index 00000000000..b9d84b82a34 --- /dev/null +++ b/arch/arm/src/stm32f0l0/stm32_spi.h @@ -0,0 +1,168 @@ +/************************************************************************************ + * arch/arm/src/stm32f0l0/stm32_spi.h + * + * Copyright (C) 2009, 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32_STM32_SPI_H +#define __ARCH_ARM_SRC_STM32_STM32_SPI_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include "chip.h" +#include "hardware/stm32_spi.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +struct spi_dev_s; + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_spibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * bus number (for hardware that has mutiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on succcess; a NULL on failure + * + ************************************************************************************/ + +FAR struct spi_dev_s *stm32_spibus_initialize(int bus); + +/************************************************************************************ + * Name: stm32_spi1/2/...select and stm32_spi1/2/...status + * + * Description: + * The external functions, stm32_spi1/2/...select, stm32_spi1/2/...status, and + * stm32_spi1/2/...cmddata must be provided by board-specific logic. These are + * implementations of the select, status, and cmddata methods of the SPI interface + * defined by struct spi_ops_s (see include/nuttx/spi/spi.h). All other methods + * (including stm32_spibus_initialize()) are provided by common STM32 logic. To use this + * common SPI logic on your board: + * + * 1. Provide logic in stm32_board_initialize() to configure SPI chip select + * pins. + * 2. Provide stm32_spi1/2/...select() and stm32_spi1/2/...status() functions in your + * board-specific logic. These functions will perform chip selection and + * status operations using GPIOs in the way your board is configured. + * 3. If CONFIG_SPI_CMDDATA is defined in your NuttX configuration file, then + * provide stm32_spi1/2/...cmddata() functions in your board-specific logic. + * These functions will perform cmd/data selection operations using GPIOs in the + * way your board is configured. + * 4. Add a calls to stm32_spibus_initialize() in your low level application + * initialization logic + * 5. The handle returned by stm32_spibus_initialize() may then be used to bind the + * SPI driver to higher level logic (e.g., calling + * mmcsd_spislotinitialize(), for example, will bind the SPI driver to + * the SPI MMC/SD driver). + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI1 +void stm32_spi1select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected); +uint8_t stm32_spi1status(FAR struct spi_dev_s *dev, uint32_t devid); +int stm32_spi1cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif + +#ifdef CONFIG_STM32F0L0_SPI2 +void stm32_spi2select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected); +uint8_t stm32_spi2status(FAR struct spi_dev_s *dev, uint32_t devid); +int stm32_spi2cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif + +/************************************************************************************ + * Name: stm32_spi1/2/...register + * + * Description: + * If the board supports a card detect callback to inform the SPI-based MMC/SD + * driver when an SD card is inserted or removed, then CONFIG_SPI_CALLBACK should + * be defined and the following function(s) must be implemented. These functions + * implements the registercallback method of the SPI interface (see + * include/nuttx/spi/spi.h for details) + * + * Input Parameters: + * dev - Device-specific state data + * callback - The function to call on the media change + * arg - A caller provided value to return with the callback + * + * Returned Value: + * 0 on success; negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_CALLBACK +#ifdef CONFIG_STM32F0L0_SPI1 +int stm32_spi1register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, + FAR void *arg); +#endif + +#ifdef CONFIG_STM32F0L0_SPI2 +int stm32_spi2register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, + FAR void *arg); +#endif +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_STM32_STM32_SPI_H */ diff --git a/configs/b-l072z-lrwan1/include/board.h b/configs/b-l072z-lrwan1/include/board.h index bb4f9db03b0..7b6a52901aa 100644 --- a/configs/b-l072z-lrwan1/include/board.h +++ b/configs/b-l072z-lrwan1/include/board.h @@ -158,7 +158,7 @@ /* The Nucleo LO73RZ supports two buttons; only one button is controllable * by software: * - * B1 USER: user button connected to the I/O PC13 of the STM32LO73RZ. + * B1 USER: user button connected to the I/O PB2/PA0 of the STM32LO73RZ. * B2 RESET: push button connected to NRST is used to RESET the * STM32LO73RZ. */ @@ -170,19 +170,50 @@ /* Alternate function pin selections ****************************************/ -/* I2C */ - -#define GPIO_I2C1_SCL GPIO_I2C1_SCL_3 -#define GPIO_I2C1_SDA GPIO_I2C1_SDA_3 - -/* SPI */ - -#define GPIO_SPI1_MISO GPIO_SPI1_MISO_1 -#define GPIO_SPI1_MOSI GPIO_SPI1_MOSI_1 -#define GPIO_SPI1_SCK GPIO_SPI1_SCK_1 +/* CMWX1ZZABZ-091 module pinout and internal connections + * + * STM32L072CZ | Function + * ------------+----------- + * PC0 | SX1276_CE (NRESET) + * PA7 | SX1276_MOSI + * PA6 | SX1276_MISO + * PB3 | SX1276_SCK + * PA15 | SX1276_NSS + * ? | SX1276_DIO0 + * ? | SX1276_DIO2 + * ? | SX1276_DIO3 + * PA5 | SX1276_DIO4 optional + * PA4 | SX1276_DIO5 optional + * ? | CRF1 + * ? | CRF2 + * ? | CRF3 + * PA3 | STLINK Virtual COM RX + * PA2 | STLINK Virtual COM TX + * PA10 | USART1_RX + * PA9 | USART1_TX + * PB15 | SPI2_MOSI + * PB14 | SPI2_MISO + * PB13 | SPI2_SCK + * PB12 | SPI2_NSS + * PB5 | LPTIM1_INI + * PB6 | LPTIM1_ETR + * PB7 | LPTIM1_IN2 + * PB2 | LPTIM1_OUT / BUTTON + * PA0 | BUTTON (optional) + * PB9 | I2C1_SDA + * PB8 | I2C1_SCL + * PA12 | USB_DP optional / TCXO_VCC + * PA11 | USB_DM optional + * + */ /* USART */ +/* USART1 */ + +#define GPIO_USART1_RX GPIO_USART1_RX_1 /* PA10 */ +#define GPIO_USART1_TX GPIO_USART1_TX_1 /* PA9 */ + /* By default the USART2 is connected to STLINK Virtual COM Port: * USART2_RX - PA3 * USART2_TX - PA2 @@ -191,7 +222,28 @@ #define GPIO_USART2_RX GPIO_USART2_RX_1 /* PA3 */ #define GPIO_USART2_TX GPIO_USART2_TX_1 /* PA2 */ -/* COMP */ +/* SPI */ + +/* SPI1 is connected to SX1276 radio */ + +#define GPIO_SPI1_MOSI GPIO_SPI1_MOSI_2 /* PA7 */ +#define GPIO_SPI1_MISO GPIO_SPI1_MOSI_2 /* PA6 */ +#define GPIO_SPI1_SCK GPIO_SPI1_SCK_2 /* PB3 */ +#define GPIO_SPI1_NSS GPIO_SPI1_NSS_1 /* PA15 */ + +/* SPI2 */ + +#define GPIO_SPI2_MOSI GPIO_SPI2_MOSI_1 /* PB15 */ +#define GPIO_SPI2_MISO GPIO_SPI2_MISO_1 /* PB14 */ +#define GPIO_SPI2_SCK GPIO_SPI2_SCK_3 /* PB13 */ +#define GPIO_SPI2_NSS GPIO_SPI2_NSS_1 /* PB12 */ + +/* I2C */ + +/* I2C1 */ + +#define GPIO_I2C1_SDA GPIO_I2C1_SDA_2 /* PB9 */ +#define GPIO_I2C1_SCLK GPIO_I2C1_SCL_2 /* PB8 */ /* DMA channels *************************************************************/ /* ADC */ diff --git a/configs/nucleo-l073rz/include/board.h b/configs/nucleo-l073rz/include/board.h index 03c264d5846..6461f2ab4d2 100644 --- a/configs/nucleo-l073rz/include/board.h +++ b/configs/nucleo-l073rz/include/board.h @@ -172,14 +172,14 @@ /* I2C */ -#define GPIO_I2C1_SCL GPIO_I2C1_SCL_3 -#define GPIO_I2C1_SDA GPIO_I2C1_SDA_3 +#define GPIO_I2C1_SCL GPIO_I2C1_SCL_2 /* D15 - PB8 */ +#define GPIO_I2C1_SDA GPIO_I2C1_SDA_2 /* D14 - PB9 */ /* SPI */ -#define GPIO_SPI1_MISO GPIO_SPI1_MISO_1 -#define GPIO_SPI1_MOSI GPIO_SPI1_MOSI_1 -#define GPIO_SPI1_SCK GPIO_SPI1_SCK_1 +#define GPIO_SPI1_MISO GPIO_SPI1_MISO_2 /* D12 - PA6 */ +#define GPIO_SPI1_MOSI GPIO_SPI1_MOSI_2 /* D11 - PA7 */ +#define GPIO_SPI1_SCK GPIO_SPI1_SCK_1 /* D13 - PA5 */ /* USART */ @@ -191,8 +191,6 @@ #define GPIO_USART2_RX GPIO_USART2_RX_1 /* PA3 */ #define GPIO_USART2_TX GPIO_USART2_TX_1 /* PA2 */ -/* COMP */ - /* DMA channels *************************************************************/ /* ADC */ diff --git a/configs/nucleo-l073rz/src/Makefile b/configs/nucleo-l073rz/src/Makefile index 7b4fd80b3d7..3057ce5adcd 100644 --- a/configs/nucleo-l073rz/src/Makefile +++ b/configs/nucleo-l073rz/src/Makefile @@ -36,7 +36,7 @@ -include $(TOPDIR)/Make.defs ASRCS = -CSRCS = stm32_boot.c +CSRCS = stm32_boot.c stm32_bringup.c ifeq ($(CONFIG_ARCH_LEDS),y) CSRCS += stm32_autoleds.c @@ -48,8 +48,16 @@ ifeq ($(CONFIG_ARCH_BUTTONS),y) CSRCS += stm32_buttons.c endif +ifeq ($(CONFIG_STM32F0L0_SPI),y) +CSRCS += stm32_spi.c +endif + ifeq ($(CONFIG_LIB_BOARDCTL),y) CSRCS += stm32_appinit.c endif +ifeq ($(CONFIG_WL_NRF24L01),y) +CSRCS += stm32_nrf24l01.c +endif + include $(TOPDIR)/configs/Board.mk diff --git a/configs/nucleo-l073rz/src/nucleo-l073rz.h b/configs/nucleo-l073rz/src/nucleo-l073rz.h index 22ed06b4567..b89aedfb475 100644 --- a/configs/nucleo-l073rz/src/nucleo-l073rz.h +++ b/configs/nucleo-l073rz/src/nucleo-l073rz.h @@ -87,6 +87,18 @@ #define GPIO_BTN_USER (GPIO_INPUT | GPIO_FLOAT | GPIO_EXTI | GPIO_PORTC | \ GPIO_PIN13) +/* NRF24L01 + * CS - PA8 (D7) + * CE - PA9 (D8) + * IRQ - PC7 (D9) + */ + +#define GPIO_NRF24L01_CS (GPIO_OUTPUT | GPIO_SPEED_HIGH | \ + GPIO_OUTPUT_SET | GPIO_PORTA | GPIO_PIN8) +#define GPIO_NRF24L01_CE (GPIO_OUTPUT | GPIO_SPEED_HIGH | \ + GPIO_OUTPUT_CLEAR | GPIO_PORTA | GPIO_PIN9) +#define GPIO_NRF24L01_IRQ (GPIO_INPUT | GPIO_FLOAT | GPIO_PORTC | GPIO_PIN7) + /**************************************************************************** * Public Data ****************************************************************************/ @@ -95,4 +107,43 @@ * Public Function Prototypes ****************************************************************************/ +/************************************************************************************ + * Name: stm32_bringup + * + * Description: + * Perform architecture-specific initialization + * + * CONFIG_BOARD_INITIALIZE=y : + * Called from board_initialize(). + * + * CONFIG_BOARD_INITIALIZE=y && CONFIG_LIB_BOARDCTL=y : + * Called from the NSH library + * + ************************************************************************************/ + +int stm32_bringup(void); + +/************************************************************************************ + * Name: stm32_spidev_initialize + * + * Description: + * Called to configure SPI chip select GPIO pins for the Nucleo-H743ZI board. + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI +void stm32_spidev_initialize(void); +#endif + +/***************************************************************************** + * Name: stm32_wlinitialize + * + * Description: + * Initialize NRF24L01 wireless interaface. + ****************************************************************************/ + +#ifdef CONFIG_WL_NRF24L01 +int stm32_wlinitialize(void); +#endif + #endif /* __CONFIGS_NUCLEO_L073RZ_SRC_NUCLEO_L073RZ_H */ diff --git a/configs/nucleo-l073rz/src/stm32_appinit.c b/configs/nucleo-l073rz/src/stm32_appinit.c index 4641cf3ab80..bcbeb860f4b 100644 --- a/configs/nucleo-l073rz/src/stm32_appinit.c +++ b/configs/nucleo-l073rz/src/stm32_appinit.c @@ -51,18 +51,6 @@ * Pre-processor Definitions ****************************************************************************/ -#undef HAVE_LEDS -#undef HAVE_DAC - -#if !defined(CONFIG_ARCH_LEDS) && defined(CONFIG_USERLED_LOWER) -# define HAVE_LEDS 1 -#endif - -#if defined(CONFIG_DAC) -# define HAVE_DAC1 1 -# define HAVE_DAC2 1 -#endif - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -94,59 +82,13 @@ int board_app_initialize(uintptr_t arg) { - int ret; +#ifdef CONFIG_BOARD_INITIALIZE + /* Board initialization already performed by board_initialize() */ -#ifdef HAVE_LEDS - /* Register the LED driver */ - - ret = userled_lower_initialize(LED_DRIVER_PATH); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: userled_lower_initialize() failed: %d\n", ret); - return ret; - } -#endif - -#ifdef CONFIG_ADC - /* Initialize ADC and register the ADC driver. */ - - ret = stm32_adc_setup(); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: stm32_adc_setup failed: %d\n", ret); - } -#endif - -#ifdef CONFIG_DAC - /* Initialize DAC and register the DAC driver. */ - - ret = stm32_dac_setup(); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: stm32_dac_setup failed: %d\n", ret); - } -#endif - -#ifdef CONFIG_COMP - /* Initialize COMP and register the COMP driver. */ - - ret = stm32_comp_setup(); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: stm32_comp_setup failed: %d\n", ret); - } -#endif - -#ifdef CONFIG_OPAMP - /* Initialize OPAMP and register the OPAMP driver. */ - - ret = stm32_opamp_setup(); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: stm32_opamp_setup failed: %d\n", ret); - } -#endif - - UNUSED(ret); return OK; +#else + /* Perform board-specific initialization */ + + return stm32_bringup(); +#endif } diff --git a/configs/nucleo-l073rz/src/stm32_boot.c b/configs/nucleo-l073rz/src/stm32_boot.c index c5ff5d420ea..cb69d7a8863 100644 --- a/configs/nucleo-l073rz/src/stm32_boot.c +++ b/configs/nucleo-l073rz/src/stm32_boot.c @@ -84,4 +84,33 @@ void stm32_boardinitialize(void) board_autoled_initialize(); #endif +#ifdef CONFIG_STM32F0L0_SPI + /* Configure SPI chip selects */ + + stm32_spidev_initialize(); +#endif } + +/************************************************************************************ + * Name: board_initialize + * + * Description: + * If CONFIG_BOARD_INITIALIZE is selected, then an additional initialization call + * will be performed in the boot-up sequence to a function called + * board_initialize(). board_initialize() will be called immediately after + * up_initialize() is called and just before the initial application is started. + * This additional initialization phase may be used, for example, to initialize + * board-specific device drivers. + * + ************************************************************************************/ + +#ifdef CONFIG_BOARD_INITIALIZE +void board_initialize(void) +{ +#if defined(CONFIG_NSH_LIBRARY) && !defined(CONFIG_LIB_BOARDCTL) + /* Perform board bring-up here instead of from the board_app_initialize(). */ + + (void)stm32_bringup(); +#endif +} +#endif diff --git a/configs/nucleo-l073rz/src/stm32_bringup.c b/configs/nucleo-l073rz/src/stm32_bringup.c new file mode 100644 index 00000000000..9d9749548f3 --- /dev/null +++ b/configs/nucleo-l073rz/src/stm32_bringup.c @@ -0,0 +1,148 @@ +/**************************************************************************** + * configs/nucleo-l073rz/src/stm32_bringup.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Authors: Mateusz Szafoni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include "nucleo-l073rz.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#undef HAVE_LEDS +#undef HAVE_DAC + +#if !defined(CONFIG_ARCH_LEDS) && defined(CONFIG_USERLED_LOWER) +# define HAVE_LEDS 1 +#endif + +#if defined(CONFIG_DAC) +# define HAVE_DAC1 1 +# define HAVE_DAC2 1 +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ +/**************************************************************************** + * Name: stm32_bringup + * + * Description: + * Perform architecture-specific initialization + * + * CONFIG_BOARD_INITIALIZE=y : + * Called from board_initialize(). + * + * CONFIG_BOARD_INITIALIZE=n && CONFIG_LIB_BOARDCTL=y && CONFIG_NSH_ARCHINIT: + * Called from the NSH library + * + ****************************************************************************/ + +int stm32_bringup(void) +{ + int ret; + +#ifdef HAVE_LEDS + /* Register the LED driver */ + + ret = userled_lower_initialize(LED_DRIVER_PATH); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: userled_lower_initialize() failed: %d\n", ret); + return ret; + } +#endif + +#ifdef CONFIG_ADC + /* Initialize ADC and register the ADC driver. */ + + ret = stm32_adc_setup(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: stm32_adc_setup failed: %d\n", ret); + } +#endif + +#ifdef CONFIG_DAC + /* Initialize DAC and register the DAC driver. */ + + ret = stm32_dac_setup(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: stm32_dac_setup failed: %d\n", ret); + } +#endif + +#ifdef CONFIG_COMP + /* Initialize COMP and register the COMP driver. */ + + ret = stm32_comp_setup(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: stm32_comp_setup failed: %d\n", ret); + } +#endif + +#ifdef CONFIG_OPAMP + /* Initialize OPAMP and register the OPAMP driver. */ + + ret = stm32_opamp_setup(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: stm32_opamp_setup failed: %d\n", ret); + } +#endif + +#ifdef CONFIG_WL_NRF24L01 + ret = stm32_wlinitialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to initialize wireless driver: %d\n", ret); + } +#endif /* CONFIG_WL_NRF24L01 */ + + UNUSED(ret); + return OK; +} diff --git a/configs/nucleo-l073rz/src/stm32_nrf24l01.c b/configs/nucleo-l073rz/src/stm32_nrf24l01.c new file mode 100644 index 00000000000..e2630beb130 --- /dev/null +++ b/configs/nucleo-l073rz/src/stm32_nrf24l01.c @@ -0,0 +1,136 @@ +/************************************************************************************ + * configs/nucleo-l073rz/src/stm32_nrf24l01.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Mateusz Szafoni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "up_arch.h" +#include "chip.h" +#include "stm32.h" +#include "nucleo-l073rz.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#define NRF24L01_SPI 1 + +/************************************************************************************ + * Private Function Prototypes + ************************************************************************************/ + +static int nrf24l01_irq_attach(xcpt_t isr, FAR void *arg); +static void nrf24l01_chip_enable(bool enable); + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +static struct nrf24l01_config_s nrf_cfg = +{ + .irqattach = nrf24l01_irq_attach, + .chipenable = nrf24l01_chip_enable, +}; + +static xcpt_t g_isr; +static FAR void *g_arg; + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +static int nrf24l01_irq_attach(xcpt_t isr, FAR void *arg) +{ + wlinfo("Attach IRQ\n"); + g_isr = isr; + g_arg = arg; + (void)stm32_gpiosetevent(GPIO_NRF24L01_IRQ, false, true, false, g_isr, g_arg); + return OK; +} + +static void nrf24l01_chip_enable(bool enable) +{ + wlinfo("CE:%d\n", enable); + stm32_gpiowrite(GPIO_NRF24L01_CE, enable); +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +int stm32_wlinitialize(void) +{ + FAR struct spi_dev_s *spidev; + int ret = OK; + + syslog(LOG_INFO, "Register the nRF24L01 module\n"); + + /* Setup CE & IRQ line IOs */ + + stm32_configgpio(GPIO_NRF24L01_CE); + stm32_configgpio(GPIO_NRF24L01_IRQ); + + /* Init SPI bus */ + + spidev = stm32_spibus_initialize(NRF24L01_SPI); + if (!spidev) + { + wlerr("ERROR: Failed to initialize SPI %d bus\n", NRF24L01_SPI); + ret = -ENODEV; + goto errout; + } + + ret = nrf24l01_register(spidev, &nrf_cfg); + if (ret != OK) + { + wlerr("ERROR: Failed to register initialize SPI bus\n"); + goto errout; + } + +errout: + return ret; +} diff --git a/configs/nucleo-l073rz/src/stm32_spi.c b/configs/nucleo-l073rz/src/stm32_spi.c new file mode 100644 index 00000000000..54848125c78 --- /dev/null +++ b/configs/nucleo-l073rz/src/stm32_spi.c @@ -0,0 +1,196 @@ +/**************************************************************************** + * configs/nucleo-l073rz/src/stm32_spi.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Mateusz Szafoni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + + +#include + +#include +#include +#include +#include + +#include + +#include "up_arch.h" +#include "chip.h" +#include "stm32_gpio.h" +#include "stm32_spi.h" + +#include "nucleo-l073rz.h" +#include + +#ifdef CONFIG_STM32F0L0_SPI + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/************************************************************************************ + * Name: stm32_spidev_initialize + * + * Description: + * Called to configure SPI chip select GPIO pins for the Nucleo-144 board. + * + ************************************************************************************/ + +void stm32_spidev_initialize(void) +{ + /* NOTE: Clocking for SPI1 and/or SPI3 was already provided in stm32_rcc.c. + * Configurations of SPI pins is performed in stm32_spi.c. + * Here, we only initialize chip select pins unique to the board + * architecture. + */ + +#ifdef CONFIG_STM32F0L0_SPI1 +# ifdef CONFIG_WL_NRF24L01 + /* Configure the SPI-based NRF24L01 chip select GPIO */ + + spiinfo("Configure GPIO for SPI1/CS\n"); + + stm32_configgpio(GPIO_NRF24L01_CS); + stm32_gpiowrite(GPIO_NRF24L01_CS, true); +# endif +#endif +} + +/**************************************************************************** + * Name: stm32_spi1/2/select and stm32_spi1/2/status + * + * Description: + * The external functions, stm32_spi1/2select and stm32_spi1/2status + * must be provided by board-specific logic. They are implementations of + * the select and status methods of the SPI interface defined by struct + * spi_ops_s (see include/nuttx/spi/spi.h). All other methods (including + * stm32_spibus_initialize()) are provided by common STM32 logic. To use this + * common SPI logic on your board: + * + * 1. Provide logic in stm32_boardinitialize() to configure SPI chip select + * pins. + * 2. Provide stm32_spi1/2select() and stm32_spi1/2status() functions + * in your board-specific logic. These functions will perform chip + * selection and status operations using GPIOs in the way your board is + * configured. + * 3. Add a calls to stm32_spibus_initialize() in your low level application + * initialization logic + * 4. The handle returned by stm32_spibus_initialize() may then be used to bind + * the SPI driver to higher level logic (e.g., calling + * mmcsd_spislotinitialize(), for example, will bind the SPI driver to + * the SPI MMC/SD driver). + * + ****************************************************************************/ + +#ifdef CONFIG_STM32F0L0_SPI1 +void stm32_spi1select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + + switch (devid) + { +#ifdef CONFIG_WL_NRF24L01 + case SPIDEV_WIRELESS(0): + { + spiinfo("nRF24L01 device %s\n", + selected ? "asserted" : "de-asserted"); + + /* Set the GPIO low to select and high to de-select */ + + stm32_gpiowrite(GPIO_NRF24L01_CS, !selected); + break; + } +#endif + default: + { + break; + } + } +} + +uint8_t stm32_spi1status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + uint8_t status = 0; + + switch (devid) + { +#ifdef CONFIG_WL_NRF24L01 + case SPIDEV_WIRELESS(0): + { + status |= SPI_STATUS_PRESENT; + break; + } +#endif + default: + { + break; + } + } + + return status; +} +#endif /* CONFIG_STM32F0L0_SPI1 */ + +#ifdef CONFIG_STM32F0L0_SPI2 +void stm32_spi2select(FAR struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); +} + +uint8_t stm32_spi2status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} +#endif /* CONFIG_STM32F0L0_SPI2 */ + +#endif