blob: f33b0906ca0fe11b7264f61fb7281e6e40eab503 [file] [log] [blame]
/*
* 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.
*/
#include "hal/hal_uart.h"
#include "bsp/bsp.h"
#include "mcu/fe310_hal.h"
#include "env/freedom-e300-hifive1/platform.h"
#include <mcu/plic.h>
#include <assert.h>
/*
* Only one UART on FE310.
*/
struct hal_uart {
uint8_t u_open:1;
uint8_t u_rx_stall:1;
uint8_t u_tx_started:1;
uint8_t u_rx_buf;
hal_uart_rx_char u_rx_func;
hal_uart_tx_char u_tx_func;
hal_uart_tx_done u_tx_done;
void *u_func_arg;
uint32_t u_baudrate;
};
static struct hal_uart uart;
int
hal_uart_init_cbs(int port, hal_uart_tx_char tx_func, hal_uart_tx_done tx_done,
hal_uart_rx_char rx_func, void *arg)
{
struct hal_uart *u;
if (port != 0) {
return -1;
}
u = &uart;
if (u->u_open) {
return -1;
}
u->u_rx_func = rx_func;
u->u_tx_func = tx_func;
u->u_tx_done = tx_done;
u->u_func_arg = arg;
return 0;
}
static int
fe310_hal_uart_tx_fill_fifo(struct hal_uart *u)
{
int data = 0;
while ((int32_t)(UART0_REG(UART_REG_TXFIFO)) >= 0) {
data = u->u_tx_func(u->u_func_arg);
if (data < 0) {
if (u->u_tx_done) {
u->u_tx_done(u->u_func_arg);
}
/* No more interrupts for TX */
UART0_REG(UART_REG_IE) &= ~UART_IP_TXWM;
u->u_tx_started = 0;
break;
} else {
UART0_REG(UART_REG_TXFIFO) = data;
}
}
return data;
}
void
hal_uart_start_tx(int port)
{
struct hal_uart *u;
int sr;
int rc;
if (port != 0) {
return;
}
u = &uart;
__HAL_DISABLE_INTERRUPTS(sr);
if (u->u_tx_started == 0) {
UART0_REG(UART_REG_TXCTRL) |= UART_TXEN;
rc = fe310_hal_uart_tx_fill_fifo(u);
if (rc >= 0) {
u->u_tx_started = 1;
UART0_REG(UART_REG_IE) |= UART_IP_TXWM;
}
}
__HAL_ENABLE_INTERRUPTS(sr);
}
void
hal_uart_start_rx(int port)
{
struct hal_uart *u;
int sr;
int rc;
if (port != 0) {
return;
}
u = &uart;
if (u->u_rx_stall) {
__HAL_DISABLE_INTERRUPTS(sr);
rc = u->u_rx_func(u->u_func_arg, u->u_rx_buf);
if (rc == 0) {
u->u_rx_stall = 0;
UART0_REG(UART_REG_IE) |= UART_IP_RXWM;
}
__HAL_ENABLE_INTERRUPTS(sr);
}
}
void
hal_uart_blocking_tx(int port, uint8_t data)
{
struct hal_uart *u;
if (port != 0) {
return;
}
u = &uart;
if (!u->u_open) {
return;
}
UART0_REG(UART_REG_TXCTRL) |= UART_TXEN;
while (UART0_REG(UART_REG_TXFIFO) < 0) {
}
UART0_REG(UART_REG_TXFIFO) = data;
}
static void
fe310_uart_irq_handler(int num)
{
struct hal_uart *u;
int rc;
int data;
u = &uart;
/* RX Path */
while (1) {
data = UART0_REG(UART_REG_RXFIFO);
if (data < 0) {
break;
}
rc = u->u_rx_func(u->u_func_arg, (uint8_t)data);
if (rc < 0) {
/* Don't rise RX interrupts till next hal_uart_start_rx */
UART0_REG(UART_REG_IE) &= ~UART_IP_RXWM;
u->u_rx_buf = (uint8_t)data;
u->u_rx_stall = 1;
}
}
/* TX Path */
if (u->u_tx_started) {
fe310_hal_uart_tx_fill_fifo(u);
}
}
int
hal_uart_init(int port, void *arg)
{
uint32_t mask;
struct fe310_uart_cfg *cfg = (struct fe310_uart_cfg *)arg;
if (port != 0) {
return -1;
}
/* Configure RX/TX pins */
mask = (1 << cfg->suc_pin_tx) | (1 << cfg->suc_pin_rx);
GPIO_REG(GPIO_IOF_EN) |= mask;
GPIO_REG(GPIO_IOF_SEL) &= ~mask;
/* Install interrupt handler */
plic_set_handler(INT_UART0_BASE, fe310_uart_irq_handler, 3);
return 0;
}
int
hal_uart_config(int port, int32_t baudrate, uint8_t databits, uint8_t stopbits,
enum hal_uart_parity parity, enum hal_uart_flow_ctl flow_ctl)
{
struct hal_uart *u;
if (port != 0) {
return -1;
}
u = &uart;
if (u->u_open) {
return -1;
}
/*
* pin config
* UART config
* nvic config
* enable uart
*/
if (databits != 8) {
return -1;
}
if (stopbits > 2) {
return -1;
}
switch (parity) {
case HAL_UART_PARITY_NONE:
break;
case HAL_UART_PARITY_ODD:
case HAL_UART_PARITY_EVEN:
return -1;
}
switch (flow_ctl) {
case HAL_UART_FLOW_CTL_NONE:
break;
case HAL_UART_FLOW_CTL_RTS_CTS:
return -1;
}
UART0_REG(UART_REG_DIV) = (get_cpu_freq() + (baudrate / 2)) / baudrate - 1;
/* Set threashold and stop bits, not enable yet */
UART0_REG(UART_REG_TXCTRL) = UART_TXWM(4) | ((stopbits - 1) << 1);
/* RX anebled with interrupt when any bytes is there */
UART0_REG(UART_REG_RXCTRL) = UART_RXWM(0) | UART_RXEN;
UART0_REG(UART_REG_IE) = UART_IP_RXWM;
plic_enable_interrupt(INT_UART0_BASE);
u->u_rx_stall = 0;
u->u_tx_started = 0;
u->u_open = 1;
u->u_baudrate = baudrate;
return 0;
}
int
hal_uart_close(int port)
{
struct hal_uart *u;
u = &uart;
if (port == 0) {
u->u_open = 0;
UART0_REG(UART_REG_TXCTRL) &= ~UART_TXEN;
UART0_REG(UART_REG_RXCTRL) = 0;
plic_disable_interrupt(INT_UART0_BASE);
return 0;
}
return -1;
}
void
hal_uart_sys_clock_changed(void)
{
struct hal_uart *u;
u = &uart;
if (u->u_open) {
UART0_REG(UART_REG_DIV) = (get_cpu_freq() + (u->u_baudrate / 2)) / u->u_baudrate - 1;
}
}