From f6f586102add59d57bcc6eea06fdeaae11bb17a1 Mon Sep 17 00:00:00 2001 From: Eric Tremblay Date: Wed, 30 Mar 2022 12:46:40 +0200 Subject: serial: 8250: Handle UART without interrupt on TEMT using em485 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 [moved to use added UART_CAP_TEMT] Signed-off-by: Heiko Stuebner [moved to use added UART_CAP_NOTEMT, improve timeout] Signed-off-by: Eric Tremblay [rebased to v5.17, making use of tty_get_frame_size] Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220330104642.229507-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_8250.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index ff84a3ed10ea..de135852107c 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -79,7 +79,9 @@ struct uart_8250_ops { struct uart_8250_em485 { struct hrtimer start_tx_timer; /* "rs485 start tx" timer */ struct hrtimer stop_tx_timer; /* "rs485 stop tx" timer */ + struct hrtimer no_temt_timer; /* "rs485 no TEMT interrupt" timer */ struct hrtimer *active_timer; /* pointer to active timer */ + unsigned long no_temt_delay; /* Delay for no_temt_timer */ struct uart_8250_port *port; /* for hrtimer callbacks */ unsigned int tx_stopped:1; /* tx is currently stopped */ }; -- cgit v1.2.3-71-gd317 From 7a107b2c6b813c3c587d1e76cb405dc637dd2b36 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Wed, 20 Apr 2022 11:19:01 +0300 Subject: Revert "serial: 8250: Handle UART without interrupt on TEMT using em485" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This partially reverts commit f6f586102add. The code added by that commit containted math overflow for 32-bit archs. In addition, the approach used in it is unnecessarily complicated requiring a dedicated timer just for notemt. A simpler approach for providing UART_CAP_NOTEMT already exists (patches 1-2): https://lore.kernel.org/linux-serial/20220411083321.9131-3-ilpo.jarvinen@linux.intel.com/T/#u Thus, simply revert the UART_CAP_NOTEMT change for now. There were two driver changes within the patch series adding UART_CAP_NOTEMT taking advantage of the newly added flag. This does not revert the driver changes and therefore also UART_CAP_NOTEMT define has to remain. UART_CAP_NOTEMT remains no-op until support is again added. Fixes: f6f586102add ("serial: 8250: Handle UART without interrupt on TEMT using em485") Signed-off-by: Ilpo Järvinen Link: https://lore.kernel.org/r/5f874142-fb1f-bff7-f33-fac823e65e2e@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 76 +------------------------------------ include/linux/serial_8250.h | 2 - 2 files changed, 2 insertions(+), 76 deletions(-) (limited to 'include/linux') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index c1f24e88ef09..33bd062140c9 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -571,21 +571,8 @@ static void serial8250_clear_fifos(struct uart_8250_port *p) } } -static inline void serial8250_em485_update_temt_delay(struct uart_8250_port *p, - unsigned int cflag, unsigned int baud) -{ - unsigned int bits; - - if (!p->em485) - return; - - bits = tty_get_frame_size(cflag); - p->em485->no_temt_delay = DIV_ROUND_UP(bits * NSEC_PER_SEC, baud); -} - static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t); static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t); -static enum hrtimer_restart serial8250_em485_handle_no_temt(struct hrtimer *t); void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) { @@ -644,16 +631,6 @@ static int serial8250_em485_init(struct uart_8250_port *p) HRTIMER_MODE_REL); hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - - if (p->capabilities & UART_CAP_NOTEMT) { - struct tty_struct *tty = p->port.state->port.tty; - - serial8250_em485_update_temt_delay(p, tty->termios.c_cflag, - tty_get_baud_rate(tty)); - hrtimer_init(&p->em485->no_temt_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - p->em485->no_temt_timer.function = &serial8250_em485_handle_no_temt; - } - p->em485->stop_tx_timer.function = &serial8250_em485_handle_stop_tx; p->em485->start_tx_timer.function = &serial8250_em485_handle_start_tx; p->em485->port = p; @@ -685,7 +662,6 @@ void serial8250_em485_destroy(struct uart_8250_port *p) hrtimer_cancel(&p->em485->start_tx_timer); hrtimer_cancel(&p->em485->stop_tx_timer); - hrtimer_cancel(&p->em485->no_temt_timer); kfree(p->em485); p->em485 = NULL; @@ -1528,11 +1504,6 @@ static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec) hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL); } -static void start_hrtimer_ns(struct hrtimer *hrt, unsigned long nsec) -{ - hrtimer_start(hrt, ns_to_ktime(nsec), HRTIMER_MODE_REL); -} - static void __stop_tx_rs485(struct uart_8250_port *p) { struct uart_8250_em485 *em485 = p->em485; @@ -1564,33 +1535,14 @@ static inline void __stop_tx(struct uart_8250_port *p) if (em485) { unsigned char lsr = serial_in(p, UART_LSR); - - p->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - /* - * To provide required timing and allow FIFO transfer, + * 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. */ - if ((lsr & BOTH_EMPTY) != BOTH_EMPTY) { - if (!(p->capabilities & UART_CAP_NOTEMT)) - /* __stop_tx will be called again once TEMT triggers */ - return; - - if (!(lsr & UART_LSR_THRE)) - /* __stop_tx will be called again once THRE triggers */ - return; - - /* - * On devices with no TEMT interrupt available, start - * a timer for a byte time. The timer will recall - * __stop_tx(). - */ - em485->active_timer = &em485->no_temt_timer; - start_hrtimer_ns(&em485->no_temt_timer, em485->no_temt_delay); + if ((lsr & BOTH_EMPTY) != BOTH_EMPTY) return; - } __stop_tx_rs485(p); } @@ -1701,27 +1653,6 @@ static inline void start_tx_rs485(struct uart_port *port) __start_tx(port); } -static enum hrtimer_restart serial8250_em485_handle_no_temt(struct hrtimer *t) -{ - struct uart_8250_em485 *em485; - struct uart_8250_port *p; - unsigned long flags; - - em485 = container_of(t, struct uart_8250_em485, no_temt_timer); - p = em485->port; - - serial8250_rpm_get(p); - spin_lock_irqsave(&p->port.lock, flags); - if (em485->active_timer == &em485->no_temt_timer) { - em485->active_timer = NULL; - __stop_tx(p); - } - - spin_unlock_irqrestore(&p->port.lock, flags); - serial8250_rpm_put(p); - return HRTIMER_NORESTART; -} - static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t) { struct uart_8250_em485 *em485 = container_of(t, struct uart_8250_em485, @@ -2927,9 +2858,6 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial8250_set_divisor(port, baud, quot, frac); - if (up->capabilities & UART_CAP_NOTEMT) - serial8250_em485_update_temt_delay(up, termios->c_cflag, baud); - /* * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR * is written without DLAB set, this mode will be disabled. diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index de135852107c..ff84a3ed10ea 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -79,9 +79,7 @@ struct uart_8250_ops { struct uart_8250_em485 { struct hrtimer start_tx_timer; /* "rs485 start tx" timer */ struct hrtimer stop_tx_timer; /* "rs485 stop tx" timer */ - struct hrtimer no_temt_timer; /* "rs485 no TEMT interrupt" timer */ struct hrtimer *active_timer; /* pointer to active timer */ - unsigned long no_temt_delay; /* Delay for no_temt_timer */ struct uart_8250_port *port; /* for hrtimer callbacks */ unsigned int tx_stopped:1; /* tx is currently stopped */ }; -- cgit v1.2.3-71-gd317 From 31f6bd7fad3b149a1eb6f67fc2e742e4df369b3d Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 25 Apr 2022 17:33:58 +0300 Subject: serial: Store character timing information to uart_port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Struct uart_port currently stores FIFO timeout. Having character timing information readily available is useful. Even serial core itself determines char_time from port->timeout using inverse calculation. Store frame_time directly into uart_port. Character time is stored in nanoseconds to have reasonable precision with high rates. To avoid overflow, 64-bit math is necessary. It might be possible to determine timeout from frame_time by multiplying it with fifosize as needed but only part of the users seem to be protected by a lock. Thus, this patch does not pursue storing only frame_time in uart_port. Signed-off-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20220425143410.12703-2-ilpo.jarvinen@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 14 ++++++++------ include/linux/serial_core.h | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 01823ce87801..82a1770dd808 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -338,15 +339,18 @@ void uart_update_timeout(struct uart_port *port, unsigned int cflag, unsigned int baud) { - unsigned int size; + unsigned int size = tty_get_frame_size(cflag); + u64 frame_time; - size = tty_get_frame_size(cflag) * port->fifosize; + frame_time = (u64)size * NSEC_PER_SEC; + size *= port->fifosize; /* * Figure the timeout to send the above number of bits. * Add .02 seconds of slop */ port->timeout = (HZ * size) / baud + HZ/50; + port->frame_time = DIV64_U64_ROUND_UP(frame_time, baud); } EXPORT_SYMBOL(uart_update_timeout); @@ -1643,10 +1647,8 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) * Note: we have to use pretty tight timings here to satisfy * the NIST-PCTS. */ - char_time = (port->timeout - HZ/50) / port->fifosize; - char_time = char_time / 5; - if (char_time == 0) - char_time = 1; + char_time = max(nsecs_to_jiffies(port->frame_time / 5), 1UL); + if (timeout && timeout < char_time) char_time = timeout; diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index d4828e69087a..cbd5070bc87f 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -232,6 +232,7 @@ struct uart_port { int hw_stopped; /* sw-assisted CTS flow state */ unsigned int mctrl; /* current modem ctrl settings */ unsigned int timeout; /* character-based timeout */ + unsigned int frame_time; /* frame timing in ns */ unsigned int type; /* port type */ const struct uart_ops *ops; unsigned int custom_divisor; -- cgit v1.2.3-71-gd317 From 135c579d77d066aece83609329150b87fe81e454 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Mon, 2 May 2022 18:25:05 +0900 Subject: tty: serial: samsung_tty: Fix suspend/resume on S5L We were restoring the IRQ masks then clearing them again, because ucon_mask wasn't set properly. Adding that makes suspend/resume work as intended. Signed-off-by: Hector Martin Link: https://lore.kernel.org/r/20220502092505.30934-1-marcan@marcan.st Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung_tty.c | 1 + include/linux/serial_s3c.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'include/linux') diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index 8af5aceb9f4e..d5ca904def34 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -2826,6 +2826,7 @@ static const struct s3c24xx_serial_drv_data s5l_serial_drv_data = { .num_clks = 1, .clksel_mask = 0, .clksel_shift = 0, + .ucon_mask = APPLE_S5L_UCON_MASK, }, .def_cfg = { .ucon = APPLE_S5L_UCON_DEFAULT, diff --git a/include/linux/serial_s3c.h b/include/linux/serial_s3c.h index f6c3323fc4c5..dec15f5b3dec 100644 --- a/include/linux/serial_s3c.h +++ b/include/linux/serial_s3c.h @@ -256,6 +256,9 @@ #define APPLE_S5L_UCON_DEFAULT (S3C2410_UCON_TXIRQMODE | \ S3C2410_UCON_RXIRQMODE | \ S3C2410_UCON_RXFIFO_TOI) +#define APPLE_S5L_UCON_MASK (APPLE_S5L_UCON_RXTO_ENA_MSK | \ + APPLE_S5L_UCON_RXTHRESH_ENA_MSK | \ + APPLE_S5L_UCON_TXTHRESH_ENA_MSK) #define APPLE_S5L_UTRSTAT_RXTHRESH (1<<4) #define APPLE_S5L_UTRSTAT_TXTHRESH (1<<5) -- cgit v1.2.3-71-gd317