@@ -248,6 +248,13 @@ struct pl011_dmatx_data {
bool queued;
};
+enum pl011_rs485_tx_state {
+ OFF,
+ WAIT_AFTER_RTS,
+ SEND,
+ WAIT_AFTER_SEND,
+};
+
/*
* We wrap our port structure around the generic uart_port.
*/
@@ -261,8 +268,10 @@ struct uart_amba_port {
unsigned int fifosize; /* vendor-specific */
unsigned int fixed_baud; /* vendor-set fixed baud rate */
char type[12];
- bool rs485_tx_started;
- unsigned int rs485_tx_drain_interval; /* usecs */
+ ktime_t rs485_tx_drain_interval; /* nano */
+ enum pl011_rs485_tx_state rs485_tx_state;
+ struct hrtimer trigger_start_tx;
+ struct hrtimer trigger_stop_tx;
#ifdef CONFIG_DMA_ENGINE
/* DMA stuff */
unsigned int dmacr; /* dma control reg */
@@ -1260,30 +1269,31 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
static void pl011_rs485_tx_stop(struct uart_amba_port *uap)
{
- /*
- * To be on the safe side only time out after twice as many iterations
- * as fifo size.
- */
- const int MAX_TX_DRAIN_ITERS = uap->port.fifosize * 2;
struct uart_port *port = &uap->port;
- int i = 0;
u32 cr;
- /* Wait until hardware tx queue is empty */
- while (!pl011_tx_empty(port)) {
- if (i > MAX_TX_DRAIN_ITERS) {
- dev_warn(port->dev,
- "timeout while draining hardware tx queue\n");
- break;
- }
+ if (uap->rs485_tx_state == SEND)
+ uap->rs485_tx_state = WAIT_AFTER_SEND;
- udelay(uap->rs485_tx_drain_interval);
- i++;
+ if (uap->rs485_tx_state == WAIT_AFTER_SEND) {
+ /* Schedule hrtimer if tx queue not empty */
+ if (!pl011_tx_empty(port)) {
+ hrtimer_start(&uap->trigger_stop_tx,
+ uap->rs485_tx_drain_interval,
+ HRTIMER_MODE_REL);
+ return;
+ }
+ if (port->rs485.delay_rts_after_send > 0) {
+ hrtimer_start(&uap->trigger_stop_tx,
+ ms_to_ktime(port->rs485.delay_rts_after_send),
+ HRTIMER_MODE_REL);
+ return;
+ }
+ /* Continue without any delay */
+ } else if (uap->rs485_tx_state == WAIT_AFTER_RTS) {
+ hrtimer_try_to_cancel(&uap->trigger_start_tx);
}
- if (port->rs485.delay_rts_after_send)
- mdelay(port->rs485.delay_rts_after_send);
-
cr = pl011_read(uap, REG_CR);
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
@@ -1296,7 +1306,7 @@ static void pl011_rs485_tx_stop(struct uart_amba_port *uap)
cr |= UART011_CR_RXE;
pl011_write(cr, uap, REG_CR);
- uap->rs485_tx_started = false;
+ uap->rs485_tx_state = OFF;
}
static void pl011_stop_tx(struct uart_port *port)
@@ -1304,11 +1314,18 @@ static void pl011_stop_tx(struct uart_port *port)
struct uart_amba_port *uap =
container_of(port, struct uart_amba_port, port);
+ if (port->rs485.flags & SER_RS485_ENABLED &&
+ uap->rs485_tx_state == WAIT_AFTER_RTS) {
+ pl011_rs485_tx_stop(uap);
+ return;
+ }
+
uap->im &= ~UART011_TXIM;
pl011_write(uap->im, uap, REG_IMSC);
pl011_dma_tx_stop(uap);
- if ((port->rs485.flags & SER_RS485_ENABLED) && uap->rs485_tx_started)
+ if (port->rs485.flags & SER_RS485_ENABLED &&
+ uap->rs485_tx_state != OFF)
pl011_rs485_tx_stop(uap);
}
@@ -1328,10 +1345,19 @@ static void pl011_rs485_tx_start(struct uart_amba_port *uap)
struct uart_port *port = &uap->port;
u32 cr;
+ if (uap->rs485_tx_state == WAIT_AFTER_RTS) {
+ uap->rs485_tx_state = SEND;
+ return;
+ }
+ if (uap->rs485_tx_state == WAIT_AFTER_SEND) {
+ hrtimer_try_to_cancel(&uap->trigger_stop_tx);
+ uap->rs485_tx_state = SEND;
+ return;
+ }
+ /* uap->rs485_tx_state == OFF */
/* Enable transmitter */
cr = pl011_read(uap, REG_CR);
cr |= UART011_CR_TXE;
-
/* Disable receiver if half-duplex */
if (!(port->rs485.flags & SER_RS485_RX_DURING_TX))
cr &= ~UART011_CR_RXE;
@@ -1343,10 +1369,14 @@ static void pl011_rs485_tx_start(struct uart_amba_port *uap)
pl011_write(cr, uap, REG_CR);
- if (port->rs485.delay_rts_before_send)
- mdelay(port->rs485.delay_rts_before_send);
-
- uap->rs485_tx_started = true;
+ if (port->rs485.delay_rts_before_send > 0) {
+ uap->rs485_tx_state = WAIT_AFTER_RTS;
+ hrtimer_start(&uap->trigger_start_tx,
+ ms_to_ktime(port->rs485.delay_rts_before_send),
+ HRTIMER_MODE_REL);
+ } else {
+ uap->rs485_tx_state = SEND;
+ }
}
static void pl011_start_tx(struct uart_port *port)
@@ -1355,13 +1385,44 @@ static void pl011_start_tx(struct uart_port *port)
container_of(port, struct uart_amba_port, port);
if ((uap->port.rs485.flags & SER_RS485_ENABLED) &&
- !uap->rs485_tx_started)
+ uap->rs485_tx_state != SEND) {
pl011_rs485_tx_start(uap);
+ if (uap->rs485_tx_state == WAIT_AFTER_RTS)
+ return;
+ }
if (!pl011_dma_tx_start(uap))
pl011_start_tx_pio(uap);
}
+static enum hrtimer_restart pl011_trigger_start_tx(struct hrtimer *t)
+{
+ struct uart_amba_port *uap =
+ container_of(t, struct uart_amba_port, trigger_start_tx);
+ unsigned long flags;
+
+ uart_port_lock_irqsave(&uap->port, &flags);
+ if (uap->rs485_tx_state == WAIT_AFTER_RTS)
+ pl011_start_tx(&uap->port);
+ uart_port_unlock_irqrestore(&uap->port, flags);
+
+ return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart pl011_trigger_stop_tx(struct hrtimer *t)
+{
+ struct uart_amba_port *uap =
+ container_of(t, struct uart_amba_port, trigger_stop_tx);
+ unsigned long flags;
+
+ uart_port_lock_irqsave(&uap->port, &flags);
+ if (uap->rs485_tx_state == WAIT_AFTER_SEND)
+ pl011_rs485_tx_stop(uap);
+ uart_port_unlock_irqrestore(&uap->port, flags);
+
+ return HRTIMER_NORESTART;
+}
+
static void pl011_stop_rx(struct uart_port *port)
{
struct uart_amba_port *uap =
@@ -1953,7 +2014,7 @@ static void pl011_shutdown(struct uart_port *port)
pl011_dma_shutdown(uap);
- if ((port->rs485.flags & SER_RS485_ENABLED) && uap->rs485_tx_started)
+ if ((port->rs485.flags & SER_RS485_ENABLED && uap->rs485_tx_state != OFF))
pl011_rs485_tx_stop(uap);
free_irq(uap->port.irq, uap);
@@ -2098,7 +2159,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
* with the given baud rate. We use this as the poll interval when we
* wait for the tx queue to empty.
*/
- uap->rs485_tx_drain_interval = DIV_ROUND_UP(bits * 1000 * 1000, baud);
+ uap->rs485_tx_drain_interval = ns_to_ktime(DIV_ROUND_UP(bits * NSEC_PER_SEC, baud));
pl011_setup_status_masks(port, termios);
@@ -2807,6 +2868,11 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
}
}
+ hrtimer_init(&uap->trigger_start_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_init(&uap->trigger_stop_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ uap->trigger_start_tx.function = pl011_trigger_start_tx;
+ uap->trigger_stop_tx.function = pl011_trigger_stop_tx;
+
ret = pl011_setup_port(&dev->dev, uap, &dev->res, portnr);
if (ret)
return ret;