text: /*
   * Copyright (c) 2012 Kent Ryhorchuk
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files
   * (the "Software"), to deal in the Software without restriction,
   * including without limitation the rights to use, copy, modify, merge,
   * publish, distribute, sublicense, and/or sell copies of the Software,
   * and to permit persons to whom the Software is furnished to do so,
   * subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  #include <stdarg.h>
  #include <reg.h>
  #include <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
  }
  
  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);
  	}
  }
  
  