Skip to content

Commit f6f5861

Browse files
Eric Tremblaygregkh
authored andcommitted
serial: 8250: Handle UART without interrupt on TEMT using em485
Introduce the UART_CAP_NOTEMT capability. The capability indicates that the UART doesn't have an interrupt available on TEMT. In the case where the device does not support it, we calculate the maximum time it could take for the transmitter to empty the shift register. When we get in the situation where we get the THRE interrupt, we check if the TEMT bit is set. If it's not, we start the a timer and recall __stop_tx() after the delay. The transmit sequence is a bit modified when the capability is set. The new timer is used between the last interrupt(THRE) and a potential stop_tx timer. Signed-off-by: Giulio Benetti <giulio.benetti@micronovasrl.com> [moved to use added UART_CAP_TEMT] Signed-off-by: Heiko Stuebner <heiko.stuebner@theobroma-systems.com> [moved to use added UART_CAP_NOTEMT, improve timeout] Signed-off-by: Eric Tremblay <etremblay@distech-controls.com> [rebased to v5.17, making use of tty_get_frame_size] Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Link: https://lore.kernel.org/r/20220330104642.229507-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 0e0fd55 commit f6f5861

File tree

3 files changed

+77
-2
lines changed

3 files changed

+77
-2
lines changed

drivers/tty/serial/8250/8250.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct serial8250_config {
8383
#define UART_CAP_MINI BIT(17) /* Mini UART on BCM283X family lacks:
8484
* STOP PARITY EPAR SPAR WLEN5 WLEN6
8585
*/
86+
#define UART_CAP_NOTEMT BIT(18) /* UART without interrupt on TEMT available */
8687

8788
#define UART_BUG_QUOT BIT(0) /* UART has buggy quot LSB */
8889
#define UART_BUG_TXEN BIT(1) /* UART has buggy TX IIR status */

drivers/tty/serial/8250/8250_port.c

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,8 +571,21 @@ static void serial8250_clear_fifos(struct uart_8250_port *p)
571571
}
572572
}
573573

574+
static inline void serial8250_em485_update_temt_delay(struct uart_8250_port *p,
575+
unsigned int cflag, unsigned int baud)
576+
{
577+
unsigned int bits;
578+
579+
if (!p->em485)
580+
return;
581+
582+
bits = tty_get_frame_size(cflag);
583+
p->em485->no_temt_delay = DIV_ROUND_UP(bits * NSEC_PER_SEC, baud);
584+
}
585+
574586
static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t);
575587
static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t);
588+
static enum hrtimer_restart serial8250_em485_handle_no_temt(struct hrtimer *t);
576589

577590
void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p)
578591
{
@@ -631,6 +644,16 @@ static int serial8250_em485_init(struct uart_8250_port *p)
631644
HRTIMER_MODE_REL);
632645
hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC,
633646
HRTIMER_MODE_REL);
647+
648+
if (p->capabilities & UART_CAP_NOTEMT) {
649+
struct tty_struct *tty = p->port.state->port.tty;
650+
651+
serial8250_em485_update_temt_delay(p, tty->termios.c_cflag,
652+
tty_get_baud_rate(tty));
653+
hrtimer_init(&p->em485->no_temt_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
654+
p->em485->no_temt_timer.function = &serial8250_em485_handle_no_temt;
655+
}
656+
634657
p->em485->stop_tx_timer.function = &serial8250_em485_handle_stop_tx;
635658
p->em485->start_tx_timer.function = &serial8250_em485_handle_start_tx;
636659
p->em485->port = p;
@@ -662,6 +685,7 @@ void serial8250_em485_destroy(struct uart_8250_port *p)
662685

663686
hrtimer_cancel(&p->em485->start_tx_timer);
664687
hrtimer_cancel(&p->em485->stop_tx_timer);
688+
hrtimer_cancel(&p->em485->no_temt_timer);
665689

666690
kfree(p->em485);
667691
p->em485 = NULL;
@@ -1504,6 +1528,11 @@ static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec)
15041528
hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL);
15051529
}
15061530

1531+
static void start_hrtimer_ns(struct hrtimer *hrt, unsigned long nsec)
1532+
{
1533+
hrtimer_start(hrt, ns_to_ktime(nsec), HRTIMER_MODE_REL);
1534+
}
1535+
15071536
static void __stop_tx_rs485(struct uart_8250_port *p)
15081537
{
15091538
struct uart_8250_em485 *em485 = p->em485;
@@ -1535,14 +1564,33 @@ static inline void __stop_tx(struct uart_8250_port *p)
15351564

15361565
if (em485) {
15371566
unsigned char lsr = serial_in(p, UART_LSR);
1567+
1568+
p->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
1569+
15381570
/*
1539-
* To provide required timeing and allow FIFO transfer,
1571+
* To provide required timing and allow FIFO transfer,
15401572
* __stop_tx_rs485() must be called only when both FIFO and
15411573
* shift register are empty. It is for device driver to enable
15421574
* interrupt on TEMT.
15431575
*/
1544-
if ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
1576+
if ((lsr & BOTH_EMPTY) != BOTH_EMPTY) {
1577+
if (!(p->capabilities & UART_CAP_NOTEMT))
1578+
/* __stop_tx will be called again once TEMT triggers */
1579+
return;
1580+
1581+
if (!(lsr & UART_LSR_THRE))
1582+
/* __stop_tx will be called again once THRE triggers */
1583+
return;
1584+
1585+
/*
1586+
* On devices with no TEMT interrupt available, start
1587+
* a timer for a byte time. The timer will recall
1588+
* __stop_tx().
1589+
*/
1590+
em485->active_timer = &em485->no_temt_timer;
1591+
start_hrtimer_ns(&em485->no_temt_timer, em485->no_temt_delay);
15451592
return;
1593+
}
15461594

15471595
__stop_tx_rs485(p);
15481596
}
@@ -1653,6 +1701,27 @@ static inline void start_tx_rs485(struct uart_port *port)
16531701
__start_tx(port);
16541702
}
16551703

1704+
static enum hrtimer_restart serial8250_em485_handle_no_temt(struct hrtimer *t)
1705+
{
1706+
struct uart_8250_em485 *em485;
1707+
struct uart_8250_port *p;
1708+
unsigned long flags;
1709+
1710+
em485 = container_of(t, struct uart_8250_em485, no_temt_timer);
1711+
p = em485->port;
1712+
1713+
serial8250_rpm_get(p);
1714+
spin_lock_irqsave(&p->port.lock, flags);
1715+
if (em485->active_timer == &em485->no_temt_timer) {
1716+
em485->active_timer = NULL;
1717+
__stop_tx(p);
1718+
}
1719+
1720+
spin_unlock_irqrestore(&p->port.lock, flags);
1721+
serial8250_rpm_put(p);
1722+
return HRTIMER_NORESTART;
1723+
}
1724+
16561725
static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t)
16571726
{
16581727
struct uart_8250_em485 *em485 = container_of(t, struct uart_8250_em485,
@@ -2858,6 +2927,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
28582927

28592928
serial8250_set_divisor(port, baud, quot, frac);
28602929

2930+
if (up->capabilities & UART_CAP_NOTEMT)
2931+
serial8250_em485_update_temt_delay(up, termios->c_cflag, baud);
2932+
28612933
/*
28622934
* LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
28632935
* is written without DLAB set, this mode will be disabled.

include/linux/serial_8250.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ struct uart_8250_ops {
7979
struct uart_8250_em485 {
8080
struct hrtimer start_tx_timer; /* "rs485 start tx" timer */
8181
struct hrtimer stop_tx_timer; /* "rs485 stop tx" timer */
82+
struct hrtimer no_temt_timer; /* "rs485 no TEMT interrupt" timer */
8283
struct hrtimer *active_timer; /* pointer to active timer */
84+
unsigned long no_temt_delay; /* Delay for no_temt_timer */
8385
struct uart_8250_port *port; /* for hrtimer callbacks */
8486
unsigned int tx_stopped:1; /* tx is currently stopped */
8587
};

0 commit comments

Comments
 (0)