/**************************************************************************** * arch/arm/src/stm32f0l0g0/stm32_serial_v2.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_SERIAL_TERMIOS # include #endif #include "arm_arch.h" #include "arm_internal.h" #include "chip.h" #include "stm32_gpio.h" #include "hardware/stm32_pinmap.h" #include "stm32_rcc.h" #include "stm32_uart.h" #include #ifdef CONFIG_STM32F0L0G0_USART3 # error not supported yet #endif #ifdef CONFIG_STM32F0L0G0_USART4 # error not supported yet #endif /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Some sanity checks *******************************************************/ /* Total number of possible serial devices */ #define STM32_NSERIAL (STM32_NUSART) #define HAVE_UART /* Power management definitions */ #if defined(CONFIG_PM) && !defined(CONFIG_STM32F0L0G0_PM_SERIAL_ACTIVITY) # define CONFIG_STM32F0L0G0_PM_SERIAL_ACTIVITY 10 #endif #if defined(CONFIG_PM) # define PM_IDLE_DOMAIN 0 /* Revisit */ #endif /* Keep track if a Break was set * * Note: * * 1) This value is set in the priv->ie but never written to the control * register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE * 2) USART_CR3_EIE is also carried in the up_dev_s ie member. * * See up_restoreusartint where the masking is done. */ #ifdef CONFIG_STM32F0L0G0_SERIALBRK_BSDCOMPAT # define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15 # define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS) #endif #ifdef USE_SERIALDRIVER #ifdef HAVE_UART /* Warnings for potentially unsafe configuration combinations. */ #if defined(CONFIG_STM32F0L0G0_FLOWCONTROL_BROKEN) && \ !defined(CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS) # error "CONFIG_STM32F0L0G0_FLOWCONTROL_BROKEN requires \ CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS to be enabled." #endif /**************************************************************************** * Private Types ****************************************************************************/ struct up_dev_s { struct uart_dev_s dev; /* Generic UART device */ uint16_t ie; /* Saved interrupt mask bits value */ uint16_t sr; /* Saved status bits */ /* Has been initialized and HW is setup. */ bool initialized; /* If termios are supported, then the following fields may vary at * runtime. */ #ifdef CONFIG_SERIAL_TERMIOS uint8_t parity; /* 0=none, 1=odd, 2=even */ uint8_t bits; /* Number of bits (7 or 8) */ bool stopbits2; /* True: Configure with 2 stop bits instead of 1 */ #ifdef CONFIG_SERIAL_IFLOWCONTROL bool iflow; /* input flow control (RTS) enabled */ #endif #ifdef CONFIG_SERIAL_OFLOWCONTROL bool oflow; /* output flow control (CTS) enabled */ #endif uint32_t baud; /* Configured baud */ #else const uint8_t rxftcfg; /* Rx FIFO threshold level */ const uint8_t parity; /* 0=none, 1=odd, 2=even */ const uint8_t bits; /* Number of bits (7 or 8) */ const bool stopbits2; /* True: Configure with 2 stop bits instead of 1 */ #ifdef CONFIG_SERIAL_IFLOWCONTROL const bool iflow; /* input flow control (RTS) enabled */ #endif #ifdef CONFIG_SERIAL_OFLOWCONTROL const bool oflow; /* output flow control (CTS) enabled */ #endif const uint32_t baud; /* Configured baud */ #endif const uint8_t irq; /* IRQ associated with this USART */ const uint32_t apbclock; /* PCLK 1 or 2 frequency */ const uint32_t usartbase; /* Base address of USART registers */ const uint32_t tx_gpio; /* U[S]ART TX GPIO pin configuration */ const uint32_t rx_gpio; /* U[S]ART RX GPIO pin configuration */ #ifdef CONFIG_SERIAL_IFLOWCONTROL const uint32_t rts_gpio; /* U[S]ART RTS GPIO pin configuration */ #endif #ifdef CONFIG_SERIAL_OFLOWCONTROL const uint32_t cts_gpio; /* U[S]ART CTS GPIO pin configuration */ #endif #ifdef HAVE_RS485 const uint32_t rs485_dir_gpio; /* U[S]ART RS-485 DIR GPIO pin configuration */ const bool rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */ #endif }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static void up_set_format(struct uart_dev_s *dev); static int up_setup(struct uart_dev_s *dev); static void up_shutdown(struct uart_dev_s *dev); static int up_attach(struct uart_dev_s *dev); static void up_detach(struct uart_dev_s *dev); static int up_interrupt(int irq, void *context, FAR void *arg); static int up_ioctl(struct file *filep, int cmd, unsigned long arg); static int up_receive(struct uart_dev_s *dev, unsigned int *status); static void up_rxint(struct uart_dev_s *dev, bool enable); static bool up_rxavailable(struct uart_dev_s *dev); #ifdef CONFIG_SERIAL_IFLOWCONTROL static bool up_rxflowcontrol(struct uart_dev_s *dev, unsigned int nbuffered, bool upper); #endif static void up_send(struct uart_dev_s *dev, int ch); static void up_txint(struct uart_dev_s *dev, bool enable); static bool up_txready(struct uart_dev_s *dev); #ifdef CONFIG_PM static void up_pm_notify(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate); static int up_pm_prepare(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate); #endif /**************************************************************************** * Private Data ****************************************************************************/ static const struct uart_ops_s g_uart_ops = { .setup = up_setup, .shutdown = up_shutdown, .attach = up_attach, .detach = up_detach, .ioctl = up_ioctl, .receive = up_receive, .rxint = up_rxint, .rxavailable = up_rxavailable, #ifdef CONFIG_SERIAL_IFLOWCONTROL .rxflowcontrol = up_rxflowcontrol, #endif .send = up_send, .txint = up_txint, .txready = up_txready, .txempty = up_txready, }; /* Receive/Transmit buffers */ #ifdef CONFIG_STM32F0L0G0_USART1 static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE]; static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE]; #endif #ifdef CONFIG_STM32F0L0G0_USART2 static char g_usart2rxbuffer[CONFIG_USART2_RXBUFSIZE]; static char g_usart2txbuffer[CONFIG_USART2_TXBUFSIZE]; #endif #ifdef CONFIG_STM32F0L0G0_USART3 static char g_usart3rxbuffer[CONFIG_USART3_RXBUFSIZE]; static char g_usart3txbuffer[CONFIG_USART3_TXBUFSIZE]; #endif #ifdef CONFIG_STM32F0L0G0_USART4 static char g_usart4rxbuffer[CONFIG_USART4_RXBUFSIZE]; static char g_usart4txbuffer[CONFIG_USART4_TXBUFSIZE]; #endif /* This describes the state of the STM32 USART1 ports. */ #ifdef CONFIG_STM32F0L0G0_USART1 static struct up_dev_s g_usart1priv = { .dev = { #if CONSOLE_USART == 1 .isconsole = true, #endif .recv = { .size = CONFIG_USART1_RXBUFSIZE, .buffer = g_usart1rxbuffer, }, .xmit = { .size = CONFIG_USART1_TXBUFSIZE, .buffer = g_usart1txbuffer, }, .ops = &g_uart_ops, .priv = &g_usart1priv, }, .irq = STM32_IRQ_USART1, .rxftcfg = CONFIG_USART1_RXFIFO_THRES, .parity = CONFIG_USART1_PARITY, .bits = CONFIG_USART1_BITS, .stopbits2 = CONFIG_USART1_2STOP, .baud = CONFIG_USART1_BAUD, .apbclock = STM32_PCLK1_FREQUENCY, .usartbase = STM32_USART1_BASE, .tx_gpio = GPIO_USART1_TX, .rx_gpio = GPIO_USART1_RX, #if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL) .oflow = true, .cts_gpio = GPIO_USART1_CTS, #endif #if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL) .iflow = true, .rts_gpio = GPIO_USART1_RTS, #endif #ifdef CONFIG_USART1_RS485 .rs485_dir_gpio = GPIO_USART1_RS485_DIR, # if (CONFIG_USART1_RS485_DIR_POLARITY == 0) .rs485_dir_polarity = false, # else .rs485_dir_polarity = true, # endif #endif }; #endif /* This describes the state of the STM32 USART2 port. */ #ifdef CONFIG_STM32F0L0G0_USART2 static struct up_dev_s g_usart2priv = { .dev = { #if CONSOLE_USART == 2 .isconsole = true, #endif .recv = { .size = CONFIG_USART2_RXBUFSIZE, .buffer = g_usart2rxbuffer, }, .xmit = { .size = CONFIG_USART2_TXBUFSIZE, .buffer = g_usart2txbuffer, }, .ops = &g_uart_ops, .priv = &g_usart2priv, }, .irq = STM32_IRQ_USART2, .rxftcfg = CONFIG_USART2_RXFIFO_THRES, .parity = CONFIG_USART2_PARITY, .bits = CONFIG_USART2_BITS, .stopbits2 = CONFIG_USART2_2STOP, .baud = CONFIG_USART2_BAUD, .apbclock = STM32_PCLK1_FREQUENCY, .usartbase = STM32_USART2_BASE, .tx_gpio = GPIO_USART2_TX, .rx_gpio = GPIO_USART2_RX, #if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART2_OFLOWCONTROL) .oflow = true, .cts_gpio = GPIO_USART2_CTS, #endif #if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART2_IFLOWCONTROL) .iflow = true, .rts_gpio = GPIO_USART2_RTS, #endif #ifdef CONFIG_USART2_RS485 .rs485_dir_gpio = GPIO_USART2_RS485_DIR, # if (CONFIG_USART2_RS485_DIR_POLARITY == 0) .rs485_dir_polarity = false, # else .rs485_dir_polarity = true, # endif #endif }; #endif /* TODO: USART3 */ /* TODO: USART4 */ /* This table lets us iterate over the configured USARTs */ static struct up_dev_s * const g_uart_devs[STM32_NSERIAL] = { #ifdef CONFIG_STM32F0L0G0_USART1 [0] = &g_usart1priv, #endif #ifdef CONFIG_STM32F0L0G0_USART2 [1] = &g_usart2priv, #endif #ifdef CONFIG_STM32F0L0G0_USART3 [2] = &g_usart3priv, #endif #ifdef CONFIG_STM32F0L0G0_USART4 [3] = &g_usart4priv #endif }; #ifdef CONFIG_PM static struct pm_callback_s g_serialcb = { .notify = up_pm_notify, .prepare = up_pm_prepare, }; #endif /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: up_serialin ****************************************************************************/ static inline uint32_t up_serialin(struct up_dev_s *priv, int offset) { return getreg32(priv->usartbase + offset); } /**************************************************************************** * Name: up_serialout ****************************************************************************/ static inline void up_serialout(struct up_dev_s *priv, int offset, uint32_t value) { putreg32(value, priv->usartbase + offset); } /**************************************************************************** * Name: up_serialmod ****************************************************************************/ static inline void up_serialmod(struct up_dev_s *priv, int offset, uint32_t clrbits, uint32_t setbits) { uint32_t addr = priv->usartbase + offset; uint32_t regval = (getreg32(addr) & ~clrbits) | setbits; putreg32(regval, addr); } /**************************************************************************** * Name: up_setusartint ****************************************************************************/ static inline void up_setusartint(struct up_dev_s *priv, uint16_t ie) { uint32_t cr; /* Save the interrupt mask */ priv->ie = ie; /* And restore the interrupt state * (see the interrupt enable/usage table above) */ cr = up_serialin(priv, STM32_USART_CR1_OFFSET); cr &= ~(USART_CR1_USED_INTS); cr |= (ie & (USART_CR1_USED_INTS)); up_serialout(priv, STM32_USART_CR1_OFFSET, cr); cr = up_serialin(priv, STM32_USART_CR3_OFFSET); cr &= ~USART_CR3_EIE; cr |= (ie & USART_CR3_EIE); up_serialout(priv, STM32_USART_CR3_OFFSET, cr); } /**************************************************************************** * Name: up_restoreusartint ****************************************************************************/ static void up_restoreusartint(struct up_dev_s *priv, uint16_t ie) { irqstate_t flags; flags = enter_critical_section(); up_setusartint(priv, ie); leave_critical_section(flags); } /**************************************************************************** * Name: up_disableusartint ****************************************************************************/ static void up_disableusartint(struct up_dev_s *priv, uint16_t *ie) { irqstate_t flags; flags = enter_critical_section(); if (ie) { uint32_t cr1; uint32_t cr3; /* USART interrupts: * * Enable Status Meaning Usage * ---------------- -------------- ----------------------- ---------- * USART_CR1_IDLEIE USART_ISR_IDLE Idle Line Detected (not used) * USART_CR1_RXNEIE USART_ISR_RXNE Received Data Ready * to be Read * " " USART_ISR_ORE Overrun Error Detected * USART_CR1_TCIE USART_ISR_TC Transmission Complete (used only * for RS-485) * USART_CR1_TXEIE USART_ISR_TXE Transmit Data Register * Empty * USART_CR1_PEIE USART_ISR_PE Parity Error * * USART_CR2_LBDIE USART_ISR_LBD Break Flag (not used) * USART_CR3_EIE USART_ISR_FE Framing Error * " " USART_ISR_NF Noise Error * " " USART_ISR_ORE Overrun Error Detected * USART_CR3_CTSIE USART_ISR_CTS CTS flag (not used) */ cr1 = up_serialin(priv, STM32_USART_CR1_OFFSET); cr3 = up_serialin(priv, STM32_USART_CR3_OFFSET); /* Return the current interrupt mask value for the used interrupts. * Notice that this depends on the fact that none of the used interrupt * enable bits overlap. * This logic would fail if we needed the break interrupt! */ *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE); } /* Disable all interrupts */ up_setusartint(priv, 0); leave_critical_section(flags); } /**************************************************************************** * Name: up_set_format * * Description: * Set the serial line format and speed. * ****************************************************************************/ #ifndef CONFIG_SUPPRESS_UART_CONFIG static void up_set_format(struct uart_dev_s *dev) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; uint32_t regval; uint32_t usartdiv8; uint32_t cr1; uint32_t cr1_ue; uint32_t brr; irqstate_t flags; flags = enter_critical_section(); /* Get the original state of UE */ cr1 = up_serialin(priv, STM32_USART_CR1_OFFSET); cr1_ue = cr1 & USART_CR1_UE; cr1 &= ~USART_CR1_UE; /* Disable UE as the format bits and baud rate registers can not be * updated while UE = 1 */ up_serialout(priv, STM32_USART_CR1_OFFSET, cr1); /* In case of oversampling by 8, the equation is: * * baud = 2 * fCK / usartdiv8 * usartdiv8 = 2 * fCK / baud */ usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud; /* Baud rate for standard USART (SPI mode included): * * In case of oversampling by 16, the equation is: * baud = fCK / usartdiv16 * usartdiv16 = fCK / baud * = 2 * usartdiv8 */ /* Use oversamply by 8 only if the divisor is small. But what is small? */ if (usartdiv8 > 100) { /* Use usartdiv16 */ brr = (usartdiv8 + 1) >> 1; /* Clear oversampling by 8 to enable oversampling by 16 */ cr1 &= ~USART_CR1_OVER8; } else { DEBUGASSERT(usartdiv8 >= 8); /* Perform mysterious operations on bits 0-3 */ brr = ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1)); /* Set oversampling by 8 */ cr1 |= USART_CR1_OVER8; } up_serialout(priv, STM32_USART_CR1_OFFSET, cr1); up_serialout(priv, STM32_USART_BRR_OFFSET, brr); /* Configure parity mode */ cr1 &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1); if (priv->parity == 1) /* Odd parity */ { cr1 |= (USART_CR1_PCE | USART_CR1_PS); } else if (priv->parity == 2) /* Even parity */ { cr1 |= USART_CR1_PCE; } /* Configure word length (parity uses one of configured bits) * * Default: 1 start, 8 data (no parity), n stop, OR * 1 start, 7 data + parity, n stop */ if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0)) { /* Select: 1 start, 8 data + parity, n stop, OR * 1 start, 9 data (no parity), n stop. */ cr1 |= USART_CR1_M0; } else if (priv->bits == 7 && priv->parity == 0) { /* Select: 1 start, 7 data (no parity), n stop, OR */ cr1 |= USART_CR1_M1; } /* Else Select: 1 start, 7 data + parity, n stop, OR * 1 start, 8 data (no parity), n stop. */ up_serialout(priv, STM32_USART_CR1_OFFSET, cr1); /* Configure STOP bits */ regval = up_serialin(priv, STM32_USART_CR2_OFFSET); regval &= ~(USART_CR2_STOP_MASK); if (priv->stopbits2) { regval |= USART_CR2_STOP2; } up_serialout(priv, STM32_USART_CR2_OFFSET, regval); /* Configure hardware flow control */ regval = up_serialin(priv, STM32_USART_CR3_OFFSET); regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE); #if defined(CONFIG_SERIAL_IFLOWCONTROL) && \ !defined(CONFIG_STM32F0L0G0_FLOWCONTROL_BROKEN) if (priv->iflow && (priv->rts_gpio != 0)) { regval |= USART_CR3_RTSE; } #endif #ifdef CONFIG_SERIAL_OFLOWCONTROL if (priv->oflow && (priv->cts_gpio != 0)) { regval |= USART_CR3_CTSE; } #endif up_serialout(priv, STM32_USART_CR3_OFFSET, regval); up_serialout(priv, STM32_USART_CR1_OFFSET, cr1 | cr1_ue); leave_critical_section(flags); } #endif /* CONFIG_SUPPRESS_UART_CONFIG */ /**************************************************************************** * Name: up_set_apb_clock * * Description: * Enable or disable APB clock for the USART peripheral * * Input Parameters: * dev - A reference to the UART driver state structure * on - Enable clock if 'on' is 'true' and disable if 'false' * ****************************************************************************/ static void up_set_apb_clock(struct uart_dev_s *dev, bool on) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; uint32_t rcc_en; uint32_t regaddr; /* Determine which USART to configure */ switch (priv->usartbase) { default: return; #ifdef CONFIG_STM32F0L0G0_USART1 case STM32_USART1_BASE: rcc_en = RCC_APB2ENR_USART1EN; regaddr = STM32_RCC_APB2ENR; break; #endif #ifdef CONFIG_STM32F0L0G0_USART2 case STM32_USART2_BASE: rcc_en = RCC_APB1ENR_USART2EN; regaddr = STM32_RCC_APB1ENR; break; #endif #ifdef CONFIG_STM32F0L0G0_USART3 case STM32_USART3_BASE: rcc_en = RCC_APB1ENR_USART3EN; regaddr = STM32_RCC_APB1ENR; break; #endif #ifdef CONFIG_STM32F0L0G0_USART4 case STM32_USART4_BASE: rcc_en = RCC_APB1ENR_USART4EN; regaddr = STM32_RCC_APB1ENR; break; #endif } /* Enable/disable APB 1/2 clock for USART */ if (on) { modifyreg32(regaddr, 0, rcc_en); } else { modifyreg32(regaddr, rcc_en, 0); } } /**************************************************************************** * Name: up_setup * * Description: * Configure the USART baud, bits, parity, etc. This method is called the * first time that the serial port is opened. * ****************************************************************************/ static int up_setup(struct uart_dev_s *dev) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; /* Make sure that USART is disabled */ up_serialout(priv, STM32_USART_CR1_OFFSET, 0); #ifndef CONFIG_SUPPRESS_UART_CONFIG uint32_t regval; /* Note: The logic here depends on the fact that that the USART module * was enabled in stm32_lowsetup(). */ /* Enable USART APB1/2 clock */ up_set_apb_clock(dev, true); /* Configure pins for USART use */ stm32_configgpio(priv->tx_gpio); stm32_configgpio(priv->rx_gpio); #ifdef CONFIG_SERIAL_OFLOWCONTROL if (priv->cts_gpio != 0) { stm32_configgpio(priv->cts_gpio); } #endif #ifdef CONFIG_SERIAL_IFLOWCONTROL if (priv->rts_gpio != 0) { uint32_t config = priv->rts_gpio; #ifdef CONFIG_STM32F0L0G0_FLOWCONTROL_BROKEN /* Instead of letting hw manage this pin, we will bitbang */ config = (config & ~GPIO_MODE_MASK) | GPIO_OUTPUT; #endif stm32_configgpio(config); } #endif #ifdef HAVE_RS485 if (priv->rs485_dir_gpio != 0) { stm32_configgpio(priv->rs485_dir_gpio); stm32_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity); } #endif /* Configure CR2 * * Clear STOP, CLKEN, CPOL, CPHA, LBCL, and interrupt enable bits */ regval = up_serialin(priv, STM32_USART_CR2_OFFSET); regval &= ~(USART_CR2_STOP_MASK | USART_CR2_CLKEN | USART_CR2_CPOL | USART_CR2_CPHA | USART_CR2_LBCL | USART_CR2_LBDIE); /* Configure STOP bits */ if (priv->stopbits2) { regval |= USART_CR2_STOP2; } up_serialout(priv, STM32_USART_CR2_OFFSET, regval); /* Configure CR1 * * Clear TE, REm and all interrupt enable bits */ regval = up_serialin(priv, STM32_USART_CR1_OFFSET); regval &= ~(USART_CR1_TE | USART_CR1_RE | USART_CR1_ALLINTS); up_serialout(priv, STM32_USART_CR1_OFFSET, regval); /* Configure CR3 * * Clear CTSE, RTSE, and all interrupt enable bits */ regval = up_serialin(priv, STM32_USART_CR3_OFFSET); regval &= ~(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE | USART_CR3_EIE); /* Set Rx FIFO threshold to the configured level */ regval |= USART_CR3_RXFTCFG(priv->rxftcfg); up_serialout(priv, STM32_USART_CR3_OFFSET, regval); /* Configure the USART line format and speed. */ up_set_format(dev); /* Enable Rx, Tx, and the USART */ /* Enable FIFO */ regval = up_serialin(priv, STM32_USART_CR1_OFFSET); regval |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE); regval |= USART_CR1_FIFOEN; up_serialout(priv, STM32_USART_CR1_OFFSET, regval); #endif /* CONFIG_SUPPRESS_UART_CONFIG */ /* Set up the cached interrupt enables value */ priv->ie = 0; /* Mark device as initialized. */ priv->initialized = true; return OK; } /**************************************************************************** * Name: up_shutdown * * Description: * Disable the USART. This method is called when the serial * port is closed * ****************************************************************************/ static void up_shutdown(struct uart_dev_s *dev) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; uint32_t regval; /* Mark device as uninitialized. */ priv->initialized = false; /* Disable all interrupts */ up_disableusartint(priv, NULL); /* Disable USART APB1/2 clock */ up_set_apb_clock(dev, false); /* Disable Rx, Tx, and the UART */ regval = up_serialin(priv, STM32_USART_CR1_OFFSET); regval &= ~(USART_CR1_UE | USART_CR1_TE | USART_CR1_RE); up_serialout(priv, STM32_USART_CR1_OFFSET, regval); /* Release pins. * "If the serial-attached device is powered down, the TX pin causes * back-powering, potentially confusing the device to the point of * complete lock-up." * * REVISIT: Is unconfiguring the pins appropriate for all device? * If not, then this may need to be a configuration option. */ stm32_unconfiggpio(priv->tx_gpio); stm32_unconfiggpio(priv->rx_gpio); #ifdef CONFIG_SERIAL_OFLOWCONTROL if (priv->cts_gpio != 0) { stm32_unconfiggpio(priv->cts_gpio); } #endif #ifdef CONFIG_SERIAL_IFLOWCONTROL if (priv->rts_gpio != 0) { stm32_unconfiggpio(priv->rts_gpio); } #endif #ifdef HAVE_RS485 if (priv->rs485_dir_gpio != 0) { stm32_unconfiggpio(priv->rs485_dir_gpio); } #endif } /**************************************************************************** * Name: up_attach * * Description: * Configure the USART to operation in interrupt driven mode. This method * is called when the serial port is opened. Normally, this is just after * the the setup() method is called, however, the serial console may * operate in a non-interrupt driven mode during the boot phase. * * RX and TX interrupts are not enabled when by the attach method (unless * the hardware supports multiple levels of interrupt enabling). The RX * and TX interrupts are not enabled until the txint() and rxint() methods * are called. * ****************************************************************************/ static int up_attach(struct uart_dev_s *dev) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; int ret; /* Attach and enable the IRQ */ ret = irq_attach(priv->irq, up_interrupt, priv); if (ret == OK) { /* Enable the interrupt (RX and TX interrupts are still disabled * in the USART */ up_enable_irq(priv->irq); } return ret; } /**************************************************************************** * Name: up_detach * * Description: * Detach USART interrupts. This method is called when the serial port is * closed normally just before the shutdown method is called. * The exception is the serial console which is never shutdown. * ****************************************************************************/ static void up_detach(struct uart_dev_s *dev) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; up_disable_irq(priv->irq); irq_detach(priv->irq); } /**************************************************************************** * Name: up_interrupt * * Description: * This is the USART interrupt handler. It will be invoked when an * interrupt received on the 'irq' It should call uart_transmitchars or * uart_receivechar to perform the appropriate data transfers. The * interrupt handling logic must be able to map the 'irq' number into the * appropriate uart_dev_s structure in order to call these functions. * ****************************************************************************/ static int up_interrupt(int irq, void *context, FAR void *arg) { struct up_dev_s *priv = (struct up_dev_s *)arg; int passes; bool handled; DEBUGASSERT(priv != NULL); /* Report serial activity to the power management logic */ #if defined(CONFIG_PM) && CONFIG_STM32F0L0G0_PM_SERIAL_ACTIVITY > 0 pm_activity(PM_IDLE_DOMAIN, CONFIG_STM32F0L0G0_PM_SERIAL_ACTIVITY); #endif /* Loop until there are no characters to be transferred or, * until we have been looping for a long time. */ handled = true; for (passes = 0; passes < 256 && handled; passes++) { handled = false; /* Get the masked USART status word. */ priv->sr = up_serialin(priv, STM32_USART_ISR_OFFSET); /* USART interrupts: * * Enable Status Meaning Usage * ---------------- -------------- ----------------------- ---------- * USART_CR1_IDLEIE USART_ISR_IDLE Idle Line Detected (not used) * USART_CR1_RXNEIE USART_ISR_RXNE Received Data Ready * to be Read * " " USART_ISR_ORE Overrun Error Detected * USART_CR1_TCIE USART_ISR_TC Transmission Complete (used only * for RS-485) * USART_CR1_TXEIE USART_ISR_TXE Transmit Data * Register Empty * USART_CR1_PEIE USART_ISR_PE Parity Error * * USART_CR2_LBDIE USART_ISR_LBD Break Flag (not used) * USART_CR3_EIE USART_ISR_FE Framing Error * " " USART_ISR_NF Noise Error * " " USART_ISR_ORE Overrun Error Detected * USART_CR3_CTSIE USART_ISR_CTS CTS flag (not used) * * NOTE: * Some of these status bits must be cleared by explicitly writing zero * to the SR register: USART_ISR_CTS, USART_ISR_LBD. Note of those are * currently being used. */ #ifdef HAVE_RS485 /* Transmission of whole buffer is over - TC is set, TXEIE is cleared. * Note - this should be first, to have the most recent TC bit value * from SR register - sending data affects TC, but without refresh we * will not know that... */ if ((priv->sr & USART_ISR_TC) != 0 && (priv->ie & USART_CR1_TCIE) != 0 && (priv->ie & USART_CR1_TXEIE) == 0) { stm32_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity); up_restoreusartint(priv, priv->ie & ~USART_CR1_TCIE); } #endif /* Handle incoming, receive bytes. */ if ((priv->sr & USART_ISR_RXNE) != 0 && (priv->ie & USART_CR1_RXNEIE) != 0) { /* Received data ready... process incoming bytes. NOTE the check * for RXNEIE: We cannot call uart_recvchards of RX interrupts are * disabled. */ uart_recvchars(&priv->dev); handled = true; } /* We may still have to read from the DR register to clear any pending * error conditions. */ else if ((priv->sr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0) { /* These errors are cleared by writing the corresponding bit to the * interrupt clear register (ICR). */ up_serialout(priv, STM32_USART_ICR_OFFSET, (USART_ICR_NCF | USART_ICR_ORECF | USART_ICR_FECF)); } /* Handle outgoing, transmit bytes */ if ((priv->sr & USART_ISR_TXE) != 0 && (priv->ie & USART_CR1_TXEIE) != 0) { /* Transmit data register empty ... process outgoing bytes */ uart_xmitchars(&priv->dev); handled = true; } } return OK; } /**************************************************************************** * Name: up_ioctl * * Description: * All ioctl calls will be routed through this method * ****************************************************************************/ static int up_ioctl(struct file *filep, int cmd, unsigned long arg) { #if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT) \ || defined(CONFIG_STM32F0L0G0_USART_SINGLEWIRE) \ || defined(CONFIG_STM32F0L0G0_SERIALBRK_BSDCOMPAT) struct inode *inode = filep->f_inode; struct uart_dev_s *dev = inode->i_private; #endif #if defined(CONFIG_SERIAL_TERMIOS) \ || defined(CONFIG_STM32F0L0G0_USART_SINGLEWIRE) \ || defined(CONFIG_STM32F0L0G0_SERIALBRK_BSDCOMPAT) struct up_dev_s *priv = (struct up_dev_s *)dev->priv; #endif int ret = OK; switch (cmd) { #ifdef CONFIG_SERIAL_TIOCSERGSTRUCT case TIOCSERGSTRUCT: { struct up_dev_s *user = (struct up_dev_s *)arg; if (!user) { ret = -EINVAL; } else { memcpy(user, dev, sizeof(struct up_dev_s)); } } break; #endif #ifdef CONFIG_STM32F0L0G0_USART_SINGLEWIRE case TIOCSSINGLEWIRE: { uint32_t cr1; uint32_t cr1_ue; irqstate_t flags; flags = enter_critical_section(); /* Get the original state of UE */ cr1 = up_serialin(priv, STM32_USART_CR1_OFFSET); cr1_ue = cr1 & USART_CR1_UE; cr1 &= ~USART_CR1_UE; /* Disable UE, HDSEL can only be written when UE=0 */ up_serialout(priv, STM32_USART_CR1_OFFSET, cr1); /* Change the TX port to be open-drain/push-pull and enable/disable * half-duplex mode. */ uint32_t cr = up_serialin(priv, STM32_USART_CR3_OFFSET); if ((arg & SER_SINGLEWIRE_ENABLED) != 0) { uint32_t gpio_val = (arg & SER_SINGLEWIRE_PUSHPULL) == SER_SINGLEWIRE_PUSHPULL ? GPIO_PUSHPULL : GPIO_OPENDRAIN; gpio_val |= (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLUP ? GPIO_PULLUP : GPIO_FLOAT; gpio_val |= (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLDOWN ? GPIO_PULLDOWN : GPIO_FLOAT; stm32_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK | GPIO_OPENDRAIN)) | gpio_val); cr |= USART_CR3_HDSEL; } else { stm32_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK | GPIO_OPENDRAIN)) | GPIO_PUSHPULL); cr &= ~USART_CR3_HDSEL; } up_serialout(priv, STM32_USART_CR3_OFFSET, cr); /* Re-enable UE if appropriate */ up_serialout(priv, STM32_USART_CR1_OFFSET, cr1 | cr1_ue); leave_critical_section(flags); } break; #endif #ifdef CONFIG_SERIAL_TERMIOS case TCGETS: { struct termios *termiosp = (struct termios *)arg; if (!termiosp) { ret = -EINVAL; break; } cfsetispeed(termiosp, priv->baud); /* Note that since we only support 8/9 bit modes and * there is no way to report 9-bit mode, we always claim 8. */ termiosp->c_cflag = ((priv->parity != 0) ? PARENB : 0) | ((priv->parity == 1) ? PARODD : 0) | ((priv->stopbits2) ? CSTOPB : 0) | #ifdef CONFIG_SERIAL_OFLOWCONTROL ((priv->oflow) ? CCTS_OFLOW : 0) | #endif #ifdef CONFIG_SERIAL_IFLOWCONTROL ((priv->iflow) ? CRTS_IFLOW : 0) | #endif CS8; /* TODO: CCTS_IFLOW, CCTS_OFLOW */ } break; case TCSETS: { struct termios *termiosp = (struct termios *)arg; if (!termiosp) { ret = -EINVAL; break; } /* Perform some sanity checks before accepting any changes */ if (((termiosp->c_cflag & CSIZE) != CS8) #ifdef CONFIG_SERIAL_OFLOWCONTROL || ((termiosp->c_cflag & CCTS_OFLOW) && (priv->cts_gpio == 0)) #endif #ifdef CONFIG_SERIAL_IFLOWCONTROL || ((termiosp->c_cflag & CRTS_IFLOW) && (priv->rts_gpio == 0)) #endif ) { ret = -EINVAL; break; } if (termiosp->c_cflag & PARENB) { priv->parity = (termiosp->c_cflag & PARODD) ? 1 : 2; } else { priv->parity = 0; } priv->stopbits2 = (termiosp->c_cflag & CSTOPB) != 0; #ifdef CONFIG_SERIAL_OFLOWCONTROL priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0; #endif #ifdef CONFIG_SERIAL_IFLOWCONTROL priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0; #endif /* Note that since there is no way to request 9-bit mode * and no way to support 5/6/7-bit modes, we ignore them * all here. */ /* Note that only cfgetispeed is used because we have knowledge * that only one speed is supported. */ priv->baud = cfgetispeed(termiosp); /* Effect the changes immediately - note that we do not implement * TCSADRAIN / TCSAFLUSH */ up_set_format(dev); } break; #endif /* CONFIG_SERIAL_TERMIOS */ #ifdef CONFIG_STM32F0L0G0_USART_BREAKS # ifdef CONFIG_STM32F0L0G0_SERIALBRK_BSDCOMPAT case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */ { irqstate_t flags; uint32_t tx_break; flags = enter_critical_section(); /* Disable any further tx activity */ priv->ie |= USART_CR1_IE_BREAK_INPROGRESS; up_txint(dev, false); /* Configure TX as a GPIO output pin and Send a break signal */ tx_break = GPIO_OUTPUT | (~(GPIO_MODE_MASK | GPIO_OUTPUT_SET) & priv->tx_gpio); stm32_configgpio(tx_break); leave_critical_section(flags); } break; case TIOCCBRK: /* BSD compatibility: Turn break off, unconditionally */ { irqstate_t flags; flags = enter_critical_section(); /* Configure TX back to U(S)ART */ stm32_configgpio(priv->tx_gpio); priv->ie &= ~USART_CR1_IE_BREAK_INPROGRESS; /* Enable further tx activity */ up_txint(dev, true); leave_critical_section(flags); } break; # else case TIOCSBRK: /* No BSD compatibility: Turn break on for M bit times */ { uint32_t cr1; irqstate_t flags; flags = enter_critical_section(); cr1 = up_serialin(priv, STM32_USART_CR1_OFFSET); up_serialout(priv, STM32_USART_RQR_OFFSET, cr1 | USART_RQR_SBKRQ); leave_critical_section(flags); } break; case TIOCCBRK: /* No BSD compatibility: HW does not support stopping a break */ break; # endif #endif default: ret = -ENOTTY; break; } return ret; } /**************************************************************************** * Name: up_receive * * Description: * Called (usually) from the interrupt level to receive one * character from the USART. Error bits associated with the * receipt are provided in the return 'status'. * ****************************************************************************/ static int up_receive(struct uart_dev_s *dev, unsigned int *status) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; uint32_t rdr; /* Get the Rx byte */ rdr = up_serialin(priv, STM32_USART_RDR_OFFSET); /* Get the Rx byte plux error information. Return those in status */ *status = priv->sr << 16 | rdr; priv->sr = 0; /* Then return the actual received byte */ return rdr & 0xff; } /**************************************************************************** * Name: up_rxint * * Description: * Call to enable or disable RX interrupts * ****************************************************************************/ static void up_rxint(struct uart_dev_s *dev, bool enable) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; irqstate_t flags; uint16_t ie; /* USART receive interrupts: * * Enable Status Meaning Usage * ---------------- -------------- ----------------------- ---------- * USART_CR1_IDLEIE USART_ISR_IDLE Idle Line Detected (not used) * USART_CR1_RXNEIE USART_ISR_RXNE Received Data Ready * to be Read * " " USART_ISR_ORE Overrun Error Detected * USART_CR1_PEIE USART_ISR_PE Parity Error * * USART_CR2_LBDIE USART_ISR_LBD Break Flag (not used) * USART_CR3_EIE USART_ISR_FE Framing Error * " " USART_ISR_NF Noise Error * " " USART_ISR_ORE Overrun Error Detected */ flags = enter_critical_section(); ie = priv->ie; if (enable) { /* Receive an interrupt when their is anything in the Rx data register * (or an Rx timeout occurs). */ #ifndef CONFIG_SUPPRESS_SERIAL_INTS #ifdef CONFIG_USART_ERRINTS ie |= (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE); #else ie |= USART_CR1_RXNEIE; #endif #endif } else { ie &= ~(USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE); } /* Then set the new interrupt state */ up_restoreusartint(priv, ie); leave_critical_section(flags); } /**************************************************************************** * Name: up_rxavailable * * Description: * Return true if the receive register is not empty * ****************************************************************************/ static bool up_rxavailable(struct uart_dev_s *dev) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; return ((up_serialin(priv, STM32_USART_ISR_OFFSET) & USART_ISR_RXNE) != 0); } /**************************************************************************** * Name: up_rxflowcontrol * * Description: * Called when Rx buffer is full (or exceeds configured watermark levels * if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined). * Return true if UART activated RX flow control to block more incoming * data * * Input Parameters: * dev - UART device instance * nbuffered - the number of characters currently buffered * (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is * not defined the value will be 0 for an empty buffer or the * defined buffer size for a full buffer) * upper - true indicates the upper watermark was crossed where * false indicates the lower watermark has been crossed * * Returned Value: * true if RX flow control activated. * ****************************************************************************/ #ifdef CONFIG_SERIAL_IFLOWCONTROL static bool up_rxflowcontrol(struct uart_dev_s *dev, unsigned int nbuffered, bool upper) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; #if defined(CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS) && \ defined(CONFIG_STM32F0L0G0_FLOWCONTROL_BROKEN) if (priv->iflow && (priv->rts_gpio != 0)) { /* Assert/de-assert nRTS set it high resume/stop sending */ stm32_gpiowrite(priv->rts_gpio, upper); if (upper) { /* With heavy Rx traffic, RXNE might be set and data pending. * Returning 'true' in such case would cause RXNE left unhandled * and causing interrupt storm. Sending end might be also be slow * to react on nRTS, and returning 'true' here would prevent * processing that data. * * Therefore, return 'false' so input data is still being processed * until sending end reacts on nRTS signal and stops sending more. */ return false; } return upper; } #else if (priv->iflow) { /* Is the RX buffer full? */ if (upper) { /* Disable Rx interrupt to prevent more data being from * peripheral. When hardware RTS is enabled, this will * prevent more data from coming in. * * This function is only called when UART recv buffer is full, * that is: "dev->recv.head + 1 == dev->recv.tail". * * Logic in "uart_read" will automatically toggle Rx interrupts * when buffer is read empty and thus we do not have to re- * enable Rx interrupts. */ uart_disablerxint(dev); return true; } /* No.. The RX buffer is empty */ else { /* We might leave Rx interrupt disabled if full recv buffer was * read empty. Enable Rx interrupt to make sure that more input is * received. */ uart_enablerxint(dev); } } #endif return false; } #endif /**************************************************************************** * Name: up_send * * Description: * This method will send one byte on the USART * ****************************************************************************/ static void up_send(struct uart_dev_s *dev, int ch) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; #ifdef HAVE_RS485 if (priv->rs485_dir_gpio != 0) { stm32_gpiowrite(priv->rs485_dir_gpio, priv->rs485_dir_polarity); } #endif up_serialout(priv, STM32_USART_TDR_OFFSET, (uint32_t)ch); } /**************************************************************************** * Name: up_txint * * Description: * Call to enable or disable TX interrupts * ****************************************************************************/ static void up_txint(struct uart_dev_s *dev, bool enable) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; irqstate_t flags; /* USART transmit interrupts: * * Enable Status Meaning Usage * ---------------- ------------- --------------------- ---------- * USART_CR1_TCIE USART_ISR_TC Transmission Complete (used only * for RS-485) * USART_CR1_TXEIE USART_ISR_TXE Transmit Data * Register Empty * USART_CR3_CTSIE USART_ISR_CTS CTS flag (not used) */ flags = enter_critical_section(); if (enable) { /* Set to receive an interrupt when the TX data register is empty */ #ifndef CONFIG_SUPPRESS_SERIAL_INTS uint16_t ie = priv->ie | USART_CR1_TXEIE; /* If RS-485 is supported on this U[S]ART, then also enable the * transmission complete interrupt. */ # ifdef HAVE_RS485 if (priv->rs485_dir_gpio != 0) { ie |= USART_CR1_TCIE; } # endif # ifdef CONFIG_STM32_SERIALBRK_BSDCOMPAT if (priv->ie & USART_CR1_IE_BREAK_INPROGRESS) { return; } # endif up_restoreusartint(priv, ie); /* Fake a TX interrupt here by just calling uart_xmitchars() with * interrupts disabled (note this may recurse). */ uart_xmitchars(dev); #endif } else { /* Disable the TX interrupt */ up_restoreusartint(priv, priv->ie & ~USART_CR1_TXEIE); } leave_critical_section(flags); } /**************************************************************************** * Name: up_txready * * Description: * Return true if the transmit data register is empty * ****************************************************************************/ static bool up_txready(struct uart_dev_s *dev) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; return ((up_serialin(priv, STM32_USART_ISR_OFFSET) & USART_ISR_TXE) != 0); } /**************************************************************************** * Name: up_pm_notify * * Description: * Notify the driver of new power state. This callback is called after * all drivers have had the opportunity to prepare for the new power state. * * 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. * * pmstate - Identifies the new PM state * * Returned Value: * None - The driver already agreed to transition to the low power * consumption state when when it returned OK to the prepare() call. * * ****************************************************************************/ #ifdef CONFIG_PM static void up_pm_notify(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate) { switch (pmstate) { case(PM_NORMAL): { /* Logic for PM_NORMAL goes here */ } break; case(PM_IDLE): { /* Logic for PM_IDLE goes here */ } break; case(PM_STANDBY): { /* Logic for PM_STANDBY goes here */ } break; case(PM_SLEEP): { /* Logic for PM_SLEEP goes here */ } break; default: /* Should not get here */ break; } } #endif /**************************************************************************** * Name: up_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. * * pmstate - Identifies the new PM state * * Returned Value: * Zero - (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 called 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 up_pm_prepare(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate) { /* Logic to prepare for a reduced power state goes here. */ return OK; } #endif #endif /* HAVE_UART */ #endif /* USE_SERIALDRIVER */ /**************************************************************************** * Public Functions ****************************************************************************/ #ifdef USE_SERIALDRIVER /**************************************************************************** * Name: stm32_serial_get_uart * * Description: * Get serial driver structure for STM32 USART * ****************************************************************************/ FAR uart_dev_t *stm32_serial_get_uart(int uart_num) { int uart_idx = uart_num - 1; if (uart_idx < 0 || uart_idx >= STM32_NSERIAL || !g_uart_devs[uart_idx]) { return NULL; } if (!g_uart_devs[uart_idx]->initialized) { return NULL; } return &g_uart_devs[uart_idx]->dev; } /**************************************************************************** * Name: arm_earlyserialinit * * Description: * Performs the low level USART initialization early in debug so that the * serial console will be available during bootup. This must be called * before arm_serialinit. * ****************************************************************************/ #ifdef USE_EARLYSERIALINIT void arm_earlyserialinit(void) { #ifdef HAVE_UART unsigned i; /* Disable all USART interrupts */ for (i = 0; i < STM32_NSERIAL; i++) { if (g_uart_devs[i]) { up_disableusartint(g_uart_devs[i], NULL); } } /* Configure whichever one is the console */ #if CONSOLE_USART > 0 up_setup(&g_uart_devs[CONSOLE_USART - 1]->dev); #endif #endif /* HAVE UART */ } #endif /**************************************************************************** * Name: arm_serialinit * * Description: * Register serial console and serial ports. This assumes * that arm_earlyserialinit was called previously. * ****************************************************************************/ void arm_serialinit(void) { #ifdef HAVE_UART char devname[16]; unsigned i; unsigned minor = 0; #ifdef CONFIG_PM int ret; #endif /* Register to receive power management callbacks */ #ifdef CONFIG_PM ret = pm_register(&g_serialcb); DEBUGASSERT(ret == OK); UNUSED(ret); #endif /* Register the console */ #if CONSOLE_USART > 0 uart_register("/dev/console", &g_uart_devs[CONSOLE_USART - 1]->dev); #ifndef CONFIG_STM32F0L0G0_SERIAL_DISABLE_REORDERING /* If not disabled, register the console UART to ttyS0 and exclude * it from initializing it further down */ uart_register("/dev/ttyS0", &g_uart_devs[CONSOLE_USART - 1]->dev); minor = 1; #endif #endif /* CONSOLE_USART > 0 */ /* Register all remaining USARTs */ strcpy(devname, "/dev/ttySx"); for (i = 0; i < STM32_NSERIAL; i++) { /* Don't create a device for non-configured ports. */ if (g_uart_devs[i] == 0) { continue; } #ifndef CONFIG_STM32F0L0G0_SERIAL_DISABLE_REORDERING /* Don't create a device for the console - we did that above */ if (g_uart_devs[i]->dev.isconsole) { continue; } #endif /* Register USARTs as devices in increasing order */ devname[9] = '0' + minor++; uart_register(devname, &g_uart_devs[i]->dev); } #endif /* HAVE UART */ } /**************************************************************************** * Name: up_putc * * Description: * Provide priority, low-level access to support OS debug writes * ****************************************************************************/ int up_putc(int ch) { #if CONSOLE_USART > 0 struct up_dev_s *priv = g_uart_devs[CONSOLE_USART - 1]; uint16_t ie; up_disableusartint(priv, &ie); /* Check for LF */ if (ch == '\n') { /* Add CR */ arm_lowputc('\r'); } arm_lowputc(ch); up_restoreusartint(priv, ie); #endif return ch; } #else /* USE_SERIALDRIVER */ /**************************************************************************** * Name: up_putc * * Description: * Provide priority, low-level access to support OS debug writes * ****************************************************************************/ int up_putc(int ch) { #if CONSOLE_USART > 0 /* Check for LF */ if (ch == '\n') { /* Add CR */ arm_lowputc('\r'); } arm_lowputc(ch); #endif return ch; } #endif /* USE_SERIALDRIVER */