serial: 8250: Handle case port doesn't have TEMT

A patch from »serial: 8250: Add rs485 emulation to 8250_dw« in state Mainline for linux-kernel

From: Giulio Benetti <giulio.benetti@...> Date: Wed, 6 Jun 2018 11:51:52 +0200

Commit-Message

Some 8250 ports only have TEMT interrupt, so current implementation can't work for ports without it. The only chance to make it work is to loop-read on LSR register. With NO TEMT interrupt check if both TEMT and THRE are set looping on LSR register. Signed-off-by: Giulio Benetti <giulio.benetti@...> Signed-off-by: Heiko Stuebner <heiko.stuebner@...>

Patch-Comment

drivers/tty/serial/8250/8250.h | 2 +- drivers/tty/serial/8250/8250_of.c | 2 +- drivers/tty/serial/8250/8250_omap.c | 2 +- drivers/tty/serial/8250/8250_port.c | 30 +++++++++++++++++++++-------- include/linux/serial_8250.h | 1 + 5 files changed, 26 insertions(+), 11 deletions(-)

Statistics

  • 26 lines added
  • 11 lines removed

Changes

------------------------ drivers/tty/serial/8250/8250.h ------------------------
index 50a4c174410d..9e049d2a039e 100644
@@ -190,7 +190,7 @@ void serial8250_rpm_put(struct uart_8250_port *p);
void serial8250_rpm_get_tx(struct uart_8250_port *p);
void serial8250_rpm_put_tx(struct uart_8250_port *p);
-int serial8250_em485_init(struct uart_8250_port *p);
+int serial8250_em485_init(struct uart_8250_port *p, bool has_temt_isr);
void serial8250_em485_destroy(struct uart_8250_port *p);
/* MCR <-> TIOCM conversion */
---------------------- drivers/tty/serial/8250/8250_of.c -----------------------
index 92fbf46ce3bd..c77ab44a5468 100644
@@ -64,7 +64,7 @@ static int of_8250_rs485_config(struct uart_port *port,
* are idempotent
*/
if (rs485->flags & SER_RS485_ENABLED) {
- int ret = serial8250_em485_init(up);
+ int ret = serial8250_em485_init(up, true);
if (ret) {
rs485->flags &= ~SER_RS485_ENABLED;
--------------------- drivers/tty/serial/8250/8250_omap.c ----------------------
index 836e736ae188..241322900298 100644
@@ -734,7 +734,7 @@ static int omap_8250_rs485_config(struct uart_port *port,
* are idempotent
*/
if (rs485->flags & SER_RS485_ENABLED) {
- int ret = serial8250_em485_init(up);
+ int ret = serial8250_em485_init(up, true);
if (ret) {
rs485->flags &= ~SER_RS485_ENABLED;
--------------------- drivers/tty/serial/8250/8250_port.c ----------------------
index 67aa3a2a9afa..3d1d8490bc42 100644
@@ -605,15 +605,17 @@ EXPORT_SYMBOL_GPL(serial8250_rpm_put);
/**
* serial8250_em485_init() - put uart_8250_port into rs485 emulating
* @p: uart_8250_port port instance
+ * @p: bool specify if 8250 port has TEMT interrupt or not
*
* The function is used to start rs485 software emulating on the
* &struct uart_8250_port* @p. Namely, RTS is switched before/after
* transmission. The function is idempotent, so it is safe to call it
* multiple times.
*
- * The caller MUST enable interrupt on empty shift register before
- * calling serial8250_em485_init(). This interrupt is not a part of
- * 8250 standard, but implementation defined.
+ * If has_temt_isr is passed as true, the caller MUST enable interrupt
+ * on empty shift register before calling serial8250_em485_init().
+ * This interrupt is not a part of 8250 standard, but implementation
+ * defined.
*
* The function is supposed to be called from .rs485_config callback
* or from any other callback protected with p->port.lock spinlock.
@@ -622,7 +624,7 @@ EXPORT_SYMBOL_GPL(serial8250_rpm_put);
*
* Return 0 - success, -errno - otherwise
*/
-int serial8250_em485_init(struct uart_8250_port *p)
+int serial8250_em485_init(struct uart_8250_port *p, bool has_temt_isr)
{
if (p->em485)
return 0;
@@ -639,6 +641,7 @@ int serial8250_em485_init(struct uart_8250_port *p)
p->em485->start_tx_timer.function = &serial8250_em485_handle_start_tx;
p->em485->port = p;
p->em485->active_timer = NULL;
+ p->em485->has_temt_isr = has_temt_isr;
serial8250_em485_rts_after_send(p);
return 0;
@@ -1475,11 +1478,22 @@ static inline void __stop_tx(struct uart_8250_port *p)
/*
* To provide required timeing and allow FIFO transfer,
* __stop_tx_rs485() must be called only when both FIFO and
- * shift register are empty. It is for device driver to enable
- * interrupt on TEMT.
+ * shift register are empty. If 8250 port supports it,
+ * it is for device driver to enable interrupt on TEMT.
+ * Otherwise must loop-read until TEMT and THRE flags are set.
*/
- if ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
- return;
+ if (em485->has_temt_isr) {
+ if ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
+ return;
+ } else {
+ int val;
+
+ if (serial_in_poll_timeout(p, UART_LSR, val,
+ (val & BOTH_EMPTY) != BOTH_EMPTY,
+ 100000) < 0)
+ pr_warn("%s: timeout waiting for fifos to empty\n",
+ p->port.name);
+ }
em485->active_timer = NULL;
------------------------- include/linux/serial_8250.h --------------------------
index bb2bc99388ca..c4b4469c272b 100644
@@ -79,6 +79,7 @@ struct uart_8250_em485 {
struct hrtimer start_tx_timer; /* "rs485 start tx" timer */
struct hrtimer stop_tx_timer; /* "rs485 stop tx" timer */
struct hrtimer *active_timer; /* pointer to active timer */
+ bool has_temt_isr; /* variant has TEMT interrupt */
struct uart_8250_port *port; /* for hrtimer callbacks */
};
 
 

Recent Patches

About Us

Sed lacus. Donec lectus. Nullam pretium nibh ut turpis. Nam bibendum. In nulla tortor, elementum vel, tempor at, varius non, purus. Mauris vitae nisl nec metus placerat consectetuer.

Read More...