Files
lk/platform/stm32f4xx/uart.c
Travis Geiselbrecht 445f3e4ee7 [platform/target][warnings] fix -Wmissing-declarations warnings in platform/ and target/
Mostly driver code in various platforms. There are still some warnings
in this part of the tree in lesser-used platforms.
2021-10-21 23:18:09 -07:00

299 lines
7.1 KiB
C

/*
* Copyright (c) 2012 Kent Ryhorchuk
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <stdarg.h>
#include <lk/reg.h>
#include <lk/debug.h>
#include <stdio.h>
#include <assert.h>
#include <lib/cbuf.h>
#include <kernel/thread.h>
#include <platform/debug.h>
#include <arch/ops.h>
#include <dev/uart.h>
#include <target/debugconfig.h>
#include <stm32f4xx_rcc.h>
#include <stm32f4xx_usart.h>
#include <arch/arm/cm.h>
#ifdef ENABLE_UART1
cbuf_t uart1_rx_buf;
#ifndef UART1_FLOWCONTROL
#define UART1_FLOWCONTROL USART_HardwareFlowControl_None
#endif
#ifndef UART1_BAUDRATE
#define UART1_BAUDRATE 115200
#endif
#ifndef UART1_RXBUF_SIZE
#define UART1_RXBUF_SIZE 64
#endif
#endif
#ifdef ENABLE_UART2
cbuf_t uart2_rx_buf;
#ifndef UART2_FLOWCONTROL
#define UART2_FLOWCONTROL USART_HardwareFlowControl_None
#endif
#ifndef UART2_BAUDRATE
#define UART2_BAUDRATE 115200
#endif
#ifndef UART2_RXBUF_SIZE
#define UART2_RXBUF_SIZE 64
#endif
#endif
#ifdef ENABLE_UART3
cbuf_t uart3_rx_buf;
#ifndef UART3_FLOWCONTROL
#define UART3_FLOWCONTROL USART_HardwareFlowControl_None
#endif
#ifndef UART3_BAUDRATE
#define UART3_BAUDRATE 115200
#endif
#ifndef UART3_RXBUF_SIZE
#define UART3_RXBUF_SIZE 64
#endif
#endif
#ifdef ENABLE_UART6
cbuf_t uart6_rx_buf;
#ifndef UART6_FLOWCONTROL
#define UART6_FLOWCONTROL USART_HardwareFlowControl_None
#endif
#ifndef UART6_BAUDRATE
#define UART6_BAUDRATE 115200
#endif
#ifndef UART6_RXBUF_SIZE
#define UART6_RXBUF_SIZE 64
#endif
#endif
static void usart_init1_early(USART_TypeDef *usart, uint32_t baud, uint16_t flowcontrol, int irqn) {
USART_InitTypeDef init;
init.USART_BaudRate = baud;
init.USART_WordLength = USART_WordLength_8b;
init.USART_StopBits = USART_StopBits_1;
init.USART_Parity = USART_Parity_No;
init.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
init.USART_HardwareFlowControl = flowcontrol;
USART_Init(usart, &init);
USART_ITConfig(usart, USART_IT_RXNE, DISABLE);
NVIC_DisableIRQ(irqn);
USART_Cmd(usart, ENABLE);
}
static void usart_init1(USART_TypeDef *usart, int irqn, cbuf_t *rxbuf, size_t rxsize) {
cbuf_initialize(rxbuf, rxsize);
USART_ITConfig(usart, USART_IT_RXNE, ENABLE);
NVIC_EnableIRQ(irqn);
USART_Cmd(usart, ENABLE);
}
void uart_init_early(void) {
#ifdef ENABLE_UART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
usart_init1_early(USART1, UART1_BAUDRATE, UART1_FLOWCONTROL, USART1_IRQn);
#endif
#ifdef ENABLE_UART2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
usart_init1_early(USART2, UART2_BAUDRATE, UART2_FLOWCONTROL, USART2_IRQn);
#endif
#ifdef ENABLE_UART3
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
usart_init1_early(USART3, UART3_BAUDRATE, UART3_FLOWCONTROL, USART3_IRQn);
#endif
#ifdef ENABLE_UART6
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);
usart_init1_early(USART6, UART6_BAUDRATE, UART6_FLOWCONTROL, USART6_IRQn);
#endif
}
void uart_init(void) {
#ifdef ENABLE_UART1
usart_init1(USART1, USART1_IRQn, &uart1_rx_buf, UART1_RXBUF_SIZE);
#endif
#ifdef ENABLE_UART2
usart_init1(USART2, USART2_IRQn, &uart2_rx_buf, UART2_RXBUF_SIZE);
#endif
#ifdef ENABLE_UART3
usart_init1(USART3, USART3_IRQn, &uart3_rx_buf, UART3_RXBUF_SIZE);
#endif
#ifdef ENABLE_UART6
usart_init1(USART6, USART6_IRQn, &uart6_rx_buf, UART6_RXBUF_SIZE);
#endif
}
static void uart_rx_irq(USART_TypeDef *usart, cbuf_t *rxbuf) {
arm_cm_irq_entry();
bool resched = false;
while (USART_GetFlagStatus(usart, USART_FLAG_RXNE)) {
if (!cbuf_space_avail(rxbuf)) {
// Overflow - let flow control do its thing by not
// reading the from the FIFO.
USART_ITConfig(usart, USART_IT_RXNE, DISABLE);
break;
}
char c = USART_ReceiveData(usart);
cbuf_write_char(rxbuf, c, false);
resched = true;
}
arm_cm_irq_exit(resched);
}
#ifdef ENABLE_UART1
void stm32_USART1_IRQ(void) {
uart_rx_irq(USART1, &uart1_rx_buf);
}
#endif
#ifdef ENABLE_UART2
void stm32_USART2_IRQ(void) {
uart_rx_irq(USART2, &uart2_rx_buf);
}
#endif
#ifdef ENABLE_UART3
void stm32_USART3_IRQ(void) {
uart_rx_irq(USART3, &uart3_rx_buf);
}
#endif
#ifdef ENABLE_UART6
void stm32_USART6_IRQ(void) {
uart_rx_irq(USART6, &uart6_rx_buf);
}
#endif
static void usart_putc(USART_TypeDef *usart, char c) {
while (USART_GetFlagStatus(usart, USART_FLAG_TXE) == 0);
USART_SendData(usart, c);
while (USART_GetFlagStatus(usart, USART_FLAG_TC) == 0);
}
static int usart_getc(USART_TypeDef *usart, cbuf_t *rxbuf, bool wait) {
unsigned char c;
if (cbuf_read_char(rxbuf, (char *) &c, wait) == 0)
return -1;
if (cbuf_space_avail(rxbuf) > cbuf_size(rxbuf))
USART_ITConfig(usart, USART_IT_RXNE, ENABLE);
return c;
}
static USART_TypeDef *get_usart(int port) {
switch (port) {
#ifdef ENABLE_UART1
case 1:
return USART1;
#endif
#ifdef ENABLE_UART2
case 2:
return USART2;
#endif
#ifdef ENABLE_UART3
case 3:
return USART3;
#endif
#ifdef ENABLE_UART6
case 6:
return USART6;
#endif
default:
ASSERT(false);
return 0;
}
}
static cbuf_t *get_rxbuf(int port) {
switch (port) {
#ifdef ENABLE_UART1
case 1:
return &uart1_rx_buf;
#endif
#ifdef ENABLE_UART2
case 2:
return &uart2_rx_buf;
#endif
#ifdef ENABLE_UART3
case 3:
return &uart3_rx_buf;
#endif
#ifdef ENABLE_UART6
case 6:
return &uart6_rx_buf;
#endif
default:
ASSERT(false);
return 0;
}
}
int uart_putc(int port, char c) {
USART_TypeDef *usart = get_usart(port);
usart_putc(usart, c);
return 1;
}
int uart_getc(int port, bool wait) {
cbuf_t *rxbuf = get_rxbuf(port);
USART_TypeDef *usart = get_usart(port);
return usart_getc(usart, rxbuf, wait);
}
void uart_flush_tx(int port) {}
void uart_flush_rx(int port) {}
void uart_init_port(int port, uint baud) {
USART_TypeDef *usart = get_usart(port);
uint32_t treg = 0;
uint32_t apbclk = 0;
uint32_t intdiv = 0;
uint32_t fracdiv = 0;
RCC_ClocksTypeDef RCC_ClksStat;
RCC_GetClocksFreq(&RCC_ClksStat);
if ((usart == USART1) || (usart == USART6)) {
apbclk = RCC_ClksStat.PCLK2_Frequency;
} else {
apbclk = RCC_ClksStat.PCLK1_Frequency;
}
if ((usart->CR1 & USART_CR1_OVER8) != 0) {
intdiv = ((25 * apbclk) / (2 * (baud)));
} else {
intdiv = ((25 * apbclk) / (4 * (baud)));
}
treg = (intdiv / 100) << 4;
fracdiv = intdiv - (100 * (treg >> 4));
if ((usart->CR1 & USART_CR1_OVER8) != 0) {
treg |= ((((fracdiv * 8) + 50) / 100)) & ((uint8_t) 0x07);
} else {
treg |= ((((fracdiv * 16) + 50) / 100)) & ((uint8_t) 0x0F);
}
usart->BRR = (uint16_t) treg;
}
// inject a character into the console uart rx buffer
void __debugger_console_putc(char c) {
cbuf_t *rxbuf = get_rxbuf(DEBUG_UART);
if (rxbuf && cbuf_space_avail(rxbuf)) {
cbuf_write_char(rxbuf, c, false);
}
}