mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-06 05:06:29 +00:00
tty: serial: qcom-geni-serial: add support for serial engine DMA
The qcom-geni-serial driver currently only works in SE FIFO mode. This limits the UART speed to around 180 kB/s. In order to achieve higher speeds we need to use SE DMA mode. Keep the console port working in FIFO mode but extend the code to use DMA for the high-speed port. Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> Link: https://lore.kernel.org/r/20221229155030.418800-15-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
7de06d8455
commit
2aaa43c707
@ -70,6 +70,8 @@
|
|||||||
#define UART_START_TX 0x1
|
#define UART_START_TX 0x1
|
||||||
/* UART S_CMD OP codes */
|
/* UART S_CMD OP codes */
|
||||||
#define UART_START_READ 0x1
|
#define UART_START_READ 0x1
|
||||||
|
#define UART_PARAM 0x1
|
||||||
|
#define UART_PARAM_RFR_OPEN BIT(7)
|
||||||
|
|
||||||
#define UART_OVERSAMPLING 32
|
#define UART_OVERSAMPLING 32
|
||||||
#define STALE_TIMEOUT 16
|
#define STALE_TIMEOUT 16
|
||||||
@ -95,9 +97,11 @@
|
|||||||
/* We always configure 4 bytes per FIFO word */
|
/* We always configure 4 bytes per FIFO word */
|
||||||
#define BYTES_PER_FIFO_WORD 4U
|
#define BYTES_PER_FIFO_WORD 4U
|
||||||
|
|
||||||
|
#define DMA_RX_BUF_SIZE 2048
|
||||||
|
|
||||||
struct qcom_geni_device_data {
|
struct qcom_geni_device_data {
|
||||||
bool console;
|
bool console;
|
||||||
void (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop);
|
enum geni_se_xfer_mode mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qcom_geni_private_data {
|
struct qcom_geni_private_data {
|
||||||
@ -118,9 +122,11 @@ struct qcom_geni_serial_port {
|
|||||||
u32 tx_fifo_depth;
|
u32 tx_fifo_depth;
|
||||||
u32 tx_fifo_width;
|
u32 tx_fifo_width;
|
||||||
u32 rx_fifo_depth;
|
u32 rx_fifo_depth;
|
||||||
|
dma_addr_t tx_dma_addr;
|
||||||
|
dma_addr_t rx_dma_addr;
|
||||||
bool setup;
|
bool setup;
|
||||||
unsigned int baud;
|
unsigned int baud;
|
||||||
void *rx_fifo;
|
void *rx_buf;
|
||||||
u32 loopback;
|
u32 loopback;
|
||||||
bool brk;
|
bool brk;
|
||||||
|
|
||||||
@ -249,6 +255,16 @@ static struct qcom_geni_serial_port *get_port_from_line(int line, bool console)
|
|||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool qcom_geni_serial_main_active(struct uart_port *uport)
|
||||||
|
{
|
||||||
|
return readl(uport->membase + SE_GENI_STATUS) & M_GENI_CMD_ACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool qcom_geni_serial_secondary_active(struct uart_port *uport)
|
||||||
|
{
|
||||||
|
return readl(uport->membase + SE_GENI_STATUS) & S_GENI_CMD_ACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
|
static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
|
||||||
int offset, int field, bool set)
|
int offset, int field, bool set)
|
||||||
{
|
{
|
||||||
@ -552,18 +568,11 @@ static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
|
|||||||
|
|
||||||
static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
|
static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
|
||||||
{
|
{
|
||||||
struct tty_port *tport;
|
|
||||||
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
u32 num_bytes_pw = port->tx_fifo_width / BITS_PER_BYTE;
|
struct tty_port *tport = &uport->state->port;
|
||||||
u32 words = ALIGN(bytes, num_bytes_pw) / num_bytes_pw;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
tport = &uport->state->port;
|
ret = tty_insert_flip_string(tport, port->rx_buf, bytes);
|
||||||
ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, port->rx_fifo, words);
|
|
||||||
if (drop)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ret = tty_insert_flip_string(tport, port->rx_fifo, bytes);
|
|
||||||
if (ret != bytes) {
|
if (ret != bytes) {
|
||||||
dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n",
|
dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n",
|
||||||
__func__, ret, bytes);
|
__func__, ret, bytes);
|
||||||
@ -578,16 +587,75 @@ static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
|
|||||||
return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS);
|
return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qcom_geni_serial_start_tx(struct uart_port *uport)
|
static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport)
|
||||||
{
|
{
|
||||||
u32 irq_en;
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
u32 status;
|
bool done;
|
||||||
|
u32 m_irq_en;
|
||||||
|
|
||||||
status = readl(uport->membase + SE_GENI_STATUS);
|
if (!qcom_geni_serial_main_active(uport))
|
||||||
if (status & M_GENI_CMD_ACTIVE)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!qcom_geni_serial_tx_empty(uport))
|
if (port->rx_dma_addr) {
|
||||||
|
geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr,
|
||||||
|
port->tx_remaining);
|
||||||
|
port->tx_dma_addr = 0;
|
||||||
|
port->tx_remaining = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
|
||||||
|
writel(m_irq_en, uport->membase + SE_GENI_M_IRQ_EN);
|
||||||
|
geni_se_cancel_m_cmd(&port->se);
|
||||||
|
|
||||||
|
done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS,
|
||||||
|
S_CMD_CANCEL_EN, true);
|
||||||
|
if (!done) {
|
||||||
|
geni_se_abort_m_cmd(&port->se);
|
||||||
|
done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
|
||||||
|
M_CMD_ABORT_EN, true);
|
||||||
|
if (!done)
|
||||||
|
dev_err_ratelimited(uport->dev, "M_CMD_ABORT_EN not set");
|
||||||
|
writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_geni_serial_start_tx_dma(struct uart_port *uport)
|
||||||
|
{
|
||||||
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
|
struct circ_buf *xmit = &uport->state->xmit;
|
||||||
|
unsigned int xmit_size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (port->tx_dma_addr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
xmit_size = uart_circ_chars_pending(xmit);
|
||||||
|
if (xmit_size < WAKEUP_CHARS)
|
||||||
|
uart_write_wakeup(uport);
|
||||||
|
|
||||||
|
xmit_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
|
||||||
|
|
||||||
|
qcom_geni_serial_setup_tx(uport, xmit_size);
|
||||||
|
|
||||||
|
ret = geni_se_tx_dma_prep(&port->se, &xmit->buf[xmit->tail],
|
||||||
|
xmit_size, &port->tx_dma_addr);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(uport->dev, "unable to start TX SE DMA: %d\n", ret);
|
||||||
|
qcom_geni_serial_stop_tx_dma(uport);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
port->tx_remaining = xmit_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport)
|
||||||
|
{
|
||||||
|
u32 irq_en;
|
||||||
|
|
||||||
|
if (qcom_geni_serial_main_active(uport) ||
|
||||||
|
!qcom_geni_serial_tx_empty(uport))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
|
irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
|
||||||
@ -597,19 +665,17 @@ static void qcom_geni_serial_start_tx(struct uart_port *uport)
|
|||||||
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
|
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qcom_geni_serial_stop_tx(struct uart_port *uport)
|
static void qcom_geni_serial_stop_tx_fifo(struct uart_port *uport)
|
||||||
{
|
{
|
||||||
u32 irq_en;
|
u32 irq_en;
|
||||||
u32 status;
|
|
||||||
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
|
|
||||||
irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
|
irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
|
||||||
irq_en &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN);
|
irq_en &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN);
|
||||||
writel(0, uport->membase + SE_GENI_TX_WATERMARK_REG);
|
writel(0, uport->membase + SE_GENI_TX_WATERMARK_REG);
|
||||||
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
|
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
|
||||||
status = readl(uport->membase + SE_GENI_STATUS);
|
|
||||||
/* Possible stop tx is called multiple times. */
|
/* Possible stop tx is called multiple times. */
|
||||||
if (!(status & M_GENI_CMD_ACTIVE))
|
if (!qcom_geni_serial_main_active(uport))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
geni_se_cancel_m_cmd(&port->se);
|
geni_se_cancel_m_cmd(&port->se);
|
||||||
@ -623,14 +689,13 @@ static void qcom_geni_serial_stop_tx(struct uart_port *uport)
|
|||||||
writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
|
writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop)
|
static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop)
|
||||||
{
|
{
|
||||||
u32 status;
|
u32 status;
|
||||||
u32 word_cnt;
|
u32 word_cnt;
|
||||||
u32 last_word_byte_cnt;
|
u32 last_word_byte_cnt;
|
||||||
u32 last_word_partial;
|
u32 last_word_partial;
|
||||||
u32 total_bytes;
|
u32 total_bytes;
|
||||||
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
|
||||||
|
|
||||||
status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS);
|
status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS);
|
||||||
word_cnt = status & RX_FIFO_WC_MSK;
|
word_cnt = status & RX_FIFO_WC_MSK;
|
||||||
@ -645,13 +710,12 @@ static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop)
|
|||||||
total_bytes += last_word_byte_cnt;
|
total_bytes += last_word_byte_cnt;
|
||||||
else
|
else
|
||||||
total_bytes += BYTES_PER_FIFO_WORD;
|
total_bytes += BYTES_PER_FIFO_WORD;
|
||||||
port->dev_data->handle_rx(uport, total_bytes, drop);
|
handle_rx_console(uport, total_bytes, drop);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qcom_geni_serial_stop_rx(struct uart_port *uport)
|
static void qcom_geni_serial_stop_rx_fifo(struct uart_port *uport)
|
||||||
{
|
{
|
||||||
u32 irq_en;
|
u32 irq_en;
|
||||||
u32 status;
|
|
||||||
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
u32 s_irq_status;
|
u32 s_irq_status;
|
||||||
|
|
||||||
@ -663,9 +727,7 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport)
|
|||||||
irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
|
irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
|
||||||
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
|
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
|
||||||
|
|
||||||
status = readl(uport->membase + SE_GENI_STATUS);
|
if (!qcom_geni_serial_secondary_active(uport))
|
||||||
/* Possible stop rx is called multiple times. */
|
|
||||||
if (!(status & S_GENI_CMD_ACTIVE))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
geni_se_cancel_s_cmd(&port->se);
|
geni_se_cancel_s_cmd(&port->se);
|
||||||
@ -678,23 +740,20 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport)
|
|||||||
s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
|
s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
|
||||||
/* Flush the Rx buffer */
|
/* Flush the Rx buffer */
|
||||||
if (s_irq_status & S_RX_FIFO_LAST_EN)
|
if (s_irq_status & S_RX_FIFO_LAST_EN)
|
||||||
qcom_geni_serial_handle_rx(uport, true);
|
qcom_geni_serial_handle_rx_fifo(uport, true);
|
||||||
writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
|
writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
|
||||||
|
|
||||||
status = readl(uport->membase + SE_GENI_STATUS);
|
if (qcom_geni_serial_secondary_active(uport))
|
||||||
if (status & S_GENI_CMD_ACTIVE)
|
|
||||||
qcom_geni_serial_abort_rx(uport);
|
qcom_geni_serial_abort_rx(uport);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qcom_geni_serial_start_rx(struct uart_port *uport)
|
static void qcom_geni_serial_start_rx_fifo(struct uart_port *uport)
|
||||||
{
|
{
|
||||||
u32 irq_en;
|
u32 irq_en;
|
||||||
u32 status;
|
|
||||||
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
|
|
||||||
status = readl(uport->membase + SE_GENI_STATUS);
|
if (qcom_geni_serial_secondary_active(uport))
|
||||||
if (status & S_GENI_CMD_ACTIVE)
|
qcom_geni_serial_stop_rx_fifo(uport);
|
||||||
qcom_geni_serial_stop_rx(uport);
|
|
||||||
|
|
||||||
geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
|
geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
|
||||||
|
|
||||||
@ -707,6 +766,94 @@ static void qcom_geni_serial_start_rx(struct uart_port *uport)
|
|||||||
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
|
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport)
|
||||||
|
{
|
||||||
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
|
|
||||||
|
if (!qcom_geni_serial_secondary_active(uport))
|
||||||
|
return;
|
||||||
|
|
||||||
|
geni_se_cancel_s_cmd(&port->se);
|
||||||
|
qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS,
|
||||||
|
S_CMD_CANCEL_EN, true);
|
||||||
|
|
||||||
|
if (qcom_geni_serial_secondary_active(uport))
|
||||||
|
qcom_geni_serial_abort_rx(uport);
|
||||||
|
|
||||||
|
if (port->rx_dma_addr) {
|
||||||
|
geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr,
|
||||||
|
DMA_RX_BUF_SIZE);
|
||||||
|
port->rx_dma_addr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_geni_serial_start_rx_dma(struct uart_port *uport)
|
||||||
|
{
|
||||||
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (qcom_geni_serial_secondary_active(uport))
|
||||||
|
qcom_geni_serial_stop_rx_dma(uport);
|
||||||
|
|
||||||
|
geni_se_setup_s_cmd(&port->se, UART_START_READ, UART_PARAM_RFR_OPEN);
|
||||||
|
|
||||||
|
ret = geni_se_rx_dma_prep(&port->se, port->rx_buf,
|
||||||
|
DMA_RX_BUF_SIZE,
|
||||||
|
&port->rx_dma_addr);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret);
|
||||||
|
qcom_geni_serial_stop_rx_dma(uport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop)
|
||||||
|
{
|
||||||
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
|
u32 rx_in;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!qcom_geni_serial_secondary_active(uport))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!port->rx_dma_addr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE);
|
||||||
|
port->rx_dma_addr = 0;
|
||||||
|
|
||||||
|
rx_in = readl(uport->membase + SE_DMA_RX_LEN_IN);
|
||||||
|
if (!rx_in) {
|
||||||
|
dev_warn(uport->dev, "serial engine reports 0 RX bytes in!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drop)
|
||||||
|
handle_rx_uart(uport, rx_in, drop);
|
||||||
|
|
||||||
|
ret = geni_se_rx_dma_prep(&port->se, port->rx_buf,
|
||||||
|
DMA_RX_BUF_SIZE,
|
||||||
|
&port->rx_dma_addr);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret);
|
||||||
|
qcom_geni_serial_stop_rx_dma(uport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_geni_serial_start_rx(struct uart_port *uport)
|
||||||
|
{
|
||||||
|
uport->ops->start_rx(uport);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_geni_serial_stop_rx(struct uart_port *uport)
|
||||||
|
{
|
||||||
|
uport->ops->stop_rx(uport);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_geni_serial_stop_tx(struct uart_port *uport)
|
||||||
|
{
|
||||||
|
uport->ops->stop_tx(uport);
|
||||||
|
}
|
||||||
|
|
||||||
static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport,
|
static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport,
|
||||||
unsigned int chunk)
|
unsigned int chunk)
|
||||||
{
|
{
|
||||||
@ -731,8 +878,8 @@ static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done,
|
static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
|
||||||
bool active)
|
bool done, bool active)
|
||||||
{
|
{
|
||||||
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
struct circ_buf *xmit = &uport->state->xmit;
|
struct circ_buf *xmit = &uport->state->xmit;
|
||||||
@ -752,7 +899,7 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done,
|
|||||||
|
|
||||||
/* All data has been transmitted and acknowledged as received */
|
/* All data has been transmitted and acknowledged as received */
|
||||||
if (!pending && !status && done) {
|
if (!pending && !status && done) {
|
||||||
qcom_geni_serial_stop_tx(uport);
|
qcom_geni_serial_stop_tx_fifo(uport);
|
||||||
goto out_write_wakeup;
|
goto out_write_wakeup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,12 +942,32 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done,
|
|||||||
uart_write_wakeup(uport);
|
uart_write_wakeup(uport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport)
|
||||||
|
{
|
||||||
|
struct qcom_geni_serial_port *port = to_dev_port(uport);
|
||||||
|
struct circ_buf *xmit = &uport->state->xmit;
|
||||||
|
|
||||||
|
uart_xmit_advance(uport, port->tx_remaining);
|
||||||
|
geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining);
|
||||||
|
port->tx_dma_addr = 0;
|
||||||
|
port->tx_remaining = 0;
|
||||||
|
|
||||||
|
if (!uart_circ_empty(xmit))
|
||||||
|
qcom_geni_serial_start_tx_dma(uport);
|
||||||
|
|
||||||
|
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||||
|
uart_write_wakeup(uport);
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
|
static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
|
||||||
{
|
{
|
||||||
u32 m_irq_en;
|
u32 m_irq_en;
|
||||||
u32 m_irq_status;
|
u32 m_irq_status;
|
||||||
u32 s_irq_status;
|
u32 s_irq_status;
|
||||||
u32 geni_status;
|
u32 geni_status;
|
||||||
|
u32 dma;
|
||||||
|
u32 dma_tx_status;
|
||||||
|
u32 dma_rx_status;
|
||||||
struct uart_port *uport = dev;
|
struct uart_port *uport = dev;
|
||||||
bool drop_rx = false;
|
bool drop_rx = false;
|
||||||
struct tty_port *tport = &uport->state->port;
|
struct tty_port *tport = &uport->state->port;
|
||||||
@ -813,10 +980,15 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
|
|||||||
|
|
||||||
m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS);
|
m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS);
|
||||||
s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
|
s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
|
||||||
|
dma_tx_status = readl(uport->membase + SE_DMA_TX_IRQ_STAT);
|
||||||
|
dma_rx_status = readl(uport->membase + SE_DMA_RX_IRQ_STAT);
|
||||||
geni_status = readl(uport->membase + SE_GENI_STATUS);
|
geni_status = readl(uport->membase + SE_GENI_STATUS);
|
||||||
|
dma = readl(uport->membase + SE_GENI_DMA_MODE_EN);
|
||||||
m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
|
m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
|
||||||
writel(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
|
writel(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
|
||||||
writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
|
writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
|
||||||
|
writel(dma_tx_status, uport->membase + SE_DMA_TX_IRQ_CLR);
|
||||||
|
writel(dma_rx_status, uport->membase + SE_DMA_RX_IRQ_CLR);
|
||||||
|
|
||||||
if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
|
if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
@ -826,10 +998,6 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
|
|||||||
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
|
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_irq_status & m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
|
|
||||||
qcom_geni_serial_handle_tx(uport, m_irq_status & M_CMD_DONE_EN,
|
|
||||||
geni_status & M_GENI_CMD_ACTIVE);
|
|
||||||
|
|
||||||
if (s_irq_status & (S_GP_IRQ_0_EN | S_GP_IRQ_1_EN)) {
|
if (s_irq_status & (S_GP_IRQ_0_EN | S_GP_IRQ_1_EN)) {
|
||||||
if (s_irq_status & S_GP_IRQ_0_EN)
|
if (s_irq_status & S_GP_IRQ_0_EN)
|
||||||
uport->icount.parity++;
|
uport->icount.parity++;
|
||||||
@ -839,8 +1007,35 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
|
|||||||
port->brk = true;
|
port->brk = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s_irq_status & (S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN))
|
if (dma) {
|
||||||
qcom_geni_serial_handle_rx(uport, drop_rx);
|
if (dma_tx_status & TX_DMA_DONE)
|
||||||
|
qcom_geni_serial_handle_tx_dma(uport);
|
||||||
|
|
||||||
|
if (dma_rx_status) {
|
||||||
|
if (dma_rx_status & RX_RESET_DONE)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
if (dma_rx_status & RX_DMA_PARITY_ERR) {
|
||||||
|
uport->icount.parity++;
|
||||||
|
drop_rx = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dma_rx_status & RX_DMA_BREAK)
|
||||||
|
uport->icount.brk++;
|
||||||
|
|
||||||
|
if (dma_rx_status & (RX_DMA_DONE | RX_EOT))
|
||||||
|
qcom_geni_serial_handle_rx_dma(uport, drop_rx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m_irq_status & m_irq_en &
|
||||||
|
(M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
|
||||||
|
qcom_geni_serial_handle_tx_fifo(uport,
|
||||||
|
m_irq_status & M_CMD_DONE_EN,
|
||||||
|
geni_status & M_GENI_CMD_ACTIVE);
|
||||||
|
|
||||||
|
if (s_irq_status & (S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN))
|
||||||
|
qcom_geni_serial_handle_rx_fifo(uport, drop_rx);
|
||||||
|
}
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
uart_unlock_and_check_sysrq(uport);
|
uart_unlock_and_check_sysrq(uport);
|
||||||
@ -909,7 +1104,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
|
|||||||
geni_se_config_packing(&port->se, BITS_PER_BYTE, BYTES_PER_FIFO_WORD,
|
geni_se_config_packing(&port->se, BITS_PER_BYTE, BYTES_PER_FIFO_WORD,
|
||||||
false, true, true);
|
false, true, true);
|
||||||
geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2);
|
geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2);
|
||||||
geni_se_select_mode(&port->se, GENI_SE_FIFO);
|
geni_se_select_mode(&port->se, port->dev_data->mode);
|
||||||
qcom_geni_serial_start_rx(uport);
|
qcom_geni_serial_start_rx(uport);
|
||||||
port->setup = true;
|
port->setup = true;
|
||||||
|
|
||||||
@ -1308,10 +1503,10 @@ static void qcom_geni_serial_pm(struct uart_port *uport,
|
|||||||
|
|
||||||
static const struct uart_ops qcom_geni_console_pops = {
|
static const struct uart_ops qcom_geni_console_pops = {
|
||||||
.tx_empty = qcom_geni_serial_tx_empty,
|
.tx_empty = qcom_geni_serial_tx_empty,
|
||||||
.stop_tx = qcom_geni_serial_stop_tx,
|
.stop_tx = qcom_geni_serial_stop_tx_fifo,
|
||||||
.start_tx = qcom_geni_serial_start_tx,
|
.start_tx = qcom_geni_serial_start_tx_fifo,
|
||||||
.stop_rx = qcom_geni_serial_stop_rx,
|
.stop_rx = qcom_geni_serial_stop_rx_fifo,
|
||||||
.start_rx = qcom_geni_serial_start_rx,
|
.start_rx = qcom_geni_serial_start_rx_fifo,
|
||||||
.set_termios = qcom_geni_serial_set_termios,
|
.set_termios = qcom_geni_serial_set_termios,
|
||||||
.startup = qcom_geni_serial_startup,
|
.startup = qcom_geni_serial_startup,
|
||||||
.request_port = qcom_geni_serial_request_port,
|
.request_port = qcom_geni_serial_request_port,
|
||||||
@ -1329,9 +1524,10 @@ static const struct uart_ops qcom_geni_console_pops = {
|
|||||||
|
|
||||||
static const struct uart_ops qcom_geni_uart_pops = {
|
static const struct uart_ops qcom_geni_uart_pops = {
|
||||||
.tx_empty = qcom_geni_serial_tx_empty,
|
.tx_empty = qcom_geni_serial_tx_empty,
|
||||||
.stop_tx = qcom_geni_serial_stop_tx,
|
.stop_tx = qcom_geni_serial_stop_tx_dma,
|
||||||
.start_tx = qcom_geni_serial_start_tx,
|
.start_tx = qcom_geni_serial_start_tx_dma,
|
||||||
.stop_rx = qcom_geni_serial_stop_rx,
|
.start_rx = qcom_geni_serial_start_rx_dma,
|
||||||
|
.stop_rx = qcom_geni_serial_stop_rx_dma,
|
||||||
.set_termios = qcom_geni_serial_set_termios,
|
.set_termios = qcom_geni_serial_set_termios,
|
||||||
.startup = qcom_geni_serial_startup,
|
.startup = qcom_geni_serial_startup,
|
||||||
.request_port = qcom_geni_serial_request_port,
|
.request_port = qcom_geni_serial_request_port,
|
||||||
@ -1400,9 +1596,9 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
|
|||||||
port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
|
port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
|
||||||
|
|
||||||
if (!data->console) {
|
if (!data->console) {
|
||||||
port->rx_fifo = devm_kcalloc(uport->dev,
|
port->rx_buf = devm_kzalloc(uport->dev,
|
||||||
port->rx_fifo_depth, sizeof(u32), GFP_KERNEL);
|
DMA_RX_BUF_SIZE, GFP_KERNEL);
|
||||||
if (!port->rx_fifo)
|
if (!port->rx_buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1564,12 +1760,12 @@ static int qcom_geni_serial_sys_hib_resume(struct device *dev)
|
|||||||
|
|
||||||
static const struct qcom_geni_device_data qcom_geni_console_data = {
|
static const struct qcom_geni_device_data qcom_geni_console_data = {
|
||||||
.console = true,
|
.console = true,
|
||||||
.handle_rx = handle_rx_console,
|
.mode = GENI_SE_FIFO,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct qcom_geni_device_data qcom_geni_uart_data = {
|
static const struct qcom_geni_device_data qcom_geni_uart_data = {
|
||||||
.console = false,
|
.console = false,
|
||||||
.handle_rx = handle_rx_uart,
|
.mode = GENI_SE_DMA,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
|
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
|
||||||
|
Loading…
Reference in New Issue
Block a user