summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/ar933x_uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/ar933x_uart.c')
-rw-r--r--drivers/tty/serial/ar933x_uart.c294
1 files changed, 224 insertions, 70 deletions
diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c
index decc7f3c1ab2..5b491db9d2fc 100644
--- a/drivers/tty/serial/ar933x_uart.c
+++ b/drivers/tty/serial/ar933x_uart.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Atheros AR933X SoC built-in UART driver
*
* Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#include <linux/module.h>
@@ -16,6 +13,7 @@
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -32,6 +30,8 @@
#include <asm/mach-ath79/ar933x_uart.h>
+#include "serial_mctrl_gpio.h"
+
#define DRIVER_NAME "ar933x-uart"
#define AR933X_UART_MAX_SCALE 0xff
@@ -50,13 +50,10 @@ struct ar933x_uart_port {
unsigned int min_baud;
unsigned int max_baud;
struct clk *clk;
+ struct mctrl_gpios *gpios;
+ struct gpio_desc *rts_gpiod;
};
-static inline bool ar933x_uart_console_enabled(void)
-{
- return IS_ENABLED(CONFIG_SERIAL_AR933X_CONSOLE);
-}
-
static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
int offset)
{
@@ -108,6 +105,18 @@ static inline void ar933x_uart_stop_tx_interrupt(struct ar933x_uart_port *up)
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
}
+static inline void ar933x_uart_start_rx_interrupt(struct ar933x_uart_port *up)
+{
+ up->ier |= AR933X_UART_INT_RX_VALID;
+ ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
+}
+
+static inline void ar933x_uart_stop_rx_interrupt(struct ar933x_uart_port *up)
+{
+ up->ier &= ~AR933X_UART_INT_RX_VALID;
+ ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
+}
+
static inline void ar933x_uart_putc(struct ar933x_uart_port *up, int ch)
{
unsigned int rdata;
@@ -124,20 +133,30 @@ static unsigned int ar933x_uart_tx_empty(struct uart_port *port)
unsigned long flags;
unsigned int rdata;
- spin_lock_irqsave(&up->port.lock, flags);
+ uart_port_lock_irqsave(&up->port, &flags);
rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG);
- spin_unlock_irqrestore(&up->port.lock, flags);
+ uart_port_unlock_irqrestore(&up->port, flags);
return (rdata & AR933X_UART_DATA_TX_CSR) ? 0 : TIOCSER_TEMT;
}
static unsigned int ar933x_uart_get_mctrl(struct uart_port *port)
{
- return TIOCM_CAR;
+ struct ar933x_uart_port *up =
+ container_of(port, struct ar933x_uart_port, port);
+ int ret = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+
+ mctrl_gpio_get(up->gpios, &ret);
+
+ return ret;
}
static void ar933x_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
+ struct ar933x_uart_port *up =
+ container_of(port, struct ar933x_uart_port, port);
+
+ mctrl_gpio_set(up->gpios, mctrl);
}
static void ar933x_uart_start_tx(struct uart_port *port)
@@ -148,6 +167,37 @@ static void ar933x_uart_start_tx(struct uart_port *port)
ar933x_uart_start_tx_interrupt(up);
}
+static void ar933x_uart_wait_tx_complete(struct ar933x_uart_port *up)
+{
+ unsigned int status;
+ unsigned int timeout = 60000;
+
+ /* Wait up to 60ms for the character(s) to be sent. */
+ do {
+ status = ar933x_uart_read(up, AR933X_UART_CS_REG);
+ if (--timeout == 0)
+ break;
+ udelay(1);
+ } while (status & AR933X_UART_CS_TX_BUSY);
+
+ if (timeout == 0)
+ dev_err(up->port.dev, "waiting for TX timed out\n");
+}
+
+static void ar933x_uart_rx_flush(struct ar933x_uart_port *up)
+{
+ unsigned int status;
+
+ /* clear RX_VALID interrupt */
+ ar933x_uart_write(up, AR933X_UART_INT_REG, AR933X_UART_INT_RX_VALID);
+
+ /* remove characters from the RX FIFO */
+ do {
+ ar933x_uart_write(up, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR);
+ status = ar933x_uart_read(up, AR933X_UART_DATA_REG);
+ } while (status & AR933X_UART_DATA_RX_CSR);
+}
+
static void ar933x_uart_stop_tx(struct uart_port *port)
{
struct ar933x_uart_port *up =
@@ -161,8 +211,7 @@ static void ar933x_uart_stop_rx(struct uart_port *port)
struct ar933x_uart_port *up =
container_of(port, struct ar933x_uart_port, port);
- up->ier &= ~AR933X_UART_INT_RX_VALID;
- ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
+ ar933x_uart_stop_rx_interrupt(up);
}
static void ar933x_uart_break_ctl(struct uart_port *port, int break_state)
@@ -171,14 +220,14 @@ static void ar933x_uart_break_ctl(struct uart_port *port, int break_state)
container_of(port, struct ar933x_uart_port, port);
unsigned long flags;
- spin_lock_irqsave(&up->port.lock, flags);
+ uart_port_lock_irqsave(&up->port, &flags);
if (break_state == -1)
ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
AR933X_UART_CS_TX_BREAK);
else
ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG,
AR933X_UART_CS_TX_BREAK);
- spin_unlock_irqrestore(&up->port.lock, flags);
+ uart_port_unlock_irqrestore(&up->port, flags);
}
/*
@@ -234,7 +283,7 @@ static void ar933x_uart_get_scale_step(unsigned int clk,
static void ar933x_uart_set_termios(struct uart_port *port,
struct ktermios *new,
- struct ktermios *old)
+ const struct ktermios *old)
{
struct ar933x_uart_port *up =
container_of(port, struct ar933x_uart_port, port);
@@ -269,7 +318,7 @@ static void ar933x_uart_set_termios(struct uart_port *port,
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
*/
- spin_lock_irqsave(&up->port.lock, flags);
+ uart_port_lock_irqsave(&up->port, &flags);
/* disable the UART */
ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG,
@@ -294,12 +343,16 @@ static void ar933x_uart_set_termios(struct uart_port *port,
ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
AR933X_UART_CS_HOST_INT_EN);
+ /* enable RX and TX ready overide */
+ ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
+ AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE);
+
/* reenable the UART */
ar933x_uart_rmw(up, AR933X_UART_CS_REG,
AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S,
AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S);
- spin_unlock_irqrestore(&up->port.lock, flags);
+ uart_port_unlock_irqrestore(&up->port, flags);
if (tty_termios_baud_rate(new))
tty_termios_encode_baud_rate(new, baud, baud);
@@ -325,29 +378,37 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
up->port.icount.rx++;
ch = rdata & AR933X_UART_DATA_TX_RX_MASK;
- if (uart_handle_sysrq_char(&up->port, ch))
+ if (uart_prepare_sysrq_char(&up->port, ch))
continue;
if ((up->port.ignore_status_mask & AR933X_DUMMY_STATUS_RD) == 0)
tty_insert_flip_char(port, ch, TTY_NORMAL);
} while (max_count-- > 0);
- spin_unlock(&up->port.lock);
tty_flip_buffer_push(port);
- spin_lock(&up->port.lock);
}
static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
{
- struct circ_buf *xmit = &up->port.state->xmit;
+ struct tty_port *tport = &up->port.state->port;
+ struct serial_rs485 *rs485conf = &up->port.rs485;
int count;
+ bool half_duplex_send = false;
if (uart_tx_stopped(&up->port))
return;
+ if ((rs485conf->flags & SER_RS485_ENABLED) &&
+ (up->port.x_char || !kfifo_is_empty(&tport->xmit_fifo))) {
+ ar933x_uart_stop_rx_interrupt(up);
+ gpiod_set_value(up->rts_gpiod, !!(rs485conf->flags & SER_RS485_RTS_ON_SEND));
+ half_duplex_send = true;
+ }
+
count = up->port.fifosize;
do {
unsigned int rdata;
+ unsigned char c;
rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG);
if ((rdata & AR933X_UART_DATA_TX_CSR) == 0)
@@ -360,20 +421,23 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
continue;
}
- if (uart_circ_empty(xmit))
+ if (!uart_fifo_get(&up->port, &c))
break;
- ar933x_uart_putc(up, xmit->buf[xmit->tail]);
-
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
+ ar933x_uart_putc(up, c);
} while (--count > 0);
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
- if (!uart_circ_empty(xmit))
+ if (!kfifo_is_empty(&tport->xmit_fifo)) {
ar933x_uart_start_tx_interrupt(up);
+ } else if (half_duplex_send) {
+ ar933x_uart_wait_tx_complete(up);
+ ar933x_uart_rx_flush(up);
+ ar933x_uart_start_rx_interrupt(up);
+ gpiod_set_value(up->rts_gpiod, !!(rs485conf->flags & SER_RS485_RTS_AFTER_SEND));
+ }
}
static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id)
@@ -385,7 +449,7 @@ static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id)
if ((status & AR933X_UART_CS_HOST_INT) == 0)
return IRQ_NONE;
- spin_lock(&up->port.lock);
+ uart_port_lock(&up->port);
status = ar933x_uart_read(up, AR933X_UART_INT_REG);
status &= ar933x_uart_read(up, AR933X_UART_INT_EN_REG);
@@ -403,7 +467,7 @@ static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id)
ar933x_uart_tx_chars(up);
}
- spin_unlock(&up->port.lock);
+ uart_unlock_and_check_sysrq(&up->port);
return IRQ_HANDLED;
}
@@ -420,17 +484,20 @@ static int ar933x_uart_startup(struct uart_port *port)
if (ret)
return ret;
- spin_lock_irqsave(&up->port.lock, flags);
+ uart_port_lock_irqsave(&up->port, &flags);
/* Enable HOST interrupts */
ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
AR933X_UART_CS_HOST_INT_EN);
+ /* enable RX and TX ready overide */
+ ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
+ AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE);
+
/* Enable RX interrupts */
- up->ier = AR933X_UART_INT_RX_VALID;
- ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
+ ar933x_uart_start_rx_interrupt(up);
- spin_unlock_irqrestore(&up->port.lock, flags);
+ uart_port_unlock_irqrestore(&up->port, flags);
return 0;
}
@@ -493,6 +560,64 @@ static int ar933x_uart_verify_port(struct uart_port *port,
return 0;
}
+#ifdef CONFIG_CONSOLE_POLL
+static int ar933x_poll_get_char(struct uart_port *port)
+{
+ struct ar933x_uart_port *up =
+ container_of(port, struct ar933x_uart_port, port);
+ unsigned int rdata;
+ unsigned char ch;
+ u32 imr;
+
+ /* Disable all interrupts */
+ imr = ar933x_uart_read(up, AR933X_UART_INT_EN_REG);
+ ar933x_uart_write(up, AR933X_UART_INT_EN_REG, 0);
+
+ rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG);
+ if ((rdata & AR933X_UART_DATA_RX_CSR) == 0) {
+ /* Enable interrupts */
+ ar933x_uart_write(up, AR933X_UART_INT_EN_REG, imr);
+ return NO_POLL_CHAR;
+ }
+
+ /* remove the character from the FIFO */
+ ar933x_uart_write(up, AR933X_UART_DATA_REG,
+ AR933X_UART_DATA_RX_CSR);
+
+ ch = rdata & AR933X_UART_DATA_TX_RX_MASK;
+
+ /* Enable interrupts */
+ ar933x_uart_write(up, AR933X_UART_INT_EN_REG, imr);
+
+ return ch;
+}
+
+static void ar933x_poll_put_char(struct uart_port *port, unsigned char c)
+{
+ struct ar933x_uart_port *up =
+ container_of(port, struct ar933x_uart_port, port);
+ u32 imr;
+
+ /* Disable all interrupts */
+ imr = ar933x_uart_read(up, AR933X_UART_INT_EN_REG);
+ ar933x_uart_write(up, AR933X_UART_INT_EN_REG, 0);
+
+ /* Wait until FIFO is empty */
+ while (!(ar933x_uart_read(up, AR933X_UART_DATA_REG) & AR933X_UART_DATA_TX_CSR))
+ cpu_relax();
+
+ /* Write a character */
+ ar933x_uart_putc(up, c);
+
+ /* Wait until FIFO is empty */
+ while (!(ar933x_uart_read(up, AR933X_UART_DATA_REG) & AR933X_UART_DATA_TX_CSR))
+ cpu_relax();
+
+ /* Enable interrupts */
+ ar933x_uart_write(up, AR933X_UART_INT_EN_REG, imr);
+}
+#endif
+
static const struct uart_ops ar933x_uart_ops = {
.tx_empty = ar933x_uart_tx_empty,
.set_mctrl = ar933x_uart_set_mctrl,
@@ -509,8 +634,26 @@ static const struct uart_ops ar933x_uart_ops = {
.request_port = ar933x_uart_request_port,
.config_port = ar933x_uart_config_port,
.verify_port = ar933x_uart_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = ar933x_poll_get_char,
+ .poll_put_char = ar933x_poll_put_char,
+#endif
};
+static int ar933x_config_rs485(struct uart_port *port, struct ktermios *termios,
+ struct serial_rs485 *rs485conf)
+{
+ struct ar933x_uart_port *up =
+ container_of(port, struct ar933x_uart_port, port);
+
+ if (port->rs485.flags & SER_RS485_ENABLED)
+ gpiod_set_value(up->rts_gpiod,
+ !!(rs485conf->flags & SER_RS485_RTS_AFTER_SEND));
+
+ return 0;
+}
+
+#ifdef CONFIG_SERIAL_AR933X_CONSOLE
static struct ar933x_uart_port *
ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS];
@@ -528,7 +671,7 @@ static void ar933x_uart_wait_xmitr(struct ar933x_uart_port *up)
} while ((status & AR933X_UART_DATA_TX_CSR) == 0);
}
-static void ar933x_uart_console_putchar(struct uart_port *port, int ch)
+static void ar933x_uart_console_putchar(struct uart_port *port, unsigned char ch)
{
struct ar933x_uart_port *up =
container_of(port, struct ar933x_uart_port, port);
@@ -545,14 +688,10 @@ static void ar933x_uart_console_write(struct console *co, const char *s,
unsigned int int_en;
int locked = 1;
- local_irq_save(flags);
-
- if (up->port.sysrq)
- locked = 0;
- else if (oops_in_progress)
- locked = spin_trylock(&up->port.lock);
+ if (oops_in_progress)
+ locked = uart_port_trylock_irqsave(&up->port, &flags);
else
- spin_lock(&up->port.lock);
+ uart_port_lock_irqsave(&up->port, &flags);
/*
* First save the IER then disable the interrupts
@@ -572,9 +711,7 @@ static void ar933x_uart_console_write(struct console *co, const char *s,
ar933x_uart_write(up, AR933X_UART_INT_REG, AR933X_UART_INT_ALLINTS);
if (locked)
- spin_unlock(&up->port.lock);
-
- local_irq_restore(flags);
+ uart_port_unlock_irqrestore(&up->port, flags);
}
static int ar933x_uart_console_setup(struct console *co, char *options)
@@ -607,14 +744,7 @@ static struct console ar933x_uart_console = {
.index = -1,
.data = &ar933x_uart_driver,
};
-
-static void ar933x_uart_add_console_port(struct ar933x_uart_port *up)
-{
- if (!ar933x_uart_console_enabled())
- return;
-
- ar933x_console_ports[up->port.line] = up;
-}
+#endif /* CONFIG_SERIAL_AR933X_CONSOLE */
static struct uart_driver ar933x_uart_driver = {
.owner = THIS_MODULE,
@@ -624,16 +754,20 @@ static struct uart_driver ar933x_uart_driver = {
.cons = NULL, /* filled in runtime */
};
+static const struct serial_rs485 ar933x_rs485_supported = {
+ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND,
+};
+
static int ar933x_uart_probe(struct platform_device *pdev)
{
struct ar933x_uart_port *up;
struct uart_port *port;
struct resource *mem_res;
- struct resource *irq_res;
struct device_node *np;
unsigned int baud;
int id;
int ret;
+ int irq;
np = pdev->dev.of_node;
if (IS_ENABLED(CONFIG_OF) && np) {
@@ -652,11 +786,9 @@ static int ar933x_uart_probe(struct platform_device *pdev)
if (id >= CONFIG_SERIAL_AR933X_NR_UARTS)
return -EINVAL;
- irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq_res) {
- dev_err(&pdev->dev, "no IRQ resource\n");
- return -EINVAL;
- }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
up = devm_kzalloc(&pdev->dev, sizeof(struct ar933x_uart_port),
GFP_KERNEL);
@@ -671,8 +803,7 @@ static int ar933x_uart_probe(struct platform_device *pdev)
port = &up->port;
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- port->membase = devm_ioremap_resource(&pdev->dev, mem_res);
+ port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res);
if (IS_ERR(port->membase))
return PTR_ERR(port->membase);
@@ -688,7 +819,7 @@ static int ar933x_uart_probe(struct platform_device *pdev)
port->mapbase = mem_res->start;
port->line = id;
- port->irq = irq_res->start;
+ port->irq = irq;
port->dev = &pdev->dev;
port->type = PORT_AR933X;
port->iotype = UPIO_MEM32;
@@ -696,6 +827,8 @@ static int ar933x_uart_probe(struct platform_device *pdev)
port->regshift = 2;
port->fifosize = AR933X_UART_FIFO_SIZE;
port->ops = &ar933x_uart_ops;
+ port->rs485_config = ar933x_config_rs485;
+ port->rs485_supported = ar933x_rs485_supported;
baud = ar933x_uart_get_baud(port->uartclk, AR933X_UART_MAX_SCALE, 1);
up->min_baud = max_t(unsigned int, baud, AR933X_UART_MIN_BAUD);
@@ -703,7 +836,29 @@ static int ar933x_uart_probe(struct platform_device *pdev)
baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP);
up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD);
- ar933x_uart_add_console_port(up);
+ ret = uart_get_rs485_mode(port);
+ if (ret)
+ goto err_disable_clk;
+
+ up->gpios = mctrl_gpio_init(port, 0);
+ if (IS_ERR(up->gpios) && PTR_ERR(up->gpios) != -ENOSYS) {
+ ret = PTR_ERR(up->gpios);
+ goto err_disable_clk;
+ }
+
+ up->rts_gpiod = mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS);
+
+ if (!up->rts_gpiod) {
+ port->rs485_supported.flags &= ~SER_RS485_ENABLED;
+ if (port->rs485.flags & SER_RS485_ENABLED) {
+ dev_err(&pdev->dev, "lacking rts-gpio, disabling RS485\n");
+ port->rs485.flags &= ~SER_RS485_ENABLED;
+ }
+ }
+
+#ifdef CONFIG_SERIAL_AR933X_CONSOLE
+ ar933x_console_ports[up->port.line] = up;
+#endif
ret = uart_add_one_port(&ar933x_uart_driver, &up->port);
if (ret)
@@ -717,7 +872,7 @@ err_disable_clk:
return ret;
}
-static int ar933x_uart_remove(struct platform_device *pdev)
+static void ar933x_uart_remove(struct platform_device *pdev)
{
struct ar933x_uart_port *up;
@@ -727,8 +882,6 @@ static int ar933x_uart_remove(struct platform_device *pdev)
uart_remove_one_port(&ar933x_uart_driver, &up->port);
clk_disable_unprepare(up->clk);
}
-
- return 0;
}
#ifdef CONFIG_OF
@@ -752,8 +905,9 @@ static int __init ar933x_uart_init(void)
{
int ret;
- if (ar933x_uart_console_enabled())
- ar933x_uart_driver.cons = &ar933x_uart_console;
+#ifdef CONFIG_SERIAL_AR933X_CONSOLE
+ ar933x_uart_driver.cons = &ar933x_uart_console;
+#endif
ret = uart_register_driver(&ar933x_uart_driver);
if (ret)