1303 lines · 30.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2012-2015 Spreadtrum Communications Inc. */ #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)#define SPRD_IEN_DATA_TIMEOUT	BIT(17) /* interrupt clear register */#define SPRD_ICLR		0x0014#define SPRD_ICLR_TIMEOUT	BIT(13)#define SPRD_ICLR_DATA_TIMEOUT	BIT(17) /* 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 SPRD_LOOPBACK_EN	BIT(14)#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_IMSR_DATA_TIMEOUT	BIT(17)#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_data {	unsigned int timeout_ien;	unsigned int timeout_iclr;	unsigned int timeout_imsr;}; 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;	const struct sprd_uart_data *pdata;}; 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 const struct sprd_uart_data sc9836_data = {	.timeout_ien = SPRD_IEN_TIMEOUT,	.timeout_iclr = SPRD_ICLR_TIMEOUT,	.timeout_imsr = SPRD_IMSR_TIMEOUT,}; static const struct sprd_uart_data sc9632_data = {	.timeout_ien = SPRD_IEN_DATA_TIMEOUT,	.timeout_iclr = SPRD_ICLR_DATA_TIMEOUT,	.timeout_imsr = SPRD_IMSR_DATA_TIMEOUT,}; 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){	u32 val = serial_in(port, SPRD_CTL1); 	if (mctrl & TIOCM_LOOP)		val |= SPRD_LOOPBACK_EN;	else		val &= ~SPRD_LOOPBACK_EN; 	serial_out(port, SPRD_CTL1, val);} 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->rx_dma.enable)		dmaengine_terminate_all(sp->rx_dma.chn); 	iclr = serial_in(port, SPRD_ICLR);	ien = serial_in(port, SPRD_IEN); 	ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT);	iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT; 	serial_out(port, SPRD_IEN, ien);	serial_out(port, SPRD_ICLR, iclr);} static void sprd_uart_dma_enable(struct uart_port *port, bool enable){	u32 val = serial_in(port, SPRD_CTL1); 	if (enable)		val |= SPRD_DMA_EN;	else		val &= ~SPRD_DMA_EN; 	serial_out(port, SPRD_CTL1, val);} static void sprd_stop_tx_dma(struct uart_port *port){	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);	struct dma_tx_state state;	u32 trans_len; 	dmaengine_pause(sp->tx_dma.chn); 	dmaengine_tx_status(sp->tx_dma.chn, sp->tx_dma.cookie, &state);	if (state.residue) {		trans_len = state.residue - sp->tx_dma.phys_addr;		uart_xmit_advance(port, trans_len);		dma_unmap_single(port->dev, sp->tx_dma.phys_addr,				 sp->tx_dma.trans_len, DMA_TO_DEVICE);	} 	dmaengine_terminate_all(sp->tx_dma.chn);	sp->tx_dma.trans_len = 0;} static int sprd_tx_buf_remap(struct uart_port *port){	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);	struct tty_port *tport = &port->state->port;	unsigned char *tail; 	sp->tx_dma.trans_len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,			UART_XMIT_SIZE); 	sp->tx_dma.phys_addr = dma_map_single(port->dev, tail,					      sp->tx_dma.trans_len,					      DMA_TO_DEVICE);	return dma_mapping_error(port->dev, sp->tx_dma.phys_addr);} static void sprd_complete_tx_dma(void *data){	struct uart_port *port = (struct uart_port *)data;	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);	struct tty_port *tport = &port->state->port;	unsigned long flags; 	uart_port_lock_irqsave(port, &flags);	dma_unmap_single(port->dev, sp->tx_dma.phys_addr,			 sp->tx_dma.trans_len, DMA_TO_DEVICE); 	uart_xmit_advance(port, sp->tx_dma.trans_len); 	if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)		uart_write_wakeup(port); 	if (kfifo_is_empty(&tport->xmit_fifo) || sprd_tx_buf_remap(port) ||	    sprd_tx_dma_config(port))		sp->tx_dma.trans_len = 0; 	uart_port_unlock_irqrestore(port, flags);} static int sprd_uart_dma_submit(struct uart_port *port,				struct sprd_uart_dma *ud, u32 trans_len,				enum dma_transfer_direction direction,				dma_async_tx_callback callback){	struct dma_async_tx_descriptor *dma_des;	unsigned long flags; 	flags = SPRD_DMA_FLAGS(SPRD_DMA_CHN_MODE_NONE,			       SPRD_DMA_NO_TRG,			       SPRD_DMA_FRAG_REQ,			       SPRD_DMA_TRANS_INT); 	dma_des = dmaengine_prep_slave_single(ud->chn, ud->phys_addr, trans_len,					      direction, flags);	if (!dma_des)		return -ENODEV; 	dma_des->callback = callback;	dma_des->callback_param = port; 	ud->cookie = dmaengine_submit(dma_des);	if (dma_submit_error(ud->cookie))		return dma_submit_error(ud->cookie); 	dma_async_issue_pending(ud->chn); 	return 0;} static int sprd_tx_dma_config(struct uart_port *port){	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);	u32 burst = sp->tx_dma.trans_len > SPRD_TX_FIFO_FULL ?		SPRD_TX_FIFO_FULL : sp->tx_dma.trans_len;	int ret;	struct dma_slave_config cfg = {		.dst_addr = port->mapbase + SPRD_TXD,		.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,		.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,		.src_maxburst = burst,	}; 	ret = dmaengine_slave_config(sp->tx_dma.chn, &cfg);	if (ret < 0)		return ret; 	return sprd_uart_dma_submit(port, &sp->tx_dma, sp->tx_dma.trans_len,				    DMA_MEM_TO_DEV, sprd_complete_tx_dma);} static void sprd_start_tx_dma(struct uart_port *port){	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);	struct tty_port *tport = &port->state->port; 	if (port->x_char) {		serial_out(port, SPRD_TXD, port->x_char);		port->icount.tx++;		port->x_char = 0;		return;	} 	if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {		sprd_stop_tx_dma(port);		return;	} 	if (sp->tx_dma.trans_len)		return; 	if (sprd_tx_buf_remap(port) || sprd_tx_dma_config(port))		sp->tx_dma.trans_len = 0;} static void sprd_rx_full_thld(struct uart_port *port, u32 thld){	u32 val = serial_in(port, SPRD_CTL2); 	val &= ~THLD_RX_FULL_MASK;	val |= thld & THLD_RX_FULL_MASK;	serial_out(port, SPRD_CTL2, val);} static int sprd_rx_alloc_buf(struct sprd_uart_port *sp){	sp->rx_dma.virt = dma_alloc_coherent(sp->port.dev, SPRD_UART_RX_SIZE,					     &sp->rx_dma.phys_addr, GFP_KERNEL);	if (!sp->rx_dma.virt)		return -ENOMEM; 	return 0;} static void sprd_rx_free_buf(struct sprd_uart_port *sp){	if (sp->rx_dma.virt)		dma_free_coherent(sp->port.dev, SPRD_UART_RX_SIZE,				  sp->rx_dma.virt, sp->rx_dma.phys_addr);	sp->rx_dma.virt = NULL;} static int sprd_rx_dma_config(struct uart_port *port, u32 burst){	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);	struct dma_slave_config cfg = {		.src_addr = port->mapbase + SPRD_RXD,		.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,		.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,		.src_maxburst = burst,	}; 	return dmaengine_slave_config(sp->rx_dma.chn, &cfg);} static void sprd_uart_dma_rx(struct uart_port *port){	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);	struct tty_port *tty = &port->state->port; 	port->icount.rx += sp->rx_dma.trans_len;	tty_insert_flip_string(tty, sp->rx_buf_tail, sp->rx_dma.trans_len);	tty_flip_buffer_push(tty);} static void sprd_uart_dma_irq(struct uart_port *port){	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);	struct dma_tx_state state;	enum dma_status status; 	status = dmaengine_tx_status(sp->rx_dma.chn,				     sp->rx_dma.cookie, &state);	if (status == DMA_ERROR)		sprd_stop_rx(port); 	if (!state.residue && sp->pos == sp->rx_dma.phys_addr)		return; 	if (!state.residue) {		sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +			sp->rx_dma.phys_addr - sp->pos;		sp->pos = sp->rx_dma.phys_addr;	} else {		sp->rx_dma.trans_len = state.residue - sp->pos;		sp->pos = state.residue;	} 	sprd_uart_dma_rx(port);	sp->rx_buf_tail += sp->rx_dma.trans_len;} static void sprd_complete_rx_dma(void *data){	struct uart_port *port = (struct uart_port *)data;	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);	struct dma_tx_state state;	enum dma_status status;	unsigned long flags; 	uart_port_lock_irqsave(port, &flags); 	status = dmaengine_tx_status(sp->rx_dma.chn,				     sp->rx_dma.cookie, &state);	if (status != DMA_COMPLETE) {		sprd_stop_rx(port);		uart_port_unlock_irqrestore(port, flags);		return;	} 	if (sp->pos != sp->rx_dma.phys_addr) {		sp->rx_dma.trans_len =  SPRD_UART_RX_SIZE +			sp->rx_dma.phys_addr - sp->pos;		sprd_uart_dma_rx(port);		sp->rx_buf_tail += sp->rx_dma.trans_len;	} 	if (sprd_start_dma_rx(port))		sprd_stop_rx(port); 	uart_port_unlock_irqrestore(port, flags);} static int sprd_start_dma_rx(struct uart_port *port){	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);	int ret; 	if (!sp->rx_dma.enable)		return 0; 	sp->pos = sp->rx_dma.phys_addr;	sp->rx_buf_tail = sp->rx_dma.virt;	sprd_rx_full_thld(port, SPRD_RX_FIFO_FULL);	ret = sprd_rx_dma_config(port, SPRD_RX_DMA_STEP);	if (ret)		return ret; 	return sprd_uart_dma_submit(port, &sp->rx_dma, SPRD_UART_RX_SIZE,				    DMA_DEV_TO_MEM, sprd_complete_rx_dma);} static void sprd_release_dma(struct uart_port *port){	struct sprd_uart_port *sp =		container_of(port, struct sprd_uart_port, port);

Lines 1–500 of 1303.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t