Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 96 additions & 30 deletions drivers/tty/serial/amba-pl011.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,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.
*/
Expand All @@ -275,8 +282,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 */
Expand Down Expand Up @@ -1281,30 +1290,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)
Expand All @@ -1317,19 +1327,26 @@ 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)
{
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);
}

Expand All @@ -1349,10 +1366,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;
Expand All @@ -1364,10 +1390,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)
Expand All @@ -1376,13 +1406,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 =
Expand Down Expand Up @@ -1979,7 +2040,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);
Expand Down Expand Up @@ -2124,7 +2185,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);

Expand Down Expand Up @@ -2838,6 +2899,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;
Expand Down