// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2012-2015 Spreadtrum Communications Inc.
*/
#if defined(CONFIG_SERIAL_SPRD_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/sprd-dma.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
/* device name */
#define UART_NR_MAX 8
#define SPRD_TTY_NAME "ttyS"
#define SPRD_FIFO_SIZE 128
#define SPRD_DEF_RATE 26000000
#define SPRD_BAUD_IO_LIMIT 3000000
#define SPRD_TIMEOUT 256000
/* the offset of serial registers and BITs for them */
/* data registers */
#define SPRD_TXD 0x0000
#define SPRD_RXD 0x0004
/* line status register and its BITs */
#define SPRD_LSR 0x0008
#define SPRD_LSR_OE BIT(4)
#define SPRD_LSR_FE BIT(3)
#define SPRD_LSR_PE BIT(2)
#define SPRD_LSR_BI BIT(7)
#define SPRD_LSR_TX_OVER BIT(15)
/* data number in TX and RX fifo */
#define SPRD_STS1 0x000C
#define SPRD_RX_FIFO_CNT_MASK GENMASK(7, 0)
#define SPRD_TX_FIFO_CNT_MASK GENMASK(15, 8)
/* interrupt enable register and its BITs */
#define SPRD_IEN 0x0010
#define SPRD_IEN_RX_FULL BIT(0)
#define SPRD_IEN_TX_EMPTY BIT(1)
#define SPRD_IEN_BREAK_DETECT BIT(7)
#define SPRD_IEN_TIMEOUT BIT(13)
/* interrupt clear register */
#define SPRD_ICLR 0x0014
#define SPRD_ICLR_TIMEOUT BIT(13)
/* line control register */
#define SPRD_LCR 0x0018
#define SPRD_LCR_STOP_1BIT 0x10
#define SPRD_LCR_STOP_2BIT 0x30
#define SPRD_LCR_DATA_LEN (BIT(2) | BIT(3))
#define SPRD_LCR_DATA_LEN5 0x0
#define SPRD_LCR_DATA_LEN6 0x4
#define SPRD_LCR_DATA_LEN7 0x8
#define SPRD_LCR_DATA_LEN8 0xc
#define SPRD_LCR_PARITY (BIT(0) | BIT(1))
#define SPRD_LCR_PARITY_EN 0x2
#define SPRD_LCR_EVEN_PAR 0x0
#define SPRD_LCR_ODD_PAR 0x1
/* control register 1 */
#define SPRD_CTL1 0x001C
#define SPRD_DMA_EN BIT(15)
#define RX_HW_FLOW_CTL_THLD BIT(6)
#define RX_HW_FLOW_CTL_EN BIT(7)
#define TX_HW_FLOW_CTL_EN BIT(8)
#define RX_TOUT_THLD_DEF 0x3E00
#define RX_HFC_THLD_DEF 0x40
/* fifo threshold register */
#define SPRD_CTL2 0x0020
#define THLD_TX_EMPTY 0x40
#define THLD_TX_EMPTY_SHIFT 8
#define THLD_RX_FULL 0x40
#define THLD_RX_FULL_MASK GENMASK(6, 0)
/* config baud rate register */
#define SPRD_CLKD0 0x0024
#define SPRD_CLKD0_MASK GENMASK(15, 0)
#define SPRD_CLKD1 0x0028
#define SPRD_CLKD1_MASK GENMASK(20, 16)
#define SPRD_CLKD1_SHIFT 16
/* interrupt mask status register */
#define SPRD_IMSR 0x002C
#define SPRD_IMSR_RX_FIFO_FULL BIT(0)
#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1)
#define SPRD_IMSR_BREAK_DETECT BIT(7)
#define SPRD_IMSR_TIMEOUT BIT(13)
#define SPRD_DEFAULT_SOURCE_CLK 26000000
#define SPRD_RX_DMA_STEP 1
#define SPRD_RX_FIFO_FULL 1
#define SPRD_TX_FIFO_FULL 0x20
#define SPRD_UART_RX_SIZE (UART_XMIT_SIZE / 4)
struct sprd_uart_dma {
struct dma_chan *chn;
unsigned char *virt;
dma_addr_t phys_addr;
dma_cookie_t cookie;
u32 trans_len;
bool enable;
};
struct sprd_uart_port {
struct uart_port port;
char name[16];
struct clk *clk;
struct sprd_uart_dma tx_dma;
struct sprd_uart_dma rx_dma;
dma_addr_t pos;
unsigned char *rx_buf_tail;
};
static struct sprd_uart_port *sprd_port[UART_NR_MAX];
static int sprd_ports_num;
static int sprd_start_dma_rx(struct uart_port *port);
static int sprd_tx_dma_config(struct uart_port *port);
static inline unsigned int serial_in(struct uart_port *port,
unsigned int offset)
{
return readl_relaxed(port->membase + offset);
}
static inline void serial_out(struct uart_port *port, unsigned int offset,
int value)
{
writel_relaxed(value, port->membase + offset);
}
static unsigned int sprd_tx_empty(struct uart_port *port)
{
if (serial_in(port, SPRD_STS1) & SPRD_TX_FIFO_CNT_MASK)
return 0;
else
return TIOCSER_TEMT;
}
static unsigned int sprd_get_mctrl(struct uart_port *port)
{
return TIOCM_DSR | TIOCM_CTS;
}
static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
/* nothing to do */
}
static void sprd_stop_rx(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
unsigned int ien, iclr;
if (sp-&g