Mostly driver code in various platforms. There are still some warnings in this part of the tree in lesser-used platforms.
299 lines
7.1 KiB
C
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);
|
|
}
|
|
}
|
|
|