请提供Linux kernel 4.14 AXI UART 16550 driver完整源代码
时间: 2024-03-09 15:45:09 浏览: 102
以下是 Linux kernel 4.14 中 AXI UART 16550 驱动程序的完整源代码,供参考:
```c
/*
* AXI 16550 UART driver
*/
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/serial_core.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/tty_port.h>
#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
#define XUARTPS_CR_STOPBRK 0x00000001 /* Stop transmitter break */
#define XUARTPS_CR_STARTBRK 0x00000002 /* Start transmitter break */
#define XUARTPS_CR_TORST 0x00000008 /* Transmit out-of-band reset */
#define XUARTPS_CR_TX_DIS 0x00000010 /* Transmit disable */
#define XUARTPS_CR_TX_EN 0x00000020 /* Transmit enable */
#define XUARTPS_CR_RX_DIS 0x00000040 /* Receive disable */
#define XUARTPS_CR_RX_EN 0x00000080 /* Receive enable */
/* RS485 support */
#define AXI_RS485_ENABLED (1 << 0)
#define AXI_RS485_RTS_ACTIVE_HIGH (1 << 1)
#define AXI_RS485_RX_DURING_TX (1 << 2)
/* AXI UART 16550 registers */
struct axi_uart16550 {
u32 rxfifo;
u32 txdata;
u32 ctrl;
u32 intr_enable;
u32 intr_status;
u32 baud_rate_gen;
u32 rcv_timeout;
u32 flow_ctrl;
u32 mode;
};
/* AXI UART 16550 driver data */
struct axi_uart16550_data {
struct uart_port port;
struct clk *clk;
void __iomem *regs;
struct tty_port tty_port;
unsigned int ns16550_irq;
spinlock_t lock;
u32 ctrl_reg;
unsigned int rs485_flags;
};
/* AXI UART 16550 driver functions */
static void axi_uart16550_set_mctrl(struct uart_port *port, u_int mctrl)
{
struct axi_uart16550_data *data = container_of(port, struct axi_uart16550_data, port);
unsigned long flags;
spin_lock_irqsave(&data->lock, flags);
if (mctrl & TIOCM_RTS) {
if (data->rs485_flags & AXI_RS485_ENABLED) {
if (data->rs485_flags & AXI_RS485_RTS_ACTIVE_HIGH)
data->ctrl_reg |= XUARTPS_CR_TX_EN;
else
data->ctrl_reg &= ~XUARTPS_CR_TX_EN;
} else {
data->ctrl_reg |= XUARTPS_CR_TX_EN;
}
} else {
data->ctrl_reg &= ~XUARTPS_CR_TX_EN;
}
if (mctrl & TIOCM_DTR) {
data->ctrl_reg |= XUARTPS_CR_RX_EN;
} else {
data->ctrl_reg &= ~XUARTPS_CR_RX_EN;
}
writel(data->ctrl_reg, data->regs + offsetof(struct axi_uart16550, ctrl));
spin_unlock_irqrestore(&data->lock, flags);
}
static u_int axi_uart16550_get_mctrl(struct uart_port *port)
{
struct axi_uart16550_data *data = container_of(port, struct axi_uart16550_data, port);
unsigned long flags;
u_int status = 0;
spin_lock_irqsave(&data->lock, flags);
if (data->ctrl_reg & XUARTPS_CR_TX_EN)
status |= TIOCM_RTS;
if (data->ctrl_reg & XUARTPS_CR_RX_EN)
status |= TIOCM_DTR;
spin_unlock_irqrestore(&data->lock, flags);
return status;
}
static void axi_uart16550_stop_tx(struct uart_port *port)
{
struct axi_uart16550_data *data = container_of(port, struct axi_uart16550_data, port);
unsigned long flags;
spin_lock_irqsave(&data->lock, flags);
data->ctrl_reg &= ~XUARTPS_CR_TX_EN;
writel(data->ctrl_reg, data->regs + offsetof(struct axi_uart16550, ctrl));
spin_unlock_irqrestore(&data->lock, flags);
}
static void axi_uart16550_start_tx(struct uart_port *port)
{
struct axi_uart16550_data *data = container_of(port, struct axi_uart16550_data, port);
unsigned long flags;
spin_lock_irqsave(&data->lock, flags);
if (data->rs485_flags & AXI_RS485_ENABLED) {
if (data->rs485_flags & AXI_RS485_RX_DURING_TX)
data->ctrl_reg |= XUARTPS_CR_RX_EN;
else
data->ctrl_reg &= ~XUARTPS_CR_RX_EN;
if (data->rs485_flags & AXI_RS485_RTS_ACTIVE_HIGH)
data->ctrl_reg |= XUARTPS_CR_TX_EN;
else
data->ctrl_reg &= ~XUARTPS_CR_TX_EN;
} else {
data->ctrl_reg |= XUARTPS_CR_TX_EN;
data->ctrl_reg |= XUARTPS_CR_RX_EN;
}
writel(data->ctrl_reg, data->regs + offsetof(struct axi_uart16550, ctrl));
spin_unlock_irqrestore(&data->lock, flags);
}
static void axi_uart16550_stop_rx(struct uart_port *port)
{
struct axi_uart16550_data *data = container_of(port, struct axi_uart16550_data, port);
unsigned long flags;
spin_lock_irqsave(&data->lock, flags);
data->ctrl_reg &= ~XUARTPS_CR_RX_EN;
writel(data->ctrl_reg, data->regs + offsetof(struct axi_uart16550, ctrl));
spin_unlock_irqrestore(&data->lock, flags);
}
static void axi_uart16550_enable_ms(struct uart_port *port)
{
struct axi_uart16550_data *data = container_of(port, struct axi_uart16550_data, port);
data->ctrl_reg |= XUARTPS_CR_RX_EN | XUARTPS_CR_TX_EN;
writel(data->ctrl_reg, data->regs + offsetof(struct axi_uart16550, ctrl));
}
static void axi_uart16550_break_ctl(struct uart_port *port, int break_state)
{
struct axi_uart16550_data *data = container_of(port, struct axi_uart16550_data, port);
if (break_state == -1) {
data->ctrl_reg &= ~XUARTPS_CR_STARTBRK;
data->ctrl_reg |= XUARTPS_CR_STOPBRK;
} else if (break_state == 1) {
data->ctrl_reg |= XUARTPS_CR_STARTBRK;
data->ctrl_reg &= ~XUARTPS_CR_STOPBRK;
}
writel(data->ctrl_reg, data->regs + offsetof(struct axi_uart16550, ctrl));
}
static int axi_uart16550_startup(struct uart_port *port)
{
struct axi_uart16550_data *data = container_of(port, struct axi_uart16550_data, port);
int ret;
/* Enable clock */
ret = clk_prepare_enable(data->clk);
if (ret < 0) {
dev_err(port->dev, "failed to enable clock: %d\n", ret);
return ret;
}
/* Initialize port */
uart_port_set_ops(port, &serial8250_ops);
port->flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT;
port->iotype = UPIO_MEM;
port->regshift = 2;
port->mapbase = (unsigned long)data->regs;
port->type = PORT_16550A;
port->irq = data->ns16550_irq;
spin_lock_init(&data->lock);
tty_port_init(&data->tty_port);
port->private_data = &data->tty_port;
/* Register port */
ret = uart_add_one_port(&serial8250_reg, port);
if (ret < 0) {
dev_err(port->dev, "failed to register port: %d\n", ret);
clk_disable_unprepare(data->clk);
return ret;
}
/* Enable IRQ */
writel(0xff, data->regs + offsetof(struct axi_uart16550, intr_enable));
return 0;
}
static void axi_uart16550_shutdown(struct uart_port *port)
{
struct axi_uart16550_data *data = container_of(port, struct axi_uart16550_data, port);
/* Disable IRQ */
writel(0x00, data->regs + offsetof(struct axi_uart16550, intr_enable));
/* Unregister port */
uart_remove_one_port(&serial8250_reg, port);
/* Free resources */
clk_disable_unprepare(data->clk);
}
static int axi_uart16550_request_port(struct uart_port *port)
{
return 0;
}
static void axi_uart16550_config_port(struct uart_port *port, int flags)
{
struct axi_uart16550_data *data = container_of(port, struct axi_uart16550_data, port);
unsigned long baud_divisor;
u32 mode_reg = 0;
/* Set baud rate */
baud_divisor = uart_get_baud_rate(port, flags, port->uartclk, 115200, 4000000);
uart_update_timeout(port, baud_divisor, 115200);
writel(baud_divisor, data->regs + offsetof(struct axi_uart16550, baud_rate_gen));
/* Set data format */
mode_reg |= (3 << 0); /* 8 data bits */
mode_reg |= (0 << 3); /* 1 stop bit */
mode_reg |= (0 << 4); /* no parity */
mode_reg |= (0 << 6); /* no break */
writel(mode_reg, data->regs + offsetof(struct axi_uart16550, mode));
}
static void axi_uart16550_release_port(struct uart_port *port)
{
/* Do nothing */
}
static const struct uart_ops axi_uart16550_ops = {
.tx_empty = serial8250_tx_empty,
.set_mctrl = axi_uart16550_set_mctrl,
.get_mctrl = axi_uart16550_get_mctrl,
.stop_tx = axi_uart16550_stop_tx,
.start_tx = axi_uart16550_start_tx,
.stop_rx = axi_uart16550_stop_rx,
.enable_ms = axi_uart16550_enable_ms,
.break_ctl = axi_uart16550_break_ctl,
.startup = axi_uart16550_startup,
.shutdown = axi_uart16550_shutdown,
.flush_buffer = serial8250_clear_and_reinit_fifos,
.set_termios = serial8250_set_termios,
.type = serial8250_type,
.release_port = axi_uart16550_release_port,
.request_port = axi_uart16550_request_port,
.config_port = axi_uart16550_config_port,
};
/* AXI UART 16550 driver initialization */
static int axi_uart16550_probe(struct platform_device *pdev)
{
struct
阅读全文