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.c58
1 files changed, 46 insertions, 12 deletions
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index f16da5982358..d01c1c7152e2 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -127,6 +127,11 @@ static unsigned long probe_rsa[PORT_RSA_MAX];
static unsigned int probe_rsa_count;
#endif /* CONFIG_SERIAL_8250_RSA */
+struct rs485_wait_state {
+ int gpio;
+ int waiting;
+};
+
struct uart_8250_port {
struct uart_port port;
struct timer_list timer; /* "no irq" timer */
@@ -156,6 +161,8 @@ struct uart_8250_port {
*/
void (*pm)(struct uart_port *port,
unsigned int state, unsigned int old);
+
+ struct rs485_wait_state rs485_wait_state;
};
struct irq_info {
@@ -169,6 +176,9 @@ struct irq_info {
static struct hlist_head irq_lists[NR_IRQ_HASH];
static DEFINE_MUTEX(hash_mutex); /* Used to walk the hash */
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+static void wait_for_xmitr(struct uart_8250_port *up, int bits);
+
/*
* Here we define the default xmit fifo size used for each type of UART.
*/
@@ -530,6 +540,31 @@ static void serial_dl_write(struct uart_8250_port *up, int value)
#define serial_dl_write(up, value) _serial_dl_write(up, value)
#endif
+// unfortunately it seems the gpio switch for RS485 can be late even with the
+// wait queue based wakeup of the task waiting on its completion, so we *have*
+// to do the gpio switch in the interrupt handler.
+static void rs485_wait_end(struct uart_8250_port *up) {
+ struct rs485_wait_state *rs485_wait_state =
+ &up->rs485_wait_state;
+ if (rs485_wait_state->waiting) {
+ int status;
+ status = serial_in(up, UART_LSR);
+ // no interrupt will be fire for shift register (TEMT) so we have to spin
+ // on it.
+ if (status & UART_LSR_THRE) {
+ wait_for_xmitr(up, BOTH_EMPTY);
+ // turn off RS485 DE pin
+ if (rs485_wait_state->gpio)
+ gpio_set_value(rs485_wait_state->gpio, 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;
+ rs485_wait_state->waiting = 0;
+ }
+ }
+}
+
/*
* For the 16C950
*/
@@ -1235,6 +1270,7 @@ static inline void __stop_tx(struct uart_8250_port *p)
p->ier &= ~UART_IER_THRI;
serial_out(p, UART_IER, p->ier);
}
+ uart_write_wakeup(&p->port);
}
static void serial8250_stop_tx(struct uart_port *port)
@@ -1436,7 +1472,7 @@ static unsigned int check_modem_status(struct uart_8250_port *up)
if (status & UART_MSR_DCTS)
uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
- wake_up_interruptible(&up->port.info->delta_msr_wait);
+ wake_up_interruptible(&up->port.info->delta_msr_wait);
}
return status;
@@ -1462,6 +1498,7 @@ static void serial8250_handle_port(struct uart_8250_port *up)
if (status & UART_LSR_THRE)
transmit_chars(up);
+ rs485_wait_end(up);
spin_unlock_irqrestore(&up->port.lock, flags);
}
@@ -1777,8 +1814,6 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
spin_unlock_irqrestore(&up->port.lock, flags);
}
-#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-
/*
* Wait for transmitter & holding register to empty
*/
@@ -2539,8 +2574,13 @@ static int serial8250_ioctl(struct uart_port *port, unsigned int cmd, unsigned l
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;
+ spin_lock_irqsave(&up->port.lock, flags);
+ up->rs485_wait_state.gpio = arg;
+ up->rs485_wait_state.waiting = 1;
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ // wait for kernel buffers and UART FIFO to both empty
wait_event_interruptible(
thre_wait,
uart_circ_empty(xmit) &&
@@ -2548,14 +2588,6 @@ static int serial8250_ioctl(struct uart_port *port, unsigned int cmd, unsigned l
// 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;
}
@@ -2607,6 +2639,8 @@ static void __init serial8250_isa_init_ports(void)
init_timer(&up->timer);
up->timer.function = serial8250_timeout;
+ up->rs485_wait_state.waiting = 0;
+ up->rs485_wait_state.gpio = 0;
/*
* ALPHA_KLUDGE_MCR needs to be killed.