summaryrefslogtreecommitdiff
path: root/drivers/serial/8250.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/serial/8250.c')
-rw-r--r--drivers/serial/8250.c39
1 files changed, 38 insertions, 1 deletions
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index a181ccf46281..f16da5982358 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -38,6 +38,7 @@
#include <linux/serial_8250.h>
#include <linux/nmi.h>
#include <linux/mutex.h>
+#include <linux/gpio.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -59,6 +60,8 @@ static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
static struct uart_driver serial8250_reg;
+DECLARE_WAIT_QUEUE_HEAD(thre_wait);
+
static int serial_index(struct uart_port *port)
{
return (serial8250_reg.minor - 64) + port->line;
@@ -1225,7 +1228,10 @@ static void autoconfig_irq(struct uart_8250_port *up)
static inline void __stop_tx(struct uart_8250_port *p)
{
- if (p->ier & UART_IER_THRI) {
+ int status = serial_in(p, UART_LSR);
+ // only turn off THRE interrupt if THRE is *currently* asserted
+ // (we still want to catch it a final time after the FIFO empties)
+ if ((p->ier & UART_IER_THRI) && (status & UART_LSR_THRE)) {
p->ier &= ~UART_IER_THRI;
serial_out(p, UART_IER, p->ier);
}
@@ -1527,6 +1533,7 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
DEBUG_INTR("end.\n");
+ wake_up(&thre_wait);
return IRQ_RETVAL(handled);
}
@@ -2526,6 +2533,35 @@ serial8250_type(struct uart_port *port)
return uart_config[type].name;
}
+static int serial8250_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) {
+ struct uart_8250_port *up = (struct uart_8250_port *)port;
+ unsigned long flags;
+ int ret = -ENOIOCTLCMD;
+ // kernel-space RS485 drain-and-switch hack
+ if (cmd == TIOCSERWAITTEMT) {
+ // wait for kernel buffers and UART FIFO to both empty
+ struct circ_buf *xmit = &up->port.info->xmit;
+ wait_event_interruptible(
+ thre_wait,
+ uart_circ_empty(xmit) &&
+ (serial_in(up, UART_LSR) & UART_LSR_THRE));
+ // spin until TEMT (transmit shift register empty)
+ spin_lock_irqsave(&up->port.lock, flags);
+ wait_for_xmitr(up, BOTH_EMPTY);
+ if (arg != 0) {
+ // turn off RS485 DE pin
+ gpio_set_value(arg, 0);
+ }
+ // grab any phantom char seen on RX when transceiver switches
+ (void) serial_inp(up, UART_RX);
+ // enable read
+ up->port.ignore_status_mask &= ~UART_LSR_DR;
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return 0;
+ }
+ return ret;
+}
+
static struct uart_ops serial8250_pops = {
.tx_empty = serial8250_tx_empty,
.set_mctrl = serial8250_set_mctrl,
@@ -2544,6 +2580,7 @@ static struct uart_ops serial8250_pops = {
.request_port = serial8250_request_port,
.config_port = serial8250_config_port,
.verify_port = serial8250_verify_port,
+ .ioctl = serial8250_ioctl,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = serial8250_get_poll_char,
.poll_put_char = serial8250_put_poll_char,