serial: added support for the SiFive Freedom E310 UART driver
Change-Id: I7552b316bd328c67eabb1dbf62342cb9b663845a Signed-off-by: Jean-Paul Etienne <fractalclone@gmail.com>
This commit is contained in:
parent
d7b713e5b8
commit
a916cf7e19
4 changed files with 565 additions and 0 deletions
|
@ -83,4 +83,6 @@ source "drivers/serial/Kconfig.cmsdk_apb"
|
|||
|
||||
source "drivers/serial/Kconfig.riscv_qemu"
|
||||
|
||||
source "drivers/serial/Kconfig.fe310"
|
||||
|
||||
endif
|
||||
|
|
110
drivers/serial/Kconfig.fe310
Normal file
110
drivers/serial/Kconfig.fe310
Normal file
|
@ -0,0 +1,110 @@
|
|||
# Kconfig.fe310 - SiFive Freedom E310 UART configuration option
|
||||
#
|
||||
# Copyright (c) 2017 Jean-Paul Etienne <fractalclone@gmail.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
menuconfig UART_FE310
|
||||
bool "SiFive Freedom E310 serial driver"
|
||||
depends on SOC_RISCV32_FE310
|
||||
default n
|
||||
select SERIAL_HAS_DRIVER
|
||||
help
|
||||
This option enables the SiFive Freedom E310 serial driver.
|
||||
|
||||
# ---------- Port 0 ----------
|
||||
|
||||
menuconfig UART_FE310_PORT_0
|
||||
bool "Enable FE310 Port 0"
|
||||
default n
|
||||
depends on UART_FE310
|
||||
help
|
||||
This tells the driver to configure the UART port at boot, depending on
|
||||
the additional configure options below.
|
||||
|
||||
config UART_FE310_PORT_0_NAME
|
||||
string "Port 0 Device Name"
|
||||
default "uart0"
|
||||
depends on UART_FE310_PORT_0
|
||||
help
|
||||
This is the device name for UART, and is included in the device
|
||||
struct.
|
||||
|
||||
config UART_FE310_PORT_0_BAUD_RATE
|
||||
int "Port 0 Baud Rate"
|
||||
default 0
|
||||
depends on UART_FE310_PORT_0
|
||||
help
|
||||
The baud rate for UART port to be set to at boot.
|
||||
|
||||
Leave at 0 to skip initialization.
|
||||
|
||||
config UART_FE310_PORT_0_IRQ_PRIORITY
|
||||
int "Port 0 Interrupt Priority"
|
||||
default 1
|
||||
depends on UART_FE310_PORT_0
|
||||
help
|
||||
Port 0 Interrupt Priority
|
||||
|
||||
config UART_FE310_PORT_0_RXCNT_IRQ
|
||||
int "Port 0 RX Interrupt Threshold Count"
|
||||
default 0
|
||||
depends on UART_FE310_PORT_0
|
||||
help
|
||||
Port 0 RX Threshold at which the RX FIFO interrupt triggers.
|
||||
|
||||
config UART_FE310_PORT_0_TXCNT_IRQ
|
||||
int "Port 0 TX Interrupt Threshold Count"
|
||||
default 0
|
||||
depends on UART_FE310_PORT_0
|
||||
help
|
||||
Port 0 TX Threshold at which the TX FIFO interrupt triggers.
|
||||
|
||||
# ---------- Port 1 ----------
|
||||
|
||||
menuconfig UART_FE310_PORT_1
|
||||
bool "Enable FE310 Port 1"
|
||||
default n
|
||||
depends on UART_FE310
|
||||
help
|
||||
This tells the driver to configure the UART port at boot, depending on
|
||||
the additional configure options below.
|
||||
|
||||
config UART_FE310_PORT_1_NAME
|
||||
string "Port 1 Device Name"
|
||||
default "uart1"
|
||||
depends on UART_FE310_PORT_1
|
||||
help
|
||||
This is the device name for UART, and is included in the device
|
||||
struct.
|
||||
|
||||
config UART_FE310_PORT_1_BAUD_RATE
|
||||
int "Port 1 Baud Rate"
|
||||
default 0
|
||||
depends on UART_FE310_PORT_1
|
||||
help
|
||||
The baud rate for UART port to be set to at boot.
|
||||
|
||||
Leave at 0 to skip initialization.
|
||||
|
||||
config UART_FE310_PORT_1_IRQ_PRIORITY
|
||||
int "Port 1 Interrupt Priority"
|
||||
default 1
|
||||
depends on UART_FE310_PORT_1
|
||||
help
|
||||
Port 1 Interrupt Priority
|
||||
|
||||
config UART_FE310_PORT_1_RXCNT_IRQ
|
||||
int "Port 0 RX Interrupt Threshold Count"
|
||||
default 0
|
||||
depends on UART_FE310_PORT_1
|
||||
help
|
||||
Port 1 RX Threshold at which the RX FIFO interrupt triggers.
|
||||
|
||||
config UART_FE310_PORT_1_TXCNT_IRQ
|
||||
int "Port 1 TX Interrupt Threshold Count"
|
||||
default 0
|
||||
depends on UART_FE310_PORT_1
|
||||
help
|
||||
Port 1 TX Threshold at which the TX FIFO interrupt triggers.
|
|
@ -15,3 +15,4 @@ obj-$(CONFIG_UART_ALTERA_JTAG) += uart_altera_jtag.o
|
|||
obj-$(CONFIG_UART_CC32XX) += uart_cc32xx.o
|
||||
obj-$(CONFIG_UART_CMSDK_APB) += uart_cmsdk_apb.o
|
||||
obj-$(CONFIG_UART_RISCV_QEMU) += uart_riscv_qemu.o
|
||||
obj-$(CONFIG_UART_FE310) += uart_fe310.o
|
||||
|
|
452
drivers/serial/uart_fe310.c
Normal file
452
drivers/serial/uart_fe310.c
Normal file
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Jean-Paul Etienne <fractalclone@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief UART driver for the SiFive Freedom E310 Processor
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <arch/cpu.h>
|
||||
#include <uart.h>
|
||||
#include <board.h>
|
||||
|
||||
#define RXDATA_EMPTY (1 << 31) /* Receive FIFO Empty */
|
||||
#define RXDATA_MASK 0xFF /* Receive Data Mask */
|
||||
|
||||
#define TXDATA_FULL (1 << 31) /* Transmit FIFO Full */
|
||||
|
||||
#define TXCTRL_TXEN (1 << 0) /* Activate Tx Channel */
|
||||
|
||||
#define RXCTRL_RXEN (1 << 0) /* Activate Rx Channel */
|
||||
|
||||
#define IE_TXWM (1 << 0) /* TX Interrupt Enable/Pending */
|
||||
#define IE_RXWM (1 << 1) /* RX Interrupt Enable/Pending */
|
||||
|
||||
/*
|
||||
* RX/TX Threshold count to generate TX/RX Interrupts.
|
||||
* Used by txctrl and rxctrl registers
|
||||
*/
|
||||
#define CTRL_CNT(x) (((x) & 0x07) << 16)
|
||||
|
||||
struct uart_fe310_regs_t {
|
||||
uint32_t tx;
|
||||
uint32_t rx;
|
||||
uint32_t txctrl;
|
||||
uint32_t rxctrl;
|
||||
uint32_t ie;
|
||||
uint32_t ip;
|
||||
uint32_t div;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
typedef void (*irq_cfg_func_t)(void);
|
||||
#endif
|
||||
|
||||
struct uart_fe310_device_config {
|
||||
uint32_t port;
|
||||
uint32_t sys_clk_freq;
|
||||
uint32_t baud_rate;
|
||||
uint32_t rxcnt_irq;
|
||||
uint32_t txcnt_irq;
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
irq_cfg_func_t cfg_func;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct uart_fe310_data {
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
uart_irq_callback_t callback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define DEV_CFG(dev) \
|
||||
((const struct uart_fe310_device_config * const) \
|
||||
(dev)->config->config_info)
|
||||
#define DEV_UART(dev) \
|
||||
((struct uart_fe310_regs_t *)(DEV_CFG(dev))->port)
|
||||
#define DEV_DATA(dev) \
|
||||
((struct uart_fe310_data * const)(dev)->driver_data)
|
||||
|
||||
/**
|
||||
* @brief Output a character in polled mode.
|
||||
*
|
||||
* Writes data to tx register if transmitter is not full.
|
||||
*
|
||||
* @param dev UART device struct
|
||||
* @param c Character to send
|
||||
*
|
||||
* @return Sent character
|
||||
*/
|
||||
static unsigned char uart_fe310_poll_out(struct device *dev,
|
||||
unsigned char c)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
|
||||
/* Wait while TX FIFO is full */
|
||||
while (uart->tx & TXDATA_FULL)
|
||||
;
|
||||
|
||||
uart->tx = (int)c;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Poll the device for input.
|
||||
*
|
||||
* @param dev UART device struct
|
||||
* @param c Pointer to character
|
||||
*
|
||||
* @return 0 if a character arrived, -1 if the input buffer if empty.
|
||||
*/
|
||||
static int uart_fe310_poll_in(struct device *dev, unsigned char *c)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
uint32_t val = uart->rx;
|
||||
|
||||
if (val & RXDATA_EMPTY)
|
||||
return -1;
|
||||
|
||||
*c = (unsigned char)(val & RXDATA_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
|
||||
/**
|
||||
* @brief Fill FIFO with data
|
||||
*
|
||||
* @param dev UART device struct
|
||||
* @param tx_data Data to transmit
|
||||
* @param size Number of bytes to send
|
||||
*
|
||||
* @return Number of bytes sent
|
||||
*/
|
||||
static int uart_fe310_fifo_fill(struct device *dev,
|
||||
const uint8_t *tx_data,
|
||||
int size)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size && !(uart->tx & TXDATA_FULL); i++)
|
||||
uart->tx = (int)tx_data[i];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read data from FIFO
|
||||
*
|
||||
* @param dev UART device struct
|
||||
* @param rxData Data container
|
||||
* @param size Container size
|
||||
*
|
||||
* @return Number of bytes read
|
||||
*/
|
||||
static int uart_fe310_fifo_read(struct device *dev,
|
||||
uint8_t *rx_data,
|
||||
const int size)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
int i;
|
||||
uint32_t val;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
val = uart->rx;
|
||||
|
||||
if (val & RXDATA_EMPTY)
|
||||
break;
|
||||
|
||||
rx_data[i] = (uint8_t)(val & RXDATA_MASK);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable TX interrupt in ie register
|
||||
*
|
||||
* @param dev UART device struct
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
static void uart_fe310_irq_tx_enable(struct device *dev)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
|
||||
uart->ie |= IE_TXWM;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable TX interrupt in ie register
|
||||
*
|
||||
* @param dev UART device struct
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
static void uart_fe310_irq_tx_disable(struct device *dev)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
|
||||
uart->ie &= ~IE_TXWM;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if Tx IRQ has been raised
|
||||
*
|
||||
* @param dev UART device struct
|
||||
*
|
||||
* @return 1 if an IRQ is ready, 0 otherwise
|
||||
*/
|
||||
static int uart_fe310_irq_tx_ready(struct device *dev)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
|
||||
return !!(uart->ip & IE_TXWM);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if nothing remains to be transmitted
|
||||
*
|
||||
* @param dev UART device struct
|
||||
*
|
||||
* @return 1 if nothing remains to be transmitted, 0 otherwise
|
||||
*/
|
||||
static int uart_fe310_irq_tx_empty(struct device *dev)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
|
||||
/*
|
||||
* No TX EMTPY flag for this controller,
|
||||
* just check if TX FIFO is not full
|
||||
*/
|
||||
return !(uart->tx & TXDATA_FULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable RX interrupt in ie register
|
||||
*
|
||||
* @param dev UART device struct
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
static void uart_fe310_irq_rx_enable(struct device *dev)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
|
||||
uart->ie |= IE_RXWM;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable RX interrupt in ie register
|
||||
*
|
||||
* @param dev UART device struct
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
static void uart_fe310_irq_rx_disable(struct device *dev)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
|
||||
uart->ie &= ~IE_RXWM;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if Rx IRQ has been raised
|
||||
*
|
||||
* @param dev UART device struct
|
||||
*
|
||||
* @return 1 if an IRQ is ready, 0 otherwise
|
||||
*/
|
||||
static int uart_fe310_irq_rx_ready(struct device *dev)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
|
||||
return !!(uart->ip & IE_RXWM);
|
||||
}
|
||||
|
||||
/* No error interrupt for this controller */
|
||||
static void uart_fe310_irq_err_enable(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
}
|
||||
|
||||
static void uart_fe310_irq_err_disable(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if any IRQ is pending
|
||||
*
|
||||
* @param dev UART device struct
|
||||
*
|
||||
* @return 1 if an IRQ is pending, 0 otherwise
|
||||
*/
|
||||
static int uart_fe310_irq_is_pending(struct device *dev)
|
||||
{
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
|
||||
return !!(uart->ip & (IE_RXWM | IE_TXWM));
|
||||
}
|
||||
|
||||
static int uart_fe310_irq_update(struct device *dev)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback function pointer for IRQ.
|
||||
*
|
||||
* @param dev UART device struct
|
||||
* @param cb Callback function pointer.
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
static void uart_fe310_irq_callback_set(struct device *dev,
|
||||
uart_irq_callback_t cb)
|
||||
{
|
||||
struct uart_fe310_data *data = DEV_DATA(dev);
|
||||
|
||||
data->callback = cb;
|
||||
}
|
||||
|
||||
static void uart_fe310_irq_handler(void *arg)
|
||||
{
|
||||
struct device *dev = (struct device *)arg;
|
||||
struct uart_fe310_data *data = DEV_DATA(dev);
|
||||
|
||||
if (data->callback)
|
||||
data->callback(dev);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
|
||||
|
||||
static int uart_fe310_init(struct device *dev)
|
||||
{
|
||||
const struct uart_fe310_device_config * const cfg = DEV_CFG(dev);
|
||||
volatile struct uart_fe310_regs_t *uart = DEV_UART(dev);
|
||||
|
||||
/* Enable TX and RX channels */
|
||||
uart->txctrl = TXCTRL_TXEN | CTRL_CNT(cfg->rxcnt_irq);
|
||||
uart->rxctrl = RXCTRL_RXEN | CTRL_CNT(cfg->txcnt_irq);
|
||||
|
||||
/* Set baud rate */
|
||||
uart->div = cfg->sys_clk_freq / cfg->baud_rate - 1;
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
/* Ensure that uart IRQ is disabled initially */
|
||||
uart->ie = 0;
|
||||
|
||||
/* Setup IRQ handler */
|
||||
cfg->cfg_func();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct uart_driver_api uart_fe310_driver_api = {
|
||||
.poll_in = uart_fe310_poll_in,
|
||||
.poll_out = uart_fe310_poll_out,
|
||||
.err_check = NULL,
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
.fifo_fill = uart_fe310_fifo_fill,
|
||||
.fifo_read = uart_fe310_fifo_read,
|
||||
.irq_tx_enable = uart_fe310_irq_tx_enable,
|
||||
.irq_tx_disable = uart_fe310_irq_tx_disable,
|
||||
.irq_tx_ready = uart_fe310_irq_tx_ready,
|
||||
.irq_tx_empty = uart_fe310_irq_tx_empty,
|
||||
.irq_rx_enable = uart_fe310_irq_rx_enable,
|
||||
.irq_rx_disable = uart_fe310_irq_rx_disable,
|
||||
.irq_rx_ready = uart_fe310_irq_rx_ready,
|
||||
.irq_err_enable = uart_fe310_irq_err_enable,
|
||||
.irq_err_disable = uart_fe310_irq_err_disable,
|
||||
.irq_is_pending = uart_fe310_irq_is_pending,
|
||||
.irq_update = uart_fe310_irq_update,
|
||||
.irq_callback_set = uart_fe310_irq_callback_set,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_UART_FE310_PORT_0
|
||||
|
||||
static struct uart_fe310_data uart_fe310_data_0;
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
static void uart_fe310_irq_cfg_func_0(void);
|
||||
#endif
|
||||
|
||||
static const struct uart_fe310_device_config uart_fe310_dev_cfg_0 = {
|
||||
.port = FE310_UART_0_BASE_ADDR,
|
||||
.sys_clk_freq = uart_fe310_port_0_clk_freq,
|
||||
.baud_rate = CONFIG_UART_FE310_PORT_0_BAUD_RATE,
|
||||
.rxcnt_irq = CONFIG_UART_FE310_PORT_0_RXCNT_IRQ,
|
||||
.txcnt_irq = CONFIG_UART_FE310_PORT_0_TXCNT_IRQ,
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
.cfg_func = uart_fe310_irq_cfg_func_0,
|
||||
#endif
|
||||
};
|
||||
|
||||
DEVICE_AND_API_INIT(uart_fe310_0, CONFIG_UART_FE310_PORT_0_NAME,
|
||||
uart_fe310_init,
|
||||
&uart_fe310_data_0, &uart_fe310_dev_cfg_0,
|
||||
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
||||
(void *)&uart_fe310_driver_api);
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
static void uart_fe310_irq_cfg_func_0(void)
|
||||
{
|
||||
IRQ_CONNECT(FE310_UART_0_IRQ,
|
||||
CONFIG_UART_FE310_PORT_0_IRQ_PRIORITY,
|
||||
uart_fe310_irq_handler, DEVICE_GET(uart_fe310_0),
|
||||
0);
|
||||
|
||||
irq_enable(FE310_UART_0_IRQ);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_FE310_UART_0 */
|
||||
|
||||
#ifdef CONFIG_UART_FE310_PORT_1
|
||||
|
||||
static struct uart_fe310_data uart_fe310_data_1;
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
static void uart_fe310_irq_cfg_func_1(void);
|
||||
#endif
|
||||
|
||||
static const struct uart_fe310_device_config uart_fe310_dev_cfg_1 = {
|
||||
.port = FE310_UART_1_BASE_ADDR,
|
||||
.sys_clk_freq = uart_fe310_port_1_clk_freq,
|
||||
.baud_rate = CONFIG_UART_FE310_PORT_1_BAUD_RATE,
|
||||
.rxcnt_irq = CONFIG_UART_FE310_PORT_1_RXCNT_IRQ,
|
||||
.txcnt_irq = CONFIG_UART_FE310_PORT_1_TXCNT_IRQ,
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
.cfg_func = uart_fe310_irq_cfg_func_1,
|
||||
#endif
|
||||
};
|
||||
|
||||
DEVICE_AND_API_INIT(uart_fe310_1, CONFIG_UART_FE310_PORT_1_NAME,
|
||||
uart_fe310_init,
|
||||
&uart_fe310_data_1, &uart_fe310_dev_cfg_1,
|
||||
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
||||
(void *)&uart_fe310_driver_api);
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
static void uart_fe310_irq_cfg_func_0(void)
|
||||
{
|
||||
IRQ_CONNECT(FE310_UART_1_IRQ,
|
||||
CONFIG_UART_FE310_PORT_1_IRQ_PRIORITY,
|
||||
uart_fe310_irq_handler, DEVICE_GET(uart_fe310_1),
|
||||
0);
|
||||
|
||||
irq_enable(FE310_UART_1_IRQ);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_FE310_UART_1 */
|
Loading…
Add table
Add a link
Reference in a new issue