// SPDX-License-Identifier: GPL-2.0
/*
* uartlite.c: Serial driver for Xilinx uartlite serial controller
*
* Copyright (C) 2006 Peter Korsgaard <jacmet@sunsite.dk>
* Copyright (C) 2007 Secret Lab Technologies Ltd.
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/bitfield.h>
#include <linux/console.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#define ULITE_NAME "ttyUL"
#if CONFIG_SERIAL_UARTLITE_NR_UARTS > 4
#define ULITE_MAJOR 0 /* use dynamic node allocation */
#define ULITE_MINOR 0
#else
#define ULITE_MAJOR 204
#define ULITE_MINOR 187
#endif
#define ULITE_NR_UARTS CONFIG_SERIAL_UARTLITE_NR_UARTS
/* ---------------------------------------------------------------------
* Register definitions
*
* For register details see datasheet:
* https://www.xilinx.com/support/documentation/ip_documentation/opb_uartlite.pdf
*/
#define ULITE_RX 0x00
#define ULITE_TX 0x04
#define ULITE_STATUS 0x08
#define ULITE_CONTROL 0x0c
#define ULITE_REGION 16
#define ULITE_STATUS_RXVALID 0x01
#define ULITE_STATUS_RXFULL 0x02
#define ULITE_STATUS_TXEMPTY 0x04
#define ULITE_STATUS_TXFULL 0x08
#define ULITE_STATUS_IE 0x10
#define ULITE_STATUS_OVERRUN 0x20
#define ULITE_STATUS_FRAME 0x40
#define ULITE_STATUS_PARITY 0x80
#define ULITE_CONTROL_RST_TX 0x01
#define ULITE_CONTROL_RST_RX 0x02
#define ULITE_CONTROL_IE 0x10
#define UART_AUTOSUSPEND_TIMEOUT 3000 /* ms */
/* Static pointer to console port */
#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
static struct uart_port *console_port;
#endif
/**
* struct uartlite_data - Driver private data
* @reg_ops: Functions to read/write registers
* @clk: Our parent clock, if present
* @baud: The baud rate configured when this device was synthesized
* @cflags: The cflags for parity and data bits
*/
struct uartlite_data {
const struct uartlite_reg_ops *reg_ops;
struct clk *clk;
unsigned int baud;
tcflag_t cflags;
};
struct uartlite_reg_ops {
u32 (*in)(void __iomem *addr);
void (*out)(u32 val, void __iomem *addr);
};
static u32 uartlite_inbe32(void __iomem *addr)
{
return ioread32be(addr);
}
static void uartlite_outbe32(u32 val, void __iomem *addr)
{
iowrite32be(val, addr);
}
static const struct uartlite_reg_ops uartlite_be = {
.in = uartlite_inbe32,
.out = uartlite_outbe32,
};
static u32 uartlite_inle32(void __iomem *addr)
{
return ioread32(addr);
}
static void uartlite_outle32(u32 val, void __iomem *addr)
{
iowrite32(val, addr);
}
static const struct uartlite_reg_ops uartlite_le = {
.in = uartlite_inle32,
.out = uartlite_outle32,
};
static inline u32 uart_in32(u32 offset, struct uart_port *port)
{
struct uartlite_data *pdata = port->private_data;
return pdata->reg_ops->in(port->membase + offset);
}
static inline void uart_out32(u32 val, u32 offset, struct uart_port *port)
{
struct uartlite_data *pdata = port->private_data;
pdata->reg_ops->out(val, port->membase + offset);
}
static struct uart_port ulite_ports[ULITE_NR_UARTS];
static struct uart_driver ulite_uart_driver;
/* ---------------------------------------------------------------------
* Core UART driver operations
*/
static int ulite_receive(struct uart_port *port, int stat)
{
struct tty_port *tport = &port->state->port;
unsigned char ch = 0;
char flag = TTY_NORMAL;
if ((stat & (ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN
| ULITE_STATUS_FRAME)) == 0)
return 0;
/* stats */
if (stat & ULITE_STATUS_RXVALID) {
port->icount.rx++;
ch = uart_in32(ULITE_RX, port);
if (stat & ULITE_STATUS_PARITY)
port->icount.parity++;
}
if (stat & ULITE_STATUS_OVERRUN)
port->icount.overrun++;
if (stat & ULITE_STATUS_FRAME)
port->icount.frame++;
/* drop byte with parity error if IGNPAR specificed */
if (stat & port->ignore_status_mask & ULITE_STATUS_PARITY)
stat &= ~ULITE_STATUS_RXVALID;
stat &= port->read_status_mask;
if (stat & ULITE_STATUS_PARITY)
flag = TTY_PARITY;
stat &= ~port->ignore_status_mask;
if (stat & ULITE_STATUS_RXVALID)
tty_insert_flip_char(tport, ch, flag);
if (stat & ULITE_STATUS_FRAME)
tty_insert_flip_char(tport, 0, TTY_FRAME);
if (stat & ULITE_STATUS_OVERRUN)
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
return 1;
}
static int ulite_transmit(struct uart_port *port, int stat)
{
struct tty_port *tport = &port->state->port;
unsigned char ch;
if (