coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/stm32/uart.c

397 lines
9.9 KiB
C

/* Copyright 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* USART driver for Chrome EC */
#include "common.h"
#include "clock.h"
#include "dma.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "uart.h"
#include "util.h"
#include "stm32-dma.h"
/* Console USART index */
#define UARTN CONFIG_UART_CONSOLE
#define UARTN_BASE STM32_USART_BASE(CONFIG_UART_CONSOLE)
#ifdef CONFIG_UART_TX_DMA
#define UART_TX_INT_ENABLE STM32_USART_CR1_TCIE
#ifndef CONFIG_UART_TX_DMA_CH
#define CONFIG_UART_TX_DMA_CH STM32_DMAC_USART1_TX
#endif
/* DMA channel options; assumes UART1 */
static const struct dma_option dma_tx_option = {
CONFIG_UART_TX_DMA_CH, (void *)&STM32_USART_TDR(UARTN_BASE),
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT
#ifdef CHIP_FAMILY_STM32F4
| STM32_DMA_CCR_CHANNEL(CONFIG_UART_TX_REQ_CH)
#endif
};
#else
#define UART_TX_INT_ENABLE STM32_USART_CR1_TXEIE
#endif
#ifdef CONFIG_UART_RX_DMA
#ifndef CONFIG_UART_RX_DMA_CH
#define CONFIG_UART_RX_DMA_CH STM32_DMAC_USART1_RX
#endif
/* DMA channel options; assumes UART1 */
static const struct dma_option dma_rx_option = {
CONFIG_UART_RX_DMA_CH, (void *)&STM32_USART_RDR(UARTN_BASE),
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT |
#ifdef CHIP_FAMILY_STM32F4
STM32_DMA_CCR_CHANNEL(CONFIG_UART_RX_REQ_CH) |
#endif
STM32_DMA_CCR_CIRC
};
static int dma_rx_len; /* Size of receive DMA circular buffer */
#endif
static int init_done; /* Initialization done? */
static int should_stop; /* Last TX control action */
int uart_init_done(void)
{
return init_done;
}
void uart_tx_start(void)
{
/* If interrupt is already enabled, nothing to do */
if (STM32_USART_CR1(UARTN_BASE) & UART_TX_INT_ENABLE)
return;
disable_sleep(SLEEP_MASK_UART);
should_stop = 0;
STM32_USART_CR1(UARTN_BASE) |= UART_TX_INT_ENABLE |
STM32_USART_CR1_TCIE;
task_trigger_irq(STM32_IRQ_USART(UARTN));
}
void uart_tx_stop(void)
{
STM32_USART_CR1(UARTN_BASE) &= ~UART_TX_INT_ENABLE;
should_stop = 1;
#ifdef CONFIG_UART_TX_DMA
enable_sleep(SLEEP_MASK_UART);
#endif
}
void uart_tx_flush(void)
{
while (!(STM32_USART_SR(UARTN_BASE) & STM32_USART_SR_TXE))
;
}
int uart_tx_ready(void)
{
return STM32_USART_SR(UARTN_BASE) & STM32_USART_SR_TXE;
}
#ifdef CONFIG_UART_TX_DMA
int uart_tx_dma_ready(void)
{
return STM32_USART_SR(UARTN_BASE) & STM32_USART_SR_TC;
}
void uart_tx_dma_start(const char *src, int len)
{
/* Prepare DMA */
dma_prepare_tx(&dma_tx_option, len, src);
/* Force clear TC so we don't re-interrupt */
STM32_USART_SR(UARTN_BASE) &= ~STM32_USART_SR_TC;
/* Enable TCIE (chrome-os-partner:28837) */
STM32_USART_CR1(UARTN_BASE) |= STM32_USART_CR1_TCIE;
/* Start DMA */
dma_go(dma_get_channel(dma_tx_option.channel));
}
#endif /* CONFIG_UART_TX_DMA */
int uart_rx_available(void)
{
return STM32_USART_SR(UARTN_BASE) & STM32_USART_SR_RXNE;
}
#ifdef CONFIG_UART_RX_DMA
void uart_rx_dma_start(char *dest, int len)
{
/* Start receiving */
dma_rx_len = len;
dma_start_rx(&dma_rx_option, len, dest);
}
int uart_rx_dma_head(void)
{
return dma_bytes_done(dma_get_channel(CONFIG_UART_RX_DMA_CH),
dma_rx_len);
}
#endif
void uart_write_char(char c)
{
/* Wait for space */
while (!uart_tx_ready())
;
STM32_USART_TDR(UARTN_BASE) = c;
}
int uart_read_char(void)
{
return STM32_USART_RDR(UARTN_BASE);
}
/* Interrupt handler for console USART */
void uart_interrupt(void)
{
#ifndef CONFIG_UART_TX_DMA
/*
* When transmission completes, enable sleep if we are done with Tx.
* After that, proceed if there is other interrupt to handle.
*/
if (STM32_USART_SR(UARTN_BASE) & STM32_USART_SR_TC) {
if (should_stop) {
STM32_USART_CR1(UARTN_BASE) &= ~STM32_USART_CR1_TCIE;
enable_sleep(SLEEP_MASK_UART);
}
#if defined(CHIP_FAMILY_STM32F4)
STM32_USART_SR(UARTN_BASE) &= ~STM32_USART_SR_TC;
#else
STM32_USART_ICR(UARTN_BASE) |= STM32_USART_SR_TC;
#endif
if (!(STM32_USART_SR(UARTN_BASE) & ~STM32_USART_SR_TC))
return;
}
#endif
#ifdef CONFIG_UART_TX_DMA
/* Disable transmission complete interrupt if DMA done */
if (STM32_USART_SR(UARTN_BASE) & STM32_USART_SR_TC)
STM32_USART_CR1(UARTN_BASE) &= ~STM32_USART_CR1_TCIE;
#else
/*
* Disable the TX empty interrupt before filling the TX buffer since it
* needs an actual write to DR to be cleared.
*/
STM32_USART_CR1(UARTN_BASE) &= ~STM32_USART_CR1_TXEIE;
#endif
#ifndef CONFIG_UART_RX_DMA
/*
* Read input FIFO until empty. DMA-based receive does this from a
* hook in the UART buffering module.
*/
uart_process_input();
#endif
/* Fill output FIFO */
uart_process_output();
#ifndef CONFIG_UART_TX_DMA
/*
* Re-enable TX empty interrupt only if it was not disabled by
* uart_process_output().
*/
if (!should_stop)
STM32_USART_CR1(UARTN_BASE) |= STM32_USART_CR1_TXEIE;
#endif
}
DECLARE_IRQ(STM32_IRQ_USART(UARTN), uart_interrupt, 2);
/**
* Handle clock frequency changes
*/
static void uart_freq_change(void)
{
int freq;
int div;
#if (defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32F3)) && \
(UARTN <= 2)
/*
* UART is clocked from HSI (8MHz) to allow it to work when waking
* up from sleep
*/
freq = 8000000;
#elif defined(CHIP_FAMILY_STM32H7)
freq = 64000000; /* from 64 Mhz HSI */
#else
/* UART clocked from the main clock */
freq = clock_get_freq();
#endif
#if (UARTN == 9) /* LPUART */
div = DIV_ROUND_NEAREST(freq, CONFIG_UART_BAUD_RATE) * 256;
#else
div = DIV_ROUND_NEAREST(freq, CONFIG_UART_BAUD_RATE);
#endif
#if defined(CHIP_FAMILY_STM32L) || defined(CHIP_FAMILY_STM32F0) || \
defined(CHIP_FAMILY_STM32F3) || defined(CHIP_FAMILY_STM32L4) || \
defined(CHIP_FAMILY_STM32F4)
if (div / 16 > 0) {
/*
* CPU clock is high enough to support x16 oversampling.
* BRR = (div mantissa)<<4 | (4-bit div fraction)
*/
STM32_USART_CR1(UARTN_BASE) &= ~STM32_USART_CR1_OVER8;
STM32_USART_BRR(UARTN_BASE) = div;
} else {
/*
* CPU clock is low; use x8 oversampling.
* BRR = (div mantissa)<<4 | (3-bit div fraction)
*/
STM32_USART_BRR(UARTN_BASE) = ((div / 8) << 4) | (div & 7);
STM32_USART_CR1(UARTN_BASE) |= STM32_USART_CR1_OVER8;
}
#else
/* STM32F only supports x16 oversampling */
STM32_USART_BRR(UARTN_BASE) = div;
#endif
}
DECLARE_HOOK(HOOK_FREQ_CHANGE, uart_freq_change, HOOK_PRIO_DEFAULT);
void uart_init(void)
{
/* Select clock source */
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32F3)
#if (UARTN == 1)
STM32_RCC_CFGR3 |= 0x0003; /* USART1 clock source from HSI(8MHz) */
#elif (UARTN == 2)
STM32_RCC_CFGR3 |= 0x030000; /* USART2 clock source from HSI(8MHz) */
#endif /* UARTN */
#elif defined(CHIP_FAMILY_STM32H7) /* Clocked from 64 Mhz HSI */
#if ((UARTN == 1) || (UARTN == 6))
STM32_RCC_D2CCIP2R |= STM32_RCC_D2CCIP2_USART16SEL_HSI;
#else
STM32_RCC_D2CCIP2R |= STM32_RCC_D2CCIP2_USART234578SEL_HSI;
#endif /* UARTN */
#elif defined(CHIP_FAMILY_STM32L4)
/* USART1 clock source from SYSCLK */
STM32_RCC_CCIPR &= ~STM32_RCC_CCIPR_USART1SEL_MASK;
STM32_RCC_CCIPR |=
(STM32_RCC_CCIPR_UART_SYSCLK << STM32_RCC_CCIPR_USART1SEL_SHIFT);
/* LPUART1 clock source from SYSCLK */
STM32_RCC_CCIPR &= ~STM32_RCC_CCIPR_LPUART1SEL_MASK;
STM32_RCC_CCIPR |=
(STM32_RCC_CCIPR_UART_SYSCLK << STM32_RCC_CCIPR_LPUART1SEL_SHIFT);
#endif /* CHIP_FAMILY_STM32F0 || CHIP_FAMILY_STM32F3 */
/* Enable USART clock */
#if (UARTN == 1)
STM32_RCC_APB2ENR |= STM32_RCC_PB2_USART1;
#elif (UARTN == 6)
STM32_RCC_APB2ENR |= STM32_RCC_PB2_USART6;
#elif (UARTN == 9)
STM32_RCC_APB1ENR2 |= STM32_RCC_APB1ENR2_LPUART1EN;
#else
STM32_RCC_APB1ENR |= CONCAT2(STM32_RCC_PB1_USART, UARTN);
#endif
/*
* For STM32F3, A delay of 1 APB clock cycles is needed before we
* can access any USART register. Fortunately, we have
* gpio_config_module() below and thus don't need to add the delay.
*/
/* Configure GPIOs */
gpio_config_module(MODULE_UART, 1);
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32F3) \
|| defined(CHIP_FAMILY_STM32H7)
/*
* Wake up on start bit detection. WUS can only be written when UE=0,
* so clear UE first.
*/
STM32_USART_CR1(UARTN_BASE) &= ~STM32_USART_CR1_UE;
/*
* Also disable the RX overrun interrupt, since we don't care about it
* and we don't want to clear an extra flag in the interrupt
*/
STM32_USART_CR3(UARTN_BASE) |= STM32_USART_CR3_WUS_START_BIT |
STM32_USART_CR3_OVRDIS;
#endif
/*
* UART enabled, 8 Data bits, oversampling x16, no parity,
* TX and RX enabled.
*/
STM32_USART_CR1(UARTN_BASE) =
STM32_USART_CR1_UE | STM32_USART_CR1_TE | STM32_USART_CR1_RE;
/* 1 stop bit, no fancy stuff */
STM32_USART_CR2(UARTN_BASE) = 0x0000;
#ifdef CONFIG_UART_TX_DMA
/* Enable DMA transmitter */
STM32_USART_CR3(UARTN_BASE) |= STM32_USART_CR3_DMAT;
#ifdef CONFIG_UART_TX_DMA_PH
dma_select_channel(CONFIG_UART_TX_DMA_CH, CONFIG_UART_TX_DMA_PH);
#endif
#else
/* DMA disabled, special modes disabled, error interrupt disabled */
STM32_USART_CR3(UARTN_BASE) &= ~STM32_USART_CR3_DMAR &
~STM32_USART_CR3_DMAT &
~STM32_USART_CR3_EIE;
#endif
#ifdef CONFIG_UART_RX_DMA
/* Enable DMA receiver */
STM32_USART_CR3(UARTN_BASE) |= STM32_USART_CR3_DMAR;
#else
/* Enable receive-not-empty interrupt */
STM32_USART_CR1(UARTN_BASE) |= STM32_USART_CR1_RXNEIE;
#endif
#if defined(CHIP_FAMILY_STM32L) || defined(CHIP_FAMILY_STM32F4)
/* Use single-bit sampling */
STM32_USART_CR3(UARTN_BASE) |= STM32_USART_CR3_ONEBIT;
#endif
/* Set initial baud rate */
uart_freq_change();
/* Enable interrupts */
task_enable_irq(STM32_IRQ_USART(UARTN));
init_done = 1;
}
#ifdef CONFIG_FORCE_CONSOLE_RESUME
void uart_enable_wakeup(int enable)
{
if (enable) {
/*
* Allow UART wake up from STOP mode. Note, UART clock must
* be HSI(8MHz) for wakeup to work.
*/
STM32_USART_CR1(UARTN_BASE) |= STM32_USART_CR1_UESM;
STM32_USART_CR3(UARTN_BASE) |= STM32_USART_CR3_WUFIE;
} else {
/* Disable wake up from STOP mode. */
STM32_USART_CR1(UARTN_BASE) &= ~STM32_USART_CR1_UESM;
}
}
#endif