From 4ea3cd65e0d47c4d3fc0c86d2d93d97457dc86fb Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 18 Dec 2020 11:42:44 +0100 Subject: tty: rename tty_kopen() and add new function tty_kopen_shared() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new function tty_kopen_shared() that yields a struct tty_struct. The semantic difference to tty_kopen() is that the tty is expected to be used already. So rename tty_kopen() to tty_kopen_exclusive() for clearness, adapt the single user and put the common code in a new static helper function. tty_kopen_shared is to be used to implement an LED trigger for tty devices in one of the next patches. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20201218104246.591315-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/spk_ttyio.c | 2 +- drivers/tty/tty_io.c | 56 +++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/accessibility/speakup/spk_ttyio.c b/drivers/accessibility/speakup/spk_ttyio.c index 6284aff434a1..835d17455fcd 100644 --- a/drivers/accessibility/speakup/spk_ttyio.c +++ b/drivers/accessibility/speakup/spk_ttyio.c @@ -152,7 +152,7 @@ static int spk_ttyio_initialise_ldisc(struct spk_synth *synth) if (ret) return ret; - tty = tty_kopen(dev); + tty = tty_kopen_exclusive(dev); if (IS_ERR(tty)) return PTR_ERR(tty); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 8034489337d7..62ccd021102d 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1875,22 +1875,7 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, return driver; } -/** - * tty_kopen - open a tty device for kernel - * @device: dev_t of device to open - * - * Opens tty exclusively for kernel. Performs the driver lookup, - * makes sure it's not already opened and performs the first-time - * tty initialization. - * - * Returns the locked initialized &tty_struct - * - * Claims the global tty_mutex to serialize: - * - concurrent first-time tty initialization - * - concurrent tty driver removal w/ lookup - * - concurrent tty removal from driver table - */ -struct tty_struct *tty_kopen(dev_t device) +static struct tty_struct *tty_kopen(dev_t device, int shared) { struct tty_struct *tty; struct tty_driver *driver; @@ -1905,7 +1890,7 @@ struct tty_struct *tty_kopen(dev_t device) /* check whether we're reopening an existing tty */ tty = tty_driver_lookup_tty(driver, NULL, index); - if (IS_ERR(tty)) + if (IS_ERR(tty) || shared) goto out; if (tty) { @@ -1923,7 +1908,42 @@ out: tty_driver_kref_put(driver); return tty; } -EXPORT_SYMBOL_GPL(tty_kopen); + +/** + * tty_kopen_exclusive - open a tty device for kernel + * @device: dev_t of device to open + * + * Opens tty exclusively for kernel. Performs the driver lookup, + * makes sure it's not already opened and performs the first-time + * tty initialization. + * + * Returns the locked initialized &tty_struct + * + * Claims the global tty_mutex to serialize: + * - concurrent first-time tty initialization + * - concurrent tty driver removal w/ lookup + * - concurrent tty removal from driver table + */ +struct tty_struct *tty_kopen_exclusive(dev_t device) +{ + return tty_kopen(device, 0); +} +EXPORT_SYMBOL_GPL(tty_kopen_exclusive); + +/** + * tty_kopen_shared - open a tty device for shared in-kernel use + * @device: dev_t of device to open + * + * Opens an already existing tty for in-kernel use. Compared to + * tty_kopen_exclusive() above it doesn't ensure to be the only user. + * + * Locking is identical to tty_kopen() above. + */ +struct tty_struct *tty_kopen_shared(dev_t device) +{ + return tty_kopen(device, 1); +} +EXPORT_SYMBOL_GPL(tty_kopen_shared); /** * tty_open_by_driver - open a tty device -- cgit From d20c219c7317843cec7f03eba15bfeae54f29654 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 18 Dec 2020 11:42:45 +0100 Subject: tty: new helper function tty_get_icount() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For a given struct tty_struct this yields the corresponding statistics about sent and received characters (and some more) which is needed to implement an LED trigger for tty devices. The new function is then used to simplify tty_tiocgicount(). Reviewed-by: Pavel Machek Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20201218104246.591315-3-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 62ccd021102d..95ba028ef668 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2488,15 +2488,36 @@ static int tty_tiocmset(struct tty_struct *tty, unsigned int cmd, return tty->ops->tiocmset(tty, set, clear); } +/** + * tty_get_icount - get tty statistics + * @tty: tty device + * @icount: output parameter + * + * Gets a copy of the tty's icount statistics. + * + * Locking: none (up to the driver) + */ +int tty_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + memset(icount, 0, sizeof(*icount)); + + if (tty->ops->get_icount) + return tty->ops->get_icount(tty, icount); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(tty_get_icount); + static int tty_tiocgicount(struct tty_struct *tty, void __user *arg) { - int retval = -EINVAL; struct serial_icounter_struct icount; - memset(&icount, 0, sizeof(icount)); - if (tty->ops->get_icount) - retval = tty->ops->get_icount(tty, &icount); + int retval; + + retval = tty_get_icount(tty, &icount); if (retval != 0) return retval; + if (copy_to_user(arg, &icount, sizeof(icount))) return -EFAULT; return 0; -- cgit From 5b10956483eaf524bd0ad2781ff27887d49cb6fc Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Mon, 14 Dec 2020 21:37:19 +0800 Subject: tty/serial/imx: convert comma to semicolon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace a comma between expression statements by a semicolon. Acked-by: Uwe Kleine-König Signed-off-by: Zheng Yongjun Link: https://lore.kernel.org/r/20201214133719.3893-1-zhengyongjun3@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 425624d794dd..8257597d034d 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -2248,7 +2248,7 @@ static int imx_uart_probe(struct platform_device *pdev) sport->port.dev = &pdev->dev; sport->port.mapbase = res->start; sport->port.membase = base; - sport->port.type = PORT_IMX, + sport->port.type = PORT_IMX; sport->port.iotype = UPIO_MEM; sport->port.irq = rxirq; sport->port.fifosize = 32; -- cgit From 345523fab82760186bd453e2e6ebf7c21961610b Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Mon, 14 Dec 2020 21:37:55 +0800 Subject: tty/serial/lantiq: convert comma to semicolon Replace a comma between expression statements by a semicolon. Signed-off-by: Zheng Yongjun Link: https://lore.kernel.org/r/20201214133755.3945-1-zhengyongjun3@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/lantiq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index 62813e421f12..497b334bc845 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -876,7 +876,7 @@ static int lqasc_probe(struct platform_device *pdev) port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; port->ops = &lqasc_pops; port->fifosize = 16; - port->type = PORT_LTQ_ASC, + port->type = PORT_LTQ_ASC; port->line = line; port->dev = &pdev->dev; /* unused, just to be backward-compatible */ -- cgit From a60526097f42eb98760d3c63c5de63fab309fe1a Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Wed, 23 Dec 2020 09:38:46 +0000 Subject: tty: serial: cpm_uart: Add udbg support for enabling xmon In order to use xmon with powerpc 8xx, the serial driver must provide udbg_putc() and udpb_getc(). Provide them via cpm_put_poll_char() and cpm_get_poll_char(). This requires CONFIG_CONSOLE_POLL. Signed-off-by: Christophe Leroy Link: https://lore.kernel.org/r/e4471bf81089252470efb3eed735d71a5b32adbd.1608716197.git.christophe.leroy@csgroup.eu Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/cpm_uart/cpm_uart_core.c | 40 ++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 4df47d02b34b..3b899cc7e362 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1107,6 +1107,32 @@ static void cpm_put_poll_char(struct uart_port *port, ch[0] = (char)c; cpm_uart_early_write(pinfo, ch, 1, false); } + +static struct uart_port *udbg_port; + +static void udbg_cpm_putc(char c) +{ + if (c == '\n') + cpm_put_poll_char(udbg_port, '\r'); + cpm_put_poll_char(udbg_port, c); +} + +static int udbg_cpm_getc_poll(void) +{ + int c = cpm_get_poll_char(udbg_port); + + return c == NO_POLL_CHAR ? -1 : c; +} + +static int udbg_cpm_getc(void) +{ + int c; + + while ((c = udbg_cpm_getc_poll()) == -1) + cpu_relax(); + return c; +} + #endif /* CONFIG_CONSOLE_POLL */ static const struct uart_ops cpm_uart_pops = { @@ -1237,7 +1263,10 @@ static int cpm_uart_init_port(struct device_node *np, } #ifdef CONFIG_PPC_EARLY_DEBUG_CPM - udbg_putc = NULL; +#ifdef CONFIG_CONSOLE_POLL + if (!udbg_port) +#endif + udbg_putc = NULL; #endif return cpm_uart_request_port(&pinfo->port); @@ -1358,6 +1387,15 @@ static int __init cpm_uart_console_setup(struct console *co, char *options) uart_set_options(port, co, baud, parity, bits, flow); cpm_line_cr_cmd(pinfo, CPM_CR_RESTART_TX); +#ifdef CONFIG_CONSOLE_POLL + if (!udbg_port) { + udbg_port = &pinfo->port; + udbg_putc = udbg_cpm_putc; + udbg_getc = udbg_cpm_getc; + udbg_getc_poll = udbg_cpm_getc_poll; + } +#endif + return 0; } -- cgit From 01493ccb4436aadcaf8f33cd5d524a5d68f22836 Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Wed, 23 Dec 2020 22:14:38 +0800 Subject: tty: serial: icom: Use DEFINE_SPINLOCK() for spinlock spinlock can be initialized automatically with DEFINE_SPINLOCK() rather than explicitly calling spin_lock_init(). Signed-off-by: Zheng Yongjun Link: https://lore.kernel.org/r/20201223141438.889-1-zhengyongjun3@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/icom.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index 94c8281ddb5f..9a872750581c 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -118,7 +118,7 @@ MODULE_DEVICE_TABLE(pci, icom_pci_table); static LIST_HEAD(icom_adapter_head); /* spinlock for adapter initialization and changing adapter operations */ -static spinlock_t icom_lock; +static DEFINE_SPINLOCK(icom_lock); #ifdef ICOM_TRACE static inline void trace(struct icom_port *icom_port, char *trace_pt, @@ -1616,8 +1616,6 @@ static int __init icom_init(void) { int ret; - spin_lock_init(&icom_lock); - ret = uart_register_driver(&icom_uart_driver); if (ret) return ret; -- cgit From 63f24a7fafd44899f001b8467b38fac5a534f63e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:28 +0100 Subject: vt: move set_leds to keyboard.c set_leds and compute_shiftstate are called from a single place in vt.c. Let's combine these two into vt_set_leds_compute_shiftstate. This allows for making keyboard_tasklet local in the next patch. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-1-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 11 ++++++++++- drivers/tty/vt/vt.c | 3 +-- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 52922d21a49f..32ec4242b1f2 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -372,6 +372,12 @@ static void to_utf8(struct vc_data *vc, uint c) } } +/* FIXME: review locking for vt.c callers */ +static void set_leds(void) +{ + tasklet_schedule(&keyboard_tasklet); +} + /* * Called after returning from RAW mode or when changing consoles - recompute * shift_down[] and shift_state from key_down[] maybe called when keymap is @@ -401,9 +407,12 @@ static void do_compute_shiftstate(void) } /* We still have to export this method to vt.c */ -void compute_shiftstate(void) +void vt_set_leds_compute_shiftstate(void) { unsigned long flags; + + set_leds(); + spin_lock_irqsave(&kbd_event_lock, flags); do_compute_shiftstate(); spin_unlock_irqrestore(&kbd_event_lock, flags); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index d04a162939a4..fe4fedbc0386 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1036,8 +1036,7 @@ void redraw_screen(struct vc_data *vc, int is_switch) } set_cursor(vc); if (is_switch) { - set_leds(); - compute_shiftstate(); + vt_set_leds_compute_shiftstate(); notify_update(vc); } } -- cgit From a18a9da82c57804ce5977ea0e01b72ee3f40a51b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:29 +0100 Subject: vt: keyboard, make keyboard_tasklet local Now that the last extern user of the tasklet (set_leds) is in keyboard.c, we can make keyboard_tasklet local to this unit too. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-2-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 32ec4242b1f2..9f2eaa104ebc 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -131,6 +131,9 @@ static const unsigned char max_vals[] = { static const int NR_TYPES = ARRAY_SIZE(max_vals); +static void kbd_bh(unsigned long dummy); +static DECLARE_TASKLET_DISABLED_OLD(keyboard_tasklet, kbd_bh); + static struct input_handler kbd_handler; static DEFINE_SPINLOCK(kbd_event_lock); static DEFINE_SPINLOCK(led_lock); @@ -1258,8 +1261,6 @@ static void kbd_bh(unsigned long dummy) } } -DECLARE_TASKLET_DISABLED_OLD(keyboard_tasklet, kbd_bh); - #if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(CONFIG_ALPHA) ||\ defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) ||\ defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\ -- cgit From f14e0394859def7a2e44c836194f628ae06ed411 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:30 +0100 Subject: vt: keyboard, defkeymap.c_shipped, approach the definitions loadkeys (from kbd) generates 'unsigned short' instead of 'u_short' since 2.0.3. It also marks maps as 'static' for longer than kbd's history. So adapt the shipped defkeymap.c to conform more to loadkeys output. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-3-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/defkeymap.c_shipped | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/defkeymap.c_shipped b/drivers/tty/vt/defkeymap.c_shipped index c7095fb7d2d1..cac1fcbd55c7 100644 --- a/drivers/tty/vt/defkeymap.c_shipped +++ b/drivers/tty/vt/defkeymap.c_shipped @@ -6,7 +6,7 @@ #include #include -u_short plain_map[NR_KEYS] = { +unsigned short plain_map[NR_KEYS] = { 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, @@ -25,7 +25,7 @@ u_short plain_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short shift_map[NR_KEYS] = { +static unsigned short shift_map[NR_KEYS] = { 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, @@ -44,7 +44,7 @@ u_short shift_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short altgr_map[NR_KEYS] = { +static unsigned short altgr_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, @@ -63,7 +63,7 @@ u_short altgr_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short ctrl_map[NR_KEYS] = { +static unsigned short ctrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, @@ -82,7 +82,7 @@ u_short ctrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short shift_ctrl_map[NR_KEYS] = { +static unsigned short shift_ctrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, @@ -101,7 +101,7 @@ u_short shift_ctrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short alt_map[NR_KEYS] = { +static unsigned short alt_map[NR_KEYS] = { 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, @@ -120,7 +120,7 @@ u_short alt_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short ctrl_alt_map[NR_KEYS] = { +static unsigned short ctrl_alt_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, -- cgit From e81de384af9b8631f9e0679eecef32d57a7ad27d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:31 +0100 Subject: vt: keyboard, defkeymap.c_shipped, approach the unicode table Commit 5ce2087ed0eb (Fix default compose table initialization) fixed unicode table so that the values are not sign extended. The upstream (kbd package) chose a different approach. They use hexadecimal values. So use the same, so that the output of loadkeys and our shipped file correspond more to each other. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-4-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/defkeymap.c_shipped | 68 +++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/defkeymap.c_shipped b/drivers/tty/vt/defkeymap.c_shipped index cac1fcbd55c7..094d95bf0005 100644 --- a/drivers/tty/vt/defkeymap.c_shipped +++ b/drivers/tty/vt/defkeymap.c_shipped @@ -224,40 +224,40 @@ char *func_table[MAX_NR_FUNC] = { }; struct kbdiacruc accent_table[MAX_DIACR] = { - {'`', 'A', 0300}, {'`', 'a', 0340}, - {'\'', 'A', 0301}, {'\'', 'a', 0341}, - {'^', 'A', 0302}, {'^', 'a', 0342}, - {'~', 'A', 0303}, {'~', 'a', 0343}, - {'"', 'A', 0304}, {'"', 'a', 0344}, - {'O', 'A', 0305}, {'o', 'a', 0345}, - {'0', 'A', 0305}, {'0', 'a', 0345}, - {'A', 'A', 0305}, {'a', 'a', 0345}, - {'A', 'E', 0306}, {'a', 'e', 0346}, - {',', 'C', 0307}, {',', 'c', 0347}, - {'`', 'E', 0310}, {'`', 'e', 0350}, - {'\'', 'E', 0311}, {'\'', 'e', 0351}, - {'^', 'E', 0312}, {'^', 'e', 0352}, - {'"', 'E', 0313}, {'"', 'e', 0353}, - {'`', 'I', 0314}, {'`', 'i', 0354}, - {'\'', 'I', 0315}, {'\'', 'i', 0355}, - {'^', 'I', 0316}, {'^', 'i', 0356}, - {'"', 'I', 0317}, {'"', 'i', 0357}, - {'-', 'D', 0320}, {'-', 'd', 0360}, - {'~', 'N', 0321}, {'~', 'n', 0361}, - {'`', 'O', 0322}, {'`', 'o', 0362}, - {'\'', 'O', 0323}, {'\'', 'o', 0363}, - {'^', 'O', 0324}, {'^', 'o', 0364}, - {'~', 'O', 0325}, {'~', 'o', 0365}, - {'"', 'O', 0326}, {'"', 'o', 0366}, - {'/', 'O', 0330}, {'/', 'o', 0370}, - {'`', 'U', 0331}, {'`', 'u', 0371}, - {'\'', 'U', 0332}, {'\'', 'u', 0372}, - {'^', 'U', 0333}, {'^', 'u', 0373}, - {'"', 'U', 0334}, {'"', 'u', 0374}, - {'\'', 'Y', 0335}, {'\'', 'y', 0375}, - {'T', 'H', 0336}, {'t', 'h', 0376}, - {'s', 's', 0337}, {'"', 'y', 0377}, - {'s', 'z', 0337}, {'i', 'j', 0377}, + {'`', 'A', 0x00c0}, {'`', 'a', 0x00e0}, + {'\'', 'A', 0x00c1}, {'\'', 'a', 0x00e1}, + {'^', 'A', 0x00c2}, {'^', 'a', 0x00e2}, + {'~', 'A', 0x00c3}, {'~', 'a', 0x00e3}, + {'"', 'A', 0x00c4}, {'"', 'a', 0x00e4}, + {'O', 'A', 0x00c5}, {'o', 'a', 0x00e5}, + {'0', 'A', 0x00c5}, {'0', 'a', 0x00e5}, + {'A', 'A', 0x00c5}, {'a', 'a', 0x00e5}, + {'A', 'E', 0x00c6}, {'a', 'e', 0x00e6}, + {',', 'C', 0x00c7}, {',', 'c', 0x00e7}, + {'`', 'E', 0x00c8}, {'`', 'e', 0x00e8}, + {'\'', 'E', 0x00c9}, {'\'', 'e', 0x00e9}, + {'^', 'E', 0x00ca}, {'^', 'e', 0x00ea}, + {'"', 'E', 0x00cb}, {'"', 'e', 0x00eb}, + {'`', 'I', 0x00cc}, {'`', 'i', 0x00ec}, + {'\'', 'I', 0x00cd}, {'\'', 'i', 0x00ed}, + {'^', 'I', 0x00ce}, {'^', 'i', 0x00ee}, + {'"', 'I', 0x00cf}, {'"', 'i', 0x00ef}, + {'-', 'D', 0x00d0}, {'-', 'd', 0x00f0}, + {'~', 'N', 0x00d1}, {'~', 'n', 0x00f1}, + {'`', 'O', 0x00d2}, {'`', 'o', 0x00f2}, + {'\'', 'O', 0x00d3}, {'\'', 'o', 0x00f3}, + {'^', 'O', 0x00d4}, {'^', 'o', 0x00f4}, + {'~', 'O', 0x00d5}, {'~', 'o', 0x00f5}, + {'"', 'O', 0x00d6}, {'"', 'o', 0x00f6}, + {'/', 'O', 0x00d8}, {'/', 'o', 0x00f8}, + {'`', 'U', 0x00d9}, {'`', 'u', 0x00f9}, + {'\'', 'U', 0x00da}, {'\'', 'u', 0x00fa}, + {'^', 'U', 0x00db}, {'^', 'u', 0x00fb}, + {'"', 'U', 0x00dc}, {'"', 'u', 0x00fc}, + {'\'', 'Y', 0x00dd}, {'\'', 'y', 0x00fd}, + {'T', 'H', 0x00de}, {'t', 'h', 0x00fe}, + {'s', 's', 0x00df}, {'"', 'y', 0x00ff}, + {'s', 'z', 0x00df}, {'i', 'j', 0x00ff}, }; unsigned int accent_table_size = 68; -- cgit From 9bc1b2b9b848e9334ab1268fb4dc35a68b70022f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:32 +0100 Subject: tty: pty, remove BUG_ON from pty_close tty->ops->close is always called with a valid tty, so the BUG_ON cannot trigger. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-5-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index a59f1e062bc6..5e2374580e27 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -45,7 +45,6 @@ static DEFINE_MUTEX(devpts_mutex); static void pty_close(struct tty_struct *tty, struct file *filp) { - BUG_ON(!tty); if (tty->driver->subtype == PTY_TYPE_MASTER) WARN_ON(tty->count > 1); else { -- cgit From 7d7dec450a66009a4738cc538e59fb507fdb9ebe Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:33 +0100 Subject: 8250_tegra: clean up tegra_uart_handle_break * switch "do { A; } while (1)" to "while (1) { A; }" * switch "if (A) B; else break;" to "if (!A) break; B;" * remove unused assignment from p->serial_in() to status Objdump -d shows no difference. Cc: Thierry Reding Cc: Jonathan Hunter Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-6-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_tegra.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_tegra.c b/drivers/tty/serial/8250/8250_tegra.c index c0ffad1572c6..e13ae18b0713 100644 --- a/drivers/tty/serial/8250/8250_tegra.c +++ b/drivers/tty/serial/8250/8250_tegra.c @@ -26,16 +26,17 @@ static void tegra_uart_handle_break(struct uart_port *p) { unsigned int status, tmout = 10000; - do { + while (1) { status = p->serial_in(p, UART_LSR); - if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) - status = p->serial_in(p, UART_RX); - else + if (!(status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS))) break; + + p->serial_in(p, UART_RX); + if (--tmout == 0) break; udelay(1); - } while (1); + } } static int tegra_uart_probe(struct platform_device *pdev) -- cgit From 9777f8e60e718f7b022a94f2524f967d8def1931 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:34 +0100 Subject: vt/consolemap: do font sum unsigned The constant 20 makes the font sum computation signed which can lead to sign extensions and signed wraps. It's not much of a problem as we build with -fno-strict-overflow. But if we ever decide not to, be ready, so switch the constant to unsigned. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-7-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/consolemap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index f7d015c67963..d815ac98b39e 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -495,7 +495,7 @@ con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos) p2[unicode & 0x3f] = fontpos; - p->sum += (fontpos << 20) + unicode; + p->sum += (fontpos << 20U) + unicode; return 0; } -- cgit From ff2047fb755d4415ec3c70ac799889371151796d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:35 +0100 Subject: vt: drop old FONT ioctls Drop support for these ioctls: * PIO_FONT, PIO_FONTX * GIO_FONT, GIO_FONTX * PIO_FONTRESET As was demonstrated by commit 90bfdeef83f1 (tty: make FONTX ioctl use the tty pointer they were actually passed), these ioctls are not used from userspace, as: 1) they used to be broken (set up font on current console, not the open one) and racy (before the commit above) 2) KDFONTOP ioctl is used for years instead Note that PIO_FONTRESET is defunct on most systems as VGA_CONSOLE is set on them for ages. That turns on BROKEN_GRAPHICS_PROGRAMS which makes PIO_FONTRESET just return an error. We are removing KD_FONT_FLAG_OLD here as it was used only by these removed ioctls. kd.h header exists both in kernel and uapi headers, so we can remove the kernel one completely. Everyone includeing kd.h will now automatically get the uapi one. There are now unused definitions of the ioctl numbers and "struct consolefontdesc" in kd.h, but as it is a uapi header, I am not touching these. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-8-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 39 +----------- drivers/tty/vt/vt_ioctl.c | 151 ---------------------------------------------- 2 files changed, 3 insertions(+), 187 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index fe4fedbc0386..284b07224c55 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -4583,16 +4583,8 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op) if (op->data && font.charcount > op->charcount) rc = -ENOSPC; - if (!(op->flags & KD_FONT_FLAG_OLD)) { - if (font.width > op->width || font.height > op->height) - rc = -ENOSPC; - } else { - if (font.width != 8) - rc = -EIO; - else if ((op->height && font.height > op->height) || - font.height > 32) - rc = -ENOSPC; - } + if (font.width > op->width || font.height > op->height) + rc = -ENOSPC; if (rc) goto out; @@ -4620,7 +4612,7 @@ static int con_font_set(struct vc_data *vc, struct console_font_op *op) return -EINVAL; if (op->charcount > 512) return -EINVAL; - if (op->width <= 0 || op->width > 32 || op->height > 32) + if (op->width <= 0 || op->width > 32 || !op->height || op->height > 32) return -EINVAL; size = (op->width+7)/8 * 32 * op->charcount; if (size > max_font_size) @@ -4630,31 +4622,6 @@ static int con_font_set(struct vc_data *vc, struct console_font_op *op) if (IS_ERR(font.data)) return PTR_ERR(font.data); - if (!op->height) { /* Need to guess font height [compat] */ - int h, i; - u8 *charmap = font.data; - - /* - * If from KDFONTOP ioctl, don't allow things which can be done - * in userland,so that we can get rid of this soon - */ - if (!(op->flags & KD_FONT_FLAG_OLD)) { - kfree(font.data); - return -EINVAL; - } - - for (h = 32; h > 0; h--) - for (i = 0; i < op->charcount; i++) - if (charmap[32*i+h-1]) - goto nonzero; - - kfree(font.data); - return -EINVAL; - - nonzero: - op->height = h; - } - font.charcount = op->charcount; font.width = op->width; font.height = op->height; diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 3813c40f1b48..4a4cbd4a5f37 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -484,70 +484,6 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd, return 0; } -static inline int do_fontx_ioctl(struct vc_data *vc, int cmd, - struct consolefontdesc __user *user_cfd, - struct console_font_op *op) -{ - struct consolefontdesc cfdarg; - int i; - - if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc))) - return -EFAULT; - - switch (cmd) { - case PIO_FONTX: - op->op = KD_FONT_OP_SET; - op->flags = KD_FONT_FLAG_OLD; - op->width = 8; - op->height = cfdarg.charheight; - op->charcount = cfdarg.charcount; - op->data = cfdarg.chardata; - return con_font_op(vc, op); - - case GIO_FONTX: - op->op = KD_FONT_OP_GET; - op->flags = KD_FONT_FLAG_OLD; - op->width = 8; - op->height = cfdarg.charheight; - op->charcount = cfdarg.charcount; - op->data = cfdarg.chardata; - i = con_font_op(vc, op); - if (i) - return i; - cfdarg.charheight = op->height; - cfdarg.charcount = op->charcount; - if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc))) - return -EFAULT; - return 0; - } - return -EINVAL; -} - -static int vt_io_fontreset(struct vc_data *vc, struct console_font_op *op) -{ - int ret; - - if (__is_defined(BROKEN_GRAPHICS_PROGRAMS)) { - /* - * With BROKEN_GRAPHICS_PROGRAMS defined, the default font is - * not saved. - */ - return -ENOSYS; - } - - op->op = KD_FONT_OP_SET_DEFAULT; - op->data = NULL; - ret = con_font_op(vc, op); - if (ret) - return ret; - - console_lock(); - con_set_default_unimap(vc); - console_unlock(); - - return 0; -} - static inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, bool perm, struct vc_data *vc) { @@ -572,29 +508,7 @@ static inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, static int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up, bool perm) { - struct console_font_op op; /* used in multiple places here */ - switch (cmd) { - case PIO_FONT: - if (!perm) - return -EPERM; - op.op = KD_FONT_OP_SET; - op.flags = KD_FONT_FLAG_OLD | KD_FONT_FLAG_DONT_RECALC; /* Compatibility */ - op.width = 8; - op.height = 0; - op.charcount = 256; - op.data = up; - return con_font_op(vc, &op); - - case GIO_FONT: - op.op = KD_FONT_OP_GET; - op.flags = KD_FONT_FLAG_OLD; - op.width = 8; - op.height = 32; - op.charcount = 256; - op.data = up; - return con_font_op(vc, &op); - case PIO_CMAP: if (!perm) return -EPERM; @@ -603,20 +517,6 @@ static int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up, case GIO_CMAP: return con_get_cmap(up); - case PIO_FONTX: - if (!perm) - return -EPERM; - - fallthrough; - case GIO_FONTX: - return do_fontx_ioctl(vc, cmd, up, &op); - - case PIO_FONTRESET: - if (!perm) - return -EPERM; - - return vt_io_fontreset(vc, &op); - case PIO_SCRNMAP: if (!perm) return -EPERM; @@ -1059,54 +959,6 @@ void vc_SAK(struct work_struct *work) #ifdef CONFIG_COMPAT -struct compat_consolefontdesc { - unsigned short charcount; /* characters in font (256 or 512) */ - unsigned short charheight; /* scan lines per character (1-32) */ - compat_caddr_t chardata; /* font data in expanded form */ -}; - -static inline int -compat_fontx_ioctl(struct vc_data *vc, int cmd, - struct compat_consolefontdesc __user *user_cfd, - int perm, struct console_font_op *op) -{ - struct compat_consolefontdesc cfdarg; - int i; - - if (copy_from_user(&cfdarg, user_cfd, sizeof(struct compat_consolefontdesc))) - return -EFAULT; - - switch (cmd) { - case PIO_FONTX: - if (!perm) - return -EPERM; - op->op = KD_FONT_OP_SET; - op->flags = KD_FONT_FLAG_OLD; - op->width = 8; - op->height = cfdarg.charheight; - op->charcount = cfdarg.charcount; - op->data = compat_ptr(cfdarg.chardata); - return con_font_op(vc, op); - - case GIO_FONTX: - op->op = KD_FONT_OP_GET; - op->flags = KD_FONT_FLAG_OLD; - op->width = 8; - op->height = cfdarg.charheight; - op->charcount = cfdarg.charcount; - op->data = compat_ptr(cfdarg.chardata); - i = con_font_op(vc, op); - if (i) - return i; - cfdarg.charheight = op->height; - cfdarg.charcount = op->charcount; - if (copy_to_user(user_cfd, &cfdarg, sizeof(struct compat_consolefontdesc))) - return -EFAULT; - return 0; - } - return -EINVAL; -} - struct compat_console_font_op { compat_uint_t op; /* operation code KD_FONT_OP_* */ compat_uint_t flags; /* KD_FONT_FLAG_* */ @@ -1183,9 +1035,6 @@ long vt_compat_ioctl(struct tty_struct *tty, /* * these need special handlers for incompatible data structures */ - case PIO_FONTX: - case GIO_FONTX: - return compat_fontx_ioctl(vc, cmd, up, perm, &op); case KDFONTOP: return compat_kdfontop_ioctl(up, perm, &op, vc); -- cgit From cac8a63063e33606c4b8419ef34ef7644d7c2fc4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:36 +0100 Subject: vgacon: drop BROKEN_GRAPHICS_PROGRAMS BROKEN_GRAPHICS_PROGRAMS is defined when CONFIG_VGA_CONSOLE=y. And vgacon.c is built exclusively in that case too. So the check for BROKEN_GRAPHICS_PROGRAMS is pointless in vgacon.c as it is always true. So remove the test and BROKEN_GRAPHICS_PROGRAMS completely. This also eliminates the need for vga_font_is_default global as it is only set and never read. Cc: dri-devel@lists.freedesktop.org Cc: linux-fbdev@vger.kernel.org Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-9-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/vgacon.c | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'drivers') diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 17876f0179b5..962c12be9774 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -90,7 +90,6 @@ static unsigned int vga_video_num_lines; /* Number of text lines */ static bool vga_can_do_color; /* Do we support colors? */ static unsigned int vga_default_font_height __read_mostly; /* Height of default screen font */ static unsigned char vga_video_type __read_mostly; /* Card type */ -static bool vga_font_is_default = true; static int vga_vesa_blanked; static bool vga_palette_blanked; static bool vga_is_gfx; @@ -878,7 +877,6 @@ static int vgacon_do_font_op(struct vgastate *state, char *arg, int set, beg = 0x0a; } -#ifdef BROKEN_GRAPHICS_PROGRAMS /* * All fonts are loaded in slot 0 (0:1 for 512 ch) */ @@ -886,24 +884,7 @@ static int vgacon_do_font_op(struct vgastate *state, char *arg, int set, if (!arg) return -EINVAL; /* Return to default font not supported */ - vga_font_is_default = false; font_select = ch512 ? 0x04 : 0x00; -#else - /* - * The default font is kept in slot 0 and is never touched. - * A custom font is loaded in slot 2 (256 ch) or 2:3 (512 ch) - */ - - if (set) { - vga_font_is_default = !arg; - if (!arg) - ch512 = false; /* Default font is always 256 */ - font_select = arg ? (ch512 ? 0x0e : 0x0a) : 0x00; - } - - if (!vga_font_is_default) - charmap += 4 * cmapsz; -#endif raw_spin_lock_irq(&vga_lock); /* First, the Sequencer */ -- cgit From bb9146688c0d48ca9f186fc7cd1d0ede6d8ff6c4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:37 +0100 Subject: tty: cpm_uart, use port->flags instead of low_latency This is the only in-kernel user of tty_port::low_latency. Switch this last one to test uport->flags directly as tty_port::low_latency is going away in the next patch. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-10-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/cpm_uart/cpm_uart_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 3b899cc7e362..58aaa533203b 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -499,8 +499,7 @@ static void cpm_uart_set_termios(struct uart_port *port, pr_debug("CPM uart[%d]:set_termios\n", port->line); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - if (baud < HW_BUF_SPD_THRESHOLD || - (pinfo->port.state && pinfo->port.state->port.low_latency)) + if (baud < HW_BUF_SPD_THRESHOLD || port->flags & UPF_LOW_LATENCY) pinfo->rx_fifosize = 1; else pinfo->rx_fifosize = RX_BUF_SIZE; -- cgit From 0bc1bd092af3c7c0b025ece93c3a86916f89f3ca Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 5 Jan 2021 13:02:38 +0100 Subject: tty_port: drop last traces of low_latency The main purpose of tty_port::low_latency was removed in commit a9c3f68f3cd8 (tty: Fix low_latency BUG) back in 2014. It was left in place for drivers as an optional tune knob. But only one driver has been using it until the previous commit. So remove this misconcept completely, given there are no users. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210105120239.28031-11-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 2 -- drivers/net/caif/caif_serial.c | 3 +-- drivers/s390/char/con3215.c | 1 - drivers/s390/char/sclp_tty.c | 1 - drivers/s390/char/sclp_vt220.c | 1 - drivers/s390/char/tty3270.c | 2 -- drivers/tty/amiserial.c | 3 --- drivers/tty/hvc/hvcs.c | 2 +- drivers/tty/ipwireless/tty.c | 1 - drivers/tty/mxser.c | 1 - drivers/tty/serial/ifx6x60.c | 3 --- drivers/tty/serial/max3100.c | 3 --- drivers/tty/serial/serial_core.c | 3 --- drivers/tty/synclink_gt.c | 1 - 14 files changed, 2 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index e342daa73d1b..2be8d9a8eec5 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2494,8 +2494,6 @@ static int mgslpc_open(struct tty_struct *tty, struct file * filp) printk("%s(%d):mgslpc_open(%s), old ref count = %d\n", __FILE__, __LINE__, tty->driver->name, port->count); - port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - spin_lock_irqsave(&info->netlock, flags); if (info->netcount) { retval = -EBUSY; diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index bcc14c5875bf..8215cd77301f 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -89,8 +89,7 @@ static inline void update_tty_status(struct ser_device *ser) ser->tty_status = ser->tty->stopped << 5 | ser->tty->flow_stopped << 3 | - ser->tty->packet << 2 | - ser->tty->port->low_latency << 1; + ser->tty->packet << 2; } static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) { diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 1354c42d95aa..671efee612af 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -914,7 +914,6 @@ static int tty3215_open(struct tty_struct *tty, struct file * filp) tty_port_tty_set(&raw->port, tty); - raw->port.low_latency = 0; /* don't use bottom half for pushing chars */ /* * Start up 3215 device */ diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 5aff8b684eb2..013bcc331305 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -65,7 +65,6 @@ sclp_tty_open(struct tty_struct *tty, struct file *filp) { tty_port_tty_set(&sclp_port, tty); tty->driver_data = NULL; - sclp_port.low_latency = 0; return 0; } diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 3f9a6ef650fa..047f812d1a1c 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -560,7 +560,6 @@ sclp_vt220_open(struct tty_struct *tty, struct file *filp) { if (tty->count == 1) { tty_port_tty_set(&sclp_vt220_port, tty); - sclp_vt220_port.low_latency = 0; if (!tty->winsize.ws_row && !tty->winsize.ws_col) { tty->winsize.ws_row = 24; tty->winsize.ws_col = 80; diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index aec996de44d9..15692449a1c3 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -967,7 +967,6 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) tty->driver_data = tp; tty->winsize.ws_row = tp->view.rows - 2; tty->winsize.ws_col = tp->view.cols; - tp->port.low_latency = 0; tp->inattr = TF_INPUT; goto port_install; } @@ -996,7 +995,6 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) return rc; } - tp->port.low_latency = 0; tty->winsize.ws_row = tp->view.rows - 2; tty->winsize.ws_col = tp->view.cols; diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 13f63c01c589..18b78ea110ef 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -998,7 +998,6 @@ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) state->custom_divisor = ss->custom_divisor; port->close_delay = ss->close_delay * HZ/100; port->closing_wait = ss->closing_wait * HZ/100; - port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: if (tty_port_initialized(port)) { @@ -1386,8 +1385,6 @@ static int rs_open(struct tty_struct *tty, struct file * filp) tty->driver_data = info; tty->port = port; - port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - retval = startup(tty, info); if (retval) { return retval; diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 509d1042825a..dfe02283ed23 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -605,7 +605,7 @@ static int hvcs_io(struct hvcs_struct *hvcsd) hvcsd->todo_mask |= HVCS_QUICK_READ; spin_unlock_irqrestore(&hvcsd->lock, flags); - /* This is synch because tty->low_latency == 1 */ + /* This is synch -- FIXME :js: it is not! */ if(got) tty_flip_buffer_push(&hvcsd->port); diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c index 23584769fc29..6dacbc5e286c 100644 --- a/drivers/tty/ipwireless/tty.c +++ b/drivers/tty/ipwireless/tty.c @@ -101,7 +101,6 @@ static int ipw_open(struct tty_struct *linux_tty, struct file *filp) tty->port.tty = linux_tty; linux_tty->driver_data = tty; - tty->port.low_latency = 1; if (tty->tty_type == TTYTYPE_MODEM) ipwireless_ppp_open(tty->network); diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 3703987c4666..4203b64bccdb 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -1273,7 +1273,6 @@ static int mxser_set_serial_info(struct tty_struct *tty, (ss->flags & ASYNC_FLAGS)); port->close_delay = ss->close_delay * HZ / 100; port->closing_wait = ss->closing_wait * HZ / 100; - port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && (ss->baud_base != info->baud_base || ss->custom_divisor != diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 182e0ccd60b2..d4ef88ee22d0 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -565,9 +565,6 @@ static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty) /* put port data into this tty */ tty->driver_data = ifx_dev; - /* allows flip string push from int context */ - port->low_latency = 1; - /* set flag to allows data transfer */ set_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags); diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 371569a0fd00..3c92d4e01488 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -521,9 +521,6 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, MAX3100_STATUS_PE | MAX3100_STATUS_FE | MAX3100_STATUS_OE; - /* we are sending char from a workqueue so enable */ - s->port.state->port.low_latency = 1; - if (s->poll_time > 0) del_timer_sync(&s->timer); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 828f9ad1be49..7dacdb6a8534 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -975,7 +975,6 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, port->closing_wait = closing_wait; if (new_info->xmit_fifo_size) uport->fifosize = new_info->xmit_fifo_size; - port->low_latency = (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: retval = 0; @@ -1795,8 +1794,6 @@ static int uart_port_activate(struct tty_port *port, struct tty_struct *tty) if (!uport || uport->flags & UPF_DEAD) return -ENXIO; - port->low_latency = (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; - /* * Start up the serial port. */ diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index c0b384e3ed4d..644173786bf0 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -672,7 +672,6 @@ static int open(struct tty_struct *tty, struct file *filp) DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count)); mutex_lock(&info->port.mutex); - info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; spin_lock_irqsave(&info->netlock, flags); if (info->netcount) { -- cgit From e7997f7ff7f8154d477f6f976698d868a2ac3934 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Wed, 6 Jan 2021 17:21:56 +0100 Subject: serial: stm32: fix DMA initialization error handling DMA initialization error handling is not properly implemented in the driver. Fix DMA initialization error handling by: - moving TX DMA descriptor request error handling in a new dedicated fallback_err label - adding error handling to TX DMA descriptor submission - adding error handling to RX DMA descriptor submission This patch depends on '24832ca3ee85 ("tty: serial: stm32-usart: Remove set but unused 'cookie' variables")' which unfortunately doesn't include a "Fixes" tag. Fixes: 3489187204eb ("serial: stm32: adding dma support") Signed-off-by: Erwan Le Ray Link: https://lore.kernel.org/r/20210106162203.28854-2-erwan.leray@foss.st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index f4de32d3f2af..6248304a001f 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -383,17 +383,18 @@ static void stm32_transmit_chars_dma(struct uart_port *port) DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); - if (!desc) { - for (i = count; i > 0; i--) - stm32_transmit_chars_pio(port); - return; - } + if (!desc) + goto fallback_err; desc->callback = stm32_tx_dma_complete; desc->callback_param = port; /* Push current DMA TX transaction in the pending queue */ - dmaengine_submit(desc); + if (dma_submit_error(dmaengine_submit(desc))) { + /* dma no yet started, safe to free resources */ + dmaengine_terminate_async(stm32port->tx_ch); + goto fallback_err; + } /* Issue pending DMA TX requests */ dma_async_issue_pending(stm32port->tx_ch); @@ -402,6 +403,11 @@ static void stm32_transmit_chars_dma(struct uart_port *port) xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); port->icount.tx += count; + return; + +fallback_err: + for (i = count; i > 0; i--) + stm32_transmit_chars_pio(port); } static void stm32_transmit_chars(struct uart_port *port) @@ -1130,7 +1136,11 @@ static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, desc->callback_param = NULL; /* Push current DMA transaction in the pending queue */ - dmaengine_submit(desc); + ret = dma_submit_error(dmaengine_submit(desc)); + if (ret) { + dmaengine_terminate_sync(stm32port->rx_ch); + goto config_err; + } /* Issue pending DMA requests */ dma_async_issue_pending(stm32port->rx_ch); -- cgit From 92fc00238675a15cc48f09694949f0c0012e0ff4 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Wed, 6 Jan 2021 17:21:57 +0100 Subject: serial: stm32: fix code cleaning warnings and checks Fixes checkpatch --strict warnings and checks: - checkpatch --strict "Unnecessary parentheses" - checkpatch --strict "Blank lines aren't necessary before a close brace - checkpatch --strict "Alignment should match open parenthesis" - checkpatch --strict "Please don't use multiple blank lines" - checkpatch --strict "Comparison to NULL could be written ..." - visual check code ordering warning Signed-off-by: Erwan Le Ray Link: https://lore.kernel.org/r/20210106162203.28854-3-erwan.leray@foss.st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 6248304a001f..a0ef86d71317 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -176,8 +176,7 @@ static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, status = dmaengine_tx_status(stm32_port->rx_ch, stm32_port->rx_ch->cookie, &state); - if ((status == DMA_IN_PROGRESS) && - (*last_res != state.residue)) + if (status == DMA_IN_PROGRESS && (*last_res != state.residue)) return 1; else return 0; @@ -464,7 +463,7 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) writel_relaxed(USART_ICR_RTOCF, port->membase + ofs->icr); - if ((sr & USART_SR_WUF) && (ofs->icr != UNDEF_REG)) + if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) writel_relaxed(USART_ICR_WUCF, port->membase + ofs->icr); @@ -620,7 +619,6 @@ static void stm32_stop_rx(struct uart_port *port) stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); if (stm32_port->cr3_irq) stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); - } /* Handle breaks - ignored by us */ @@ -724,7 +722,7 @@ static unsigned int stm32_get_databits(struct ktermios *termios) } static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) + struct ktermios *old) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -923,7 +921,7 @@ stm32_verify_port(struct uart_port *port, struct serial_struct *ser) } static void stm32_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) + unsigned int oldstate) { struct stm32_port *stm32port = container_of(port, struct stm32_port, port); @@ -973,18 +971,17 @@ static int stm32_init_port(struct stm32_port *stm32port, struct resource *res; int ret; + ret = platform_get_irq(pdev, 0); + if (ret <= 0) + return ret ? : -ENODEV; + port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; port->ops = &stm32_uart_ops; port->dev = &pdev->dev; port->fifosize = stm32port->info->cfg.fifosize; port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_STM32_CONSOLE); - - ret = platform_get_irq(pdev, 0); - if (ret <= 0) - return ret ? : -ENODEV; port->irq = ret; - port->rs485_config = stm32_config_rs485; ret = stm32_init_rs485(port, pdev); @@ -1101,8 +1098,8 @@ static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, return -ENODEV; } stm32port->rx_buf = dma_alloc_coherent(&pdev->dev, RX_BUF_L, - &stm32port->rx_dma_buf, - GFP_KERNEL); + &stm32port->rx_dma_buf, + GFP_KERNEL); if (!stm32port->rx_buf) { ret = -ENOMEM; goto alloc_err; @@ -1177,8 +1174,8 @@ static int stm32_of_dma_tx_probe(struct stm32_port *stm32port, return -ENODEV; } stm32port->tx_buf = dma_alloc_coherent(&pdev->dev, TX_BUF_L, - &stm32port->tx_dma_buf, - GFP_KERNEL); + &stm32port->tx_dma_buf, + GFP_KERNEL); if (!stm32port->tx_buf) { ret = -ENOMEM; goto alloc_err; @@ -1322,7 +1319,6 @@ static int stm32_serial_remove(struct platform_device *pdev) return err; } - #ifdef CONFIG_SERIAL_STM32_CONSOLE static void stm32_console_putchar(struct uart_port *port, int ch) { @@ -1335,7 +1331,8 @@ static void stm32_console_putchar(struct uart_port *port, int ch) writel_relaxed(ch, port->membase + ofs->tdr); } -static void stm32_console_write(struct console *co, const char *s, unsigned cnt) +static void stm32_console_write(struct console *co, const char *s, + unsigned int cnt) { struct uart_port *port = &stm32_ports[co->index].port; struct stm32_port *stm32_port = to_stm32_port(port); @@ -1388,7 +1385,7 @@ static int stm32_console_setup(struct console *co, char *options) * this to be called during the uart port registration when the * driver gets probed and the port should be mapped at that point. */ - if (stm32port->port.mapbase == 0 || stm32port->port.membase == NULL) + if (stm32port->port.mapbase == 0 || !stm32port->port.membase) return -ENXIO; if (options) -- cgit From 56f9a76c27b51bc8e9bb938734e3de03819569ae Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Wed, 6 Jan 2021 17:21:58 +0100 Subject: serial: stm32: add "_usart" prefix in functions name Adds the prefix "_usart" in the name of stm32 usart functions in order to ease the usage of kernel trace and tools, such as f-trace. Allows to trace "stm32_usart_*" functions with f-trace. Without this patch, all the driver functions needs to be added manually in f-trace filter. Signed-off-by: Erwan Le Ray Signed-off-by: Valentin Caron Link: https://lore.kernel.org/r/20210106162203.28854-4-erwan.leray@foss.st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 348 ++++++++++++++++++++------------------- 1 file changed, 177 insertions(+), 171 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index a0ef86d71317..717a97759928 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -34,15 +34,15 @@ #include "serial_mctrl_gpio.h" #include "stm32-usart.h" -static void stm32_stop_tx(struct uart_port *port); -static void stm32_transmit_chars(struct uart_port *port); +static void stm32_usart_stop_tx(struct uart_port *port); +static void stm32_usart_transmit_chars(struct uart_port *port); static inline struct stm32_port *to_stm32_port(struct uart_port *port) { return container_of(port, struct stm32_port, port); } -static void stm32_set_bits(struct uart_port *port, u32 reg, u32 bits) +static void stm32_usart_set_bits(struct uart_port *port, u32 reg, u32 bits) { u32 val; @@ -51,7 +51,7 @@ static void stm32_set_bits(struct uart_port *port, u32 reg, u32 bits) writel_relaxed(val, port->membase + reg); } -static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) +static void stm32_usart_clr_bits(struct uart_port *port, u32 reg, u32 bits) { u32 val; @@ -60,8 +60,8 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) writel_relaxed(val, port->membase + reg); } -static void stm32_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE, - u32 delay_DDE, u32 baud) +static void stm32_usart_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE, + u32 delay_DDE, u32 baud) { u32 rs485_deat_dedt; u32 rs485_deat_dedt_max = (USART_CR1_DEAT_MASK >> USART_CR1_DEAT_SHIFT); @@ -95,8 +95,8 @@ static void stm32_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE, *cr1 |= rs485_deat_dedt; } -static int stm32_config_rs485(struct uart_port *port, - struct serial_rs485 *rs485conf) +static int stm32_usart_config_rs485(struct uart_port *port, + struct serial_rs485 *rs485conf) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -104,7 +104,7 @@ static int stm32_config_rs485(struct uart_port *port, u32 usartdiv, baud, cr1, cr3; bool over8; - stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); port->rs485 = *rs485conf; @@ -122,9 +122,10 @@ static int stm32_config_rs485(struct uart_port *port, << USART_BRR_04_R_SHIFT; baud = DIV_ROUND_CLOSEST(port->uartclk, usartdiv); - stm32_config_reg_rs485(&cr1, &cr3, - rs485conf->delay_rts_before_send, - rs485conf->delay_rts_after_send, baud); + stm32_usart_config_reg_rs485(&cr1, &cr3, + rs485conf->delay_rts_before_send, + rs485conf->delay_rts_after_send, + baud); if (rs485conf->flags & SER_RS485_RTS_ON_SEND) { cr3 &= ~USART_CR3_DEP; @@ -137,18 +138,19 @@ static int stm32_config_rs485(struct uart_port *port, writel_relaxed(cr3, port->membase + ofs->cr3); writel_relaxed(cr1, port->membase + ofs->cr1); } else { - stm32_clr_bits(port, ofs->cr3, USART_CR3_DEM | USART_CR3_DEP); - stm32_clr_bits(port, ofs->cr1, - USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK); + stm32_usart_clr_bits(port, ofs->cr3, + USART_CR3_DEM | USART_CR3_DEP); + stm32_usart_clr_bits(port, ofs->cr1, + USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK); } - stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); return 0; } -static int stm32_init_rs485(struct uart_port *port, - struct platform_device *pdev) +static int stm32_usart_init_rs485(struct uart_port *port, + struct platform_device *pdev) { struct serial_rs485 *rs485conf = &port->rs485; @@ -162,8 +164,8 @@ static int stm32_init_rs485(struct uart_port *port, return uart_get_rs485_mode(port); } -static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, - bool threaded) +static int stm32_usart_pending_rx(struct uart_port *port, u32 *sr, + int *last_res, bool threaded) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -186,8 +188,8 @@ static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, return 0; } -static unsigned long stm32_get_char(struct uart_port *port, u32 *sr, - int *last_res) +static unsigned long stm32_usart_get_char(struct uart_port *port, u32 *sr, + int *last_res) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -206,7 +208,7 @@ static unsigned long stm32_get_char(struct uart_port *port, u32 *sr, return c; } -static void stm32_receive_chars(struct uart_port *port, bool threaded) +static void stm32_usart_receive_chars(struct uart_port *port, bool threaded) { struct tty_port *tport = &port->state->port; struct stm32_port *stm32_port = to_stm32_port(port); @@ -218,7 +220,8 @@ static void stm32_receive_chars(struct uart_port *port, bool threaded) if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) pm_wakeup_event(tport->tty->dev, 0); - while (stm32_pending_rx(port, &sr, &stm32_port->last_res, threaded)) { + while (stm32_usart_pending_rx(port, &sr, &stm32_port->last_res, + threaded)) { sr |= USART_SR_DUMMY_RX; flag = TTY_NORMAL; @@ -237,7 +240,7 @@ static void stm32_receive_chars(struct uart_port *port, bool threaded) writel_relaxed(sr & USART_SR_ERR_MASK, port->membase + ofs->icr); - c = stm32_get_char(port, &sr, &stm32_port->last_res); + c = stm32_usart_get_char(port, &sr, &stm32_port->last_res); port->icount.rx++; if (sr & USART_SR_ERR_MASK) { if (sr & USART_SR_ORE) { @@ -277,20 +280,20 @@ static void stm32_receive_chars(struct uart_port *port, bool threaded) spin_lock(&port->lock); } -static void stm32_tx_dma_complete(void *arg) +static void stm32_usart_tx_dma_complete(void *arg) { struct uart_port *port = arg; struct stm32_port *stm32port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32port->info->ofs; - stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT); stm32port->tx_dma_busy = false; /* Let's see if we have pending data to send */ - stm32_transmit_chars(port); + stm32_usart_transmit_chars(port); } -static void stm32_tx_interrupt_enable(struct uart_port *port) +static void stm32_usart_tx_interrupt_enable(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -300,30 +303,30 @@ static void stm32_tx_interrupt_enable(struct uart_port *port) * or TX empty irq when FIFO is disabled */ if (stm32_port->fifoen) - stm32_set_bits(port, ofs->cr3, USART_CR3_TXFTIE); + stm32_usart_set_bits(port, ofs->cr3, USART_CR3_TXFTIE); else - stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); + stm32_usart_set_bits(port, ofs->cr1, USART_CR1_TXEIE); } -static void stm32_tx_interrupt_disable(struct uart_port *port) +static void stm32_usart_tx_interrupt_disable(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; if (stm32_port->fifoen) - stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); else - stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); } -static void stm32_transmit_chars_pio(struct uart_port *port) +static void stm32_usart_transmit_chars_pio(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct circ_buf *xmit = &port->state->xmit; if (stm32_port->tx_dma_busy) { - stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT); stm32_port->tx_dma_busy = false; } @@ -338,12 +341,12 @@ static void stm32_transmit_chars_pio(struct uart_port *port) /* rely on TXE irq (mask or unmask) for sending remaining data */ if (uart_circ_empty(xmit)) - stm32_tx_interrupt_disable(port); + stm32_usart_tx_interrupt_disable(port); else - stm32_tx_interrupt_enable(port); + stm32_usart_tx_interrupt_enable(port); } -static void stm32_transmit_chars_dma(struct uart_port *port) +static void stm32_usart_transmit_chars_dma(struct uart_port *port) { struct stm32_port *stm32port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32port->info->ofs; @@ -385,7 +388,7 @@ static void stm32_transmit_chars_dma(struct uart_port *port) if (!desc) goto fallback_err; - desc->callback = stm32_tx_dma_complete; + desc->callback = stm32_usart_tx_dma_complete; desc->callback_param = port; /* Push current DMA TX transaction in the pending queue */ @@ -398,7 +401,7 @@ static void stm32_transmit_chars_dma(struct uart_port *port) /* Issue pending DMA TX requests */ dma_async_issue_pending(stm32port->tx_ch); - stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT); xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); port->icount.tx += count; @@ -406,10 +409,10 @@ static void stm32_transmit_chars_dma(struct uart_port *port) fallback_err: for (i = count; i > 0; i--) - stm32_transmit_chars_pio(port); + stm32_usart_transmit_chars_pio(port); } -static void stm32_transmit_chars(struct uart_port *port) +static void stm32_usart_transmit_chars(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -417,38 +420,38 @@ static void stm32_transmit_chars(struct uart_port *port) if (port->x_char) { if (stm32_port->tx_dma_busy) - stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT); writel_relaxed(port->x_char, port->membase + ofs->tdr); port->x_char = 0; port->icount.tx++; if (stm32_port->tx_dma_busy) - stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT); return; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - stm32_tx_interrupt_disable(port); + stm32_usart_tx_interrupt_disable(port); return; } if (ofs->icr == UNDEF_REG) - stm32_clr_bits(port, ofs->isr, USART_SR_TC); + stm32_usart_clr_bits(port, ofs->isr, USART_SR_TC); else writel_relaxed(USART_ICR_TCCF, port->membase + ofs->icr); if (stm32_port->tx_ch) - stm32_transmit_chars_dma(port); + stm32_usart_transmit_chars_dma(port); else - stm32_transmit_chars_pio(port); + stm32_usart_transmit_chars_pio(port); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); if (uart_circ_empty(xmit)) - stm32_tx_interrupt_disable(port); + stm32_usart_tx_interrupt_disable(port); } -static irqreturn_t stm32_interrupt(int irq, void *ptr) +static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; struct stm32_port *stm32_port = to_stm32_port(port); @@ -468,10 +471,10 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) port->membase + ofs->icr); if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch)) - stm32_receive_chars(port, false); + stm32_usart_receive_chars(port, false); if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) - stm32_transmit_chars(port); + stm32_usart_transmit_chars(port); spin_unlock(&port->lock); @@ -481,7 +484,7 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) return IRQ_HANDLED; } -static irqreturn_t stm32_threaded_interrupt(int irq, void *ptr) +static irqreturn_t stm32_usart_threaded_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; struct stm32_port *stm32_port = to_stm32_port(port); @@ -489,14 +492,14 @@ static irqreturn_t stm32_threaded_interrupt(int irq, void *ptr) spin_lock(&port->lock); if (stm32_port->rx_ch) - stm32_receive_chars(port, true); + stm32_usart_receive_chars(port, true); spin_unlock(&port->lock); return IRQ_HANDLED; } -static unsigned int stm32_tx_empty(struct uart_port *port) +static unsigned int stm32_usart_tx_empty(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -504,20 +507,20 @@ static unsigned int stm32_tx_empty(struct uart_port *port) return readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE; } -static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl) +static void stm32_usart_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS)) - stm32_set_bits(port, ofs->cr3, USART_CR3_RTSE); + stm32_usart_set_bits(port, ofs->cr3, USART_CR3_RTSE); else - stm32_clr_bits(port, ofs->cr3, USART_CR3_RTSE); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_RTSE); mctrl_gpio_set(stm32_port->gpios, mctrl); } -static unsigned int stm32_get_mctrl(struct uart_port *port) +static unsigned int stm32_usart_get_mctrl(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); unsigned int ret; @@ -528,23 +531,23 @@ static unsigned int stm32_get_mctrl(struct uart_port *port) return mctrl_gpio_get(stm32_port->gpios, &ret); } -static void stm32_enable_ms(struct uart_port *port) +static void stm32_usart_enable_ms(struct uart_port *port) { mctrl_gpio_enable_ms(to_stm32_port(port)->gpios); } -static void stm32_disable_ms(struct uart_port *port) +static void stm32_usart_disable_ms(struct uart_port *port) { mctrl_gpio_disable_ms(to_stm32_port(port)->gpios); } /* Transmit stop */ -static void stm32_stop_tx(struct uart_port *port) +static void stm32_usart_stop_tx(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct serial_rs485 *rs485conf = &port->rs485; - stm32_tx_interrupt_disable(port); + stm32_usart_tx_interrupt_disable(port); if (rs485conf->flags & SER_RS485_ENABLED) { if (rs485conf->flags & SER_RS485_RTS_ON_SEND) { @@ -558,7 +561,7 @@ static void stm32_stop_tx(struct uart_port *port) } /* There are probably characters waiting to be transmitted. */ -static void stm32_start_tx(struct uart_port *port) +static void stm32_usart_start_tx(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct serial_rs485 *rs485conf = &port->rs485; @@ -577,56 +580,56 @@ static void stm32_start_tx(struct uart_port *port) } } - stm32_transmit_chars(port); + stm32_usart_transmit_chars(port); } /* Throttle the remote when input buffer is about to overflow. */ -static void stm32_throttle(struct uart_port *port) +static void stm32_usart_throttle(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; spin_lock_irqsave(&port->lock, flags); - stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); + stm32_usart_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); if (stm32_port->cr3_irq) - stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); + stm32_usart_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); spin_unlock_irqrestore(&port->lock, flags); } /* Unthrottle the remote, the input buffer can now accept data. */ -static void stm32_unthrottle(struct uart_port *port) +static void stm32_usart_unthrottle(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; spin_lock_irqsave(&port->lock, flags); - stm32_set_bits(port, ofs->cr1, stm32_port->cr1_irq); + stm32_usart_set_bits(port, ofs->cr1, stm32_port->cr1_irq); if (stm32_port->cr3_irq) - stm32_set_bits(port, ofs->cr3, stm32_port->cr3_irq); + stm32_usart_set_bits(port, ofs->cr3, stm32_port->cr3_irq); spin_unlock_irqrestore(&port->lock, flags); } /* Receive stop */ -static void stm32_stop_rx(struct uart_port *port) +static void stm32_usart_stop_rx(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); + stm32_usart_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); if (stm32_port->cr3_irq) - stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); + stm32_usart_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); } /* Handle breaks - ignored by us */ -static void stm32_break_ctl(struct uart_port *port, int break_state) +static void stm32_usart_break_ctl(struct uart_port *port, int break_state) { } -static int stm32_startup(struct uart_port *port) +static int stm32_usart_startup(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -634,15 +637,15 @@ static int stm32_startup(struct uart_port *port) u32 val; int ret; - ret = request_threaded_irq(port->irq, stm32_interrupt, - stm32_threaded_interrupt, + ret = request_threaded_irq(port->irq, stm32_usart_interrupt, + stm32_usart_threaded_interrupt, IRQF_NO_SUSPEND, name, port); if (ret) return ret; /* RX FIFO Flush */ if (ofs->rqr != UNDEF_REG) - stm32_set_bits(port, ofs->rqr, USART_RQR_RXFRQ); + stm32_usart_set_bits(port, ofs->rqr, USART_RQR_RXFRQ); /* Tx and RX FIFO configuration */ if (stm32_port->fifoen) { @@ -657,12 +660,12 @@ static int stm32_startup(struct uart_port *port) val = stm32_port->cr1_irq | USART_CR1_RE; if (stm32_port->fifoen) val |= USART_CR1_FIFOEN; - stm32_set_bits(port, ofs->cr1, val); + stm32_usart_set_bits(port, ofs->cr1, val); return 0; } -static void stm32_shutdown(struct uart_port *port) +static void stm32_usart_shutdown(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -671,7 +674,7 @@ static void stm32_shutdown(struct uart_port *port) int ret; /* Disable modem control interrupts */ - stm32_disable_ms(port); + stm32_usart_disable_ms(port); val = USART_CR1_TXEIE | USART_CR1_TE; val |= stm32_port->cr1_irq | USART_CR1_RE; @@ -686,12 +689,12 @@ static void stm32_shutdown(struct uart_port *port) if (ret) dev_err(port->dev, "transmission complete not set\n"); - stm32_clr_bits(port, ofs->cr1, val); + stm32_usart_clr_bits(port, ofs->cr1, val); free_irq(port->irq, port); } -static unsigned int stm32_get_databits(struct ktermios *termios) +static unsigned int stm32_usart_get_databits(struct ktermios *termios) { unsigned int bits; @@ -721,8 +724,9 @@ static unsigned int stm32_get_databits(struct ktermios *termios) return bits; } -static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) +static void stm32_usart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -746,8 +750,8 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, /* flush RX & TX FIFO */ if (ofs->rqr != UNDEF_REG) - stm32_set_bits(port, ofs->rqr, - USART_RQR_TXFRQ | USART_RQR_RXFRQ); + stm32_usart_set_bits(port, ofs->rqr, + USART_RQR_TXFRQ | USART_RQR_RXFRQ); cr1 = USART_CR1_TE | USART_CR1_RE; if (stm32_port->fifoen) @@ -760,7 +764,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (cflag & CSTOPB) cr2 |= USART_CR2_STOP_2B; - bits = stm32_get_databits(termios); + bits = stm32_usart_get_databits(termios); stm32_port->rdr_mask = (BIT(bits) - 1); if (cflag & PARENB) { @@ -813,9 +817,9 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, /* Handle modem control interrupts */ if (UART_ENABLE_MS(port, termios->c_cflag)) - stm32_enable_ms(port); + stm32_usart_enable_ms(port); else - stm32_disable_ms(port); + stm32_usart_disable_ms(port); usartdiv = DIV_ROUND_CLOSEST(port->uartclk, baud); @@ -828,11 +832,11 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (usartdiv < 16) { oversampling = 8; cr1 |= USART_CR1_OVER8; - stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8); + stm32_usart_set_bits(port, ofs->cr1, USART_CR1_OVER8); } else { oversampling = 16; cr1 &= ~USART_CR1_OVER8; - stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8); + stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_OVER8); } mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT; @@ -869,9 +873,10 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, cr3 |= USART_CR3_DMAR; if (rs485conf->flags & SER_RS485_ENABLED) { - stm32_config_reg_rs485(&cr1, &cr3, - rs485conf->delay_rts_before_send, - rs485conf->delay_rts_after_send, baud); + stm32_usart_config_reg_rs485(&cr1, &cr3, + rs485conf->delay_rts_before_send, + rs485conf->delay_rts_after_send, + baud); if (rs485conf->flags & SER_RS485_RTS_ON_SEND) { cr3 &= ~USART_CR3_DEP; rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND; @@ -889,39 +894,39 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, writel_relaxed(cr2, port->membase + ofs->cr2); writel_relaxed(cr1, port->membase + ofs->cr1); - stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); spin_unlock_irqrestore(&port->lock, flags); } -static const char *stm32_type(struct uart_port *port) +static const char *stm32_usart_type(struct uart_port *port) { return (port->type == PORT_STM32) ? DRIVER_NAME : NULL; } -static void stm32_release_port(struct uart_port *port) +static void stm32_usart_release_port(struct uart_port *port) { } -static int stm32_request_port(struct uart_port *port) +static int stm32_usart_request_port(struct uart_port *port) { return 0; } -static void stm32_config_port(struct uart_port *port, int flags) +static void stm32_usart_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) port->type = PORT_STM32; } static int -stm32_verify_port(struct uart_port *port, struct serial_struct *ser) +stm32_usart_verify_port(struct uart_port *port, struct serial_struct *ser) { /* No user changeable parameters */ return -EINVAL; } -static void stm32_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) +static void stm32_usart_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) { struct stm32_port *stm32port = container_of(port, struct stm32_port, port); @@ -935,7 +940,7 @@ static void stm32_pm(struct uart_port *port, unsigned int state, break; case UART_PM_STATE_OFF: spin_lock_irqsave(&port->lock, flags); - stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); spin_unlock_irqrestore(&port->lock, flags); pm_runtime_put_sync(port->dev); break; @@ -943,29 +948,29 @@ static void stm32_pm(struct uart_port *port, unsigned int state, } static const struct uart_ops stm32_uart_ops = { - .tx_empty = stm32_tx_empty, - .set_mctrl = stm32_set_mctrl, - .get_mctrl = stm32_get_mctrl, - .stop_tx = stm32_stop_tx, - .start_tx = stm32_start_tx, - .throttle = stm32_throttle, - .unthrottle = stm32_unthrottle, - .stop_rx = stm32_stop_rx, - .enable_ms = stm32_enable_ms, - .break_ctl = stm32_break_ctl, - .startup = stm32_startup, - .shutdown = stm32_shutdown, - .set_termios = stm32_set_termios, - .pm = stm32_pm, - .type = stm32_type, - .release_port = stm32_release_port, - .request_port = stm32_request_port, - .config_port = stm32_config_port, - .verify_port = stm32_verify_port, + .tx_empty = stm32_usart_tx_empty, + .set_mctrl = stm32_usart_set_mctrl, + .get_mctrl = stm32_usart_get_mctrl, + .stop_tx = stm32_usart_stop_tx, + .start_tx = stm32_usart_start_tx, + .throttle = stm32_usart_throttle, + .unthrottle = stm32_usart_unthrottle, + .stop_rx = stm32_usart_stop_rx, + .enable_ms = stm32_usart_enable_ms, + .break_ctl = stm32_usart_break_ctl, + .startup = stm32_usart_startup, + .shutdown = stm32_usart_shutdown, + .set_termios = stm32_usart_set_termios, + .pm = stm32_usart_pm, + .type = stm32_usart_type, + .release_port = stm32_usart_release_port, + .request_port = stm32_usart_request_port, + .config_port = stm32_usart_config_port, + .verify_port = stm32_usart_verify_port, }; -static int stm32_init_port(struct stm32_port *stm32port, - struct platform_device *pdev) +static int stm32_usart_init_port(struct stm32_port *stm32port, + struct platform_device *pdev) { struct uart_port *port = &stm32port->port; struct resource *res; @@ -982,9 +987,9 @@ static int stm32_init_port(struct stm32_port *stm32port, port->fifosize = stm32port->info->cfg.fifosize; port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_STM32_CONSOLE); port->irq = ret; - port->rs485_config = stm32_config_rs485; + port->rs485_config = stm32_usart_config_rs485; - ret = stm32_init_rs485(port, pdev); + ret = stm32_usart_init_rs485(port, pdev); if (ret) return ret; @@ -1043,7 +1048,7 @@ err_clk: return ret; } -static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev) +static struct stm32_port *stm32_usart_of_get_port(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; int id; @@ -1081,8 +1086,8 @@ static const struct of_device_id stm32_match[] = { MODULE_DEVICE_TABLE(of, stm32_match); #endif -static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, - struct platform_device *pdev) +static int stm32_usart_of_dma_rx_probe(struct stm32_port *stm32port, + struct platform_device *pdev) { struct stm32_usart_offsets *ofs = &stm32port->info->ofs; struct uart_port *port = &stm32port->port; @@ -1156,8 +1161,8 @@ alloc_err: return ret; } -static int stm32_of_dma_tx_probe(struct stm32_port *stm32port, - struct platform_device *pdev) +static int stm32_usart_of_dma_tx_probe(struct stm32_port *stm32port, + struct platform_device *pdev) { struct stm32_usart_offsets *ofs = &stm32port->info->ofs; struct uart_port *port = &stm32port->port; @@ -1207,13 +1212,13 @@ alloc_err: return ret; } -static int stm32_serial_probe(struct platform_device *pdev) +static int stm32_usart_serial_probe(struct platform_device *pdev) { const struct of_device_id *match; struct stm32_port *stm32port; int ret; - stm32port = stm32_of_get_stm32_port(pdev); + stm32port = stm32_usart_of_get_port(pdev); if (!stm32port) return -ENODEV; @@ -1223,7 +1228,7 @@ static int stm32_serial_probe(struct platform_device *pdev) else return -EINVAL; - ret = stm32_init_port(stm32port, pdev); + ret = stm32_usart_init_port(stm32port, pdev); if (ret) return ret; @@ -1244,11 +1249,11 @@ static int stm32_serial_probe(struct platform_device *pdev) if (ret) goto err_wirq; - ret = stm32_of_dma_rx_probe(stm32port, pdev); + ret = stm32_usart_of_dma_rx_probe(stm32port, pdev); if (ret) dev_info(&pdev->dev, "interrupt mode used for rx (no dma)\n"); - ret = stm32_of_dma_tx_probe(stm32port, pdev); + ret = stm32_usart_of_dma_tx_probe(stm32port, pdev); if (ret) dev_info(&pdev->dev, "interrupt mode used for tx (no dma)\n"); @@ -1275,7 +1280,7 @@ err_uninit: return ret; } -static int stm32_serial_remove(struct platform_device *pdev) +static int stm32_usart_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); struct stm32_port *stm32_port = to_stm32_port(port); @@ -1284,7 +1289,7 @@ static int stm32_serial_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); - stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR); if (stm32_port->rx_ch) dma_release_channel(stm32_port->rx_ch); @@ -1294,7 +1299,7 @@ static int stm32_serial_remove(struct platform_device *pdev) RX_BUF_L, stm32_port->rx_buf, stm32_port->rx_dma_buf); - stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT); if (stm32_port->tx_ch) dma_release_channel(stm32_port->tx_ch); @@ -1320,7 +1325,7 @@ static int stm32_serial_remove(struct platform_device *pdev) } #ifdef CONFIG_SERIAL_STM32_CONSOLE -static void stm32_console_putchar(struct uart_port *port, int ch) +static void stm32_usart_console_putchar(struct uart_port *port, int ch) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -1331,8 +1336,8 @@ static void stm32_console_putchar(struct uart_port *port, int ch) writel_relaxed(ch, port->membase + ofs->tdr); } -static void stm32_console_write(struct console *co, const char *s, - unsigned int cnt) +static void stm32_usart_console_write(struct console *co, const char *s, + unsigned int cnt) { struct uart_port *port = &stm32_ports[co->index].port; struct stm32_port *stm32_port = to_stm32_port(port); @@ -1356,7 +1361,7 @@ static void stm32_console_write(struct console *co, const char *s, new_cr1 |= USART_CR1_TE | BIT(cfg->uart_enable_bit); writel_relaxed(new_cr1, port->membase + ofs->cr1); - uart_console_write(port, s, cnt, stm32_console_putchar); + uart_console_write(port, s, cnt, stm32_usart_console_putchar); /* Restore interrupt state */ writel_relaxed(old_cr1, port->membase + ofs->cr1); @@ -1366,7 +1371,7 @@ static void stm32_console_write(struct console *co, const char *s, local_irq_restore(flags); } -static int stm32_console_setup(struct console *co, char *options) +static int stm32_usart_console_setup(struct console *co, char *options) { struct stm32_port *stm32port; int baud = 9600; @@ -1397,8 +1402,8 @@ static int stm32_console_setup(struct console *co, char *options) static struct console stm32_console = { .name = STM32_SERIAL_NAME, .device = uart_console_device, - .write = stm32_console_write, - .setup = stm32_console_setup, + .write = stm32_usart_console_write, + .setup = stm32_usart_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &stm32_usart_driver, @@ -1419,8 +1424,8 @@ static struct uart_driver stm32_usart_driver = { .cons = STM32_SERIAL_CONSOLE, }; -static void __maybe_unused stm32_serial_enable_wakeup(struct uart_port *port, - bool enable) +static void __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port, + bool enable) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -1431,29 +1436,29 @@ static void __maybe_unused stm32_serial_enable_wakeup(struct uart_port *port, return; if (enable) { - stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); - stm32_set_bits(port, ofs->cr1, USART_CR1_UESM); + stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + stm32_usart_set_bits(port, ofs->cr1, USART_CR1_UESM); val = readl_relaxed(port->membase + ofs->cr3); val &= ~USART_CR3_WUS_MASK; /* Enable Wake up interrupt from low power on start bit */ val |= USART_CR3_WUS_START_BIT | USART_CR3_WUFIE; writel_relaxed(val, port->membase + ofs->cr3); - stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); } else { - stm32_clr_bits(port, ofs->cr1, USART_CR1_UESM); + stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_UESM); } } -static int __maybe_unused stm32_serial_suspend(struct device *dev) +static int __maybe_unused stm32_usart_serial_suspend(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); uart_suspend_port(&stm32_usart_driver, port); if (device_may_wakeup(dev)) - stm32_serial_enable_wakeup(port, true); + stm32_usart_serial_en_wakeup(port, true); else - stm32_serial_enable_wakeup(port, false); + stm32_usart_serial_en_wakeup(port, false); /* * When "no_console_suspend" is enabled, keep the pinctrl default state @@ -1471,19 +1476,19 @@ static int __maybe_unused stm32_serial_suspend(struct device *dev) return 0; } -static int __maybe_unused stm32_serial_resume(struct device *dev) +static int __maybe_unused stm32_usart_serial_resume(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); pinctrl_pm_select_default_state(dev); if (device_may_wakeup(dev)) - stm32_serial_enable_wakeup(port, false); + stm32_usart_serial_en_wakeup(port, false); return uart_resume_port(&stm32_usart_driver, port); } -static int __maybe_unused stm32_serial_runtime_suspend(struct device *dev) +static int __maybe_unused stm32_usart_runtime_suspend(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); struct stm32_port *stm32port = container_of(port, @@ -1494,7 +1499,7 @@ static int __maybe_unused stm32_serial_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused stm32_serial_runtime_resume(struct device *dev) +static int __maybe_unused stm32_usart_runtime_resume(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); struct stm32_port *stm32port = container_of(port, @@ -1504,14 +1509,15 @@ static int __maybe_unused stm32_serial_runtime_resume(struct device *dev) } static const struct dev_pm_ops stm32_serial_pm_ops = { - SET_RUNTIME_PM_OPS(stm32_serial_runtime_suspend, - stm32_serial_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(stm32_serial_suspend, stm32_serial_resume) + SET_RUNTIME_PM_OPS(stm32_usart_runtime_suspend, + stm32_usart_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(stm32_usart_serial_suspend, + stm32_usart_serial_resume) }; static struct platform_driver stm32_serial_driver = { - .probe = stm32_serial_probe, - .remove = stm32_serial_remove, + .probe = stm32_usart_serial_probe, + .remove = stm32_usart_serial_remove, .driver = { .name = DRIVER_NAME, .pm = &stm32_serial_pm_ops, @@ -1519,7 +1525,7 @@ static struct platform_driver stm32_serial_driver = { }, }; -static int __init usart_init(void) +static int __init stm32_usart_init(void) { static char banner[] __initdata = "STM32 USART driver initialized"; int ret; @@ -1537,14 +1543,14 @@ static int __init usart_init(void) return ret; } -static void __exit usart_exit(void) +static void __exit stm32_usart_exit(void) { platform_driver_unregister(&stm32_serial_driver); uart_unregister_driver(&stm32_usart_driver); } -module_init(usart_init); -module_exit(usart_exit); +module_init(stm32_usart_init); +module_exit(stm32_usart_exit); MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_DESCRIPTION("STMicroelectronics STM32 serial port driver"); -- cgit From 8ebd966576ab9e43b1b620a37a616c685b202972 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Wed, 6 Jan 2021 17:21:59 +0100 Subject: serial: stm32: add author Update email address add new author in authors list. Signed-off-by: Erwan Le Ray Link: https://lore.kernel.org/r/20210106162203.28854-5-erwan.leray@foss.st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 717a97759928..938d2c4aeaed 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -3,7 +3,8 @@ * Copyright (C) Maxime Coquelin 2015 * Copyright (C) STMicroelectronics SA 2017 * Authors: Maxime Coquelin - * Gerald Baeza + * Gerald Baeza + * Erwan Le Ray * * Inspired by st-asc.c from STMicroelectronics (c) */ -- cgit From 9359369ada36260a47983ac8018249dfa1c7a11b Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Wed, 6 Jan 2021 17:22:01 +0100 Subject: serial: stm32: update conflicting RTS/CTS config comment The comment for conflicting RTS/CTS config refers to "st, hw-flow-ctrl", but this property is deprecated since the generic RTS/CTS property has been introduced by the patch 'serial: stm32: Use generic DT binding for announcing RTS/CTS lines'. Update the comment to refer to both generic and deprecated RTS/CTS properties. Signed-off-by: Erwan Le Ray Link: https://lore.kernel.org/r/20210106162203.28854-7-erwan.leray@foss.st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 938d2c4aeaed..0d6c7f3375f0 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -1031,7 +1031,10 @@ static int stm32_usart_init_port(struct stm32_port *stm32port, goto err_clk; } - /* Both CTS/RTS gpios and "st,hw-flow-ctrl" should not be specified */ + /* + * Both CTS/RTS gpios and "st,hw-flow-ctrl" (deprecated) or "uart-has-rtscts" + * properties should not be specified. + */ if (stm32port->hw_flow_control) { if (mctrl_gpio_to_gpiod(stm32port->gpios, UART_GPIO_CTS) || mctrl_gpio_to_gpiod(stm32port->gpios, UART_GPIO_RTS)) { -- cgit From 97f3a0850ae42f29a4d19105274f19ceb2902313 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Wed, 6 Jan 2021 17:22:02 +0100 Subject: serial: stm32: clean probe and remove port deinit Clean probe and remove port deinit by moving clk_disable_unprepare in a new dedicated deinit_port function. Signed-off-by: Erwan Le Ray Signed-off-by: Etienne Carriere Link: https://lore.kernel.org/r/20210106162203.28854-8-erwan.leray@foss.st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 0d6c7f3375f0..9d73f6976586 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -970,6 +970,11 @@ static const struct uart_ops stm32_uart_ops = { .verify_port = stm32_usart_verify_port, }; +static void stm32_usart_deinit_port(struct stm32_port *stm32port) +{ + clk_disable_unprepare(stm32port->clk); +} + static int stm32_usart_init_port(struct stm32_port *stm32port, struct platform_device *pdev) { @@ -1279,7 +1284,7 @@ err_nowup: device_init_wakeup(&pdev->dev, false); err_uninit: - clk_disable_unprepare(stm32port->clk); + stm32_usart_deinit_port(stm32port); return ret; } @@ -1318,7 +1323,7 @@ static int stm32_usart_serial_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, false); } - clk_disable_unprepare(stm32_port->clk); + stm32_usart_deinit_port(stm32_port); err = uart_remove_one_port(&stm32_usart_driver, port); -- cgit From c31c3ea02e218998da19f4cd6b7d86eb6e873df4 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Wed, 6 Jan 2021 17:22:03 +0100 Subject: serial: stm32: update transmission complete error message in shutdown The transmission complete error message provides the status of the ISR_USART_TC bit. This bit, when set, indicates that the transmission has not been completed. The bit status indication is not a very understandable information. The error message sent on console should indicate that the transmission is not complete, instead of providing USART_TC bit status. Update the error message and add a comment for better understanding. Signed-off-by: Erwan Le Ray Link: https://lore.kernel.org/r/20210106162203.28854-9-erwan.leray@foss.st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 9d73f6976586..6a9a5ef5f5ba 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -687,8 +687,9 @@ static void stm32_usart_shutdown(struct uart_port *port) isr, (isr & USART_SR_TC), 10, 100000); + /* Send the TC error message only when ISR_TC is not set */ if (ret) - dev_err(port->dev, "transmission complete not set\n"); + dev_err(port->dev, "Transmission is not complete\n"); stm32_usart_clr_bits(port, ofs->cr1, val); -- cgit From f446776ebffb9d3fb145b8d84f7f358f64cb54d6 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 23 Nov 2020 18:49:01 -0600 Subject: tty: Export redirect release This will be required by the pty code when it removes tty_vhangup() on master close. Signed-off-by: Corey Minyard Link: https://lore.kernel.org/r/20201124004902.1398477-2-minyard@acm.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 95ba028ef668..225f4933fbde 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -540,6 +540,28 @@ void tty_wakeup(struct tty_struct *tty) EXPORT_SYMBOL_GPL(tty_wakeup); +/** + * tty_release_redirect - Release a redirect on a pty if present + * @tty: tty device + * + * This is available to the pty code so if the master closes, if the + * slave is a redirect it can release the redirect. + */ +struct file *tty_release_redirect(struct tty_struct *tty) +{ + struct file *f = NULL; + + spin_lock(&redirect_lock); + if (redirect && file_tty(redirect) == tty) { + f = redirect; + redirect = NULL; + } + spin_unlock(&redirect_lock); + + return f; +} +EXPORT_SYMBOL_GPL(tty_release_redirect); + /** * __tty_hangup - actual handler for hangup events * @tty: tty device @@ -566,7 +588,7 @@ EXPORT_SYMBOL_GPL(tty_wakeup); static void __tty_hangup(struct tty_struct *tty, int exit_session) { struct file *cons_filp = NULL; - struct file *filp, *f = NULL; + struct file *filp, *f; struct tty_file_private *priv; int closecount = 0, n; int refs; @@ -574,13 +596,7 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) if (!tty) return; - - spin_lock(&redirect_lock); - if (redirect && file_tty(redirect) == tty) { - f = redirect; - redirect = NULL; - } - spin_unlock(&redirect_lock); + f = tty_release_redirect(tty); tty_lock(tty); -- cgit From 33d4ae98859873ddd49e22e4ca724387548b3d89 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 23 Nov 2020 18:49:02 -0600 Subject: drivers:tty:pty: Fix a race causing data loss on close Remove the tty_vhangup() from the pty code and just release the redirect. The tty_vhangup() results in data loss and data out of order issues. If you write to a pty master an immediately close the pty master, the receiver might get a chunk of data dropped, but then receive some later data. That's obviously something rather unexpected for a user. It certainly confused my test program. It turns out that tty_vhangup() on the slave pty gets called from pty_close(), and that causes the data on the slave side to be flushed, but due to races more data can be copied into the slave side's buffer after that. Consider the following sequence: thread1 thread2 thread3 ------- ------- ------- | |-write data into buffer, | | n_tty buffer is filled | | along with other buffers | |-pty_close(master) | |--tty_vhangup(slave) | |---tty_ldisc_hangup() | |----n_tty_flush_buffer() | |-----reset_buffer_flags() |-n_tty_read() | |--up_read(&tty->termios_rwsem); | |------down_read(&tty->termios_rwsem) | |------clear n_tty buffer contents | |------up_read(&tty->termios_rwsem) |--tty_buffer_flush_work() | |--schedules work calling | | flush_to_ldisc() | | |-flush_to_ldisc() | |--receive_buf() | |---tty_port_default_receive_buf() | |----tty_ldisc_receive_buf() | |-----n_tty_receive_buf2() | |------n_tty_receive_buf_common() | |-------down_read(&tty->termios_rwsem) | |-------__receive_buf() | | copies data into n_tty buffer | |-------up_read(&tty->termios_rwsem) |--down_read(&tty->termios_rwsem) |--copy buffer data to user >From this sequence, you can see that thread2 writes to the buffer then only clears the part of the buffer in n_tty. The n_tty receive buffer code then copies more data into the n_tty buffer. But part of the vhangup, releasing the redirect, is still required to avoid issues with consoles running on pty slaves. So do that. As far as I can tell, that is all that should be required. Signed-off-by: Corey Minyard Link: https://lore.kernel.org/r/20201124004902.1398477-3-minyard@acm.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 15 +++++++++++++-- drivers/tty/tty_io.c | 5 +++-- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 5e2374580e27..8b2797b6ee44 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -66,7 +66,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&tty->link->read_wait); wake_up_interruptible(&tty->link->write_wait); if (tty->driver->subtype == PTY_TYPE_MASTER) { - set_bit(TTY_OTHER_CLOSED, &tty->flags); + struct file *f; + #ifdef CONFIG_UNIX98_PTYS if (tty->driver == ptm_driver) { mutex_lock(&devpts_mutex); @@ -75,7 +76,17 @@ static void pty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&devpts_mutex); } #endif - tty_vhangup(tty->link); + + /* + * This hack is required because a program can open a + * pty and redirect a console to it, but if the pty is + * closed and the console is not released, then the + * slave side will never close. So release the + * redirect when the master closes. + */ + f = tty_release_redirect(tty->link); + if (f) + fput(f); } } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 225f4933fbde..f19a34a93fe5 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -545,7 +545,9 @@ EXPORT_SYMBOL_GPL(tty_wakeup); * @tty: tty device * * This is available to the pty code so if the master closes, if the - * slave is a redirect it can release the redirect. + * slave is a redirect it can release the redirect. It returns the + * filp for the redirect, which must be fput when the operations on + * the tty are completed. */ struct file *tty_release_redirect(struct tty_struct *tty) { @@ -560,7 +562,6 @@ struct file *tty_release_redirect(struct tty_struct *tty) return f; } -EXPORT_SYMBOL_GPL(tty_release_redirect); /** * __tty_hangup - actual handler for hangup events -- cgit From c9cd57bf57fd450972a7802b9f09a680dbb4634e Mon Sep 17 00:00:00 2001 From: "Yan.Gao" Date: Thu, 10 Dec 2020 10:25:07 +0800 Subject: tty: Protect disc_data in n_tty_close and n_tty_flush_buffer n_tty_flush_buffer can happen in parallel with n_tty_close that the tty->disc_data will be set to NULL. n_tty_flush_buffer accesses tty->disc_data, so we must prevent n_tty_close clear tty->disc_data while n_tty_flush_buffer has a non-NULL view of tty->disc_data. So we need to make sure that accesses to disc_data are atomic using tty->termios_rwsem. There is an example I meet: When n_tty_flush_buffer accesses tty struct, the disc_data is right. However, then reset_buffer_flags accesses tty->disc_data, disc_data become NULL, So kernel crash when accesses tty->disc_data->real_tail. I guess there could be another thread change tty->disc_data to NULL, and during N_TTY line discipline, n_tty_close will set tty->disc_data to be NULL. So use tty->termios_rwsem to protect disc_data between close and flush_buffer. IP: reset_buffer_flags+0x9/0xf0 PGD 0 P4D 0 Oops: 0002 [#1] SMP CPU: 23 PID: 2087626 Comm: (agetty) Kdump: loaded Tainted: G Hardware name: UNISINSIGHT X3036P-G3/ST01M2C7S, BIOS 2.00.13 01/11/2019 task: ffff9c4e9da71e80 task.stack: ffffb30cfe898000 RIP: 0010:reset_buffer_flags+0x9/0xf0 RSP: 0018:ffffb30cfe89bca8 EFLAGS: 00010246 RAX: ffff9c4e9da71e80 RBX: ffff9c368d1bac00 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffff9c4ea17b50f0 RDI: 0000000000000000 RBP: ffffb30cfe89bcc8 R08: 0000000000000100 R09: 0000000000000001 R10: 0000000000000001 R11: 0000000000000000 R12: ffff9c368d1bacc0 R13: ffff9c20cfd18428 R14: ffff9c4ea17b50f0 R15: ffff9c368d1bac00 FS: 00007f9fbbe97940(0000) GS:ffff9c375c740000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000002260 CR3: 0000002f72233003 CR4: 00000000007606e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 PKRU: 55555554 Call Trace: ? n_tty_flush_buffer+0x2a/0x60 tty_buffer_flush+0x76/0x90 tty_ldisc_flush+0x22/0x40 vt_ioctl+0x5a7/0x10b0 ? n_tty_ioctl_helper+0x27/0x110 tty_ioctl+0xef/0x8c0 do_vfs_ioctl+0xa7/0x5e0 ? __audit_syscall_entry+0xaf/0x100 ? syscall_trace_enter+0x1d0/0x2b0 SyS_ioctl+0x79/0x90 do_syscall_64+0x6c/0x1b0 entry_SYSCALL64_slow_path+0x25/0x25 n_tty_flush_buffer --->tty->disc_data is OK ->reset_buffer_flags -->tty->disc_data is NULL Signed-off-by: Yan.Gao Reviewed-by: Xianting Tian Link: https://lore.kernel.org/r/20201210022507.30729-1-gao.yanB@h3c.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 319d68c8a5df..d633ba56cf83 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1894,8 +1894,10 @@ static void n_tty_close(struct tty_struct *tty) if (tty->link) n_tty_packet_mode_flush(tty); + down_write(&tty->termios_rwsem); vfree(ldata); tty->disc_data = NULL; + up_write(&tty->termios_rwsem); } /** -- cgit From 9335e23ddc33b5298b4cefdecc962736449fe596 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 12 Jan 2021 22:55:38 +0200 Subject: tty: serial: owl: Add support for kernel debugger Implement 'poll_put_char' and 'poll_get_char' callbacks in struct 'owl_uart_ops' that enables OWL UART to be used for kernel debugging over serial line. Signed-off-by: Cristian Ciocaltea Link: https://lore.kernel.org/r/026543195b9aeefb339d90abc5660a6ac7463c63.1610484108.git.cristian.ciocaltea@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/owl-uart.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index c149f8c30007..abc6042f0378 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,9 @@ #define OWL_UART_STAT_TRFL_MASK GENMASK(16, 11) #define OWL_UART_STAT_UTBB BIT(17) +#define OWL_UART_POLL_USEC 5 +#define OWL_UART_TIMEOUT_USEC 10000 + static struct uart_driver owl_uart_driver; struct owl_uart_info { @@ -461,6 +465,36 @@ static void owl_uart_config_port(struct uart_port *port, int flags) } } +#ifdef CONFIG_CONSOLE_POLL + +static int owl_uart_poll_get_char(struct uart_port *port) +{ + if (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_RFEM) + return NO_POLL_CHAR; + + return owl_uart_read(port, OWL_UART_RXDAT); +} + +static void owl_uart_poll_put_char(struct uart_port *port, unsigned char ch) +{ + u32 reg; + int ret; + + /* Wait while FIFO is full or timeout */ + ret = readl_poll_timeout_atomic(port->membase + OWL_UART_STAT, reg, + !(reg & OWL_UART_STAT_TFFU), + OWL_UART_POLL_USEC, + OWL_UART_TIMEOUT_USEC); + if (ret == -ETIMEDOUT) { + dev_err(port->dev, "Timeout waiting while UART TX FULL\n"); + return; + } + + owl_uart_write(port, ch, OWL_UART_TXDAT); +} + +#endif /* CONFIG_CONSOLE_POLL */ + static const struct uart_ops owl_uart_ops = { .set_mctrl = owl_uart_set_mctrl, .get_mctrl = owl_uart_get_mctrl, @@ -476,6 +510,10 @@ static const struct uart_ops owl_uart_ops = { .request_port = owl_uart_request_port, .release_port = owl_uart_release_port, .verify_port = owl_uart_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = owl_uart_poll_get_char, + .poll_put_char = owl_uart_poll_put_char, +#endif }; #ifdef CONFIG_SERIAL_OWL_CONSOLE -- cgit From c24dc4bab20c88bd8f493e0339b8cc65df828acd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 14 Jan 2021 18:57:16 +0100 Subject: tty: hvcs: Drop unnecessary if block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If hvcs_probe() succeeded dev_set_drvdata() is called with a non-NULL value, and if hvcs_probe() failed hvcs_remove() isn't called. So there is no way dev_get_drvdata() can return NULL in hvcs_remove() and the check can just go away. Reviewed-by: Jiri Slaby Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20210114175718.137483-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvcs.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index dfe02283ed23..c90848919644 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -825,9 +825,6 @@ static int hvcs_remove(struct vio_dev *dev) unsigned long flags; struct tty_struct *tty; - if (!hvcsd) - return -ENODEV; - /* By this time the vty-server won't be getting any more interrupts */ spin_lock_irqsave(&hvcsd->lock, flags); -- cgit From 6da629c85871a0cb65fad3e0c0a440d64002b0a7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 14 Jan 2021 18:57:17 +0100 Subject: tty: vcc: Drop unnecessary if block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If vcc_probe() succeeded dev_set_drvdata() is called with a non-NULL value, and if vcc_probe() failed vcc_remove() isn't called. So there is no way dev_get_drvdata() can return NULL in vcc_remove() and the check can just go away. Reviewed-by: Jiri Slaby Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20210114175718.137483-3-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vcc.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 9ffd42e333b8..d9b0dc6deae9 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -681,9 +681,6 @@ static int vcc_remove(struct vio_dev *vdev) { struct vcc_port *port = dev_get_drvdata(&vdev->dev); - if (!port) - return -ENODEV; - del_timer_sync(&port->rx_timer); del_timer_sync(&port->tx_timer); -- cgit From 63e34e707c6248645da340d4aff7714ec27b5fb1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 14 Jan 2021 18:57:18 +0100 Subject: tty: vcc: Drop impossible to hit WARN_ON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vcc_get() returns the port that has provided port->index. As the port that is about to be removed isn't removed yet this trivially will find this port. So simplify the call to not assign an identical value to the port pointer and drop the warning that is never hit. Reviewed-by: Jiri Slaby Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20210114175718.137483-4-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vcc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index d9b0dc6deae9..e2d6205f83ce 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -692,12 +692,9 @@ static int vcc_remove(struct vio_dev *vdev) tty_vhangup(port->tty); /* Get exclusive reference to VCC, ensures that there are no other - * clients to this port + * clients to this port. This cannot fail. */ - port = vcc_get(port->index, true); - - if (WARN_ON(!port)) - return -ENODEV; + vcc_get(port->index, true); tty_unregister_device(vcc_tty_driver, port->index); -- cgit From fd4a641ac88fbbaf8b90e00823397597a287cfcd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Jan 2021 18:30:18 +0100 Subject: leds: trigger: implement a tty trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Usage is as follows: myled=ledname tty=ttyS0 echo tty > /sys/class/leds/$myled/trigger echo $tty > /sys/class/leds/$myled/ttyname . When this new trigger is active it periodically checks the tty's statistics and when it changed since the last check the led is flashed once. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20210113173018.bq2fkea2o3yp6rf6@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/leds/trigger/Kconfig | 9 ++ drivers/leds/trigger/Makefile | 1 + drivers/leds/trigger/ledtrig-tty.c | 183 +++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 drivers/leds/trigger/ledtrig-tty.c (limited to 'drivers') diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index ce9429ca6dde..b77a01bd27f4 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -144,4 +144,13 @@ config LEDS_TRIGGER_AUDIO the audio mute and mic-mute changes. If unsure, say N +config LEDS_TRIGGER_TTY + tristate "LED Trigger for TTY devices" + depends on TTY + help + This allows LEDs to be controlled by activity on ttys which includes + serial devices like /dev/ttyS0. + + When build as a module this driver will be called ledtrig-tty. + endif # LEDS_TRIGGERS diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile index 733a83e2a718..25c4db97cdd4 100644 --- a/drivers/leds/trigger/Makefile +++ b/drivers/leds/trigger/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o obj-$(CONFIG_LEDS_TRIGGER_PATTERN) += ledtrig-pattern.o obj-$(CONFIG_LEDS_TRIGGER_AUDIO) += ledtrig-audio.o +obj-$(CONFIG_LEDS_TRIGGER_TTY) += ledtrig-tty.o diff --git a/drivers/leds/trigger/ledtrig-tty.c b/drivers/leds/trigger/ledtrig-tty.c new file mode 100644 index 000000000000..d2ab6ab080ac --- /dev/null +++ b/drivers/leds/trigger/ledtrig-tty.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include + +struct ledtrig_tty_data { + struct led_classdev *led_cdev; + struct delayed_work dwork; + struct mutex mutex; + const char *ttyname; + struct tty_struct *tty; + int rx, tx; +}; + +static void ledtrig_tty_restart(struct ledtrig_tty_data *trigger_data) +{ + schedule_delayed_work(&trigger_data->dwork, 0); +} + +static ssize_t ttyname_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); + ssize_t len = 0; + + mutex_lock(&trigger_data->mutex); + + if (trigger_data->ttyname) + len = sprintf(buf, "%s\n", trigger_data->ttyname); + + mutex_unlock(&trigger_data->mutex); + + return len; +} + +static ssize_t ttyname_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); + char *ttyname; + ssize_t ret = size; + bool running; + + if (size > 0 && buf[size - 1] == '\n') + size -= 1; + + if (size) { + ttyname = kmemdup_nul(buf, size, GFP_KERNEL); + if (!ttyname) { + ret = -ENOMEM; + goto out_unlock; + } + } else { + ttyname = NULL; + } + + mutex_lock(&trigger_data->mutex); + + running = trigger_data->ttyname != NULL; + + kfree(trigger_data->ttyname); + tty_kref_put(trigger_data->tty); + trigger_data->tty = NULL; + + trigger_data->ttyname = ttyname; + +out_unlock: + mutex_unlock(&trigger_data->mutex); + + if (ttyname && !running) + ledtrig_tty_restart(trigger_data); + + return ret; +} +static DEVICE_ATTR_RW(ttyname); + +static void ledtrig_tty_work(struct work_struct *work) +{ + struct ledtrig_tty_data *trigger_data = + container_of(work, struct ledtrig_tty_data, dwork.work); + struct serial_icounter_struct icount; + int ret; + + mutex_lock(&trigger_data->mutex); + + if (!trigger_data->ttyname) { + /* exit without rescheduling */ + mutex_unlock(&trigger_data->mutex); + return; + } + + /* try to get the tty corresponding to $ttyname */ + if (!trigger_data->tty) { + dev_t devno; + struct tty_struct *tty; + int ret; + + ret = tty_dev_name_to_number(trigger_data->ttyname, &devno); + if (ret < 0) + /* + * A device with this name might appear later, so keep + * retrying. + */ + goto out; + + tty = tty_kopen_shared(devno); + if (IS_ERR(tty) || !tty) + /* What to do? retry or abort */ + goto out; + + trigger_data->tty = tty; + } + + ret = tty_get_icount(trigger_data->tty, &icount); + if (ret) { + dev_info(trigger_data->tty->dev, "Failed to get icount, stopped polling\n"); + mutex_unlock(&trigger_data->mutex); + return; + } + + if (icount.rx != trigger_data->rx || + icount.tx != trigger_data->tx) { + led_set_brightness(trigger_data->led_cdev, LED_ON); + + trigger_data->rx = icount.rx; + trigger_data->tx = icount.tx; + } else { + led_set_brightness(trigger_data->led_cdev, LED_OFF); + } + +out: + mutex_unlock(&trigger_data->mutex); + schedule_delayed_work(&trigger_data->dwork, msecs_to_jiffies(100)); +} + +static struct attribute *ledtrig_tty_attrs[] = { + &dev_attr_ttyname.attr, + NULL +}; +ATTRIBUTE_GROUPS(ledtrig_tty); + +static int ledtrig_tty_activate(struct led_classdev *led_cdev) +{ + struct ledtrig_tty_data *trigger_data; + + trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL); + if (!trigger_data) + return -ENOMEM; + + led_set_trigger_data(led_cdev, trigger_data); + + INIT_DELAYED_WORK(&trigger_data->dwork, ledtrig_tty_work); + trigger_data->led_cdev = led_cdev; + mutex_init(&trigger_data->mutex); + + return 0; +} + +static void ledtrig_tty_deactivate(struct led_classdev *led_cdev) +{ + struct ledtrig_tty_data *trigger_data = led_get_trigger_data(led_cdev); + + cancel_delayed_work_sync(&trigger_data->dwork); + + kfree(trigger_data); +} + +static struct led_trigger ledtrig_tty = { + .name = "tty", + .activate = ledtrig_tty_activate, + .deactivate = ledtrig_tty_deactivate, + .groups = ledtrig_tty_groups, +}; +module_led_trigger(ledtrig_tty); + +MODULE_AUTHOR("Uwe Kleine-König "); +MODULE_DESCRIPTION("UART LED trigger"); +MODULE_LICENSE("GPL v2"); -- cgit From 429b29aef7f841086949c7359f9c3ccb051e7ea3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 15 Jan 2021 16:51:29 +0100 Subject: tty: serial: Drop unused efm32 serial driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for this machine was just removed, so drop the now unused UART driver, too. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20210115155130.185010-7-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 13 - drivers/tty/serial/Makefile | 1 - drivers/tty/serial/efm32-uart.c | 852 ---------------------------------------- 3 files changed, 866 deletions(-) delete mode 100644 drivers/tty/serial/efm32-uart.c (limited to 'drivers') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 34a2899e69c0..83f6ca4bf210 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1295,14 +1295,6 @@ config SERIAL_AR933X_NR_UARTS Set this to the number of serial ports you want the driver to support. -config SERIAL_EFM32_UART - tristate "EFM32 UART/USART port" - depends on ARM && (ARCH_EFM32 || COMPILE_TEST) - select SERIAL_CORE - help - This driver support the USART and UART ports on - Energy Micro's efm32 SoCs. - config SERIAL_MPS2_UART_CONSOLE bool "MPS2 UART console support" depends on SERIAL_MPS2_UART @@ -1316,11 +1308,6 @@ config SERIAL_MPS2_UART help This driver support the UART ports on ARM MPS2. -config SERIAL_EFM32_UART_CONSOLE - bool "EFM32 UART/USART console support" - depends on SERIAL_EFM32_UART=y - select SERIAL_CORE_CONSOLE - config SERIAL_ARC tristate "ARC UART driver support" select SERIAL_CORE diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index b85d53f9e9ff..ec2b74091f0c 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -73,7 +73,6 @@ obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o obj-$(CONFIG_SERIAL_TEGRA) += serial-tegra.o obj-$(CONFIG_SERIAL_TEGRA_TCU) += tegra-tcu.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o -obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o obj-$(CONFIG_SERIAL_ARC) += arc_uart.o obj-$(CONFIG_SERIAL_RP2) += rp2.o obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c deleted file mode 100644 index f12f29cf4f31..000000000000 --- a/drivers/tty/serial/efm32-uart.c +++ /dev/null @@ -1,852 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DRIVER_NAME "efm32-uart" -#define DEV_NAME "ttyefm" - -#define UARTn_CTRL 0x00 -#define UARTn_CTRL_SYNC 0x0001 -#define UARTn_CTRL_TXBIL 0x1000 - -#define UARTn_FRAME 0x04 -#define UARTn_FRAME_DATABITS__MASK 0x000f -#define UARTn_FRAME_DATABITS(n) ((n) - 3) -#define UARTn_FRAME_PARITY__MASK 0x0300 -#define UARTn_FRAME_PARITY_NONE 0x0000 -#define UARTn_FRAME_PARITY_EVEN 0x0200 -#define UARTn_FRAME_PARITY_ODD 0x0300 -#define UARTn_FRAME_STOPBITS_HALF 0x0000 -#define UARTn_FRAME_STOPBITS_ONE 0x1000 -#define UARTn_FRAME_STOPBITS_TWO 0x3000 - -#define UARTn_CMD 0x0c -#define UARTn_CMD_RXEN 0x0001 -#define UARTn_CMD_RXDIS 0x0002 -#define UARTn_CMD_TXEN 0x0004 -#define UARTn_CMD_TXDIS 0x0008 - -#define UARTn_STATUS 0x10 -#define UARTn_STATUS_TXENS 0x0002 -#define UARTn_STATUS_TXC 0x0020 -#define UARTn_STATUS_TXBL 0x0040 -#define UARTn_STATUS_RXDATAV 0x0080 - -#define UARTn_CLKDIV 0x14 - -#define UARTn_RXDATAX 0x18 -#define UARTn_RXDATAX_RXDATA__MASK 0x01ff -#define UARTn_RXDATAX_PERR 0x4000 -#define UARTn_RXDATAX_FERR 0x8000 -/* - * This is a software only flag used for ignore_status_mask and - * read_status_mask! It's used for breaks that the hardware doesn't report - * explicitly. - */ -#define SW_UARTn_RXDATAX_BERR 0x2000 - -#define UARTn_TXDATA 0x34 - -#define UARTn_IF 0x40 -#define UARTn_IF_TXC 0x0001 -#define UARTn_IF_TXBL 0x0002 -#define UARTn_IF_RXDATAV 0x0004 -#define UARTn_IF_RXOF 0x0010 - -#define UARTn_IFS 0x44 -#define UARTn_IFC 0x48 -#define UARTn_IEN 0x4c - -#define UARTn_ROUTE 0x54 -#define UARTn_ROUTE_LOCATION__MASK 0x0700 -#define UARTn_ROUTE_LOCATION(n) (((n) << 8) & UARTn_ROUTE_LOCATION__MASK) -#define UARTn_ROUTE_RXPEN 0x0001 -#define UARTn_ROUTE_TXPEN 0x0002 - -struct efm32_uart_port { - struct uart_port port; - unsigned int txirq; - struct clk *clk; - struct efm32_uart_pdata pdata; -}; -#define to_efm_port(_port) container_of(_port, struct efm32_uart_port, port) -#define efm_debug(efm_port, format, arg...) \ - dev_dbg(efm_port->port.dev, format, ##arg) - -static void efm32_uart_write32(struct efm32_uart_port *efm_port, - u32 value, unsigned offset) -{ - writel_relaxed(value, efm_port->port.membase + offset); -} - -static u32 efm32_uart_read32(struct efm32_uart_port *efm_port, - unsigned offset) -{ - return readl_relaxed(efm_port->port.membase + offset); -} - -static unsigned int efm32_uart_tx_empty(struct uart_port *port) -{ - struct efm32_uart_port *efm_port = to_efm_port(port); - u32 status = efm32_uart_read32(efm_port, UARTn_STATUS); - - if (status & UARTn_STATUS_TXC) - return TIOCSER_TEMT; - else - return 0; -} - -static void efm32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* sorry, neither handshaking lines nor loop functionallity */ -} - -static unsigned int efm32_uart_get_mctrl(struct uart_port *port) -{ - /* sorry, no handshaking lines available */ - return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR; -} - -static void efm32_uart_stop_tx(struct uart_port *port) -{ - struct efm32_uart_port *efm_port = to_efm_port(port); - u32 ien = efm32_uart_read32(efm_port, UARTn_IEN); - - efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD); - ien &= ~(UARTn_IF_TXC | UARTn_IF_TXBL); - efm32_uart_write32(efm_port, ien, UARTn_IEN); -} - -static void efm32_uart_tx_chars(struct efm32_uart_port *efm_port) -{ - struct uart_port *port = &efm_port->port; - struct circ_buf *xmit = &port->state->xmit; - - while (efm32_uart_read32(efm_port, UARTn_STATUS) & - UARTn_STATUS_TXBL) { - if (port->x_char) { - port->icount.tx++; - efm32_uart_write32(efm_port, port->x_char, - UARTn_TXDATA); - port->x_char = 0; - continue; - } - if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { - port->icount.tx++; - efm32_uart_write32(efm_port, xmit->buf[xmit->tail], - UARTn_TXDATA); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - } else - break; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (!port->x_char && uart_circ_empty(xmit) && - efm32_uart_read32(efm_port, UARTn_STATUS) & - UARTn_STATUS_TXC) - efm32_uart_stop_tx(port); -} - -static void efm32_uart_start_tx(struct uart_port *port) -{ - struct efm32_uart_port *efm_port = to_efm_port(port); - u32 ien; - - efm32_uart_write32(efm_port, - UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IFC); - ien = efm32_uart_read32(efm_port, UARTn_IEN); - efm32_uart_write32(efm_port, - ien | UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IEN); - efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD); - - efm32_uart_tx_chars(efm_port); -} - -static void efm32_uart_stop_rx(struct uart_port *port) -{ - struct efm32_uart_port *efm_port = to_efm_port(port); - - efm32_uart_write32(efm_port, UARTn_CMD_RXDIS, UARTn_CMD); -} - -static void efm32_uart_break_ctl(struct uart_port *port, int ctl) -{ - /* not possible without fiddling with gpios */ -} - -static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port) -{ - struct uart_port *port = &efm_port->port; - - while (efm32_uart_read32(efm_port, UARTn_STATUS) & - UARTn_STATUS_RXDATAV) { - u32 rxdata = efm32_uart_read32(efm_port, UARTn_RXDATAX); - int flag = 0; - - /* - * This is a reserved bit and I only saw it read as 0. But to be - * sure not to be confused too much by new devices adhere to the - * warning in the reference manual that reserved bits might - * read as 1 in the future. - */ - rxdata &= ~SW_UARTn_RXDATAX_BERR; - - port->icount.rx++; - - if ((rxdata & UARTn_RXDATAX_FERR) && - !(rxdata & UARTn_RXDATAX_RXDATA__MASK)) { - rxdata |= SW_UARTn_RXDATAX_BERR; - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (rxdata & UARTn_RXDATAX_PERR) - port->icount.parity++; - else if (rxdata & UARTn_RXDATAX_FERR) - port->icount.frame++; - - rxdata &= port->read_status_mask; - - if (rxdata & SW_UARTn_RXDATAX_BERR) - flag = TTY_BREAK; - else if (rxdata & UARTn_RXDATAX_PERR) - flag = TTY_PARITY; - else if (rxdata & UARTn_RXDATAX_FERR) - flag = TTY_FRAME; - else if (uart_handle_sysrq_char(port, - rxdata & UARTn_RXDATAX_RXDATA__MASK)) - continue; - - if ((rxdata & port->ignore_status_mask) == 0) - tty_insert_flip_char(&port->state->port, - rxdata & UARTn_RXDATAX_RXDATA__MASK, flag); - } -} - -static irqreturn_t efm32_uart_rxirq(int irq, void *data) -{ - struct efm32_uart_port *efm_port = data; - u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF); - int handled = IRQ_NONE; - struct uart_port *port = &efm_port->port; - struct tty_port *tport = &port->state->port; - - spin_lock(&port->lock); - - if (irqflag & UARTn_IF_RXDATAV) { - efm32_uart_write32(efm_port, UARTn_IF_RXDATAV, UARTn_IFC); - efm32_uart_rx_chars(efm_port); - - handled = IRQ_HANDLED; - } - - if (irqflag & UARTn_IF_RXOF) { - efm32_uart_write32(efm_port, UARTn_IF_RXOF, UARTn_IFC); - port->icount.overrun++; - tty_insert_flip_char(tport, 0, TTY_OVERRUN); - - handled = IRQ_HANDLED; - } - - spin_unlock(&port->lock); - - tty_flip_buffer_push(tport); - - return handled; -} - -static irqreturn_t efm32_uart_txirq(int irq, void *data) -{ - struct efm32_uart_port *efm_port = data; - u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF); - - /* TXBL doesn't need to be cleared */ - if (irqflag & UARTn_IF_TXC) - efm32_uart_write32(efm_port, UARTn_IF_TXC, UARTn_IFC); - - if (irqflag & (UARTn_IF_TXC | UARTn_IF_TXBL)) { - efm32_uart_tx_chars(efm_port); - return IRQ_HANDLED; - } else - return IRQ_NONE; -} - -static int efm32_uart_startup(struct uart_port *port) -{ - struct efm32_uart_port *efm_port = to_efm_port(port); - int ret; - - ret = clk_enable(efm_port->clk); - if (ret) { - efm_debug(efm_port, "failed to enable clk\n"); - goto err_clk_enable; - } - port->uartclk = clk_get_rate(efm_port->clk); - - /* Enable pins at configured location */ - efm32_uart_write32(efm_port, - UARTn_ROUTE_LOCATION(efm_port->pdata.location) | - UARTn_ROUTE_RXPEN | UARTn_ROUTE_TXPEN, - UARTn_ROUTE); - - ret = request_irq(port->irq, efm32_uart_rxirq, 0, - DRIVER_NAME, efm_port); - if (ret) { - efm_debug(efm_port, "failed to register rxirq\n"); - goto err_request_irq_rx; - } - - /* disable all irqs */ - efm32_uart_write32(efm_port, 0, UARTn_IEN); - - ret = request_irq(efm_port->txirq, efm32_uart_txirq, 0, - DRIVER_NAME, efm_port); - if (ret) { - efm_debug(efm_port, "failed to register txirq\n"); - free_irq(port->irq, efm_port); -err_request_irq_rx: - - clk_disable(efm_port->clk); - } else { - efm32_uart_write32(efm_port, - UARTn_IF_RXDATAV | UARTn_IF_RXOF, UARTn_IEN); - efm32_uart_write32(efm_port, UARTn_CMD_RXEN, UARTn_CMD); - } - -err_clk_enable: - return ret; -} - -static void efm32_uart_shutdown(struct uart_port *port) -{ - struct efm32_uart_port *efm_port = to_efm_port(port); - - efm32_uart_write32(efm_port, 0, UARTn_IEN); - free_irq(port->irq, efm_port); - - clk_disable(efm_port->clk); -} - -static void efm32_uart_set_termios(struct uart_port *port, - struct ktermios *new, struct ktermios *old) -{ - struct efm32_uart_port *efm_port = to_efm_port(port); - unsigned long flags; - unsigned baud; - u32 clkdiv; - u32 frame = 0; - - /* no modem control lines */ - new->c_cflag &= ~(CRTSCTS | CMSPAR); - - baud = uart_get_baud_rate(port, new, old, - DIV_ROUND_CLOSEST(port->uartclk, 16 * 8192), - DIV_ROUND_CLOSEST(port->uartclk, 16)); - - switch (new->c_cflag & CSIZE) { - case CS5: - frame |= UARTn_FRAME_DATABITS(5); - break; - case CS6: - frame |= UARTn_FRAME_DATABITS(6); - break; - case CS7: - frame |= UARTn_FRAME_DATABITS(7); - break; - case CS8: - frame |= UARTn_FRAME_DATABITS(8); - break; - } - - if (new->c_cflag & CSTOPB) - /* the receiver only verifies the first stop bit */ - frame |= UARTn_FRAME_STOPBITS_TWO; - else - frame |= UARTn_FRAME_STOPBITS_ONE; - - if (new->c_cflag & PARENB) { - if (new->c_cflag & PARODD) - frame |= UARTn_FRAME_PARITY_ODD; - else - frame |= UARTn_FRAME_PARITY_EVEN; - } else - frame |= UARTn_FRAME_PARITY_NONE; - - /* - * the 6 lowest bits of CLKDIV are dc, bit 6 has value 0.25. - * port->uartclk <= 14e6, so 4 * port->uartclk doesn't overflow. - */ - clkdiv = (DIV_ROUND_CLOSEST(4 * port->uartclk, 16 * baud) - 4) << 6; - - spin_lock_irqsave(&port->lock, flags); - - efm32_uart_write32(efm_port, - UARTn_CMD_TXDIS | UARTn_CMD_RXDIS, UARTn_CMD); - - port->read_status_mask = UARTn_RXDATAX_RXDATA__MASK; - if (new->c_iflag & INPCK) - port->read_status_mask |= - UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR; - if (new->c_iflag & (IGNBRK | BRKINT | PARMRK)) - port->read_status_mask |= SW_UARTn_RXDATAX_BERR; - - port->ignore_status_mask = 0; - if (new->c_iflag & IGNPAR) - port->ignore_status_mask |= - UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR; - if (new->c_iflag & IGNBRK) - port->ignore_status_mask |= SW_UARTn_RXDATAX_BERR; - - uart_update_timeout(port, new->c_cflag, baud); - - efm32_uart_write32(efm_port, UARTn_CTRL_TXBIL, UARTn_CTRL); - efm32_uart_write32(efm_port, frame, UARTn_FRAME); - efm32_uart_write32(efm_port, clkdiv, UARTn_CLKDIV); - - efm32_uart_write32(efm_port, UARTn_CMD_TXEN | UARTn_CMD_RXEN, - UARTn_CMD); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *efm32_uart_type(struct uart_port *port) -{ - return port->type == PORT_EFMUART ? "efm32-uart" : NULL; -} - -static void efm32_uart_release_port(struct uart_port *port) -{ - struct efm32_uart_port *efm_port = to_efm_port(port); - - clk_unprepare(efm_port->clk); - clk_put(efm_port->clk); - iounmap(port->membase); -} - -static int efm32_uart_request_port(struct uart_port *port) -{ - struct efm32_uart_port *efm_port = to_efm_port(port); - int ret; - - port->membase = ioremap(port->mapbase, 60); - if (!efm_port->port.membase) { - ret = -ENOMEM; - efm_debug(efm_port, "failed to remap\n"); - goto err_ioremap; - } - - efm_port->clk = clk_get(port->dev, NULL); - if (IS_ERR(efm_port->clk)) { - ret = PTR_ERR(efm_port->clk); - efm_debug(efm_port, "failed to get clock\n"); - goto err_clk_get; - } - - ret = clk_prepare(efm_port->clk); - if (ret) { - clk_put(efm_port->clk); -err_clk_get: - - iounmap(port->membase); -err_ioremap: - return ret; - } - return 0; -} - -static void efm32_uart_config_port(struct uart_port *port, int type) -{ - if (type & UART_CONFIG_TYPE && - !efm32_uart_request_port(port)) - port->type = PORT_EFMUART; -} - -static int efm32_uart_verify_port(struct uart_port *port, - struct serial_struct *serinfo) -{ - int ret = 0; - - if (serinfo->type != PORT_UNKNOWN && serinfo->type != PORT_EFMUART) - ret = -EINVAL; - - return ret; -} - -static const struct uart_ops efm32_uart_pops = { - .tx_empty = efm32_uart_tx_empty, - .set_mctrl = efm32_uart_set_mctrl, - .get_mctrl = efm32_uart_get_mctrl, - .stop_tx = efm32_uart_stop_tx, - .start_tx = efm32_uart_start_tx, - .stop_rx = efm32_uart_stop_rx, - .break_ctl = efm32_uart_break_ctl, - .startup = efm32_uart_startup, - .shutdown = efm32_uart_shutdown, - .set_termios = efm32_uart_set_termios, - .type = efm32_uart_type, - .release_port = efm32_uart_release_port, - .request_port = efm32_uart_request_port, - .config_port = efm32_uart_config_port, - .verify_port = efm32_uart_verify_port, -}; - -static struct efm32_uart_port *efm32_uart_ports[5]; - -#ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE -static void efm32_uart_console_putchar(struct uart_port *port, int ch) -{ - struct efm32_uart_port *efm_port = to_efm_port(port); - unsigned int timeout = 0x400; - u32 status; - - while (1) { - status = efm32_uart_read32(efm_port, UARTn_STATUS); - - if (status & UARTn_STATUS_TXBL) - break; - if (!timeout--) - return; - } - efm32_uart_write32(efm_port, ch, UARTn_TXDATA); -} - -static void efm32_uart_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct efm32_uart_port *efm_port = efm32_uart_ports[co->index]; - u32 status = efm32_uart_read32(efm_port, UARTn_STATUS); - unsigned int timeout = 0x400; - - if (!(status & UARTn_STATUS_TXENS)) - efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD); - - uart_console_write(&efm_port->port, s, count, - efm32_uart_console_putchar); - - /* Wait for the transmitter to become empty */ - while (1) { - u32 status = efm32_uart_read32(efm_port, UARTn_STATUS); - if (status & UARTn_STATUS_TXC) - break; - if (!timeout--) - break; - } - - if (!(status & UARTn_STATUS_TXENS)) - efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD); -} - -static void efm32_uart_console_get_options(struct efm32_uart_port *efm_port, - int *baud, int *parity, int *bits) -{ - u32 ctrl = efm32_uart_read32(efm_port, UARTn_CTRL); - u32 route, clkdiv, frame; - - if (ctrl & UARTn_CTRL_SYNC) - /* not operating in async mode */ - return; - - route = efm32_uart_read32(efm_port, UARTn_ROUTE); - if (!(route & UARTn_ROUTE_TXPEN)) - /* tx pin not routed */ - return; - - clkdiv = efm32_uart_read32(efm_port, UARTn_CLKDIV); - - *baud = DIV_ROUND_CLOSEST(4 * efm_port->port.uartclk, - 16 * (4 + (clkdiv >> 6))); - - frame = efm32_uart_read32(efm_port, UARTn_FRAME); - switch (frame & UARTn_FRAME_PARITY__MASK) { - case UARTn_FRAME_PARITY_ODD: - *parity = 'o'; - break; - case UARTn_FRAME_PARITY_EVEN: - *parity = 'e'; - break; - default: - *parity = 'n'; - } - - *bits = (frame & UARTn_FRAME_DATABITS__MASK) - - UARTn_FRAME_DATABITS(4) + 4; - - efm_debug(efm_port, "get_opts: options=%d%c%d\n", - *baud, *parity, *bits); -} - -static int efm32_uart_console_setup(struct console *co, char *options) -{ - struct efm32_uart_port *efm_port; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - int ret; - - if (co->index < 0 || co->index >= ARRAY_SIZE(efm32_uart_ports)) { - unsigned i; - for (i = 0; i < ARRAY_SIZE(efm32_uart_ports); ++i) { - if (efm32_uart_ports[i]) { - pr_warn("efm32-console: fall back to console index %u (from %hhi)\n", - i, co->index); - co->index = i; - break; - } - } - } - - efm_port = efm32_uart_ports[co->index]; - if (!efm_port) { - pr_warn("efm32-console: No port at %d\n", co->index); - return -ENODEV; - } - - ret = clk_prepare(efm_port->clk); - if (ret) { - dev_warn(efm_port->port.dev, - "console: clk_prepare failed: %d\n", ret); - return ret; - } - - efm_port->port.uartclk = clk_get_rate(efm_port->clk); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - efm32_uart_console_get_options(efm_port, - &baud, &parity, &bits); - - return uart_set_options(&efm_port->port, co, baud, parity, bits, flow); -} - -static struct uart_driver efm32_uart_reg; - -static struct console efm32_uart_console = { - .name = DEV_NAME, - .write = efm32_uart_console_write, - .device = uart_console_device, - .setup = efm32_uart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &efm32_uart_reg, -}; - -#else -#define efm32_uart_console (*(struct console *)NULL) -#endif /* ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE / else */ - -static struct uart_driver efm32_uart_reg = { - .owner = THIS_MODULE, - .driver_name = DRIVER_NAME, - .dev_name = DEV_NAME, - .nr = ARRAY_SIZE(efm32_uart_ports), - .cons = &efm32_uart_console, -}; - -static int efm32_uart_probe_dt(struct platform_device *pdev, - struct efm32_uart_port *efm_port) -{ - struct device_node *np = pdev->dev.of_node; - u32 location; - int ret; - - if (!np) - return 1; - - ret = of_property_read_u32(np, "energymicro,location", &location); - - if (ret) - /* fall back to wrongly namespaced property */ - ret = of_property_read_u32(np, "efm32,location", &location); - - if (ret) - /* fall back to old and (wrongly) generic property "location" */ - ret = of_property_read_u32(np, "location", &location); - - if (!ret) { - if (location > 5) { - dev_err(&pdev->dev, "invalid location\n"); - return -EINVAL; - } - efm_debug(efm_port, "using location %u\n", location); - efm_port->pdata.location = location; - } else { - efm_debug(efm_port, "fall back to location 0\n"); - } - - ret = of_alias_get_id(np, "serial"); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias id: %d\n", ret); - return ret; - } else { - efm_port->port.line = ret; - return 0; - } - -} - -static int efm32_uart_probe(struct platform_device *pdev) -{ - struct efm32_uart_port *efm_port; - struct resource *res; - unsigned int line; - int ret; - - efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL); - if (!efm_port) { - dev_dbg(&pdev->dev, "failed to allocate private data\n"); - return -ENOMEM; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - dev_dbg(&pdev->dev, "failed to determine base address\n"); - goto err_get_base; - } - - if (resource_size(res) < 60) { - ret = -EINVAL; - dev_dbg(&pdev->dev, "memory resource too small\n"); - goto err_too_small; - } - - ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_dbg(&pdev->dev, "failed to get rx irq\n"); - goto err_get_rxirq; - } - - efm_port->port.irq = ret; - - ret = platform_get_irq(pdev, 1); - if (ret <= 0) - ret = efm_port->port.irq + 1; - - efm_port->txirq = ret; - - efm_port->port.dev = &pdev->dev; - efm_port->port.mapbase = res->start; - efm_port->port.type = PORT_EFMUART; - efm_port->port.iotype = UPIO_MEM32; - efm_port->port.fifosize = 2; - efm_port->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_EFM32_UART_CONSOLE); - efm_port->port.ops = &efm32_uart_pops; - efm_port->port.flags = UPF_BOOT_AUTOCONF; - - ret = efm32_uart_probe_dt(pdev, efm_port); - if (ret > 0) { - /* not created by device tree */ - const struct efm32_uart_pdata *pdata = dev_get_platdata(&pdev->dev); - - efm_port->port.line = pdev->id; - - if (pdata) - efm_port->pdata = *pdata; - } else if (ret < 0) - goto err_probe_dt; - - line = efm_port->port.line; - - if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports)) - efm32_uart_ports[line] = efm_port; - - ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port); - if (ret) { - dev_dbg(&pdev->dev, "failed to add port: %d\n", ret); - - if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports)) - efm32_uart_ports[line] = NULL; -err_probe_dt: -err_get_rxirq: -err_too_small: -err_get_base: - kfree(efm_port); - } else { - platform_set_drvdata(pdev, efm_port); - dev_dbg(&pdev->dev, "\\o/\n"); - } - - return ret; -} - -static int efm32_uart_remove(struct platform_device *pdev) -{ - struct efm32_uart_port *efm_port = platform_get_drvdata(pdev); - unsigned int line = efm_port->port.line; - - uart_remove_one_port(&efm32_uart_reg, &efm_port->port); - - if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports)) - efm32_uart_ports[line] = NULL; - - kfree(efm_port); - - return 0; -} - -static const struct of_device_id efm32_uart_dt_ids[] = { - { - .compatible = "energymicro,efm32-uart", - }, { - /* doesn't follow the "vendor,device" scheme, don't use */ - .compatible = "efm32,uart", - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(of, efm32_uart_dt_ids); - -static struct platform_driver efm32_uart_driver = { - .probe = efm32_uart_probe, - .remove = efm32_uart_remove, - - .driver = { - .name = DRIVER_NAME, - .of_match_table = efm32_uart_dt_ids, - }, -}; - -static int __init efm32_uart_init(void) -{ - int ret; - - ret = uart_register_driver(&efm32_uart_reg); - if (ret) - return ret; - - ret = platform_driver_register(&efm32_uart_driver); - if (ret) - uart_unregister_driver(&efm32_uart_reg); - - pr_info("EFM32 UART/USART driver\n"); - - return ret; -} -module_init(efm32_uart_init); - -static void __exit efm32_uart_exit(void) -{ - platform_driver_unregister(&efm32_uart_driver); - uart_unregister_driver(&efm32_uart_reg); -} -module_exit(efm32_uart_exit); - -MODULE_AUTHOR("Uwe Kleine-Koenig "); -MODULE_DESCRIPTION("EFM32 UART/USART driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" DRIVER_NAME); -- cgit From e8372c4f51d01c3db3d067ee8d62d24368606262 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 18 Jan 2021 09:44:47 -0300 Subject: serial: fsl_lpuart: Use of_device_get_match_data() The retrieval of driver data via of_device_get_match_data() can make the code simpler. Use of_device_get_match_data() to simplify the code. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20210118124447.1632092-1-festevam@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index bd047e1f9bea..794035041744 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -2580,9 +2580,7 @@ static struct uart_driver lpuart_reg = { static int lpuart_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = of_match_device(lpuart_dt_ids, - &pdev->dev); - const struct lpuart_soc_data *sdata = of_id->data; + const struct lpuart_soc_data *sdata = of_device_get_match_data(&pdev->dev); struct device_node *np = pdev->dev.of_node; struct lpuart_port *sport; struct resource *res; -- cgit From 08fdc69945603355d24ef4adc811d2e368adbb56 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 18 Jan 2021 12:21:53 -0300 Subject: serial: mxs-auart: Remove serial_mxs_probe_dt() The mxs platform is devicetree-only, so there is no need to check whether it was instantiated via devicetree. Simplify the code my removing serial_mxs_probe_dt() and add its content into the main probe function. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20210118152154.1644569-1-festevam@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 43 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 8ecf622602cb..01fedb41cf93 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1535,34 +1535,6 @@ disable_clk_ahb: return err; } -/* - * This function returns 1 if pdev isn't a device instatiated by dt, 0 if it - * could successfully get all information from dt or a negative errno. - */ -static int serial_mxs_probe_dt(struct mxs_auart_port *s, - struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - int ret; - - if (!np) - /* no device tree device */ - return 1; - - ret = of_alias_get_id(np, "serial"); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias id: %d\n", ret); - return ret; - } - s->port.line = ret; - - if (of_get_property(np, "uart-has-rtscts", NULL) || - of_get_property(np, "fsl,uart-has-rtscts", NULL) /* deprecated */) - set_bit(MXS_AUART_RTSCTS, &s->flags); - - return 0; -} - static int mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev) { enum mctrl_gpio_idx i; @@ -1631,6 +1603,7 @@ static int mxs_auart_request_gpio_irq(struct mxs_auart_port *s) static int mxs_auart_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct mxs_auart_port *s; u32 version; int ret, irq; @@ -1643,11 +1616,17 @@ static int mxs_auart_probe(struct platform_device *pdev) s->port.dev = &pdev->dev; s->dev = &pdev->dev; - ret = serial_mxs_probe_dt(s, pdev); - if (ret > 0) - s->port.line = pdev->id < 0 ? 0 : pdev->id; - else if (ret < 0) + ret = of_alias_get_id(np, "serial"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias id: %d\n", ret); return ret; + } + s->port.line = ret; + + if (of_get_property(np, "uart-has-rtscts", NULL) || + of_get_property(np, "fsl,uart-has-rtscts", NULL) /* deprecated */) + set_bit(MXS_AUART_RTSCTS, &s->flags); + if (s->port.line >= ARRAY_SIZE(auart_port)) { dev_err(&pdev->dev, "serial%d out of range\n", s->port.line); return -EINVAL; -- cgit From 532b7cecdd4bee0e50c704b1281194c88f19cef1 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 18 Jan 2021 12:21:54 -0300 Subject: serial: mxs-auart: Remove There is nothing in the driver that uses the definitions from . Remove the unused header file inclusion. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20210118152154.1644569-2-festevam@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 01fedb41cf93..f414d6acad69 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -34,8 +34,6 @@ #include #include -#include - #include #include #include -- cgit From 4776a4a0a29c64b954a445ff65848bd376a50fcc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 20 Jan 2021 17:12:26 +0100 Subject: serial: remove sirf prima/atlas driver The CSR SiRF prima2/atlas platforms are getting removed, so this driver is no longer needed. Cc: Barry Song Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20210120161324.3728294-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 22 - drivers/tty/serial/Makefile | 1 - drivers/tty/serial/sirfsoc_uart.c | 1503 ------------------------------------- drivers/tty/serial/sirfsoc_uart.h | 447 ----------- 4 files changed, 1973 deletions(-) delete mode 100644 drivers/tty/serial/sirfsoc_uart.c delete mode 100644 drivers/tty/serial/sirfsoc_uart.h (limited to 'drivers') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 83f6ca4bf210..22e86ba4f827 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -276,28 +276,6 @@ config SERIAL_SAMSUNG_CONSOLE your boot loader about how to pass options to the kernel at boot time.) -config SERIAL_SIRFSOC - tristate "SiRF SoC Platform Serial port support" - depends on ARCH_SIRF - select SERIAL_CORE - help - Support for the on-chip UART on the CSR SiRFprimaII series, - providing /dev/ttySiRF0, 1 and 2 (note, some machines may not - provide all of these ports, depending on how the serial port - pins are configured). - -config SERIAL_SIRFSOC_CONSOLE - bool "Support for console on SiRF SoC serial port" - depends on SERIAL_SIRFSOC=y - select SERIAL_CORE_CONSOLE - help - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttySiRFx". (Try "man bootparam" or see the documentation of - your boot loader about how to pass options to the kernel at - boot time.) - config SERIAL_TEGRA tristate "NVIDIA Tegra20/30 SoC serial controller" depends on ARCH_TEGRA && TEGRA20_APB_DMA diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index ec2b74091f0c..931dc1d6885e 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -69,7 +69,6 @@ obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o -obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o obj-$(CONFIG_SERIAL_TEGRA) += serial-tegra.o obj-$(CONFIG_SERIAL_TEGRA_TCU) += tegra-tcu.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c deleted file mode 100644 index 38622f2a30a9..000000000000 --- a/drivers/tty/serial/sirfsoc_uart.c +++ /dev/null @@ -1,1503 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for CSR SiRFprimaII onboard UARTs. - * - * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sirfsoc_uart.h" - -static unsigned int -sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count); -static unsigned int -sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count); -static struct uart_driver sirfsoc_uart_drv; - -static void sirfsoc_uart_tx_dma_complete_callback(void *param); -static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = { - {4000000, 2359296}, - {3500000, 1310721}, - {3000000, 1572865}, - {2500000, 1245186}, - {2000000, 1572866}, - {1500000, 1245188}, - {1152000, 1638404}, - {1000000, 1572869}, - {921600, 1114120}, - {576000, 1245196}, - {500000, 1245198}, - {460800, 1572876}, - {230400, 1310750}, - {115200, 1310781}, - {57600, 1310843}, - {38400, 1114328}, - {19200, 1114545}, - {9600, 1114979}, -}; - -static struct sirfsoc_uart_port *sirf_ports[SIRFSOC_UART_NR]; - -static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port) -{ - return container_of(port, struct sirfsoc_uart_port, port); -} - -static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port) -{ - unsigned long reg; - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; - reg = rd_regl(port, ureg->sirfsoc_tx_fifo_status); - return (reg & ufifo_st->ff_empty(port)) ? TIOCSER_TEMT : 0; -} - -static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled) - goto cts_asserted; - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { - if (!(rd_regl(port, ureg->sirfsoc_afc_ctrl) & - SIRFUART_AFC_CTS_STATUS)) - goto cts_asserted; - else - goto cts_deasserted; - } else { - if (!gpio_get_value(sirfport->cts_gpio)) - goto cts_asserted; - else - goto cts_deasserted; - } -cts_deasserted: - return TIOCM_CAR | TIOCM_DSR; -cts_asserted: - return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; -} - -static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - unsigned int assert = mctrl & TIOCM_RTS; - unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0; - unsigned int current_val; - - if (mctrl & TIOCM_LOOP) { - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) - wr_regl(port, ureg->sirfsoc_line_ctrl, - rd_regl(port, ureg->sirfsoc_line_ctrl) | - SIRFUART_LOOP_BACK); - else - wr_regl(port, ureg->sirfsoc_mode1, - rd_regl(port, ureg->sirfsoc_mode1) | - SIRFSOC_USP_LOOP_BACK_CTRL); - } else { - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) - wr_regl(port, ureg->sirfsoc_line_ctrl, - rd_regl(port, ureg->sirfsoc_line_ctrl) & - ~SIRFUART_LOOP_BACK); - else - wr_regl(port, ureg->sirfsoc_mode1, - rd_regl(port, ureg->sirfsoc_mode1) & - ~SIRFSOC_USP_LOOP_BACK_CTRL); - } - - if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled) - return; - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { - current_val = rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0xFF; - val |= current_val; - wr_regl(port, ureg->sirfsoc_afc_ctrl, val); - } else { - if (!val) - gpio_set_value(sirfport->rts_gpio, 1); - else - gpio_set_value(sirfport->rts_gpio, 0); - } -} - -static void sirfsoc_uart_stop_tx(struct uart_port *port) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - - if (sirfport->tx_dma_chan) { - if (sirfport->tx_dma_state == TX_DMA_RUNNING) { - dmaengine_pause(sirfport->tx_dma_chan); - sirfport->tx_dma_state = TX_DMA_PAUSE; - } else { - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) & - ~uint_en->sirfsoc_txfifo_empty_en); - else - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, - uint_en->sirfsoc_txfifo_empty_en); - } - } else { - if (sirfport->uart_reg->uart_type == SIRF_USP_UART) - wr_regl(port, ureg->sirfsoc_tx_rx_en, rd_regl(port, - ureg->sirfsoc_tx_rx_en) & ~SIRFUART_TX_EN); - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) & - ~uint_en->sirfsoc_txfifo_empty_en); - else - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, - uint_en->sirfsoc_txfifo_empty_en); - } -} - -static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport) -{ - struct uart_port *port = &sirfport->port; - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - struct circ_buf *xmit = &port->state->xmit; - unsigned long tran_size; - unsigned long tran_start; - unsigned long pio_tx_size; - - tran_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - tran_start = (unsigned long)(xmit->buf + xmit->tail); - if (uart_circ_empty(xmit) || uart_tx_stopped(port) || - !tran_size) - return; - if (sirfport->tx_dma_state == TX_DMA_PAUSE) { - dmaengine_resume(sirfport->tx_dma_chan); - return; - } - if (sirfport->tx_dma_state == TX_DMA_RUNNING) - return; - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg)& - ~(uint_en->sirfsoc_txfifo_empty_en)); - else - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, - uint_en->sirfsoc_txfifo_empty_en); - /* - * DMA requires buffer address and buffer length are both aligned with - * 4 bytes, so we use PIO for - * 1. if address is not aligned with 4bytes, use PIO for the first 1~3 - * bytes, and move to DMA for the left part aligned with 4bytes - * 2. if buffer length is not aligned with 4bytes, use DMA for aligned - * part first, move to PIO for the left 1~3 bytes - */ - if (tran_size < 4 || BYTES_TO_ALIGN(tran_start)) { - wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP); - wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)| - SIRFUART_IO_MODE); - if (BYTES_TO_ALIGN(tran_start)) { - pio_tx_size = sirfsoc_uart_pio_tx_chars(sirfport, - BYTES_TO_ALIGN(tran_start)); - tran_size -= pio_tx_size; - } - if (tran_size < 4) - sirfsoc_uart_pio_tx_chars(sirfport, tran_size); - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg)| - uint_en->sirfsoc_txfifo_empty_en); - else - wr_regl(port, ureg->sirfsoc_int_en_reg, - uint_en->sirfsoc_txfifo_empty_en); - wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); - } else { - /* tx transfer mode switch into dma mode */ - wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP); - wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)& - ~SIRFUART_IO_MODE); - wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); - tran_size &= ~(0x3); - - sirfport->tx_dma_addr = dma_map_single(port->dev, - xmit->buf + xmit->tail, - tran_size, DMA_TO_DEVICE); - sirfport->tx_dma_desc = dmaengine_prep_slave_single( - sirfport->tx_dma_chan, sirfport->tx_dma_addr, - tran_size, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); - if (!sirfport->tx_dma_desc) { - dev_err(port->dev, "DMA prep slave single fail\n"); - return; - } - sirfport->tx_dma_desc->callback = - sirfsoc_uart_tx_dma_complete_callback; - sirfport->tx_dma_desc->callback_param = (void *)sirfport; - sirfport->transfer_size = tran_size; - - dmaengine_submit(sirfport->tx_dma_desc); - dma_async_issue_pending(sirfport->tx_dma_chan); - sirfport->tx_dma_state = TX_DMA_RUNNING; - } -} - -static void sirfsoc_uart_start_tx(struct uart_port *port) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - if (sirfport->tx_dma_chan) - sirfsoc_uart_tx_with_dma(sirfport); - else { - if (sirfport->uart_reg->uart_type == SIRF_USP_UART) - wr_regl(port, ureg->sirfsoc_tx_rx_en, rd_regl(port, - ureg->sirfsoc_tx_rx_en) | SIRFUART_TX_EN); - wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP); - sirfsoc_uart_pio_tx_chars(sirfport, port->fifosize); - wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg)| - uint_en->sirfsoc_txfifo_empty_en); - else - wr_regl(port, ureg->sirfsoc_int_en_reg, - uint_en->sirfsoc_txfifo_empty_en); - } -} - -static void sirfsoc_uart_stop_rx(struct uart_port *port) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - - wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); - if (sirfport->rx_dma_chan) { - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) & - ~(SIRFUART_RX_DMA_INT_EN(uint_en, - sirfport->uart_reg->uart_type) | - uint_en->sirfsoc_rx_done_en)); - else - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, - SIRFUART_RX_DMA_INT_EN(uint_en, - sirfport->uart_reg->uart_type)| - uint_en->sirfsoc_rx_done_en); - dmaengine_terminate_all(sirfport->rx_dma_chan); - } else { - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg)& - ~(SIRFUART_RX_IO_INT_EN(uint_en, - sirfport->uart_reg->uart_type))); - else - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, - SIRFUART_RX_IO_INT_EN(uint_en, - sirfport->uart_reg->uart_type)); - } -} - -static void sirfsoc_uart_disable_ms(struct uart_port *port) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - - if (!sirfport->hw_flow_ctrl) - return; - sirfport->ms_enabled = false; - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { - wr_regl(port, ureg->sirfsoc_afc_ctrl, - rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF); - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg)& - ~uint_en->sirfsoc_cts_en); - else - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, - uint_en->sirfsoc_cts_en); - } else - disable_irq(gpio_to_irq(sirfport->cts_gpio)); -} - -static irqreturn_t sirfsoc_uart_usp_cts_handler(int irq, void *dev_id) -{ - struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id; - struct uart_port *port = &sirfport->port; - spin_lock(&port->lock); - if (gpio_is_valid(sirfport->cts_gpio) && sirfport->ms_enabled) - uart_handle_cts_change(port, - !gpio_get_value(sirfport->cts_gpio)); - spin_unlock(&port->lock); - return IRQ_HANDLED; -} - -static void sirfsoc_uart_enable_ms(struct uart_port *port) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - - if (!sirfport->hw_flow_ctrl) - return; - sirfport->ms_enabled = true; - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { - wr_regl(port, ureg->sirfsoc_afc_ctrl, - rd_regl(port, ureg->sirfsoc_afc_ctrl) | - SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN | - SIRFUART_AFC_CTRL_RX_THD); - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) - | uint_en->sirfsoc_cts_en); - else - wr_regl(port, ureg->sirfsoc_int_en_reg, - uint_en->sirfsoc_cts_en); - } else - enable_irq(gpio_to_irq(sirfport->cts_gpio)); -} - -static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { - unsigned long ulcon = rd_regl(port, ureg->sirfsoc_line_ctrl); - if (break_state) - ulcon |= SIRFUART_SET_BREAK; - else - ulcon &= ~SIRFUART_SET_BREAK; - wr_regl(port, ureg->sirfsoc_line_ctrl, ulcon); - } -} - -static unsigned int -sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; - unsigned int ch, rx_count = 0; - struct tty_struct *tty; - tty = tty_port_tty_get(&port->state->port); - if (!tty) - return -ENODEV; - while (!(rd_regl(port, ureg->sirfsoc_rx_fifo_status) & - ufifo_st->ff_empty(port))) { - ch = rd_regl(port, ureg->sirfsoc_rx_fifo_data) | - SIRFUART_DUMMY_READ; - if (unlikely(uart_handle_sysrq_char(port, ch))) - continue; - uart_insert_char(port, 0, 0, ch, TTY_NORMAL); - rx_count++; - if (rx_count >= max_rx_count) - break; - } - - port->icount.rx += rx_count; - - return rx_count; -} - -static unsigned int -sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count) -{ - struct uart_port *port = &sirfport->port; - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; - struct circ_buf *xmit = &port->state->xmit; - unsigned int num_tx = 0; - while (!uart_circ_empty(xmit) && - !(rd_regl(port, ureg->sirfsoc_tx_fifo_status) & - ufifo_st->ff_full(port)) && - count--) { - wr_regl(port, ureg->sirfsoc_tx_fifo_data, - xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - num_tx++; - } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - return num_tx; -} - -static void sirfsoc_uart_tx_dma_complete_callback(void *param) -{ - struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; - struct uart_port *port = &sirfport->port; - struct circ_buf *xmit = &port->state->xmit; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - xmit->tail = (xmit->tail + sirfport->transfer_size) & - (UART_XMIT_SIZE - 1); - port->icount.tx += sirfport->transfer_size; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - if (sirfport->tx_dma_addr) - dma_unmap_single(port->dev, sirfport->tx_dma_addr, - sirfport->transfer_size, DMA_TO_DEVICE); - sirfport->tx_dma_state = TX_DMA_IDLE; - sirfsoc_uart_tx_with_dma(sirfport); - spin_unlock_irqrestore(&port->lock, flags); -} - -static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) -{ - unsigned long intr_status; - unsigned long cts_status; - unsigned long flag = TTY_NORMAL; - struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id; - struct uart_port *port = &sirfport->port; - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; - struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - struct uart_state *state = port->state; - struct circ_buf *xmit = &port->state->xmit; - spin_lock(&port->lock); - intr_status = rd_regl(port, ureg->sirfsoc_int_st_reg); - wr_regl(port, ureg->sirfsoc_int_st_reg, intr_status); - intr_status &= rd_regl(port, ureg->sirfsoc_int_en_reg); - if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT(uint_st, - sirfport->uart_reg->uart_type)))) { - if (intr_status & uint_st->sirfsoc_rxd_brk) { - port->icount.brk++; - if (uart_handle_break(port)) - goto recv_char; - } - if (intr_status & uint_st->sirfsoc_rx_oflow) { - port->icount.overrun++; - flag = TTY_OVERRUN; - } - if (intr_status & uint_st->sirfsoc_frm_err) { - port->icount.frame++; - flag = TTY_FRAME; - } - if (intr_status & uint_st->sirfsoc_parity_err) { - port->icount.parity++; - flag = TTY_PARITY; - } - wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); - wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); - wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START); - intr_status &= port->read_status_mask; - uart_insert_char(port, intr_status, - uint_en->sirfsoc_rx_oflow_en, 0, flag); - } -recv_char: - if ((sirfport->uart_reg->uart_type == SIRF_REAL_UART) && - (intr_status & SIRFUART_CTS_INT_ST(uint_st)) && - !sirfport->tx_dma_state) { - cts_status = rd_regl(port, ureg->sirfsoc_afc_ctrl) & - SIRFUART_AFC_CTS_STATUS; - if (cts_status != 0) - cts_status = 0; - else - cts_status = 1; - uart_handle_cts_change(port, cts_status); - wake_up_interruptible(&state->port.delta_msr_wait); - } - if (!sirfport->rx_dma_chan && - (intr_status & SIRFUART_RX_IO_INT_ST(uint_st))) { - /* - * chip will trigger continuous RX_TIMEOUT interrupt - * in RXFIFO empty and not trigger if RXFIFO recevice - * data in limit time, original method use RX_TIMEOUT - * will trigger lots of useless interrupt in RXFIFO - * empty.RXFIFO received one byte will trigger RX_DONE - * interrupt.use RX_DONE to wait for data received - * into RXFIFO, use RX_THD/RX_FULL for lots data receive - * and use RX_TIMEOUT for the last left data. - */ - if (intr_status & uint_st->sirfsoc_rx_done) { - if (!sirfport->is_atlas7) { - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) - & ~(uint_en->sirfsoc_rx_done_en)); - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) - | (uint_en->sirfsoc_rx_timeout_en)); - } else { - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, - uint_en->sirfsoc_rx_done_en); - wr_regl(port, ureg->sirfsoc_int_en_reg, - uint_en->sirfsoc_rx_timeout_en); - } - } else { - if (intr_status & uint_st->sirfsoc_rx_timeout) { - if (!sirfport->is_atlas7) { - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) - & ~(uint_en->sirfsoc_rx_timeout_en)); - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) - | (uint_en->sirfsoc_rx_done_en)); - } else { - wr_regl(port, - ureg->sirfsoc_int_en_clr_reg, - uint_en->sirfsoc_rx_timeout_en); - wr_regl(port, ureg->sirfsoc_int_en_reg, - uint_en->sirfsoc_rx_done_en); - } - } - sirfsoc_uart_pio_rx_chars(port, port->fifosize); - } - } - spin_unlock(&port->lock); - tty_flip_buffer_push(&state->port); - spin_lock(&port->lock); - if (intr_status & uint_st->sirfsoc_txfifo_empty) { - if (sirfport->tx_dma_chan) - sirfsoc_uart_tx_with_dma(sirfport); - else { - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - spin_unlock(&port->lock); - return IRQ_HANDLED; - } else { - sirfsoc_uart_pio_tx_chars(sirfport, - port->fifosize); - if ((uart_circ_empty(xmit)) && - (rd_regl(port, ureg->sirfsoc_tx_fifo_status) & - ufifo_st->ff_empty(port))) - sirfsoc_uart_stop_tx(port); - } - } - } - spin_unlock(&port->lock); - - return IRQ_HANDLED; -} - -static void sirfsoc_uart_rx_dma_complete_callback(void *param) -{ -} - -/* submit rx dma task into dmaengine */ -static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) & - ~SIRFUART_IO_MODE); - sirfport->rx_dma_items.xmit.tail = - sirfport->rx_dma_items.xmit.head = 0; - sirfport->rx_dma_items.desc = - dmaengine_prep_dma_cyclic(sirfport->rx_dma_chan, - sirfport->rx_dma_items.dma_addr, SIRFSOC_RX_DMA_BUF_SIZE, - SIRFSOC_RX_DMA_BUF_SIZE / 2, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); - if (IS_ERR_OR_NULL(sirfport->rx_dma_items.desc)) { - dev_err(port->dev, "DMA slave single fail\n"); - return; - } - sirfport->rx_dma_items.desc->callback = - sirfsoc_uart_rx_dma_complete_callback; - sirfport->rx_dma_items.desc->callback_param = sirfport; - sirfport->rx_dma_items.cookie = - dmaengine_submit(sirfport->rx_dma_items.desc); - dma_async_issue_pending(sirfport->rx_dma_chan); - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) | - SIRFUART_RX_DMA_INT_EN(uint_en, - sirfport->uart_reg->uart_type)); - else - wr_regl(port, ureg->sirfsoc_int_en_reg, - SIRFUART_RX_DMA_INT_EN(uint_en, - sirfport->uart_reg->uart_type)); -} - -static unsigned int -sirfsoc_usp_calc_sample_div(unsigned long set_rate, - unsigned long ioclk_rate, unsigned long *sample_reg) -{ - unsigned long min_delta = ~0UL; - unsigned short sample_div; - unsigned long ioclk_div = 0; - unsigned long temp_delta; - - for (sample_div = SIRF_USP_MIN_SAMPLE_DIV; - sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) { - temp_delta = ioclk_rate - - (ioclk_rate + (set_rate * sample_div) / 2) - / (set_rate * sample_div) * set_rate * sample_div; - - temp_delta = (temp_delta > 0) ? temp_delta : -temp_delta; - if (temp_delta < min_delta) { - ioclk_div = (2 * ioclk_rate / - (set_rate * sample_div) + 1) / 2 - 1; - if (ioclk_div > SIRF_IOCLK_DIV_MAX) - continue; - min_delta = temp_delta; - *sample_reg = sample_div; - if (!temp_delta) - break; - } - } - return ioclk_div; -} - -static unsigned int -sirfsoc_uart_calc_sample_div(unsigned long baud_rate, - unsigned long ioclk_rate, unsigned long *set_baud) -{ - unsigned long min_delta = ~0UL; - unsigned short sample_div; - unsigned int regv = 0; - unsigned long ioclk_div; - unsigned long baud_tmp; - int temp_delta; - - for (sample_div = SIRF_MIN_SAMPLE_DIV; - sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) { - ioclk_div = (ioclk_rate / (baud_rate * (sample_div + 1))) - 1; - if (ioclk_div > SIRF_IOCLK_DIV_MAX) - continue; - baud_tmp = ioclk_rate / ((ioclk_div + 1) * (sample_div + 1)); - temp_delta = baud_tmp - baud_rate; - temp_delta = (temp_delta > 0) ? temp_delta : -temp_delta; - if (temp_delta < min_delta) { - regv = regv & (~SIRF_IOCLK_DIV_MASK); - regv = regv | ioclk_div; - regv = regv & (~SIRF_SAMPLE_DIV_MASK); - regv = regv | (sample_div << SIRF_SAMPLE_DIV_SHIFT); - min_delta = temp_delta; - *set_baud = baud_tmp; - } - } - return regv; -} - -static void sirfsoc_uart_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - unsigned long config_reg = 0; - unsigned long baud_rate; - unsigned long set_baud; - unsigned long flags; - unsigned long ic; - unsigned int clk_div_reg = 0; - unsigned long txfifo_op_reg, ioclk_rate; - unsigned long rx_time_out; - int threshold_div; - u32 data_bit_len, stop_bit_len, len_val; - unsigned long sample_div_reg = 0xf; - ioclk_rate = port->uartclk; - - switch (termios->c_cflag & CSIZE) { - default: - case CS8: - data_bit_len = 8; - config_reg |= SIRFUART_DATA_BIT_LEN_8; - break; - case CS7: - data_bit_len = 7; - config_reg |= SIRFUART_DATA_BIT_LEN_7; - break; - case CS6: - data_bit_len = 6; - config_reg |= SIRFUART_DATA_BIT_LEN_6; - break; - case CS5: - data_bit_len = 5; - config_reg |= SIRFUART_DATA_BIT_LEN_5; - break; - } - if (termios->c_cflag & CSTOPB) { - config_reg |= SIRFUART_STOP_BIT_LEN_2; - stop_bit_len = 2; - } else - stop_bit_len = 1; - - spin_lock_irqsave(&port->lock, flags); - port->read_status_mask = uint_en->sirfsoc_rx_oflow_en; - port->ignore_status_mask = 0; - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { - if (termios->c_iflag & INPCK) - port->read_status_mask |= uint_en->sirfsoc_frm_err_en | - uint_en->sirfsoc_parity_err_en; - } else { - if (termios->c_iflag & INPCK) - port->read_status_mask |= uint_en->sirfsoc_frm_err_en; - } - if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) - port->read_status_mask |= uint_en->sirfsoc_rxd_brk_en; - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= - uint_en->sirfsoc_frm_err_en | - uint_en->sirfsoc_parity_err_en; - if (termios->c_cflag & PARENB) { - if (termios->c_cflag & CMSPAR) { - if (termios->c_cflag & PARODD) - config_reg |= SIRFUART_STICK_BIT_MARK; - else - config_reg |= SIRFUART_STICK_BIT_SPACE; - } else { - if (termios->c_cflag & PARODD) - config_reg |= SIRFUART_STICK_BIT_ODD; - else - config_reg |= SIRFUART_STICK_BIT_EVEN; - } - } - } else { - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= - uint_en->sirfsoc_frm_err_en; - if (termios->c_cflag & PARENB) - dev_warn(port->dev, - "USP-UART not support parity err\n"); - } - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= - uint_en->sirfsoc_rxd_brk_en; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= - uint_en->sirfsoc_rx_oflow_en; - } - if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= SIRFUART_DUMMY_READ; - /* Hardware Flow Control Settings */ - if (UART_ENABLE_MS(port, termios->c_cflag)) { - if (!sirfport->ms_enabled) - sirfsoc_uart_enable_ms(port); - } else { - if (sirfport->ms_enabled) - sirfsoc_uart_disable_ms(port); - } - baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000); - if (ioclk_rate == 150000000) { - for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++) - if (baud_rate == baudrate_to_regv[ic].baud_rate) - clk_div_reg = baudrate_to_regv[ic].reg_val; - } - set_baud = baud_rate; - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { - if (unlikely(clk_div_reg == 0)) - clk_div_reg = sirfsoc_uart_calc_sample_div(baud_rate, - ioclk_rate, &set_baud); - wr_regl(port, ureg->sirfsoc_divisor, clk_div_reg); - } else { - clk_div_reg = sirfsoc_usp_calc_sample_div(baud_rate, - ioclk_rate, &sample_div_reg); - sample_div_reg--; - set_baud = ((ioclk_rate / (clk_div_reg+1) - 1) / - (sample_div_reg + 1)); - /* setting usp mode 2 */ - len_val = ((1 << SIRFSOC_USP_MODE2_RXD_DELAY_OFFSET) | - (1 << SIRFSOC_USP_MODE2_TXD_DELAY_OFFSET)); - len_val |= ((clk_div_reg & SIRFSOC_USP_MODE2_CLK_DIVISOR_MASK) - << SIRFSOC_USP_MODE2_CLK_DIVISOR_OFFSET); - wr_regl(port, ureg->sirfsoc_mode2, len_val); - } - if (tty_termios_baud_rate(termios)) - tty_termios_encode_baud_rate(termios, set_baud, set_baud); - /* set receive timeout && data bits len */ - rx_time_out = SIRFSOC_UART_RX_TIMEOUT(set_baud, 20000); - rx_time_out = SIRFUART_RECV_TIMEOUT_VALUE(rx_time_out); - txfifo_op_reg = rd_regl(port, ureg->sirfsoc_tx_fifo_op); - wr_regl(port, ureg->sirfsoc_tx_fifo_op, - (txfifo_op_reg & ~SIRFUART_FIFO_START)); - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { - config_reg |= SIRFUART_UART_RECV_TIMEOUT(rx_time_out); - wr_regl(port, ureg->sirfsoc_line_ctrl, config_reg); - } else { - /*tx frame ctrl*/ - len_val = (data_bit_len - 1) << SIRFSOC_USP_TX_DATA_LEN_OFFSET; - len_val |= (data_bit_len + 1 + stop_bit_len - 1) << - SIRFSOC_USP_TX_FRAME_LEN_OFFSET; - len_val |= ((data_bit_len - 1) << - SIRFSOC_USP_TX_SHIFTER_LEN_OFFSET); - len_val |= (((clk_div_reg & 0xc00) >> 10) << - SIRFSOC_USP_TX_CLK_DIVISOR_OFFSET); - wr_regl(port, ureg->sirfsoc_tx_frame_ctrl, len_val); - /*rx frame ctrl*/ - len_val = (data_bit_len - 1) << SIRFSOC_USP_RX_DATA_LEN_OFFSET; - len_val |= (data_bit_len + 1 + stop_bit_len - 1) << - SIRFSOC_USP_RX_FRAME_LEN_OFFSET; - len_val |= (data_bit_len - 1) << - SIRFSOC_USP_RX_SHIFTER_LEN_OFFSET; - len_val |= (((clk_div_reg & 0xf000) >> 12) << - SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET); - wr_regl(port, ureg->sirfsoc_rx_frame_ctrl, len_val); - /*async param*/ - wr_regl(port, ureg->sirfsoc_async_param_reg, - (SIRFUART_USP_RECV_TIMEOUT(rx_time_out)) | - (sample_div_reg & SIRFSOC_USP_ASYNC_DIV2_MASK) << - SIRFSOC_USP_ASYNC_DIV2_OFFSET); - } - if (sirfport->tx_dma_chan) - wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_DMA_MODE); - else - wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_IO_MODE); - if (sirfport->rx_dma_chan) - wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) & - ~SIRFUART_IO_MODE); - else - wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | - SIRFUART_IO_MODE); - sirfport->rx_period_time = 20000000; - /* Reset Rx/Tx FIFO Threshold level for proper baudrate */ - if (set_baud < 1000000) - threshold_div = 1; - else - threshold_div = 2; - wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl, - SIRFUART_FIFO_THD(port) / threshold_div); - wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl, - SIRFUART_FIFO_THD(port) / threshold_div); - txfifo_op_reg |= SIRFUART_FIFO_START; - wr_regl(port, ureg->sirfsoc_tx_fifo_op, txfifo_op_reg); - uart_update_timeout(port, termios->c_cflag, set_baud); - wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_TX_EN | SIRFUART_RX_EN); - spin_unlock_irqrestore(&port->lock, flags); -} - -static void sirfsoc_uart_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - if (!state) - clk_prepare_enable(sirfport->clk); - else - clk_disable_unprepare(sirfport->clk); -} - -static int sirfsoc_uart_startup(struct uart_port *port) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - unsigned int index = port->line; - int ret; - irq_modify_status(port->irq, IRQ_NOREQUEST, IRQ_NOAUTOEN); - ret = request_irq(port->irq, - sirfsoc_uart_isr, - 0, - SIRFUART_PORT_NAME, - sirfport); - if (ret != 0) { - dev_err(port->dev, "UART%d request IRQ line (%d) failed.\n", - index, port->irq); - goto irq_err; - } - /* initial hardware settings */ - wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl) | - SIRFUART_IO_MODE); - wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | - SIRFUART_IO_MODE); - wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) & - ~SIRFUART_RX_DMA_FLUSH); - wr_regl(port, ureg->sirfsoc_tx_dma_io_len, 0); - wr_regl(port, ureg->sirfsoc_rx_dma_io_len, 0); - wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_RX_EN | SIRFUART_TX_EN); - if (sirfport->uart_reg->uart_type == SIRF_USP_UART) - wr_regl(port, ureg->sirfsoc_mode1, - SIRFSOC_USP_ENDIAN_CTRL_LSBF | - SIRFSOC_USP_EN); - wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_RESET); - wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); - wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); - wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl, SIRFUART_FIFO_THD(port)); - wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl, SIRFUART_FIFO_THD(port)); - if (sirfport->rx_dma_chan) - wr_regl(port, ureg->sirfsoc_rx_fifo_level_chk, - SIRFUART_RX_FIFO_CHK_SC(port->line, 0x1) | - SIRFUART_RX_FIFO_CHK_LC(port->line, 0x2) | - SIRFUART_RX_FIFO_CHK_HC(port->line, 0x4)); - if (sirfport->tx_dma_chan) { - sirfport->tx_dma_state = TX_DMA_IDLE; - wr_regl(port, ureg->sirfsoc_tx_fifo_level_chk, - SIRFUART_TX_FIFO_CHK_SC(port->line, 0x1b) | - SIRFUART_TX_FIFO_CHK_LC(port->line, 0xe) | - SIRFUART_TX_FIFO_CHK_HC(port->line, 0x4)); - } - sirfport->ms_enabled = false; - if (sirfport->uart_reg->uart_type == SIRF_USP_UART && - sirfport->hw_flow_ctrl) { - irq_modify_status(gpio_to_irq(sirfport->cts_gpio), - IRQ_NOREQUEST, IRQ_NOAUTOEN); - ret = request_irq(gpio_to_irq(sirfport->cts_gpio), - sirfsoc_uart_usp_cts_handler, IRQF_TRIGGER_FALLING | - IRQF_TRIGGER_RISING, "usp_cts_irq", sirfport); - if (ret != 0) { - dev_err(port->dev, "UART-USP:request gpio irq fail\n"); - goto init_rx_err; - } - } - if (sirfport->uart_reg->uart_type == SIRF_REAL_UART && - sirfport->rx_dma_chan) - wr_regl(port, ureg->sirfsoc_swh_dma_io, - SIRFUART_CLEAR_RX_ADDR_EN); - if (sirfport->uart_reg->uart_type == SIRF_USP_UART && - sirfport->rx_dma_chan) - wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | - SIRFSOC_USP_FRADDR_CLR_EN); - if (sirfport->rx_dma_chan && !sirfport->is_hrt_enabled) { - sirfport->is_hrt_enabled = true; - sirfport->rx_period_time = 20000000; - sirfport->rx_last_pos = -1; - sirfport->pio_fetch_cnt = 0; - sirfport->rx_dma_items.xmit.tail = - sirfport->rx_dma_items.xmit.head = 0; - hrtimer_start(&sirfport->hrt, - ns_to_ktime(sirfport->rx_period_time), - HRTIMER_MODE_REL); - } - wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START); - if (sirfport->rx_dma_chan) - sirfsoc_uart_start_next_rx_dma(port); - else { - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) | - SIRFUART_RX_IO_INT_EN(uint_en, - sirfport->uart_reg->uart_type)); - else - wr_regl(port, ureg->sirfsoc_int_en_reg, - SIRFUART_RX_IO_INT_EN(uint_en, - sirfport->uart_reg->uart_type)); - } - enable_irq(port->irq); - - return 0; -init_rx_err: - free_irq(port->irq, sirfport); -irq_err: - return ret; -} - -static void sirfsoc_uart_shutdown(struct uart_port *port) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct circ_buf *xmit; - - xmit = &sirfport->rx_dma_items.xmit; - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, 0); - else - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, ~0UL); - - free_irq(port->irq, sirfport); - if (sirfport->ms_enabled) - sirfsoc_uart_disable_ms(port); - if (sirfport->uart_reg->uart_type == SIRF_USP_UART && - sirfport->hw_flow_ctrl) { - gpio_set_value(sirfport->rts_gpio, 1); - free_irq(gpio_to_irq(sirfport->cts_gpio), sirfport); - } - if (sirfport->tx_dma_chan) - sirfport->tx_dma_state = TX_DMA_IDLE; - if (sirfport->rx_dma_chan && sirfport->is_hrt_enabled) { - while (((rd_regl(port, ureg->sirfsoc_rx_fifo_status) & - SIRFUART_RX_FIFO_MASK) > sirfport->pio_fetch_cnt) && - !CIRC_CNT(xmit->head, xmit->tail, - SIRFSOC_RX_DMA_BUF_SIZE)) - ; - sirfport->is_hrt_enabled = false; - hrtimer_cancel(&sirfport->hrt); - } -} - -static const char *sirfsoc_uart_type(struct uart_port *port) -{ - return port->type == SIRFSOC_PORT_TYPE ? SIRFUART_PORT_NAME : NULL; -} - -static int sirfsoc_uart_request_port(struct uart_port *port) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_uart_param *uart_param = &sirfport->uart_reg->uart_param; - void *ret; - ret = request_mem_region(port->mapbase, - SIRFUART_MAP_SIZE, uart_param->port_name); - return ret ? 0 : -EBUSY; -} - -static void sirfsoc_uart_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, SIRFUART_MAP_SIZE); -} - -static void sirfsoc_uart_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = SIRFSOC_PORT_TYPE; - sirfsoc_uart_request_port(port); - } -} - -static const struct uart_ops sirfsoc_uart_ops = { - .tx_empty = sirfsoc_uart_tx_empty, - .get_mctrl = sirfsoc_uart_get_mctrl, - .set_mctrl = sirfsoc_uart_set_mctrl, - .stop_tx = sirfsoc_uart_stop_tx, - .start_tx = sirfsoc_uart_start_tx, - .stop_rx = sirfsoc_uart_stop_rx, - .enable_ms = sirfsoc_uart_enable_ms, - .break_ctl = sirfsoc_uart_break_ctl, - .startup = sirfsoc_uart_startup, - .shutdown = sirfsoc_uart_shutdown, - .set_termios = sirfsoc_uart_set_termios, - .pm = sirfsoc_uart_pm, - .type = sirfsoc_uart_type, - .release_port = sirfsoc_uart_release_port, - .request_port = sirfsoc_uart_request_port, - .config_port = sirfsoc_uart_config_port, -}; - -#ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE -static int __init -sirfsoc_uart_console_setup(struct console *co, char *options) -{ - unsigned int baud = 115200; - unsigned int bits = 8; - unsigned int parity = 'n'; - unsigned int flow = 'n'; - struct sirfsoc_uart_port *sirfport; - struct sirfsoc_register *ureg; - if (co->index < 0 || co->index >= SIRFSOC_UART_NR) - co->index = 1; - sirfport = sirf_ports[co->index]; - if (!sirfport) - return -ENODEV; - ureg = &sirfport->uart_reg->uart_reg; - if (!sirfport->port.mapbase) - return -ENODEV; - - /* enable usp in mode1 register */ - if (sirfport->uart_reg->uart_type == SIRF_USP_UART) - wr_regl(&sirfport->port, ureg->sirfsoc_mode1, SIRFSOC_USP_EN | - SIRFSOC_USP_ENDIAN_CTRL_LSBF); - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - sirfport->port.cons = co; - - /* default console tx/rx transfer using io mode */ - sirfport->rx_dma_chan = NULL; - sirfport->tx_dma_chan = NULL; - return uart_set_options(&sirfport->port, co, baud, parity, bits, flow); -} - -static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; - while (rd_regl(port, ureg->sirfsoc_tx_fifo_status) & - ufifo_st->ff_full(port)) - cpu_relax(); - wr_regl(port, ureg->sirfsoc_tx_fifo_data, ch); -} - -static void sirfsoc_uart_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct sirfsoc_uart_port *sirfport = sirf_ports[co->index]; - - uart_console_write(&sirfport->port, s, count, - sirfsoc_uart_console_putchar); -} - -static struct console sirfsoc_uart_console = { - .name = SIRFSOC_UART_NAME, - .device = uart_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, - .write = sirfsoc_uart_console_write, - .setup = sirfsoc_uart_console_setup, - .data = &sirfsoc_uart_drv, -}; - -static int __init sirfsoc_uart_console_init(void) -{ - register_console(&sirfsoc_uart_console); - return 0; -} -console_initcall(sirfsoc_uart_console_init); -#endif - -static struct uart_driver sirfsoc_uart_drv = { - .owner = THIS_MODULE, - .driver_name = SIRFUART_PORT_NAME, - .nr = SIRFSOC_UART_NR, - .dev_name = SIRFSOC_UART_NAME, - .major = SIRFSOC_UART_MAJOR, - .minor = SIRFSOC_UART_MINOR, -#ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE - .cons = &sirfsoc_uart_console, -#else - .cons = NULL, -#endif -}; - -static enum hrtimer_restart - sirfsoc_uart_rx_dma_hrtimer_callback(struct hrtimer *hrt) -{ - struct sirfsoc_uart_port *sirfport; - struct uart_port *port; - int count, inserted; - struct dma_tx_state tx_state; - struct tty_struct *tty; - struct sirfsoc_register *ureg; - struct circ_buf *xmit; - struct sirfsoc_fifo_status *ufifo_st; - int max_pio_cnt; - - sirfport = container_of(hrt, struct sirfsoc_uart_port, hrt); - port = &sirfport->port; - inserted = 0; - tty = port->state->port.tty; - ureg = &sirfport->uart_reg->uart_reg; - xmit = &sirfport->rx_dma_items.xmit; - ufifo_st = &sirfport->uart_reg->fifo_status; - - dmaengine_tx_status(sirfport->rx_dma_chan, - sirfport->rx_dma_items.cookie, &tx_state); - if (SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue != - sirfport->rx_last_pos) { - xmit->head = SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue; - sirfport->rx_last_pos = xmit->head; - sirfport->pio_fetch_cnt = 0; - } - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, - SIRFSOC_RX_DMA_BUF_SIZE); - while (count > 0) { - inserted = tty_insert_flip_string(tty->port, - (const unsigned char *)&xmit->buf[xmit->tail], count); - if (!inserted) - goto next_hrt; - port->icount.rx += inserted; - xmit->tail = (xmit->tail + inserted) & - (SIRFSOC_RX_DMA_BUF_SIZE - 1); - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, - SIRFSOC_RX_DMA_BUF_SIZE); - tty_flip_buffer_push(tty->port); - } - /* - * if RX DMA buffer data have all push into tty buffer, and there is - * only little data(less than a dma transfer unit) left in rxfifo, - * fetch it out in pio mode and switch back to dma immediately - */ - if (!inserted && !count && - ((rd_regl(port, ureg->sirfsoc_rx_fifo_status) & - SIRFUART_RX_FIFO_MASK) > sirfport->pio_fetch_cnt)) { - dmaengine_pause(sirfport->rx_dma_chan); - /* switch to pio mode */ - wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | - SIRFUART_IO_MODE); - /* - * UART controller SWH_DMA_IO register have CLEAR_RX_ADDR_EN - * When found changing I/O to DMA mode, it clears - * two low bits of read point; - * USP have similar FRADDR_CLR_EN bit in USP_RX_DMA_IO_CTRL. - * Fetch data out from rxfifo into DMA buffer in PIO mode, - * while switch back to DMA mode, the data fetched will override - * by DMA, as hardware have a strange behaviour: - * after switch back to DMA mode, check rxfifo status it will - * be the number PIO fetched, so record the fetched data count - * to avoid the repeated fetch - */ - max_pio_cnt = 3; - while (!(rd_regl(port, ureg->sirfsoc_rx_fifo_status) & - ufifo_st->ff_empty(port)) && max_pio_cnt--) { - xmit->buf[xmit->head] = - rd_regl(port, ureg->sirfsoc_rx_fifo_data); - xmit->head = (xmit->head + 1) & - (SIRFSOC_RX_DMA_BUF_SIZE - 1); - sirfport->pio_fetch_cnt++; - } - /* switch back to dma mode */ - wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) & - ~SIRFUART_IO_MODE); - dmaengine_resume(sirfport->rx_dma_chan); - } -next_hrt: - hrtimer_forward_now(hrt, ns_to_ktime(sirfport->rx_period_time)); - return HRTIMER_RESTART; -} - -static const struct of_device_id sirfsoc_uart_ids[] = { - { .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,}, - { .compatible = "sirf,atlas7-uart", .data = &sirfsoc_uart}, - { .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp}, - { .compatible = "sirf,atlas7-usp-uart", .data = &sirfsoc_usp}, - {} -}; -MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids); - -static int sirfsoc_uart_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct sirfsoc_uart_port *sirfport; - struct uart_port *port; - struct resource *res; - int ret; - struct dma_slave_config slv_cfg = { - .src_maxburst = 1, - }; - struct dma_slave_config tx_slv_cfg = { - .dst_maxburst = 2, - }; - const struct of_device_id *match; - - match = of_match_node(sirfsoc_uart_ids, np); - sirfport = devm_kzalloc(&pdev->dev, sizeof(*sirfport), GFP_KERNEL); - if (!sirfport) { - ret = -ENOMEM; - goto err; - } - sirfport->port.line = of_alias_get_id(np, "serial"); - if (sirfport->port.line >= ARRAY_SIZE(sirf_ports)) { - dev_err(&pdev->dev, "serial%d out of range\n", - sirfport->port.line); - return -EINVAL; - } - sirf_ports[sirfport->port.line] = sirfport; - sirfport->port.iotype = UPIO_MEM; - sirfport->port.flags = UPF_BOOT_AUTOCONF; - port = &sirfport->port; - port->dev = &pdev->dev; - port->private_data = sirfport; - sirfport->uart_reg = (struct sirfsoc_uart_register *)match->data; - - sirfport->hw_flow_ctrl = - of_property_read_bool(np, "uart-has-rtscts") || - of_property_read_bool(np, "sirf,uart-has-rtscts") /* deprecated */; - if (of_device_is_compatible(np, "sirf,prima2-uart") || - of_device_is_compatible(np, "sirf,atlas7-uart")) - sirfport->uart_reg->uart_type = SIRF_REAL_UART; - if (of_device_is_compatible(np, "sirf,prima2-usp-uart") || - of_device_is_compatible(np, "sirf,atlas7-usp-uart")) { - sirfport->uart_reg->uart_type = SIRF_USP_UART; - if (!sirfport->hw_flow_ctrl) - goto usp_no_flow_control; - if (of_find_property(np, "cts-gpios", NULL)) - sirfport->cts_gpio = - of_get_named_gpio(np, "cts-gpios", 0); - else - sirfport->cts_gpio = -1; - if (of_find_property(np, "rts-gpios", NULL)) - sirfport->rts_gpio = - of_get_named_gpio(np, "rts-gpios", 0); - else - sirfport->rts_gpio = -1; - - if ((!gpio_is_valid(sirfport->cts_gpio) || - !gpio_is_valid(sirfport->rts_gpio))) { - ret = -EINVAL; - dev_err(&pdev->dev, - "Usp flow control must have cts and rts gpio"); - goto err; - } - ret = devm_gpio_request(&pdev->dev, sirfport->cts_gpio, - "usp-cts-gpio"); - if (ret) { - dev_err(&pdev->dev, "Unable request cts gpio"); - goto err; - } - gpio_direction_input(sirfport->cts_gpio); - ret = devm_gpio_request(&pdev->dev, sirfport->rts_gpio, - "usp-rts-gpio"); - if (ret) { - dev_err(&pdev->dev, "Unable request rts gpio"); - goto err; - } - gpio_direction_output(sirfport->rts_gpio, 1); - } -usp_no_flow_control: - if (of_device_is_compatible(np, "sirf,atlas7-uart") || - of_device_is_compatible(np, "sirf,atlas7-usp-uart")) - sirfport->is_atlas7 = true; - - if (of_property_read_u32(np, "fifosize", &port->fifosize)) { - dev_err(&pdev->dev, - "Unable to find fifosize in uart node.\n"); - ret = -EFAULT; - goto err; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "Insufficient resources.\n"); - ret = -EFAULT; - goto err; - } - port->mapbase = res->start; - port->membase = devm_ioremap(&pdev->dev, - res->start, resource_size(res)); - if (!port->membase) { - dev_err(&pdev->dev, "Cannot remap resource.\n"); - ret = -ENOMEM; - goto err; - } - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(&pdev->dev, "Insufficient resources.\n"); - ret = -EFAULT; - goto err; - } - port->irq = res->start; - - sirfport->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(sirfport->clk)) { - ret = PTR_ERR(sirfport->clk); - goto err; - } - port->uartclk = clk_get_rate(sirfport->clk); - - port->ops = &sirfsoc_uart_ops; - spin_lock_init(&port->lock); - - platform_set_drvdata(pdev, sirfport); - ret = uart_add_one_port(&sirfsoc_uart_drv, port); - if (ret != 0) { - dev_err(&pdev->dev, "Cannot add UART port(%d).\n", pdev->id); - goto err; - } - - sirfport->rx_dma_chan = dma_request_slave_channel(port->dev, "rx"); - sirfport->rx_dma_items.xmit.buf = - dma_alloc_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, - &sirfport->rx_dma_items.dma_addr, GFP_KERNEL); - if (!sirfport->rx_dma_items.xmit.buf) { - dev_err(port->dev, "Uart alloc bufa failed\n"); - ret = -ENOMEM; - goto alloc_coherent_err; - } - sirfport->rx_dma_items.xmit.head = - sirfport->rx_dma_items.xmit.tail = 0; - if (sirfport->rx_dma_chan) - dmaengine_slave_config(sirfport->rx_dma_chan, &slv_cfg); - sirfport->tx_dma_chan = dma_request_slave_channel(port->dev, "tx"); - if (sirfport->tx_dma_chan) - dmaengine_slave_config(sirfport->tx_dma_chan, &tx_slv_cfg); - if (sirfport->rx_dma_chan) { - hrtimer_init(&sirfport->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - sirfport->hrt.function = sirfsoc_uart_rx_dma_hrtimer_callback; - sirfport->is_hrt_enabled = false; - } - - return 0; -alloc_coherent_err: - dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, - sirfport->rx_dma_items.xmit.buf, - sirfport->rx_dma_items.dma_addr); - dma_release_channel(sirfport->rx_dma_chan); -err: - return ret; -} - -static int sirfsoc_uart_remove(struct platform_device *pdev) -{ - struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); - struct uart_port *port = &sirfport->port; - uart_remove_one_port(&sirfsoc_uart_drv, port); - if (sirfport->rx_dma_chan) { - dmaengine_terminate_all(sirfport->rx_dma_chan); - dma_release_channel(sirfport->rx_dma_chan); - dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, - sirfport->rx_dma_items.xmit.buf, - sirfport->rx_dma_items.dma_addr); - } - if (sirfport->tx_dma_chan) { - dmaengine_terminate_all(sirfport->tx_dma_chan); - dma_release_channel(sirfport->tx_dma_chan); - } - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int -sirfsoc_uart_suspend(struct device *pdev) -{ - struct sirfsoc_uart_port *sirfport = dev_get_drvdata(pdev); - struct uart_port *port = &sirfport->port; - uart_suspend_port(&sirfsoc_uart_drv, port); - return 0; -} - -static int sirfsoc_uart_resume(struct device *pdev) -{ - struct sirfsoc_uart_port *sirfport = dev_get_drvdata(pdev); - struct uart_port *port = &sirfport->port; - uart_resume_port(&sirfsoc_uart_drv, port); - return 0; -} -#endif - -static const struct dev_pm_ops sirfsoc_uart_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(sirfsoc_uart_suspend, sirfsoc_uart_resume) -}; - -static struct platform_driver sirfsoc_uart_driver = { - .probe = sirfsoc_uart_probe, - .remove = sirfsoc_uart_remove, - .driver = { - .name = SIRFUART_PORT_NAME, - .of_match_table = sirfsoc_uart_ids, - .pm = &sirfsoc_uart_pm_ops, - }, -}; - -static int __init sirfsoc_uart_init(void) -{ - int ret = 0; - - ret = uart_register_driver(&sirfsoc_uart_drv); - if (ret) - goto out; - - ret = platform_driver_register(&sirfsoc_uart_driver); - if (ret) - uart_unregister_driver(&sirfsoc_uart_drv); -out: - return ret; -} -module_init(sirfsoc_uart_init); - -static void __exit sirfsoc_uart_exit(void) -{ - platform_driver_unregister(&sirfsoc_uart_driver); - uart_unregister_driver(&sirfsoc_uart_drv); -} -module_exit(sirfsoc_uart_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Bin Shi , Rong Wang"); -MODULE_DESCRIPTION("CSR SiRFprimaII Uart Driver"); diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h deleted file mode 100644 index fb88ac565227..000000000000 --- a/drivers/tty/serial/sirfsoc_uart.h +++ /dev/null @@ -1,447 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Drivers for CSR SiRFprimaII onboard UARTs. - * - * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. - */ -#include -#include -#include -struct sirfsoc_uart_param { - const char *uart_name; - const char *port_name; -}; - -struct sirfsoc_register { - /* hardware uart specific */ - u32 sirfsoc_line_ctrl; - u32 sirfsoc_divisor; - /* uart - usp common */ - u32 sirfsoc_tx_rx_en; - u32 sirfsoc_int_en_reg; - u32 sirfsoc_int_st_reg; - u32 sirfsoc_int_en_clr_reg; - u32 sirfsoc_tx_dma_io_ctrl; - u32 sirfsoc_tx_dma_io_len; - u32 sirfsoc_tx_fifo_ctrl; - u32 sirfsoc_tx_fifo_level_chk; - u32 sirfsoc_tx_fifo_op; - u32 sirfsoc_tx_fifo_status; - u32 sirfsoc_tx_fifo_data; - u32 sirfsoc_rx_dma_io_ctrl; - u32 sirfsoc_rx_dma_io_len; - u32 sirfsoc_rx_fifo_ctrl; - u32 sirfsoc_rx_fifo_level_chk; - u32 sirfsoc_rx_fifo_op; - u32 sirfsoc_rx_fifo_status; - u32 sirfsoc_rx_fifo_data; - u32 sirfsoc_afc_ctrl; - u32 sirfsoc_swh_dma_io; - /* hardware usp specific */ - u32 sirfsoc_mode1; - u32 sirfsoc_mode2; - u32 sirfsoc_tx_frame_ctrl; - u32 sirfsoc_rx_frame_ctrl; - u32 sirfsoc_async_param_reg; -}; - -typedef u32 (*fifo_full_mask)(struct uart_port *port); -typedef u32 (*fifo_empty_mask)(struct uart_port *port); - -struct sirfsoc_fifo_status { - fifo_full_mask ff_full; - fifo_empty_mask ff_empty; -}; - -struct sirfsoc_int_en { - u32 sirfsoc_rx_done_en; - u32 sirfsoc_tx_done_en; - u32 sirfsoc_rx_oflow_en; - u32 sirfsoc_tx_allout_en; - u32 sirfsoc_rx_io_dma_en; - u32 sirfsoc_tx_io_dma_en; - u32 sirfsoc_rxfifo_full_en; - u32 sirfsoc_txfifo_empty_en; - u32 sirfsoc_rxfifo_thd_en; - u32 sirfsoc_txfifo_thd_en; - u32 sirfsoc_frm_err_en; - u32 sirfsoc_rxd_brk_en; - u32 sirfsoc_rx_timeout_en; - u32 sirfsoc_parity_err_en; - u32 sirfsoc_cts_en; - u32 sirfsoc_rts_en; -}; - -struct sirfsoc_int_status { - u32 sirfsoc_rx_done; - u32 sirfsoc_tx_done; - u32 sirfsoc_rx_oflow; - u32 sirfsoc_tx_allout; - u32 sirfsoc_rx_io_dma; - u32 sirfsoc_tx_io_dma; - u32 sirfsoc_rxfifo_full; - u32 sirfsoc_txfifo_empty; - u32 sirfsoc_rxfifo_thd; - u32 sirfsoc_txfifo_thd; - u32 sirfsoc_frm_err; - u32 sirfsoc_rxd_brk; - u32 sirfsoc_rx_timeout; - u32 sirfsoc_parity_err; - u32 sirfsoc_cts; - u32 sirfsoc_rts; -}; - -enum sirfsoc_uart_type { - SIRF_REAL_UART, - SIRF_USP_UART, -}; - -struct sirfsoc_uart_register { - struct sirfsoc_register uart_reg; - struct sirfsoc_int_en uart_int_en; - struct sirfsoc_int_status uart_int_st; - struct sirfsoc_fifo_status fifo_status; - struct sirfsoc_uart_param uart_param; - enum sirfsoc_uart_type uart_type; -}; - -static u32 uart_usp_ff_full_mask(struct uart_port *port) -{ - u32 full_bit; - - full_bit = ilog2(port->fifosize); - return (1 << full_bit); -} - -static u32 uart_usp_ff_empty_mask(struct uart_port *port) -{ - u32 empty_bit; - - empty_bit = ilog2(port->fifosize) + 1; - return (1 << empty_bit); -} - -static struct sirfsoc_uart_register sirfsoc_usp = { - .uart_reg = { - .sirfsoc_mode1 = 0x0000, - .sirfsoc_mode2 = 0x0004, - .sirfsoc_tx_frame_ctrl = 0x0008, - .sirfsoc_rx_frame_ctrl = 0x000c, - .sirfsoc_tx_rx_en = 0x0010, - .sirfsoc_int_en_reg = 0x0014, - .sirfsoc_int_st_reg = 0x0018, - .sirfsoc_async_param_reg = 0x0024, - .sirfsoc_tx_dma_io_ctrl = 0x0100, - .sirfsoc_tx_dma_io_len = 0x0104, - .sirfsoc_tx_fifo_ctrl = 0x0108, - .sirfsoc_tx_fifo_level_chk = 0x010c, - .sirfsoc_tx_fifo_op = 0x0110, - .sirfsoc_tx_fifo_status = 0x0114, - .sirfsoc_tx_fifo_data = 0x0118, - .sirfsoc_rx_dma_io_ctrl = 0x0120, - .sirfsoc_rx_dma_io_len = 0x0124, - .sirfsoc_rx_fifo_ctrl = 0x0128, - .sirfsoc_rx_fifo_level_chk = 0x012c, - .sirfsoc_rx_fifo_op = 0x0130, - .sirfsoc_rx_fifo_status = 0x0134, - .sirfsoc_rx_fifo_data = 0x0138, - .sirfsoc_int_en_clr_reg = 0x140, - }, - .uart_int_en = { - .sirfsoc_rx_done_en = BIT(0), - .sirfsoc_tx_done_en = BIT(1), - .sirfsoc_rx_oflow_en = BIT(2), - .sirfsoc_tx_allout_en = BIT(3), - .sirfsoc_rx_io_dma_en = BIT(4), - .sirfsoc_tx_io_dma_en = BIT(5), - .sirfsoc_rxfifo_full_en = BIT(6), - .sirfsoc_txfifo_empty_en = BIT(7), - .sirfsoc_rxfifo_thd_en = BIT(8), - .sirfsoc_txfifo_thd_en = BIT(9), - .sirfsoc_frm_err_en = BIT(10), - .sirfsoc_rx_timeout_en = BIT(11), - .sirfsoc_rxd_brk_en = BIT(15), - }, - .uart_int_st = { - .sirfsoc_rx_done = BIT(0), - .sirfsoc_tx_done = BIT(1), - .sirfsoc_rx_oflow = BIT(2), - .sirfsoc_tx_allout = BIT(3), - .sirfsoc_rx_io_dma = BIT(4), - .sirfsoc_tx_io_dma = BIT(5), - .sirfsoc_rxfifo_full = BIT(6), - .sirfsoc_txfifo_empty = BIT(7), - .sirfsoc_rxfifo_thd = BIT(8), - .sirfsoc_txfifo_thd = BIT(9), - .sirfsoc_frm_err = BIT(10), - .sirfsoc_rx_timeout = BIT(11), - .sirfsoc_rxd_brk = BIT(15), - }, - .fifo_status = { - .ff_full = uart_usp_ff_full_mask, - .ff_empty = uart_usp_ff_empty_mask, - }, - .uart_param = { - .uart_name = "ttySiRF", - .port_name = "sirfsoc-uart", - }, -}; - -static struct sirfsoc_uart_register sirfsoc_uart = { - .uart_reg = { - .sirfsoc_line_ctrl = 0x0040, - .sirfsoc_tx_rx_en = 0x004c, - .sirfsoc_divisor = 0x0050, - .sirfsoc_int_en_reg = 0x0054, - .sirfsoc_int_st_reg = 0x0058, - .sirfsoc_int_en_clr_reg = 0x0060, - .sirfsoc_tx_dma_io_ctrl = 0x0100, - .sirfsoc_tx_dma_io_len = 0x0104, - .sirfsoc_tx_fifo_ctrl = 0x0108, - .sirfsoc_tx_fifo_level_chk = 0x010c, - .sirfsoc_tx_fifo_op = 0x0110, - .sirfsoc_tx_fifo_status = 0x0114, - .sirfsoc_tx_fifo_data = 0x0118, - .sirfsoc_rx_dma_io_ctrl = 0x0120, - .sirfsoc_rx_dma_io_len = 0x0124, - .sirfsoc_rx_fifo_ctrl = 0x0128, - .sirfsoc_rx_fifo_level_chk = 0x012c, - .sirfsoc_rx_fifo_op = 0x0130, - .sirfsoc_rx_fifo_status = 0x0134, - .sirfsoc_rx_fifo_data = 0x0138, - .sirfsoc_afc_ctrl = 0x0140, - .sirfsoc_swh_dma_io = 0x0148, - }, - .uart_int_en = { - .sirfsoc_rx_done_en = BIT(0), - .sirfsoc_tx_done_en = BIT(1), - .sirfsoc_rx_oflow_en = BIT(2), - .sirfsoc_tx_allout_en = BIT(3), - .sirfsoc_rx_io_dma_en = BIT(4), - .sirfsoc_tx_io_dma_en = BIT(5), - .sirfsoc_rxfifo_full_en = BIT(6), - .sirfsoc_txfifo_empty_en = BIT(7), - .sirfsoc_rxfifo_thd_en = BIT(8), - .sirfsoc_txfifo_thd_en = BIT(9), - .sirfsoc_frm_err_en = BIT(10), - .sirfsoc_rxd_brk_en = BIT(11), - .sirfsoc_rx_timeout_en = BIT(12), - .sirfsoc_parity_err_en = BIT(13), - .sirfsoc_cts_en = BIT(14), - .sirfsoc_rts_en = BIT(15), - }, - .uart_int_st = { - .sirfsoc_rx_done = BIT(0), - .sirfsoc_tx_done = BIT(1), - .sirfsoc_rx_oflow = BIT(2), - .sirfsoc_tx_allout = BIT(3), - .sirfsoc_rx_io_dma = BIT(4), - .sirfsoc_tx_io_dma = BIT(5), - .sirfsoc_rxfifo_full = BIT(6), - .sirfsoc_txfifo_empty = BIT(7), - .sirfsoc_rxfifo_thd = BIT(8), - .sirfsoc_txfifo_thd = BIT(9), - .sirfsoc_frm_err = BIT(10), - .sirfsoc_rxd_brk = BIT(11), - .sirfsoc_rx_timeout = BIT(12), - .sirfsoc_parity_err = BIT(13), - .sirfsoc_cts = BIT(14), - .sirfsoc_rts = BIT(15), - }, - .fifo_status = { - .ff_full = uart_usp_ff_full_mask, - .ff_empty = uart_usp_ff_empty_mask, - }, - .uart_param = { - .uart_name = "ttySiRF", - .port_name = "sirfsoc_uart", - }, -}; -/* uart io ctrl */ -#define SIRFUART_DATA_BIT_LEN_MASK 0x3 -#define SIRFUART_DATA_BIT_LEN_5 BIT(0) -#define SIRFUART_DATA_BIT_LEN_6 1 -#define SIRFUART_DATA_BIT_LEN_7 2 -#define SIRFUART_DATA_BIT_LEN_8 3 -#define SIRFUART_STOP_BIT_LEN_1 0 -#define SIRFUART_STOP_BIT_LEN_2 BIT(2) -#define SIRFUART_PARITY_EN BIT(3) -#define SIRFUART_EVEN_BIT BIT(4) -#define SIRFUART_STICK_BIT_MASK (7 << 3) -#define SIRFUART_STICK_BIT_NONE (0 << 3) -#define SIRFUART_STICK_BIT_EVEN BIT(3) -#define SIRFUART_STICK_BIT_ODD (3 << 3) -#define SIRFUART_STICK_BIT_MARK (5 << 3) -#define SIRFUART_STICK_BIT_SPACE (7 << 3) -#define SIRFUART_SET_BREAK BIT(6) -#define SIRFUART_LOOP_BACK BIT(7) -#define SIRFUART_PARITY_MASK (7 << 3) -#define SIRFUART_DUMMY_READ BIT(16) -#define SIRFUART_AFC_CTRL_RX_THD 0x70 -#define SIRFUART_AFC_RX_EN BIT(8) -#define SIRFUART_AFC_TX_EN BIT(9) -#define SIRFUART_AFC_CTS_CTRL BIT(10) -#define SIRFUART_AFC_RTS_CTRL BIT(11) -#define SIRFUART_AFC_CTS_STATUS BIT(12) -#define SIRFUART_AFC_RTS_STATUS BIT(13) -/* UART FIFO Register */ -#define SIRFUART_FIFO_STOP 0x0 -#define SIRFUART_FIFO_RESET BIT(0) -#define SIRFUART_FIFO_START BIT(1) - -#define SIRFUART_RX_EN BIT(0) -#define SIRFUART_TX_EN BIT(1) - -#define SIRFUART_IO_MODE BIT(0) -#define SIRFUART_DMA_MODE 0x0 -#define SIRFUART_RX_DMA_FLUSH 0x4 - -#define SIRFUART_CLEAR_RX_ADDR_EN 0x2 -/* Baud Rate Calculation */ -#define SIRF_USP_MIN_SAMPLE_DIV 0x1 -#define SIRF_MIN_SAMPLE_DIV 0xf -#define SIRF_MAX_SAMPLE_DIV 0x3f -#define SIRF_IOCLK_DIV_MAX 0xffff -#define SIRF_SAMPLE_DIV_SHIFT 16 -#define SIRF_IOCLK_DIV_MASK 0xffff -#define SIRF_SAMPLE_DIV_MASK 0x3f0000 -#define SIRF_BAUD_RATE_SUPPORT_NR 18 - -/* USP SPEC */ -#define SIRFSOC_USP_ENDIAN_CTRL_LSBF BIT(4) -#define SIRFSOC_USP_EN BIT(5) -#define SIRFSOC_USP_MODE2_RXD_DELAY_OFFSET 0 -#define SIRFSOC_USP_MODE2_TXD_DELAY_OFFSET 8 -#define SIRFSOC_USP_MODE2_CLK_DIVISOR_MASK 0x3ff -#define SIRFSOC_USP_MODE2_CLK_DIVISOR_OFFSET 21 -#define SIRFSOC_USP_TX_DATA_LEN_OFFSET 0 -#define SIRFSOC_USP_TX_SYNC_LEN_OFFSET 8 -#define SIRFSOC_USP_TX_FRAME_LEN_OFFSET 16 -#define SIRFSOC_USP_TX_SHIFTER_LEN_OFFSET 24 -#define SIRFSOC_USP_TX_CLK_DIVISOR_OFFSET 30 -#define SIRFSOC_USP_RX_DATA_LEN_OFFSET 0 -#define SIRFSOC_USP_RX_FRAME_LEN_OFFSET 8 -#define SIRFSOC_USP_RX_SHIFTER_LEN_OFFSET 16 -#define SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET 24 -#define SIRFSOC_USP_ASYNC_DIV2_MASK 0x3f -#define SIRFSOC_USP_ASYNC_DIV2_OFFSET 16 -#define SIRFSOC_USP_LOOP_BACK_CTRL BIT(2) -#define SIRFSOC_USP_FRADDR_CLR_EN BIT(1) -/* USP-UART Common */ -#define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000) -#define SIRFUART_RECV_TIMEOUT_VALUE(x) \ - (((x) > 0xFFFF) ? 0xFFFF : ((x) & 0xFFFF)) -#define SIRFUART_USP_RECV_TIMEOUT(x) (x & 0xFFFF) -#define SIRFUART_UART_RECV_TIMEOUT(x) ((x & 0xFFFF) << 16) - -#define SIRFUART_FIFO_THD(port) (port->fifosize >> 1) -#define SIRFUART_ERR_INT_STAT(unit_st, uart_type) \ - (uint_st->sirfsoc_rx_oflow | \ - uint_st->sirfsoc_frm_err | \ - uint_st->sirfsoc_rxd_brk | \ - ((uart_type != SIRF_REAL_UART) ? \ - 0 : uint_st->sirfsoc_parity_err)) -#define SIRFUART_RX_IO_INT_EN(uint_en, uart_type) \ - (uint_en->sirfsoc_rx_done_en |\ - uint_en->sirfsoc_rxfifo_thd_en |\ - uint_en->sirfsoc_rxfifo_full_en |\ - uint_en->sirfsoc_frm_err_en |\ - uint_en->sirfsoc_rx_oflow_en |\ - uint_en->sirfsoc_rxd_brk_en |\ - ((uart_type != SIRF_REAL_UART) ? \ - 0 : uint_en->sirfsoc_parity_err_en)) -#define SIRFUART_RX_IO_INT_ST(uint_st) \ - (uint_st->sirfsoc_rxfifo_thd |\ - uint_st->sirfsoc_rxfifo_full|\ - uint_st->sirfsoc_rx_done |\ - uint_st->sirfsoc_rx_timeout) -#define SIRFUART_CTS_INT_ST(uint_st) (uint_st->sirfsoc_cts) -#define SIRFUART_RX_DMA_INT_EN(uint_en, uart_type) \ - (uint_en->sirfsoc_frm_err_en |\ - uint_en->sirfsoc_rx_oflow_en |\ - uint_en->sirfsoc_rxd_brk_en |\ - ((uart_type != SIRF_REAL_UART) ? \ - 0 : uint_en->sirfsoc_parity_err_en)) -/* Generic Definitions */ -#define SIRFSOC_UART_NAME "ttySiRF" -#define SIRFSOC_UART_MAJOR 0 -#define SIRFSOC_UART_MINOR 0 -#define SIRFUART_PORT_NAME "sirfsoc-uart" -#define SIRFUART_MAP_SIZE 0x200 -#define SIRFSOC_UART_NR 11 -#define SIRFSOC_PORT_TYPE 0xa5 - -/* Uart Common Use Macro*/ -#define SIRFSOC_RX_DMA_BUF_SIZE (1024 * 32) -#define BYTES_TO_ALIGN(dma_addr) ((unsigned long)(dma_addr) & 0x3) -/* Uart Fifo Level Chk */ -#define SIRFUART_TX_FIFO_SC_OFFSET 0 -#define SIRFUART_TX_FIFO_LC_OFFSET 10 -#define SIRFUART_TX_FIFO_HC_OFFSET 20 -#define SIRFUART_TX_FIFO_CHK_SC(line, value) ((((line) == 1) ? (value & 0x3) :\ - (value & 0x1f)) << SIRFUART_TX_FIFO_SC_OFFSET) -#define SIRFUART_TX_FIFO_CHK_LC(line, value) ((((line) == 1) ? (value & 0x3) :\ - (value & 0x1f)) << SIRFUART_TX_FIFO_LC_OFFSET) -#define SIRFUART_TX_FIFO_CHK_HC(line, value) ((((line) == 1) ? (value & 0x3) :\ - (value & 0x1f)) << SIRFUART_TX_FIFO_HC_OFFSET) - -#define SIRFUART_RX_FIFO_CHK_SC SIRFUART_TX_FIFO_CHK_SC -#define SIRFUART_RX_FIFO_CHK_LC SIRFUART_TX_FIFO_CHK_LC -#define SIRFUART_RX_FIFO_CHK_HC SIRFUART_TX_FIFO_CHK_HC -#define SIRFUART_RX_FIFO_MASK 0x7f -/* Indicate how many buffers used */ - -/* For Fast Baud Rate Calculation */ -struct sirfsoc_baudrate_to_regv { - unsigned int baud_rate; - unsigned int reg_val; -}; - -enum sirfsoc_tx_state { - TX_DMA_IDLE, - TX_DMA_RUNNING, - TX_DMA_PAUSE, -}; - -struct sirfsoc_rx_buffer { - struct circ_buf xmit; - dma_cookie_t cookie; - struct dma_async_tx_descriptor *desc; - dma_addr_t dma_addr; -}; - -struct sirfsoc_uart_port { - bool hw_flow_ctrl; - bool ms_enabled; - - struct uart_port port; - struct clk *clk; - /* for SiRFatlas7, there are SET/CLR for UART_INT_EN */ - bool is_atlas7; - struct sirfsoc_uart_register *uart_reg; - struct dma_chan *rx_dma_chan; - struct dma_chan *tx_dma_chan; - dma_addr_t tx_dma_addr; - struct dma_async_tx_descriptor *tx_dma_desc; - unsigned long transfer_size; - enum sirfsoc_tx_state tx_dma_state; - unsigned int cts_gpio; - unsigned int rts_gpio; - - struct sirfsoc_rx_buffer rx_dma_items; - struct hrtimer hrt; - bool is_hrt_enabled; - unsigned long rx_period_time; - unsigned long rx_last_pos; - unsigned long pio_fetch_cnt; -}; - -/* Register Access Control */ -#define portaddr(port, reg) ((port)->membase + (reg)) -#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) -#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) - -/* UART Port Mask */ -#define SIRFUART_FIFOLEVEL_MASK(port) ((port->fifosize - 1) & 0xFFF) -#define SIRFUART_FIFOFULL_MASK(port) (port->fifosize & 0xFFF) -#define SIRFUART_FIFOEMPTY_MASK(port) ((port->fifosize & 0xFFF) << 1) -- cgit From 3b830a9c34d5897be07176ce4e6f2d75e2c8cfd7 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 18 Jan 2021 13:31:30 -0800 Subject: tty: convert tty_ldisc_ops 'read()' function to take a kernel pointer The tty line discipline .read() function was passed the final user pointer destination as an argument, which doesn't match the 'write()' function, and makes it very inconvenient to do a splice method for ttys. This is a conversion to use a kernel buffer instead. NOTE! It does this by passing the tty line discipline ->read() function an additional "cookie" to fill in, and an offset into the cookie data. The line discipline can fill in the cookie data with its own private information, and then the reader will repeat the read until either the cookie is cleared or it runs out of data. The only real user of this is N_HDLC, which can use this to handle big packets, even if the kernel buffer is smaller than the whole packet. Cc: Christoph Hellwig Cc: Greg Kroah-Hartman Cc: Al Viro Signed-off-by: Linus Torvalds --- drivers/bluetooth/hci_ldisc.c | 34 +++++++++--------- drivers/input/serio/serport.c | 4 ++- drivers/net/ppp/ppp_async.c | 3 +- drivers/net/ppp/ppp_synctty.c | 3 +- drivers/tty/n_gsm.c | 3 +- drivers/tty/n_hdlc.c | 60 +++++++++++++++++++++---------- drivers/tty/n_null.c | 3 +- drivers/tty/n_r3964.c | 10 +++--- drivers/tty/n_tracerouter.c | 4 ++- drivers/tty/n_tracesink.c | 4 ++- drivers/tty/n_tty.c | 82 ++++++++++++++++++------------------------- drivers/tty/tty_io.c | 64 +++++++++++++++++++++++++++++++-- 12 files changed, 174 insertions(+), 100 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index f83d67eafc9f..dd92aea15b8b 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -802,7 +802,8 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file, * We don't provide read/write/poll interface for user space. */ static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t nr) + unsigned char *buf, size_t nr, + void **cookie, unsigned long offset) { return 0; } @@ -819,29 +820,28 @@ static __poll_t hci_uart_tty_poll(struct tty_struct *tty, return 0; } +static struct tty_ldisc_ops hci_uart_ldisc = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "n_hci", + .open = hci_uart_tty_open, + .close = hci_uart_tty_close, + .read = hci_uart_tty_read, + .write = hci_uart_tty_write, + .ioctl = hci_uart_tty_ioctl, + .compat_ioctl = hci_uart_tty_ioctl, + .poll = hci_uart_tty_poll, + .receive_buf = hci_uart_tty_receive, + .write_wakeup = hci_uart_tty_wakeup, +}; + static int __init hci_uart_init(void) { - static struct tty_ldisc_ops hci_uart_ldisc; int err; BT_INFO("HCI UART driver ver %s", VERSION); /* Register the tty discipline */ - - memset(&hci_uart_ldisc, 0, sizeof(hci_uart_ldisc)); - hci_uart_ldisc.magic = TTY_LDISC_MAGIC; - hci_uart_ldisc.name = "n_hci"; - hci_uart_ldisc.open = hci_uart_tty_open; - hci_uart_ldisc.close = hci_uart_tty_close; - hci_uart_ldisc.read = hci_uart_tty_read; - hci_uart_ldisc.write = hci_uart_tty_write; - hci_uart_ldisc.ioctl = hci_uart_tty_ioctl; - hci_uart_ldisc.compat_ioctl = hci_uart_tty_ioctl; - hci_uart_ldisc.poll = hci_uart_tty_poll; - hci_uart_ldisc.receive_buf = hci_uart_tty_receive; - hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup; - hci_uart_ldisc.owner = THIS_MODULE; - err = tty_register_ldisc(N_HCI, &hci_uart_ldisc); if (err) { BT_ERR("HCI line discipline registration failed. (%d)", err); diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 8ac970a423de..33e9d9bfd036 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -156,7 +156,9 @@ out: * returning 0 characters. */ -static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char __user * buf, size_t nr) +static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, + unsigned char *kbuf, size_t nr, + void **cookie, unsigned long offset) { struct serport *serport = (struct serport*) tty->disc_data; struct serio *serio; diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c index 29a0917a81e6..f14a9d190de9 100644 --- a/drivers/net/ppp/ppp_async.c +++ b/drivers/net/ppp/ppp_async.c @@ -259,7 +259,8 @@ static int ppp_asynctty_hangup(struct tty_struct *tty) */ static ssize_t ppp_asynctty_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t count) + unsigned char *buf, size_t count, + void **cookie, unsigned long offset) { return -EAGAIN; } diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c index 0f338752c38b..f774b7e52da4 100644 --- a/drivers/net/ppp/ppp_synctty.c +++ b/drivers/net/ppp/ppp_synctty.c @@ -257,7 +257,8 @@ static int ppp_sync_hangup(struct tty_struct *tty) */ static ssize_t ppp_sync_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t count) + unsigned char *buf, size_t count, + void **cookie, unsigned long offset) { return -EAGAIN; } diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 25f3152089c2..fea1eeac5b90 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2557,7 +2557,8 @@ static void gsmld_write_wakeup(struct tty_struct *tty) */ static ssize_t gsmld_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t nr) + unsigned char *buf, size_t nr, + void **cookie, unsigned long offset) { return -EOPNOTSUPP; } diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 12557ee1edb6..1363e659dc1d 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -416,13 +416,19 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, * Returns the number of bytes returned or error code. */ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, - __u8 __user *buf, size_t nr) + __u8 *kbuf, size_t nr, + void **cookie, unsigned long offset) { struct n_hdlc *n_hdlc = tty->disc_data; int ret = 0; struct n_hdlc_buf *rbuf; DECLARE_WAITQUEUE(wait, current); + /* Is this a repeated call for an rbuf we already found earlier? */ + rbuf = *cookie; + if (rbuf) + goto have_rbuf; + add_wait_queue(&tty->read_wait, &wait); for (;;) { @@ -436,25 +442,8 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, set_current_state(TASK_INTERRUPTIBLE); rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); - if (rbuf) { - if (rbuf->count > nr) { - /* too large for caller's buffer */ - ret = -EOVERFLOW; - } else { - __set_current_state(TASK_RUNNING); - if (copy_to_user(buf, rbuf->buf, rbuf->count)) - ret = -EFAULT; - else - ret = rbuf->count; - } - - if (n_hdlc->rx_free_buf_list.count > - DEFAULT_RX_BUF_COUNT) - kfree(rbuf); - else - n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, rbuf); + if (rbuf) break; - } /* no data */ if (tty_io_nonblock(tty, file)) { @@ -473,6 +462,39 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, remove_wait_queue(&tty->read_wait, &wait); __set_current_state(TASK_RUNNING); + if (!rbuf) + return ret; + *cookie = rbuf; + +have_rbuf: + /* Have we used it up entirely? */ + if (offset >= rbuf->count) + goto done_with_rbuf; + + /* More data to go, but can't copy any more? EOVERFLOW */ + ret = -EOVERFLOW; + if (!nr) + goto done_with_rbuf; + + /* Copy as much data as possible */ + ret = rbuf->count - offset; + if (ret > nr) + ret = nr; + memcpy(kbuf, rbuf->buf+offset, ret); + offset += ret; + + /* If we still have data left, we leave the rbuf in the cookie */ + if (offset < rbuf->count) + return ret; + +done_with_rbuf: + *cookie = NULL; + + if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT) + kfree(rbuf); + else + n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, rbuf); + return ret; } /* end of n_hdlc_tty_read() */ diff --git a/drivers/tty/n_null.c b/drivers/tty/n_null.c index 96feabae4740..ce03ae78f5c6 100644 --- a/drivers/tty/n_null.c +++ b/drivers/tty/n_null.c @@ -20,7 +20,8 @@ static void n_null_close(struct tty_struct *tty) } static ssize_t n_null_read(struct tty_struct *tty, struct file *file, - unsigned char __user * buf, size_t nr) + unsigned char *buf, size_t nr, + void **cookie, unsigned long offset) { return -EOPNOTSUPP; } diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 934dd2fb2ec8..3161f0a535e3 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -129,7 +129,7 @@ static void remove_client_block(struct r3964_info *pInfo, static int r3964_open(struct tty_struct *tty); static void r3964_close(struct tty_struct *tty); static ssize_t r3964_read(struct tty_struct *tty, struct file *file, - unsigned char __user * buf, size_t nr); + void *cookie, unsigned char *buf, size_t nr); static ssize_t r3964_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr); static int r3964_ioctl(struct tty_struct *tty, struct file *file, @@ -1058,7 +1058,8 @@ static void r3964_close(struct tty_struct *tty) } static ssize_t r3964_read(struct tty_struct *tty, struct file *file, - unsigned char __user * buf, size_t nr) + unsigned char *kbuf, size_t nr, + void **cookie, unsigned long offset) { struct r3964_info *pInfo = tty->disc_data; struct r3964_client_info *pClient; @@ -1109,10 +1110,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, kfree(pMsg); TRACE_M("r3964_read - msg kfree %p", pMsg); - if (copy_to_user(buf, &theMsg, ret)) { - ret = -EFAULT; - goto unlock; - } + memcpy(kbuf, &theMsg, ret); TRACE_PS("read - return %d", ret); goto unlock; diff --git a/drivers/tty/n_tracerouter.c b/drivers/tty/n_tracerouter.c index 4479af4d2fa5..3490ed51b1a3 100644 --- a/drivers/tty/n_tracerouter.c +++ b/drivers/tty/n_tracerouter.c @@ -118,7 +118,9 @@ static void n_tracerouter_close(struct tty_struct *tty) * -EINVAL */ static ssize_t n_tracerouter_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t nr) { + unsigned char *buf, size_t nr, + void **cookie, unsigned long offset) +{ return -EINVAL; } diff --git a/drivers/tty/n_tracesink.c b/drivers/tty/n_tracesink.c index d96ba82cc356..1d9931041fd8 100644 --- a/drivers/tty/n_tracesink.c +++ b/drivers/tty/n_tracesink.c @@ -115,7 +115,9 @@ static void n_tracesink_close(struct tty_struct *tty) * -EINVAL */ static ssize_t n_tracesink_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t nr) { + unsigned char *buf, size_t nr, + void **cookie, unsigned long offset) +{ return -EINVAL; } diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 7e5e36315260..4a34a9f43b29 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -164,29 +164,24 @@ static void zero_buffer(struct tty_struct *tty, u8 *buffer, int size) memset(buffer, 0x00, size); } -static int tty_copy_to_user(struct tty_struct *tty, void __user *to, - size_t tail, size_t n) +static void tty_copy(struct tty_struct *tty, void *to, size_t tail, size_t n) { struct n_tty_data *ldata = tty->disc_data; size_t size = N_TTY_BUF_SIZE - tail; void *from = read_buf_addr(ldata, tail); - int uncopied; if (n > size) { tty_audit_add_data(tty, from, size); - uncopied = copy_to_user(to, from, size); - zero_buffer(tty, from, size - uncopied); - if (uncopied) - return uncopied; + memcpy(to, from, size); + zero_buffer(tty, from, size); to += size; n -= size; from = ldata->read_buf; } tty_audit_add_data(tty, from, n); - uncopied = copy_to_user(to, from, n); - zero_buffer(tty, from, n - uncopied); - return uncopied; + memcpy(to, from, n); + zero_buffer(tty, from, n); } /** @@ -1942,15 +1937,16 @@ static inline int input_available_p(struct tty_struct *tty, int poll) /** * copy_from_read_buf - copy read data directly * @tty: terminal device - * @b: user data + * @kbp: data * @nr: size of data * * Helper function to speed up n_tty_read. It is only called when - * ICANON is off; it copies characters straight from the tty queue to - * user space directly. It can be profitably called twice; once to - * drain the space from the tail pointer to the (physical) end of the - * buffer, and once to drain the space from the (physical) beginning of - * the buffer to head pointer. + * ICANON is off; it copies characters straight from the tty queue. + * + * It can be profitably called twice; once to drain the space from + * the tail pointer to the (physical) end of the buffer, and once + * to drain the space from the (physical) beginning of the buffer + * to head pointer. * * Called under the ldata->atomic_read_lock sem * @@ -1960,7 +1956,7 @@ static inline int input_available_p(struct tty_struct *tty, int poll) */ static int copy_from_read_buf(struct tty_struct *tty, - unsigned char __user **b, + unsigned char **kbp, size_t *nr) { @@ -1976,8 +1972,7 @@ static int copy_from_read_buf(struct tty_struct *tty, n = min(*nr, n); if (n) { unsigned char *from = read_buf_addr(ldata, tail); - retval = copy_to_user(*b, from, n); - n -= retval; + memcpy(*kbp, from, n); is_eof = n == 1 && *from == EOF_CHAR(tty); tty_audit_add_data(tty, from, n); zero_buffer(tty, from, n); @@ -1986,7 +1981,7 @@ static int copy_from_read_buf(struct tty_struct *tty, if (L_EXTPROC(tty) && ldata->icanon && is_eof && (head == ldata->read_tail)) n = 0; - *b += n; + *kbp += n; *nr -= n; } return retval; @@ -1995,12 +1990,12 @@ static int copy_from_read_buf(struct tty_struct *tty, /** * canon_copy_from_read_buf - copy read data in canonical mode * @tty: terminal device - * @b: user data + * @kbp: data * @nr: size of data * * Helper function for n_tty_read. It is only called when ICANON is on; * it copies one line of input up to and including the line-delimiting - * character into the user-space buffer. + * character into the result buffer. * * NB: When termios is changed from non-canonical to canonical mode and * the read buffer contains data, n_tty_set_termios() simulates an EOF @@ -2016,14 +2011,14 @@ static int copy_from_read_buf(struct tty_struct *tty, */ static int canon_copy_from_read_buf(struct tty_struct *tty, - unsigned char __user **b, + unsigned char **kbp, size_t *nr) { struct n_tty_data *ldata = tty->disc_data; size_t n, size, more, c; size_t eol; size_t tail; - int ret, found = 0; + int found = 0; /* N.B. avoid overrun if nr == 0 */ if (!*nr) @@ -2059,10 +2054,8 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n", __func__, eol, found, n, c, tail, more); - ret = tty_copy_to_user(tty, *b, tail, n); - if (ret) - return -EFAULT; - *b += n; + tty_copy(tty, *kbp, tail, n); + *kbp += n; *nr -= n; if (found) @@ -2130,10 +2123,11 @@ static int job_control(struct tty_struct *tty, struct file *file) */ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t nr) + unsigned char *kbuf, size_t nr, + void **cookie, unsigned long offset) { struct n_tty_data *ldata = tty->disc_data; - unsigned char __user *b = buf; + unsigned char *kb = kbuf; DEFINE_WAIT_FUNC(wait, woken_wake_function); int c; int minimum, time; @@ -2179,17 +2173,13 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, /* First test for status change. */ if (packet && tty->link->ctrl_status) { unsigned char cs; - if (b != buf) + if (kb != kbuf) break; spin_lock_irq(&tty->link->ctrl_lock); cs = tty->link->ctrl_status; tty->link->ctrl_status = 0; spin_unlock_irq(&tty->link->ctrl_lock); - if (put_user(cs, b)) { - retval = -EFAULT; - break; - } - b++; + *kb++ = cs; nr--; break; } @@ -2232,24 +2222,20 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, } if (ldata->icanon && !L_EXTPROC(tty)) { - retval = canon_copy_from_read_buf(tty, &b, &nr); + retval = canon_copy_from_read_buf(tty, &kb, &nr); if (retval) break; } else { int uncopied; /* Deal with packet mode. */ - if (packet && b == buf) { - if (put_user(TIOCPKT_DATA, b)) { - retval = -EFAULT; - break; - } - b++; + if (packet && kb == kbuf) { + *kb++ = TIOCPKT_DATA; nr--; } - uncopied = copy_from_read_buf(tty, &b, &nr); - uncopied += copy_from_read_buf(tty, &b, &nr); + uncopied = copy_from_read_buf(tty, &kb, &nr); + uncopied += copy_from_read_buf(tty, &kb, &nr); if (uncopied) { retval = -EFAULT; break; @@ -2258,7 +2244,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, n_tty_check_unthrottle(tty); - if (b - buf >= minimum) + if (kb - kbuf >= minimum) break; if (time) timeout = time; @@ -2270,8 +2256,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, remove_wait_queue(&tty->read_wait, &wait); mutex_unlock(&ldata->atomic_read_lock); - if (b - buf) - retval = b - buf; + if (kb - kbuf) + retval = kb - kbuf; return retval; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 338bc4ef5549..a34f8bcf875e 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -833,6 +833,65 @@ static void tty_update_time(struct timespec64 *time) time->tv_sec = sec; } +/* + * Iterate on the ldisc ->read() function until we've gotten all + * the data the ldisc has for us. + * + * The "cookie" is something that the ldisc read function can fill + * in to let us know that there is more data to be had. + * + * We promise to continue to call the ldisc until it stops returning + * data or clears the cookie. The cookie may be something that the + * ldisc maintains state for and needs to free. + */ +static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, struct file *file, + char __user *buf, size_t count) +{ + int retval = 0; + void *cookie = NULL; + unsigned long offset = 0; + char kernel_buf[64]; + + do { + int size, uncopied; + + size = count > sizeof(kernel_buf) ? sizeof(kernel_buf) : count; + size = ld->ops->read(tty, file, kernel_buf, size, &cookie, offset); + if (!size) + break; + + /* + * A ldisc read error return will override any previously copied + * data (eg -EOVERFLOW from HDLC) + */ + if (size < 0) { + memzero_explicit(kernel_buf, sizeof(kernel_buf)); + return size; + } + + uncopied = copy_to_user(buf+offset, kernel_buf, size); + size -= uncopied; + offset += size; + count -= size; + + /* + * If the user copy failed, we still need to do another ->read() + * call if we had a cookie to let the ldisc clear up. + * + * But make sure size is zeroed. + */ + if (unlikely(uncopied)) { + count = 0; + retval = -EFAULT; + } + } while (cookie); + + /* We always clear tty buffer in case they contained passwords */ + memzero_explicit(kernel_buf, sizeof(kernel_buf)); + return offset ? offset : retval; +} + + /** * tty_read - read method for tty device files * @file: pointer to tty file @@ -866,10 +925,9 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, ld = tty_ldisc_ref_wait(tty); if (!ld) return hung_up_tty_read(file, buf, count, ppos); + i = -EIO; if (ld->ops->read) - i = ld->ops->read(tty, file, buf, count); - else - i = -EIO; + i = iterate_tty_read(ld, tty, file, buf, count); tty_ldisc_deref(ld); if (i > 0) -- cgit From dd78b0c483e33225e0e0782b0ed887129b00f956 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 19 Jan 2021 10:49:19 -0800 Subject: tty: implement read_iter Now that the ldisc read() function takes kernel pointers, it's fairly straightforward to make the tty file operations use .read_iter() instead of .read(). That automatically gives us vread() and friends, and also makes it possible to do .splice_read() on ttys again. Fixes: 36e2c7421f02 ("fs: don't allow splice read/write without explicit ops") Reported-by: Oliver Giles Cc: Christoph Hellwig Cc: Greg Kroah-Hartman Cc: Al Viro Signed-off-by: Linus Torvalds --- drivers/tty/tty_io.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index a34f8bcf875e..8846d3b99845 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -142,7 +142,7 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */ /* Mutex to protect creating and releasing a tty */ DEFINE_MUTEX(tty_mutex); -static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); +static ssize_t tty_read(struct kiocb *, struct iov_iter *); static ssize_t tty_write(struct kiocb *, struct iov_iter *); ssize_t redirected_tty_write(struct kiocb *, struct iov_iter *); static __poll_t tty_poll(struct file *, poll_table *); @@ -476,8 +476,9 @@ static void tty_show_fdinfo(struct seq_file *m, struct file *file) static const struct file_operations tty_fops = { .llseek = no_llseek, - .read = tty_read, + .read_iter = tty_read, .write_iter = tty_write, + .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .poll = tty_poll, .unlocked_ioctl = tty_ioctl, @@ -490,8 +491,9 @@ static const struct file_operations tty_fops = { static const struct file_operations console_fops = { .llseek = no_llseek, - .read = tty_read, + .read_iter = tty_read, .write_iter = redirected_tty_write, + .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .poll = tty_poll, .unlocked_ioctl = tty_ioctl, @@ -844,16 +846,17 @@ static void tty_update_time(struct timespec64 *time) * data or clears the cookie. The cookie may be something that the * ldisc maintains state for and needs to free. */ -static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, struct file *file, - char __user *buf, size_t count) +static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, + struct file *file, struct iov_iter *to) { int retval = 0; void *cookie = NULL; unsigned long offset = 0; char kernel_buf[64]; + size_t count = iov_iter_count(to); do { - int size, uncopied; + int size, copied; size = count > sizeof(kernel_buf) ? sizeof(kernel_buf) : count; size = ld->ops->read(tty, file, kernel_buf, size, &cookie, offset); @@ -869,10 +872,9 @@ static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, struct return size; } - uncopied = copy_to_user(buf+offset, kernel_buf, size); - size -= uncopied; - offset += size; - count -= size; + copied = copy_to_iter(kernel_buf, size, to); + offset += copied; + count -= copied; /* * If the user copy failed, we still need to do another ->read() @@ -880,7 +882,7 @@ static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, struct * * But make sure size is zeroed. */ - if (unlikely(uncopied)) { + if (unlikely(copied != size)) { count = 0; retval = -EFAULT; } @@ -907,10 +909,10 @@ static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, struct * read calls may be outstanding in parallel. */ -static ssize_t tty_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) +static ssize_t tty_read(struct kiocb *iocb, struct iov_iter *to) { int i; + struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); struct tty_struct *tty = file_tty(file); struct tty_ldisc *ld; @@ -923,11 +925,9 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, /* We want to wait for the line discipline to sort out in this situation */ ld = tty_ldisc_ref_wait(tty); - if (!ld) - return hung_up_tty_read(file, buf, count, ppos); i = -EIO; - if (ld->ops->read) - i = iterate_tty_read(ld, tty, file, buf, count); + if (ld && ld->ops->read) + i = iterate_tty_read(ld, tty, file, to); tty_ldisc_deref(ld); if (i > 0) @@ -2927,7 +2927,7 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, static int this_tty(const void *t, struct file *file, unsigned fd) { - if (likely(file->f_op->read != tty_read)) + if (likely(file->f_op->read_iter != tty_read)) return 0; return file_tty(file) != t ? 0 : fd + 1; } -- cgit From 64a69892afadd6fffaeadc65427bb7601161139d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 19 Jan 2021 13:46:28 -0800 Subject: tty: clean up legacy leftovers from n_tty line discipline Back when the line disciplines did their own direct user accesses, they had to deal with the data copy possibly failing in the middle. Now that the user copy is done by the tty_io.c code, that failure case no longer exists. Remove the left-over error handling code that cannot trigger. Signed-off-by: Linus Torvalds --- drivers/tty/n_tty.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 4a34a9f43b29..3a1a79462d16 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1955,19 +1955,17 @@ static inline int input_available_p(struct tty_struct *tty, int poll) * read_tail published */ -static int copy_from_read_buf(struct tty_struct *tty, +static void copy_from_read_buf(struct tty_struct *tty, unsigned char **kbp, size_t *nr) { struct n_tty_data *ldata = tty->disc_data; - int retval; size_t n; bool is_eof; size_t head = smp_load_acquire(&ldata->commit_head); size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); - retval = 0; n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail); n = min(*nr, n); if (n) { @@ -1984,7 +1982,6 @@ static int copy_from_read_buf(struct tty_struct *tty, *kbp += n; *nr -= n; } - return retval; } /** @@ -2010,9 +2007,9 @@ static int copy_from_read_buf(struct tty_struct *tty, * read_tail published */ -static int canon_copy_from_read_buf(struct tty_struct *tty, - unsigned char **kbp, - size_t *nr) +static void canon_copy_from_read_buf(struct tty_struct *tty, + unsigned char **kbp, + size_t *nr) { struct n_tty_data *ldata = tty->disc_data; size_t n, size, more, c; @@ -2022,7 +2019,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, /* N.B. avoid overrun if nr == 0 */ if (!*nr) - return 0; + return; n = min(*nr + 1, smp_load_acquire(&ldata->canon_head) - ldata->read_tail); @@ -2069,7 +2066,6 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, ldata->push = 0; tty_audit_push(); } - return 0; } extern ssize_t redirected_tty_write(struct file *, const char __user *, @@ -2222,24 +2218,17 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, } if (ldata->icanon && !L_EXTPROC(tty)) { - retval = canon_copy_from_read_buf(tty, &kb, &nr); - if (retval) - break; + canon_copy_from_read_buf(tty, &kb, &nr); } else { - int uncopied; - /* Deal with packet mode. */ if (packet && kb == kbuf) { *kb++ = TIOCPKT_DATA; nr--; } - uncopied = copy_from_read_buf(tty, &kb, &nr); - uncopied += copy_from_read_buf(tty, &kb, &nr); - if (uncopied) { - retval = -EFAULT; - break; - } + /* See comment above copy_from_read_buf() why twice */ + copy_from_read_buf(tty, &kb, &nr); + copy_from_read_buf(tty, &kb, &nr); } n_tty_check_unthrottle(tty); -- cgit From 15ea8ae8e03fdb845ed3ff5d9f11dd5f4f60252c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 19 Jan 2021 18:14:20 -0800 Subject: tty: teach n_tty line discipline about the new "cookie continuations" With the conversion to do the tty ldisc read operations in small chunks, the n_tty line discipline became noticeably slower for throughput oriented loads, because rather than read things in up to 2kB chunks, it would return at most 64 bytes per read() system call. The cost is mainly all in the "do system calls over and over", not really in the new "copy to an extra kernel buffer". This can be fixed by teaching the n_tty line discipline about the "cookie continuation" model, which the chunking code supports because things like hdlc need to be able to handle packets up to 64kB in size. Doing that doesn't just get us back to the old performace, but to much better performance: my stupid "copy 10MB of data over a pty" test program is now almost twice as fast as it used to be (going down from 0.1s to 0.054s). This is entirely because it now creates maximal chunks (which happens to be "one byte less than one page" due to how we do the circular tty buffers). NOTE! This case only handles the simpler non-icanon case, which is the one where people may care about throughput. I'm going to do the icanon case later too, because while performance isn't a major issue for that, there may be programs that think they'll always get a full line and don't like the 64-byte chunking for that reason. Such programs are arguably buggy (signals etc can cause random partial results from tty reads anyway), and good programs will handle such partial reads, but expecting everybody to write "good programs" has never been a winning policy for the kernel.. Signed-off-by: Linus Torvalds --- drivers/tty/n_tty.c | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 3a1a79462d16..b89308d52ade 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1943,19 +1943,17 @@ static inline int input_available_p(struct tty_struct *tty, int poll) * Helper function to speed up n_tty_read. It is only called when * ICANON is off; it copies characters straight from the tty queue. * - * It can be profitably called twice; once to drain the space from - * the tail pointer to the (physical) end of the buffer, and once - * to drain the space from the (physical) beginning of the buffer - * to head pointer. - * * Called under the ldata->atomic_read_lock sem * + * Returns true if it successfully copied data, but there is still + * more data to be had. + * * n_tty_read()/consumer path: * caller holds non-exclusive termios_rwsem * read_tail published */ -static void copy_from_read_buf(struct tty_struct *tty, +static bool copy_from_read_buf(struct tty_struct *tty, unsigned char **kbp, size_t *nr) @@ -1978,10 +1976,14 @@ static void copy_from_read_buf(struct tty_struct *tty, /* Turn single EOF into zero-length read */ if (L_EXTPROC(tty) && ldata->icanon && is_eof && (head == ldata->read_tail)) - n = 0; + return false; *kbp += n; *nr -= n; + + /* If we have more to copy, let the caller know */ + return head != ldata->read_tail; } + return false; } /** @@ -2132,6 +2134,25 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, int packet; size_t tail; + /* + * Is this a continuation of a read started earler? + * + * If so, we still hold the atomic_read_lock and the + * termios_rwsem, and can just continue to copy data. + */ + if (*cookie) { + if (copy_from_read_buf(tty, &kb, &nr)) + return kb - kbuf; + + /* No more data - release locks and stop retries */ + n_tty_kick_worker(tty); + n_tty_check_unthrottle(tty); + up_read(&tty->termios_rwsem); + mutex_unlock(&ldata->atomic_read_lock); + *cookie = NULL; + return kb - kbuf; + } + c = job_control(tty, file); if (c < 0) return c; @@ -2226,9 +2247,20 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, nr--; } - /* See comment above copy_from_read_buf() why twice */ - copy_from_read_buf(tty, &kb, &nr); - copy_from_read_buf(tty, &kb, &nr); + /* + * Copy data, and if there is more to be had + * and we have nothing more to wait for, then + * let's mark us for retries. + * + * NOTE! We return here with both the termios_sem + * and atomic_read_lock still held, the retries + * will release them when done. + */ + if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) { + remove_wait_queue(&tty->read_wait, &wait); + *cookie = cookie; + return kb - kbuf; + } } n_tty_check_unthrottle(tty); -- cgit From d7fe75cbc23c7d225eee2ef04def239b6603dce7 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 20 Jan 2021 15:43:38 -0800 Subject: tty: teach the n_tty ICANON case about the new "cookie continuations" too The ICANON case is a bit messy, since it has to look for the line ending, and has special code to then suppress line ending characters if they match the __DISABLED_CHAR. So it actually looks up the line ending even past the point where it knows it won't copy it to the result buffer. That said, apart from all those odd legacy N_TTY ICANON cases, the actual "should we continue copying" logic isn't really all that complicated or different from the non-canon case. In fact, the lack of "wait for at least N characters" arguably makes the repeat case slightly simpler. It really just boils down to "there's more of the line to be copied". So add the necessarily trivial logic, and now the N_TTY case will give long result lines even when in canon mode. Signed-off-by: Linus Torvalds --- drivers/tty/n_tty.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index b89308d52ade..9e546d0cc55c 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2009,21 +2009,22 @@ static bool copy_from_read_buf(struct tty_struct *tty, * read_tail published */ -static void canon_copy_from_read_buf(struct tty_struct *tty, +static bool canon_copy_from_read_buf(struct tty_struct *tty, unsigned char **kbp, size_t *nr) { struct n_tty_data *ldata = tty->disc_data; size_t n, size, more, c; size_t eol; - size_t tail; + size_t tail, canon_head; int found = 0; /* N.B. avoid overrun if nr == 0 */ if (!*nr) - return; + return false; - n = min(*nr + 1, smp_load_acquire(&ldata->canon_head) - ldata->read_tail); + canon_head = smp_load_acquire(&ldata->canon_head); + n = min(*nr + 1, canon_head - ldata->read_tail); tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); size = min_t(size_t, tail + n, N_TTY_BUF_SIZE); @@ -2067,7 +2068,11 @@ static void canon_copy_from_read_buf(struct tty_struct *tty, else ldata->push = 0; tty_audit_push(); + return false; } + + /* No EOL found - do a continuation retry if there is more data */ + return ldata->read_tail != canon_head; } extern ssize_t redirected_tty_write(struct file *, const char __user *, @@ -2141,8 +2146,13 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, * termios_rwsem, and can just continue to copy data. */ if (*cookie) { - if (copy_from_read_buf(tty, &kb, &nr)) - return kb - kbuf; + if (ldata->icanon && !L_EXTPROC(tty)) { + if (canon_copy_from_read_buf(tty, &kb, &nr)) + return kb - kbuf; + } else { + if (copy_from_read_buf(tty, &kb, &nr)) + return kb - kbuf; + } /* No more data - release locks and stop retries */ n_tty_kick_worker(tty); @@ -2239,7 +2249,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, } if (ldata->icanon && !L_EXTPROC(tty)) { - canon_copy_from_read_buf(tty, &kb, &nr); + if (canon_copy_from_read_buf(tty, &kb, &nr)) + goto more_to_be_read; } else { /* Deal with packet mode. */ if (packet && kb == kbuf) { @@ -2257,6 +2268,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, * will release them when done. */ if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) { +more_to_be_read: remove_wait_queue(&tty->read_wait, &wait); *cookie = cookie; return kb - kbuf; -- cgit From c7135bbe5af2094ef48dff684a5de045f6df66ce Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 21 Jan 2021 10:04:27 -0800 Subject: tty: fix up hung_up_tty_write() conversion In commit "tty: implement write_iter", I left the write_iter conversion of the hung up tty case alone, because I incorrectly thought it didn't matter. Jiri showed me the errors of my ways, and pointed out the problems with that incomplete conversion. Fix it all up. Reported-by: Jiri Slaby Signed-off-by: Linus Torvalds Reviewed-by: Jiri Slaby Link: https://lore.kernel.org/r/CAHk-=wh+-rGsa=xruEWdg_fJViFG8rN9bpLrfLz=_yBYh2tBhA@mail.gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index c7763743a3d6..e19071fb9b5b 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -437,8 +437,7 @@ static ssize_t hung_up_tty_read(struct file *file, char __user *buf, return 0; } -static ssize_t hung_up_tty_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t hung_up_tty_write(struct kiocb *iocb, struct iov_iter *from) { return -EIO; } @@ -506,7 +505,7 @@ static const struct file_operations console_fops = { static const struct file_operations hung_up_tty_fops = { .llseek = no_llseek, .read = hung_up_tty_read, - .write = hung_up_tty_write, + .write_iter = hung_up_tty_write, .poll = hung_up_tty_poll, .unlocked_ioctl = hung_up_tty_ioctl, .compat_ioctl = hung_up_tty_compat_ioctl, @@ -1119,7 +1118,9 @@ static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from) if (tty->ops->write_room == NULL) tty_err(tty, "missing write_room method\n"); ld = tty_ldisc_ref_wait(tty); - if (!ld || !ld->ops->write) + if (!ld) + return hung_up_tty_write(iocb, from); + if (!ld->ops->write) ret = -EIO; else ret = do_tty_write(ld->ops->write, tty, file, from); -- cgit From ddc5fda7456178e2cbc87675b370920d98360daf Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 21 Jan 2021 10:08:15 -0800 Subject: tty: fix up hung_up_tty_read() conversion In commit "tty: implement read_iter", I left the read_iter conversion of the hung up tty case alone, because I incorrectly thought it didn't matter. Jiri showed me the errors of my ways, and pointed out the problems with that incomplete conversion. Fix it all up. Reported-by: Jiri Slaby Signed-off-by: Linus Torvalds Reviewed-by: Jiri Slaby Link: https://lore.kernel.org/r/CAHk-=wh+-rGsa=xruEWdg_fJViFG8rN9bpLrfLz=_yBYh2tBhA@mail.gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e19071fb9b5b..d4b1a4824924 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -431,8 +431,7 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) EXPORT_SYMBOL_GPL(tty_find_polling_driver); #endif -static ssize_t hung_up_tty_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) +static ssize_t hung_up_tty_read(struct kiocb *iocb, struct iov_iter *to) { return 0; } @@ -504,7 +503,7 @@ static const struct file_operations console_fops = { static const struct file_operations hung_up_tty_fops = { .llseek = no_llseek, - .read = hung_up_tty_read, + .read_iter = hung_up_tty_read, .write_iter = hung_up_tty_write, .poll = hung_up_tty_poll, .unlocked_ioctl = hung_up_tty_ioctl, @@ -940,8 +939,10 @@ static ssize_t tty_read(struct kiocb *iocb, struct iov_iter *to) /* We want to wait for the line discipline to sort out in this situation */ ld = tty_ldisc_ref_wait(tty); + if (!ld) + return hung_up_tty_read(iocb, to); i = -EIO; - if (ld && ld->ops->read) + if (ld->ops->read) i = iterate_tty_read(ld, tty, file, to); tty_ldisc_deref(ld); -- cgit From e71a8d5cf4b4f274740e31b601216071e2a11afa Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 21 Jan 2021 10:17:25 -0800 Subject: tty: fix up iterate_tty_read() EOVERFLOW handling When I converted the tty_ldisc_ops 'read()' function to take a kernel pointer, I was a bit too aggressive about the ldisc returning EOVERFLOW. Yes, we want to have EOVERFLOW override any partially read data (because the whole point is that the buffer was too small for the whole packet, and we don't want to see partial packets), but it shouldn't override a previous EFAULT. And in fact, it really is just EOVERFLOW that is special and should throw away any partially read data, not "any error". Admittedly EOVERFLOW is currently the only one that can happen for a continuation read - and if the first read iteration returns an error we won't have this issue. So this is more of a technicality, but let's just make the intent very explicit, and re-organize the error handling a bit so that this is all clearer. Reported-by: Jiri Slaby Signed-off-by: Linus Torvalds Reviewed-by: Jiri Slaby Link: https://lore.kernel.org/r/CAHk-=wh+-rGsa=xruEWdg_fJViFG8rN9bpLrfLz=_yBYh2tBhA@mail.gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d4b1a4824924..eb7bd80cd155 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -877,13 +877,20 @@ static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, if (!size) break; - /* - * A ldisc read error return will override any previously copied - * data (eg -EOVERFLOW from HDLC) - */ if (size < 0) { - memzero_explicit(kernel_buf, sizeof(kernel_buf)); - return size; + /* Did we have an earlier error (ie -EFAULT)? */ + if (retval) + break; + retval = size; + + /* + * -EOVERFLOW means we didn't have enough space + * for a whole packet, and we shouldn't return + * a partial result. + */ + if (retval == -EOVERFLOW) + offset = 0; + break; } copied = copy_to_iter(kernel_buf, size, to); -- cgit From e9103f47bf1a1bbf0ab0ea90eda3e208653a5f57 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Jan 2021 17:02:38 +0200 Subject: serial: ifx6x60: Remove driver for deprecated platform Intel Moorestown and Medfield are quite old Intel Atom based 32-bit platforms, which were in limited use in some Android phones, tablets and consumer electronics more than eight years ago. There are no bugs or problems ever reported outside from Intel for breaking any of that platforms for years. It seems no real users exists who run more or less fresh kernel on it. The commit 05f4434bc130 ("ASoC: Intel: remove mfld_machine") also in align with this theory. Due to above and to reduce a burden of supporting outdated drivers we remove the support of outdated platforms completely. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20210125150238.16980-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 7 - drivers/tty/serial/Makefile | 1 - drivers/tty/serial/ifx6x60.c | 1387 ------------------------------------------ drivers/tty/serial/ifx6x60.h | 118 ---- 4 files changed, 1513 deletions(-) delete mode 100644 drivers/tty/serial/ifx6x60.c delete mode 100644 drivers/tty/serial/ifx6x60.h (limited to 'drivers') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 22e86ba4f827..0c4cd4a348f4 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1182,13 +1182,6 @@ config SERIAL_ALTERA_UART_CONSOLE help Enable a Altera UART port to be the system console. -config SERIAL_IFX6X60 - tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)" - depends on GPIOLIB || COMPILE_TEST - depends on SPI && HAS_DMA - help - Support for the IFX6x60 modem devices on Intel MID platforms. - config SERIAL_PCH_UART tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) UART" depends on PCI && (X86_32 || MIPS || COMPILE_TEST) diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 931dc1d6885e..7da0856cd198 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -64,7 +64,6 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o -obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c deleted file mode 100644 index d4ef88ee22d0..000000000000 --- a/drivers/tty/serial/ifx6x60.c +++ /dev/null @@ -1,1387 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/**************************************************************************** - * - * Driver for the IFX 6x60 spi modem. - * - * Copyright (C) 2008 Option International - * Copyright (C) 2008 Filip Aben - * Denis Joseph Barrow - * Jan Dumon - * - * Copyright (C) 2009, 2010 Intel Corp - * Russ Gorby - * - * Driver modified by Intel from Option gtm501l_spi.c - * - * Notes - * o The driver currently assumes a single device only. If you need to - * change this then look for saved_ifx_dev and add a device lookup - * o The driver is intended to be big-endian safe but has never been - * tested that way (no suitable hardware). There are a couple of FIXME - * notes by areas that may need addressing - * o Some of the GPIO naming/setup assumptions may need revisiting if - * you need to use this driver for another platform. - * - *****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ifx6x60.h" - -#define IFX_SPI_MORE_MASK 0x10 -#define IFX_SPI_MORE_BIT 4 /* bit position in u8 */ -#define IFX_SPI_CTS_BIT 6 /* bit position in u8 */ -#define IFX_SPI_MODE SPI_MODE_1 -#define IFX_SPI_TTY_ID 0 -#define IFX_SPI_TIMEOUT_SEC 2 -#define IFX_SPI_HEADER_0 (-1) -#define IFX_SPI_HEADER_F (-2) - -#define PO_POST_DELAY 200 - -/* forward reference */ -static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev); -static int ifx_modem_reboot_callback(struct notifier_block *nfb, - unsigned long event, void *data); -static int ifx_modem_power_off(struct ifx_spi_device *ifx_dev); - -/* local variables */ -static int spi_bpw = 16; /* 8, 16 or 32 bit word length */ -static struct tty_driver *tty_drv; -static struct ifx_spi_device *saved_ifx_dev; -static struct lock_class_key ifx_spi_key; - -static struct notifier_block ifx_modem_reboot_notifier_block = { - .notifier_call = ifx_modem_reboot_callback, -}; - -static int ifx_modem_power_off(struct ifx_spi_device *ifx_dev) -{ - gpiod_set_value(ifx_dev->gpio.pmu_reset, 1); - msleep(PO_POST_DELAY); - - return 0; -} - -static int ifx_modem_reboot_callback(struct notifier_block *nfb, - unsigned long event, void *data) -{ - if (saved_ifx_dev) - ifx_modem_power_off(saved_ifx_dev); - else - pr_warn("no ifx modem active;\n"); - - return NOTIFY_OK; -} - -/* GPIO/GPE settings */ - -/** - * mrdy_set_high - set MRDY GPIO - * @ifx: device we are controlling - * - */ -static inline void mrdy_set_high(struct ifx_spi_device *ifx) -{ - gpiod_set_value(ifx->gpio.mrdy, 1); -} - -/** - * mrdy_set_low - clear MRDY GPIO - * @ifx: device we are controlling - * - */ -static inline void mrdy_set_low(struct ifx_spi_device *ifx) -{ - gpiod_set_value(ifx->gpio.mrdy, 0); -} - -/** - * ifx_spi_power_state_set - * @ifx_dev: our SPI device - * @val: bits to set - * - * Set bit in power status and signal power system if status becomes non-0 - */ -static void -ifx_spi_power_state_set(struct ifx_spi_device *ifx_dev, unsigned char val) -{ - unsigned long flags; - - spin_lock_irqsave(&ifx_dev->power_lock, flags); - - /* - * if power status is already non-0, just update, else - * tell power system - */ - if (!ifx_dev->power_status) - pm_runtime_get(&ifx_dev->spi_dev->dev); - ifx_dev->power_status |= val; - - spin_unlock_irqrestore(&ifx_dev->power_lock, flags); -} - -/** - * ifx_spi_power_state_clear - clear power bit - * @ifx_dev: our SPI device - * @val: bits to clear - * - * clear bit in power status and signal power system if status becomes 0 - */ -static void -ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val) -{ - unsigned long flags; - - spin_lock_irqsave(&ifx_dev->power_lock, flags); - - if (ifx_dev->power_status) { - ifx_dev->power_status &= ~val; - if (!ifx_dev->power_status) - pm_runtime_put(&ifx_dev->spi_dev->dev); - } - - spin_unlock_irqrestore(&ifx_dev->power_lock, flags); -} - -/** - * swap_buf_8 - * @buf: our buffer - * @len : number of bytes (not words) in the buffer - * @end: end of buffer - * - * Swap the contents of a buffer into big endian format - */ -static inline void swap_buf_8(unsigned char *buf, int len, void *end) -{ - /* don't swap buffer if SPI word width is 8 bits */ - return; -} - -/** - * swap_buf_16 - * @buf: our buffer - * @len : number of bytes (not words) in the buffer - * @end: end of buffer - * - * Swap the contents of a buffer into big endian format - */ -static inline void swap_buf_16(unsigned char *buf, int len, void *end) -{ - int n; - - u16 *buf_16 = (u16 *)buf; - len = ((len + 1) >> 1); - if ((void *)&buf_16[len] > end) { - pr_err("swap_buf_16: swap exceeds boundary (%p > %p)!", - &buf_16[len], end); - return; - } - for (n = 0; n < len; n++) { - *buf_16 = cpu_to_be16(*buf_16); - buf_16++; - } -} - -/** - * swap_buf_32 - * @buf: our buffer - * @len : number of bytes (not words) in the buffer - * @end: end of buffer - * - * Swap the contents of a buffer into big endian format - */ -static inline void swap_buf_32(unsigned char *buf, int len, void *end) -{ - int n; - - u32 *buf_32 = (u32 *)buf; - len = (len + 3) >> 2; - - if ((void *)&buf_32[len] > end) { - pr_err("swap_buf_32: swap exceeds boundary (%p > %p)!\n", - &buf_32[len], end); - return; - } - for (n = 0; n < len; n++) { - *buf_32 = cpu_to_be32(*buf_32); - buf_32++; - } -} - -/** - * mrdy_assert - assert MRDY line - * @ifx_dev: our SPI device - * - * Assert mrdy and set timer to wait for SRDY interrupt, if SRDY is low - * now. - * - * FIXME: Can SRDY even go high as we are running this code ? - */ -static void mrdy_assert(struct ifx_spi_device *ifx_dev) -{ - int val = gpiod_get_value(ifx_dev->gpio.srdy); - if (!val) { - if (!test_and_set_bit(IFX_SPI_STATE_TIMER_PENDING, - &ifx_dev->flags)) { - mod_timer(&ifx_dev->spi_timer,jiffies + IFX_SPI_TIMEOUT_SEC*HZ); - - } - } - ifx_spi_power_state_set(ifx_dev, IFX_SPI_POWER_DATA_PENDING); - mrdy_set_high(ifx_dev); -} - -/** - * ifx_spi_timeout - SPI timeout - * @t: timer in our SPI device - * - * The SPI has timed out: hang up the tty. Users will then see a hangup - * and error events. - */ -static void ifx_spi_timeout(struct timer_list *t) -{ - struct ifx_spi_device *ifx_dev = from_timer(ifx_dev, t, spi_timer); - - dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***"); - tty_port_tty_hangup(&ifx_dev->tty_port, false); - mrdy_set_low(ifx_dev); - clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); -} - -/* char/tty operations */ - -/** - * ifx_spi_tiocmget - get modem lines - * @tty: our tty device - * - * Map the signal state into Linux modem flags and report the value - * in Linux terms - */ -static int ifx_spi_tiocmget(struct tty_struct *tty) -{ - unsigned int value; - struct ifx_spi_device *ifx_dev = tty->driver_data; - - value = - (test_bit(IFX_SPI_RTS, &ifx_dev->signal_state) ? TIOCM_RTS : 0) | - (test_bit(IFX_SPI_DTR, &ifx_dev->signal_state) ? TIOCM_DTR : 0) | - (test_bit(IFX_SPI_CTS, &ifx_dev->signal_state) ? TIOCM_CTS : 0) | - (test_bit(IFX_SPI_DSR, &ifx_dev->signal_state) ? TIOCM_DSR : 0) | - (test_bit(IFX_SPI_DCD, &ifx_dev->signal_state) ? TIOCM_CAR : 0) | - (test_bit(IFX_SPI_RI, &ifx_dev->signal_state) ? TIOCM_RNG : 0); - return value; -} - -/** - * ifx_spi_tiocmset - set modem bits - * @tty: the tty structure - * @set: bits to set - * @clear: bits to clear - * - * The IFX6x60 only supports DTR and RTS. Set them accordingly - * and flag that an update to the modem is needed. - * - * FIXME: do we need to kick the tranfers when we do this ? - */ -static int ifx_spi_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - - if (set & TIOCM_RTS) - set_bit(IFX_SPI_RTS, &ifx_dev->signal_state); - if (set & TIOCM_DTR) - set_bit(IFX_SPI_DTR, &ifx_dev->signal_state); - if (clear & TIOCM_RTS) - clear_bit(IFX_SPI_RTS, &ifx_dev->signal_state); - if (clear & TIOCM_DTR) - clear_bit(IFX_SPI_DTR, &ifx_dev->signal_state); - - set_bit(IFX_SPI_UPDATE, &ifx_dev->signal_state); - return 0; -} - -/** - * ifx_spi_open - called on tty open - * @tty: our tty device - * @filp: file handle being associated with the tty - * - * Open the tty interface. We let the tty_port layer do all the work - * for us. - * - * FIXME: Remove single device assumption and saved_ifx_dev - */ -static int ifx_spi_open(struct tty_struct *tty, struct file *filp) -{ - return tty_port_open(&saved_ifx_dev->tty_port, tty, filp); -} - -/** - * ifx_spi_close - called when our tty closes - * @tty: the tty being closed - * @filp: the file handle being closed - * - * Perform the close of the tty. We use the tty_port layer to do all - * our hard work. - */ -static void ifx_spi_close(struct tty_struct *tty, struct file *filp) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - tty_port_close(&ifx_dev->tty_port, tty, filp); - /* FIXME: should we do an ifx_spi_reset here ? */ -} - -/** - * ifx_decode_spi_header - decode received header - * @buffer: the received data - * @length: decoded length - * @more: decoded more flag - * @received_cts: status of cts we received - * - * Note how received_cts is handled -- if header is all F it is left - * the same as it was, if header is all 0 it is set to 0 otherwise it is - * taken from the incoming header. - * - * FIXME: endianness - */ -static int ifx_spi_decode_spi_header(unsigned char *buffer, int *length, - unsigned char *more, unsigned char *received_cts) -{ - u16 h1; - u16 h2; - u16 *in_buffer = (u16 *)buffer; - - h1 = *in_buffer; - h2 = *(in_buffer+1); - - if (h1 == 0 && h2 == 0) { - *received_cts = 0; - *more = 0; - return IFX_SPI_HEADER_0; - } else if (h1 == 0xffff && h2 == 0xffff) { - *more = 0; - /* spi_slave_cts remains as it was */ - return IFX_SPI_HEADER_F; - } - - *length = h1 & 0xfff; /* upper bits of byte are flags */ - *more = (buffer[1] >> IFX_SPI_MORE_BIT) & 1; - *received_cts = (buffer[3] >> IFX_SPI_CTS_BIT) & 1; - return 0; -} - -/** - * ifx_setup_spi_header - set header fields - * @txbuffer: pointer to start of SPI buffer - * @tx_count: bytes - * @more: indicate if more to follow - * - * Format up an SPI header for a transfer - * - * FIXME: endianness? - */ -static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count, - unsigned char more) -{ - *(u16 *)(txbuffer) = tx_count; - *(u16 *)(txbuffer+2) = IFX_SPI_PAYLOAD_SIZE; - txbuffer[1] |= (more << IFX_SPI_MORE_BIT) & IFX_SPI_MORE_MASK; -} - -/** - * ifx_spi_prepare_tx_buffer - prepare transmit frame - * @ifx_dev: our SPI device - * - * The transmit buffr needs a header and various other bits of - * information followed by as much data as we can pull from the FIFO - * and transfer. This function formats up a suitable buffer in the - * ifx_dev->tx_buffer - * - * FIXME: performance - should we wake the tty when the queue is half - * empty ? - */ -static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) -{ - int temp_count; - int queue_length; - int tx_count; - unsigned char *tx_buffer; - - tx_buffer = ifx_dev->tx_buffer; - - /* make room for required SPI header */ - tx_buffer += IFX_SPI_HEADER_OVERHEAD; - tx_count = IFX_SPI_HEADER_OVERHEAD; - - /* clear to signal no more data if this turns out to be the - * last buffer sent in a sequence */ - ifx_dev->spi_more = 0; - - /* if modem cts is set, just send empty buffer */ - if (!ifx_dev->spi_slave_cts) { - /* see if there's tx data */ - queue_length = kfifo_len(&ifx_dev->tx_fifo); - if (queue_length != 0) { - /* data to mux -- see if there's room for it */ - temp_count = min(queue_length, IFX_SPI_PAYLOAD_SIZE); - temp_count = kfifo_out_locked(&ifx_dev->tx_fifo, - tx_buffer, temp_count, - &ifx_dev->fifo_lock); - - /* update buffer pointer and data count in message */ - tx_buffer += temp_count; - tx_count += temp_count; - if (temp_count == queue_length) - /* poke port to get more data */ - tty_port_tty_wakeup(&ifx_dev->tty_port); - else /* more data in port, use next SPI message */ - ifx_dev->spi_more = 1; - } - } - /* have data and info for header -- set up SPI header in buffer */ - /* spi header needs payload size, not entire buffer size */ - ifx_spi_setup_spi_header(ifx_dev->tx_buffer, - tx_count-IFX_SPI_HEADER_OVERHEAD, - ifx_dev->spi_more); - /* swap actual data in the buffer */ - ifx_dev->swap_buf((ifx_dev->tx_buffer), tx_count, - &ifx_dev->tx_buffer[IFX_SPI_TRANSFER_SIZE]); - return tx_count; -} - -/** - * ifx_spi_write - line discipline write - * @tty: our tty device - * @buf: pointer to buffer to write (kernel space) - * @count: size of buffer - * - * Write the characters we have been given into the FIFO. If the device - * is not active then activate it, when the SRDY line is asserted back - * this will commence I/O - */ -static int ifx_spi_write(struct tty_struct *tty, const unsigned char *buf, - int count) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - unsigned char *tmp_buf = (unsigned char *)buf; - unsigned long flags; - bool is_fifo_empty; - int tx_count; - - spin_lock_irqsave(&ifx_dev->fifo_lock, flags); - is_fifo_empty = kfifo_is_empty(&ifx_dev->tx_fifo); - tx_count = kfifo_in(&ifx_dev->tx_fifo, tmp_buf, count); - spin_unlock_irqrestore(&ifx_dev->fifo_lock, flags); - if (is_fifo_empty) - mrdy_assert(ifx_dev); - - return tx_count; -} - -/** - * ifx_spi_chars_in_buffer - line discipline helper - * @tty: our tty device - * - * Report how much data we can accept before we drop bytes. As we use - * a simple FIFO this is nice and easy. - */ -static int ifx_spi_write_room(struct tty_struct *tty) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - return IFX_SPI_FIFO_SIZE - kfifo_len(&ifx_dev->tx_fifo); -} - -/** - * ifx_spi_chars_in_buffer - line discipline helper - * @tty: our tty device - * - * Report how many characters we have buffered. In our case this is the - * number of bytes sitting in our transmit FIFO. - */ -static int ifx_spi_chars_in_buffer(struct tty_struct *tty) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - return kfifo_len(&ifx_dev->tx_fifo); -} - -/** - * ifx_port_hangup - * @tty: our tty - * - * tty port hang up. Called when tty_hangup processing is invoked either - * by loss of carrier, or by software (eg vhangup). Serialized against - * activate/shutdown by the tty layer. - */ -static void ifx_spi_hangup(struct tty_struct *tty) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - tty_port_hangup(&ifx_dev->tty_port); -} - -/** - * ifx_port_activate - * @port: our tty port - * @tty: our tty device - * - * tty port activate method - called for first open. Serialized - * with hangup and shutdown by the tty layer. - */ -static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty) -{ - struct ifx_spi_device *ifx_dev = - container_of(port, struct ifx_spi_device, tty_port); - - /* clear any old data; can't do this in 'close' */ - kfifo_reset(&ifx_dev->tx_fifo); - - /* clear any flag which may be set in port shutdown procedure */ - clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags); - clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags); - - /* put port data into this tty */ - tty->driver_data = ifx_dev; - - /* set flag to allows data transfer */ - set_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags); - - return 0; -} - -/** - * ifx_port_shutdown - * @port: our tty port - * - * tty port shutdown method - called for last port close. Serialized - * with hangup and activate by the tty layer. - */ -static void ifx_port_shutdown(struct tty_port *port) -{ - struct ifx_spi_device *ifx_dev = - container_of(port, struct ifx_spi_device, tty_port); - - clear_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags); - mrdy_set_low(ifx_dev); - del_timer(&ifx_dev->spi_timer); - clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); - tasklet_kill(&ifx_dev->io_work_tasklet); -} - -static const struct tty_port_operations ifx_tty_port_ops = { - .activate = ifx_port_activate, - .shutdown = ifx_port_shutdown, -}; - -static const struct tty_operations ifx_spi_serial_ops = { - .open = ifx_spi_open, - .close = ifx_spi_close, - .write = ifx_spi_write, - .hangup = ifx_spi_hangup, - .write_room = ifx_spi_write_room, - .chars_in_buffer = ifx_spi_chars_in_buffer, - .tiocmget = ifx_spi_tiocmget, - .tiocmset = ifx_spi_tiocmset, -}; - -/** - * ifx_spi_insert_fip_string - queue received data - * @ifx_dev: our SPI device - * @chars: buffer we have received - * @size: number of chars reeived - * - * Queue bytes to the tty assuming the tty side is currently open. If - * not the discard the data. - */ -static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev, - unsigned char *chars, size_t size) -{ - tty_insert_flip_string(&ifx_dev->tty_port, chars, size); - tty_flip_buffer_push(&ifx_dev->tty_port); -} - -/** - * ifx_spi_complete - SPI transfer completed - * @ctx: our SPI device - * - * An SPI transfer has completed. Process any received data and kick off - * any further transmits we can commence. - */ -static void ifx_spi_complete(void *ctx) -{ - struct ifx_spi_device *ifx_dev = ctx; - int length; - int actual_length; - unsigned char more = 0; - unsigned char cts; - int local_write_pending = 0; - int queue_length; - int srdy; - int decode_result; - - mrdy_set_low(ifx_dev); - - if (!ifx_dev->spi_msg.status) { - /* check header validity, get comm flags */ - ifx_dev->swap_buf(ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD, - &ifx_dev->rx_buffer[IFX_SPI_HEADER_OVERHEAD]); - decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer, - &length, &more, &cts); - if (decode_result == IFX_SPI_HEADER_0) { - dev_dbg(&ifx_dev->spi_dev->dev, - "ignore input: invalid header 0"); - ifx_dev->spi_slave_cts = 0; - goto complete_exit; - } else if (decode_result == IFX_SPI_HEADER_F) { - dev_dbg(&ifx_dev->spi_dev->dev, - "ignore input: invalid header F"); - goto complete_exit; - } - - ifx_dev->spi_slave_cts = cts; - - actual_length = min((unsigned int)length, - ifx_dev->spi_msg.actual_length); - ifx_dev->swap_buf( - (ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD), - actual_length, - &ifx_dev->rx_buffer[IFX_SPI_TRANSFER_SIZE]); - ifx_spi_insert_flip_string( - ifx_dev, - ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD, - (size_t)actual_length); - } else { - more = 0; - dev_dbg(&ifx_dev->spi_dev->dev, "SPI transfer error %d", - ifx_dev->spi_msg.status); - } - -complete_exit: - if (ifx_dev->write_pending) { - ifx_dev->write_pending = 0; - local_write_pending = 1; - } - - clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags)); - - queue_length = kfifo_len(&ifx_dev->tx_fifo); - srdy = gpiod_get_value(ifx_dev->gpio.srdy); - if (!srdy) - ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_SRDY); - - /* schedule output if there is more to do */ - if (test_and_clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags)) - tasklet_schedule(&ifx_dev->io_work_tasklet); - else { - if (more || ifx_dev->spi_more || queue_length > 0 || - local_write_pending) { - if (ifx_dev->spi_slave_cts) { - if (more) - mrdy_assert(ifx_dev); - } else - mrdy_assert(ifx_dev); - } else { - /* - * poke line discipline driver if any for more data - * may or may not get more data to write - * for now, say not busy - */ - ifx_spi_power_state_clear(ifx_dev, - IFX_SPI_POWER_DATA_PENDING); - tty_port_tty_wakeup(&ifx_dev->tty_port); - } - } -} - -/** - * ifx_spio_io - I/O tasklet - * @t: tasklet construct used to fetch the SPI device - * - * Queue data for transmission if possible and then kick off the - * transfer. - */ -static void ifx_spi_io(struct tasklet_struct *t) -{ - int retval; - struct ifx_spi_device *ifx_dev = from_tasklet(ifx_dev, t, - io_work_tasklet); - - if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags) && - test_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags)) { - if (ifx_dev->gpio.unack_srdy_int_nb > 0) - ifx_dev->gpio.unack_srdy_int_nb--; - - ifx_spi_prepare_tx_buffer(ifx_dev); - - spi_message_init(&ifx_dev->spi_msg); - INIT_LIST_HEAD(&ifx_dev->spi_msg.queue); - - ifx_dev->spi_msg.context = ifx_dev; - ifx_dev->spi_msg.complete = ifx_spi_complete; - - /* set up our spi transfer */ - /* note len is BYTES, not transfers */ - ifx_dev->spi_xfer.len = IFX_SPI_TRANSFER_SIZE; - ifx_dev->spi_xfer.cs_change = 0; - ifx_dev->spi_xfer.speed_hz = ifx_dev->spi_dev->max_speed_hz; - /* ifx_dev->spi_xfer.speed_hz = 390625; */ - ifx_dev->spi_xfer.bits_per_word = - ifx_dev->spi_dev->bits_per_word; - - ifx_dev->spi_xfer.tx_buf = ifx_dev->tx_buffer; - ifx_dev->spi_xfer.rx_buf = ifx_dev->rx_buffer; - - /* - * setup dma pointers - */ - if (ifx_dev->use_dma) { - ifx_dev->spi_msg.is_dma_mapped = 1; - ifx_dev->tx_dma = ifx_dev->tx_bus; - ifx_dev->rx_dma = ifx_dev->rx_bus; - ifx_dev->spi_xfer.tx_dma = ifx_dev->tx_dma; - ifx_dev->spi_xfer.rx_dma = ifx_dev->rx_dma; - } else { - ifx_dev->spi_msg.is_dma_mapped = 0; - ifx_dev->tx_dma = (dma_addr_t)0; - ifx_dev->rx_dma = (dma_addr_t)0; - ifx_dev->spi_xfer.tx_dma = (dma_addr_t)0; - ifx_dev->spi_xfer.rx_dma = (dma_addr_t)0; - } - - spi_message_add_tail(&ifx_dev->spi_xfer, &ifx_dev->spi_msg); - - /* Assert MRDY. This may have already been done by the write - * routine. - */ - mrdy_assert(ifx_dev); - - retval = spi_async(ifx_dev->spi_dev, &ifx_dev->spi_msg); - if (retval) { - clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, - &ifx_dev->flags); - tasklet_schedule(&ifx_dev->io_work_tasklet); - return; - } - } else - ifx_dev->write_pending = 1; -} - -/** - * ifx_spi_free_port - free up the tty side - * @ifx_dev: IFX device going away - * - * Unregister and free up a port when the device goes away - */ -static void ifx_spi_free_port(struct ifx_spi_device *ifx_dev) -{ - if (ifx_dev->tty_dev) - tty_unregister_device(tty_drv, ifx_dev->minor); - tty_port_destroy(&ifx_dev->tty_port); - kfifo_free(&ifx_dev->tx_fifo); -} - -/** - * ifx_spi_create_port - create a new port - * @ifx_dev: our spi device - * - * Allocate and initialise the tty port that goes with this interface - * and add it to the tty layer so that it can be opened. - */ -static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev) -{ - int ret = 0; - struct tty_port *pport = &ifx_dev->tty_port; - - spin_lock_init(&ifx_dev->fifo_lock); - lockdep_set_class_and_subclass(&ifx_dev->fifo_lock, - &ifx_spi_key, 0); - - if (kfifo_alloc(&ifx_dev->tx_fifo, IFX_SPI_FIFO_SIZE, GFP_KERNEL)) { - ret = -ENOMEM; - goto error_ret; - } - - tty_port_init(pport); - pport->ops = &ifx_tty_port_ops; - ifx_dev->minor = IFX_SPI_TTY_ID; - ifx_dev->tty_dev = tty_port_register_device(pport, tty_drv, - ifx_dev->minor, &ifx_dev->spi_dev->dev); - if (IS_ERR(ifx_dev->tty_dev)) { - dev_dbg(&ifx_dev->spi_dev->dev, - "%s: registering tty device failed", __func__); - ret = PTR_ERR(ifx_dev->tty_dev); - goto error_port; - } - return 0; - -error_port: - tty_port_destroy(pport); -error_ret: - ifx_spi_free_port(ifx_dev); - return ret; -} - -/** - * ifx_spi_handle_srdy - handle SRDY - * @ifx_dev: device asserting SRDY - * - * Check our device state and see what we need to kick off when SRDY - * is asserted. This usually means killing the timer and firing off the - * I/O processing. - */ -static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev) -{ - if (test_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) { - del_timer(&ifx_dev->spi_timer); - clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); - } - - ifx_spi_power_state_set(ifx_dev, IFX_SPI_POWER_SRDY); - - if (!test_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags)) - tasklet_schedule(&ifx_dev->io_work_tasklet); - else - set_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags); -} - -/** - * ifx_spi_srdy_interrupt - SRDY asserted - * @irq: our IRQ number - * @dev: our ifx device - * - * The modem asserted SRDY. Handle the srdy event - */ -static irqreturn_t ifx_spi_srdy_interrupt(int irq, void *dev) -{ - struct ifx_spi_device *ifx_dev = dev; - ifx_dev->gpio.unack_srdy_int_nb++; - ifx_spi_handle_srdy(ifx_dev); - return IRQ_HANDLED; -} - -/** - * ifx_spi_reset_interrupt - Modem has changed reset state - * @irq: interrupt number - * @dev: our device pointer - * - * The modem has either entered or left reset state. Check the GPIO - * line to see which. - * - * FIXME: review locking on MR_INPROGRESS versus - * parallel unsolicited reset/solicited reset - */ -static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev) -{ - struct ifx_spi_device *ifx_dev = dev; - int val = gpiod_get_value(ifx_dev->gpio.reset_out); - int solreset = test_bit(MR_START, &ifx_dev->mdm_reset_state); - - if (val == 0) { - /* entered reset */ - set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); - if (!solreset) { - /* unsolicited reset */ - tty_port_tty_hangup(&ifx_dev->tty_port, false); - } - } else { - /* exited reset */ - clear_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); - if (solreset) { - set_bit(MR_COMPLETE, &ifx_dev->mdm_reset_state); - wake_up(&ifx_dev->mdm_reset_wait); - } - } - return IRQ_HANDLED; -} - -/** - * ifx_spi_free_device - free device - * @ifx_dev: device to free - * - * Free the IFX device - */ -static void ifx_spi_free_device(struct ifx_spi_device *ifx_dev) -{ - ifx_spi_free_port(ifx_dev); - dma_free_coherent(&ifx_dev->spi_dev->dev, - IFX_SPI_TRANSFER_SIZE, - ifx_dev->tx_buffer, - ifx_dev->tx_bus); - dma_free_coherent(&ifx_dev->spi_dev->dev, - IFX_SPI_TRANSFER_SIZE, - ifx_dev->rx_buffer, - ifx_dev->rx_bus); -} - -/** - * ifx_spi_reset - reset modem - * @ifx_dev: modem to reset - * - * Perform a reset on the modem - */ -static int ifx_spi_reset(struct ifx_spi_device *ifx_dev) -{ - int ret; - /* - * set up modem power, reset - * - * delays are required on some platforms for the modem - * to reset properly - */ - set_bit(MR_START, &ifx_dev->mdm_reset_state); - gpiod_set_value(ifx_dev->gpio.po, 0); - gpiod_set_value(ifx_dev->gpio.reset, 0); - msleep(25); - gpiod_set_value(ifx_dev->gpio.reset, 1); - msleep(1); - gpiod_set_value(ifx_dev->gpio.po, 1); - msleep(1); - gpiod_set_value(ifx_dev->gpio.po, 0); - ret = wait_event_timeout(ifx_dev->mdm_reset_wait, - test_bit(MR_COMPLETE, - &ifx_dev->mdm_reset_state), - IFX_RESET_TIMEOUT); - if (!ret) - dev_warn(&ifx_dev->spi_dev->dev, "Modem reset timeout: (state:%lx)", - ifx_dev->mdm_reset_state); - - ifx_dev->mdm_reset_state = 0; - return ret; -} - -/** - * ifx_spi_spi_probe - probe callback - * @spi: our possible matching SPI device - * - * Probe for a 6x60 modem on SPI bus. Perform any needed device and - * GPIO setup. - * - * FIXME: - * - Support for multiple devices - * - Split out MID specific GPIO handling eventually - */ - -static int ifx_spi_spi_probe(struct spi_device *spi) -{ - int ret; - int srdy; - struct ifx_modem_platform_data *pl_data; - struct ifx_spi_device *ifx_dev; - struct device *dev = &spi->dev; - - if (saved_ifx_dev) { - dev_dbg(dev, "ignoring subsequent detection"); - return -ENODEV; - } - - pl_data = dev_get_platdata(dev); - if (!pl_data) { - dev_err(dev, "missing platform data!"); - return -ENODEV; - } - - /* initialize structure to hold our device variables */ - ifx_dev = kzalloc(sizeof(struct ifx_spi_device), GFP_KERNEL); - if (!ifx_dev) { - dev_err(dev, "spi device allocation failed"); - return -ENOMEM; - } - saved_ifx_dev = ifx_dev; - ifx_dev->spi_dev = spi; - clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags); - spin_lock_init(&ifx_dev->write_lock); - spin_lock_init(&ifx_dev->power_lock); - ifx_dev->power_status = 0; - timer_setup(&ifx_dev->spi_timer, ifx_spi_timeout, 0); - ifx_dev->modem = pl_data->modem_type; - ifx_dev->use_dma = pl_data->use_dma; - ifx_dev->max_hz = pl_data->max_hz; - /* initialize spi mode, etc */ - spi->max_speed_hz = ifx_dev->max_hz; - spi->mode = IFX_SPI_MODE | (SPI_LOOP & spi->mode); - spi->bits_per_word = spi_bpw; - ret = spi_setup(spi); - if (ret) { - dev_err(dev, "SPI setup wasn't successful %d", ret); - kfree(ifx_dev); - return -ENODEV; - } - - /* init swap_buf function according to word width configuration */ - if (spi->bits_per_word == 32) - ifx_dev->swap_buf = swap_buf_32; - else if (spi->bits_per_word == 16) - ifx_dev->swap_buf = swap_buf_16; - else - ifx_dev->swap_buf = swap_buf_8; - - /* ensure SPI protocol flags are initialized to enable transfer */ - ifx_dev->spi_more = 0; - ifx_dev->spi_slave_cts = 0; - - /*initialize transfer and dma buffers */ - ifx_dev->tx_buffer = dma_alloc_coherent(ifx_dev->spi_dev->dev.parent, - IFX_SPI_TRANSFER_SIZE, - &ifx_dev->tx_bus, - GFP_KERNEL); - if (!ifx_dev->tx_buffer) { - dev_err(dev, "DMA-TX buffer allocation failed"); - ret = -ENOMEM; - goto error_ret; - } - ifx_dev->rx_buffer = dma_alloc_coherent(ifx_dev->spi_dev->dev.parent, - IFX_SPI_TRANSFER_SIZE, - &ifx_dev->rx_bus, - GFP_KERNEL); - if (!ifx_dev->rx_buffer) { - dev_err(dev, "DMA-RX buffer allocation failed"); - ret = -ENOMEM; - goto error_ret; - } - - /* initialize waitq for modem reset */ - init_waitqueue_head(&ifx_dev->mdm_reset_wait); - - spi_set_drvdata(spi, ifx_dev); - tasklet_setup(&ifx_dev->io_work_tasklet, ifx_spi_io); - - set_bit(IFX_SPI_STATE_PRESENT, &ifx_dev->flags); - - /* create our tty port */ - ret = ifx_spi_create_port(ifx_dev); - if (ret != 0) { - dev_err(dev, "create default tty port failed"); - goto error_ret; - } - - ifx_dev->gpio.reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ifx_dev->gpio.reset)) { - dev_err(dev, "could not obtain reset GPIO\n"); - ret = PTR_ERR(ifx_dev->gpio.reset); - goto error_ret; - } - gpiod_set_consumer_name(ifx_dev->gpio.reset, "ifxModem reset"); - ifx_dev->gpio.po = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); - if (IS_ERR(ifx_dev->gpio.po)) { - dev_err(dev, "could not obtain power GPIO\n"); - ret = PTR_ERR(ifx_dev->gpio.po); - goto error_ret; - } - gpiod_set_consumer_name(ifx_dev->gpio.po, "ifxModem power"); - ifx_dev->gpio.mrdy = devm_gpiod_get(dev, "mrdy", GPIOD_OUT_LOW); - if (IS_ERR(ifx_dev->gpio.mrdy)) { - dev_err(dev, "could not obtain mrdy GPIO\n"); - ret = PTR_ERR(ifx_dev->gpio.mrdy); - goto error_ret; - } - gpiod_set_consumer_name(ifx_dev->gpio.mrdy, "ifxModem mrdy"); - ifx_dev->gpio.srdy = devm_gpiod_get(dev, "srdy", GPIOD_IN); - if (IS_ERR(ifx_dev->gpio.srdy)) { - dev_err(dev, "could not obtain srdy GPIO\n"); - ret = PTR_ERR(ifx_dev->gpio.srdy); - goto error_ret; - } - gpiod_set_consumer_name(ifx_dev->gpio.srdy, "ifxModem srdy"); - ifx_dev->gpio.reset_out = devm_gpiod_get(dev, "rst_out", GPIOD_IN); - if (IS_ERR(ifx_dev->gpio.reset_out)) { - dev_err(dev, "could not obtain rst_out GPIO\n"); - ret = PTR_ERR(ifx_dev->gpio.reset_out); - goto error_ret; - } - gpiod_set_consumer_name(ifx_dev->gpio.reset_out, "ifxModem reset out"); - ifx_dev->gpio.pmu_reset = devm_gpiod_get(dev, "pmu_reset", GPIOD_ASIS); - if (IS_ERR(ifx_dev->gpio.pmu_reset)) { - dev_err(dev, "could not obtain pmu_reset GPIO\n"); - ret = PTR_ERR(ifx_dev->gpio.pmu_reset); - goto error_ret; - } - gpiod_set_consumer_name(ifx_dev->gpio.pmu_reset, "ifxModem PMU reset"); - - ret = request_irq(gpiod_to_irq(ifx_dev->gpio.reset_out), - ifx_spi_reset_interrupt, - IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DRVNAME, - ifx_dev); - if (ret) { - dev_err(dev, "Unable to get irq %x\n", - gpiod_to_irq(ifx_dev->gpio.reset_out)); - goto error_ret; - } - - ret = ifx_spi_reset(ifx_dev); - - ret = request_irq(gpiod_to_irq(ifx_dev->gpio.srdy), - ifx_spi_srdy_interrupt, IRQF_TRIGGER_RISING, DRVNAME, - ifx_dev); - if (ret) { - dev_err(dev, "Unable to get irq %x", - gpiod_to_irq(ifx_dev->gpio.srdy)); - goto error_ret2; - } - - /* set pm runtime power state and register with power system */ - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - /* handle case that modem is already signaling SRDY */ - /* no outgoing tty open at this point, this just satisfies the - * modem's read and should reset communication properly - */ - srdy = gpiod_get_value(ifx_dev->gpio.srdy); - - if (srdy) { - mrdy_assert(ifx_dev); - ifx_spi_handle_srdy(ifx_dev); - } else - mrdy_set_low(ifx_dev); - return 0; - -error_ret2: - free_irq(gpiod_to_irq(ifx_dev->gpio.reset_out), ifx_dev); -error_ret: - ifx_spi_free_device(ifx_dev); - saved_ifx_dev = NULL; - return ret; -} - -/** - * ifx_spi_spi_remove - SPI device was removed - * @spi: SPI device - * - * FIXME: We should be shutting the device down here not in - * the module unload path. - */ - -static int ifx_spi_spi_remove(struct spi_device *spi) -{ - struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi); - /* stop activity */ - tasklet_kill(&ifx_dev->io_work_tasklet); - - pm_runtime_disable(&spi->dev); - - /* free irq */ - free_irq(gpiod_to_irq(ifx_dev->gpio.reset_out), ifx_dev); - free_irq(gpiod_to_irq(ifx_dev->gpio.srdy), ifx_dev); - - /* free allocations */ - ifx_spi_free_device(ifx_dev); - - saved_ifx_dev = NULL; - return 0; -} - -/** - * ifx_spi_spi_shutdown - called on SPI shutdown - * @spi: SPI device - * - * No action needs to be taken here - */ - -static void ifx_spi_spi_shutdown(struct spi_device *spi) -{ - struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi); - - ifx_modem_power_off(ifx_dev); -} - -/* - * various suspends and resumes have nothing to do - * no hardware to save state for - */ - -/** - * ifx_spi_pm_suspend - suspend modem on system suspend - * @dev: device being suspended - * - * Suspend the modem. No action needed on Intel MID platforms, may - * need extending for other systems. - */ -static int ifx_spi_pm_suspend(struct device *dev) -{ - return 0; -} - -/** - * ifx_spi_pm_resume - resume modem on system resume - * @dev: device being suspended - * - * Allow the modem to resume. No action needed. - * - * FIXME: do we need to reset anything here ? - */ -static int ifx_spi_pm_resume(struct device *dev) -{ - return 0; -} - -/** - * ifx_spi_pm_runtime_resume - suspend modem - * @dev: device being suspended - * - * Allow the modem to resume. No action needed. - */ -static int ifx_spi_pm_runtime_resume(struct device *dev) -{ - return 0; -} - -/** - * ifx_spi_pm_runtime_suspend - suspend modem - * @dev: device being suspended - * - * Allow the modem to suspend and thus suspend to continue up the - * device tree. - */ -static int ifx_spi_pm_runtime_suspend(struct device *dev) -{ - return 0; -} - -/** - * ifx_spi_pm_runtime_idle - check if modem idle - * @dev: our device - * - * Check conditions and queue runtime suspend if idle. - */ -static int ifx_spi_pm_runtime_idle(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi); - - if (!ifx_dev->power_status) - pm_runtime_suspend(dev); - - return 0; -} - -static const struct dev_pm_ops ifx_spi_pm = { - .resume = ifx_spi_pm_resume, - .suspend = ifx_spi_pm_suspend, - .runtime_resume = ifx_spi_pm_runtime_resume, - .runtime_suspend = ifx_spi_pm_runtime_suspend, - .runtime_idle = ifx_spi_pm_runtime_idle -}; - -static const struct spi_device_id ifx_id_table[] = { - {"ifx6160", 0}, - {"ifx6260", 0}, - { } -}; -MODULE_DEVICE_TABLE(spi, ifx_id_table); - -/* spi operations */ -static struct spi_driver ifx_spi_driver = { - .driver = { - .name = DRVNAME, - .pm = &ifx_spi_pm, - }, - .probe = ifx_spi_spi_probe, - .shutdown = ifx_spi_spi_shutdown, - .remove = ifx_spi_spi_remove, - .id_table = ifx_id_table -}; - -/** - * ifx_spi_exit - module exit - * - * Unload the module. - */ - -static void __exit ifx_spi_exit(void) -{ - /* unregister */ - spi_unregister_driver(&ifx_spi_driver); - tty_unregister_driver(tty_drv); - put_tty_driver(tty_drv); - unregister_reboot_notifier(&ifx_modem_reboot_notifier_block); -} - -/** - * ifx_spi_init - module entry point - * - * Initialise the SPI and tty interfaces for the IFX SPI driver - * We need to initialize upper-edge spi driver after the tty - * driver because otherwise the spi probe will race - */ - -static int __init ifx_spi_init(void) -{ - int result; - - tty_drv = alloc_tty_driver(1); - if (!tty_drv) { - pr_err("%s: alloc_tty_driver failed", DRVNAME); - return -ENOMEM; - } - - tty_drv->driver_name = DRVNAME; - tty_drv->name = TTYNAME; - tty_drv->minor_start = IFX_SPI_TTY_ID; - tty_drv->type = TTY_DRIVER_TYPE_SERIAL; - tty_drv->subtype = SERIAL_TYPE_NORMAL; - tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - tty_drv->init_termios = tty_std_termios; - - tty_set_operations(tty_drv, &ifx_spi_serial_ops); - - result = tty_register_driver(tty_drv); - if (result) { - pr_err("%s: tty_register_driver failed(%d)", - DRVNAME, result); - goto err_free_tty; - } - - result = spi_register_driver(&ifx_spi_driver); - if (result) { - pr_err("%s: spi_register_driver failed(%d)", - DRVNAME, result); - goto err_unreg_tty; - } - - result = register_reboot_notifier(&ifx_modem_reboot_notifier_block); - if (result) { - pr_err("%s: register ifx modem reboot notifier failed(%d)", - DRVNAME, result); - goto err_unreg_spi; - } - - return 0; -err_unreg_spi: - spi_unregister_driver(&ifx_spi_driver); -err_unreg_tty: - tty_unregister_driver(tty_drv); -err_free_tty: - put_tty_driver(tty_drv); - - return result; -} - -module_init(ifx_spi_init); -module_exit(ifx_spi_exit); - -MODULE_AUTHOR("Intel"); -MODULE_DESCRIPTION("IFX6x60 spi driver"); -MODULE_LICENSE("GPL"); -MODULE_INFO(Version, "0.1-IFX6x60"); diff --git a/drivers/tty/serial/ifx6x60.h b/drivers/tty/serial/ifx6x60.h deleted file mode 100644 index ecb841d928a7..000000000000 --- a/drivers/tty/serial/ifx6x60.h +++ /dev/null @@ -1,118 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/**************************************************************************** - * - * Driver for the IFX spi modem. - * - * Copyright (C) 2009, 2010 Intel Corp - * Jim Stanley - * - *****************************************************************************/ -#ifndef _IFX6X60_H -#define _IFX6X60_H - -struct gpio_desc; - -#define DRVNAME "ifx6x60" -#define TTYNAME "ttyIFX" - -#define IFX_SPI_MAX_MINORS 1 -#define IFX_SPI_TRANSFER_SIZE 2048 -#define IFX_SPI_FIFO_SIZE 4096 - -#define IFX_SPI_HEADER_OVERHEAD 4 -#define IFX_RESET_TIMEOUT msecs_to_jiffies(50) - -/* device flags bitfield definitions */ -#define IFX_SPI_STATE_PRESENT 0 -#define IFX_SPI_STATE_IO_IN_PROGRESS 1 -#define IFX_SPI_STATE_IO_READY 2 -#define IFX_SPI_STATE_TIMER_PENDING 3 -#define IFX_SPI_STATE_IO_AVAILABLE 4 - -/* flow control bitfields */ -#define IFX_SPI_DCD 0 -#define IFX_SPI_CTS 1 -#define IFX_SPI_DSR 2 -#define IFX_SPI_RI 3 -#define IFX_SPI_DTR 4 -#define IFX_SPI_RTS 5 -#define IFX_SPI_TX_FC 6 -#define IFX_SPI_RX_FC 7 -#define IFX_SPI_UPDATE 8 - -#define IFX_SPI_PAYLOAD_SIZE (IFX_SPI_TRANSFER_SIZE - \ - IFX_SPI_HEADER_OVERHEAD) - -#define IFX_SPI_IRQ_TYPE DETECT_EDGE_RISING -#define IFX_SPI_GPIO_TARGET 0 -#define IFX_SPI_GPIO0 0x105 - -#define IFX_SPI_STATUS_TIMEOUT (2000*HZ) - -/* values for bits in power status byte */ -#define IFX_SPI_POWER_DATA_PENDING 1 -#define IFX_SPI_POWER_SRDY 2 - -struct ifx_spi_device { - /* Our SPI device */ - struct spi_device *spi_dev; - - /* Port specific data */ - struct kfifo tx_fifo; - spinlock_t fifo_lock; - unsigned long signal_state; - - /* TTY Layer logic */ - struct tty_port tty_port; - struct device *tty_dev; - int minor; - - /* Low level I/O work */ - struct tasklet_struct io_work_tasklet; - unsigned long flags; - dma_addr_t rx_dma; - dma_addr_t tx_dma; - - int modem; /* Modem type */ - int use_dma; /* provide dma-able addrs in SPI msg */ - long max_hz; /* max SPI frequency */ - - spinlock_t write_lock; - int write_pending; - spinlock_t power_lock; - unsigned char power_status; - - unsigned char *rx_buffer; - unsigned char *tx_buffer; - dma_addr_t rx_bus; - dma_addr_t tx_bus; - unsigned char spi_more; - unsigned char spi_slave_cts; - - struct timer_list spi_timer; - - struct spi_message spi_msg; - struct spi_transfer spi_xfer; - - struct { - /* gpio lines */ - struct gpio_desc *srdy; /* slave-ready gpio */ - struct gpio_desc *mrdy; /* master-ready gpio */ - struct gpio_desc *reset; /* modem-reset gpio */ - struct gpio_desc *po; /* modem-on gpio */ - struct gpio_desc *reset_out; /* modem-in-reset gpio */ - struct gpio_desc *pmu_reset; /* PMU reset gpio */ - /* state/stats */ - int unack_srdy_int_nb; - } gpio; - - /* modem reset */ - unsigned long mdm_reset_state; -#define MR_START 0 -#define MR_INPROGRESS 1 -#define MR_COMPLETE 2 - wait_queue_head_t mdm_reset_wait; - void (*swap_buf)(unsigned char *buf, int len, void *end); -}; - -#endif /* _IFX6X60_H */ -- cgit From e0f2a902c9f02fcb36c22f63e0db67e73375c843 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Thu, 21 Jan 2021 15:23:09 +0100 Subject: serial: stm32: improve platform_get_irq condition handling in init_port Replace "ret" variable by "irq" variable from platform_get_irq condition handling in stm32_init_port as suggested by Jiri in "STM32 uart cleanup and improvement" series review. This change will prevent port->irq to be unexpectly modified by a potential change of "ret" value introduced by a new patch. Suggested-by: Jiri Slaby Signed-off-by: Erwan Le Ray Link: https://lore.kernel.org/r/20210121142309.6327-1-erwan.leray@foss.st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 6a9a5ef5f5ba..dde6d526362d 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -981,11 +981,11 @@ static int stm32_usart_init_port(struct stm32_port *stm32port, { struct uart_port *port = &stm32port->port; struct resource *res; - int ret; + int ret, irq; - ret = platform_get_irq(pdev, 0); - if (ret <= 0) - return ret ? : -ENODEV; + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return irq ? : -ENODEV; port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; @@ -993,7 +993,7 @@ static int stm32_usart_init_port(struct stm32_port *stm32port, port->dev = &pdev->dev; port->fifosize = stm32port->info->cfg.fifosize; port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_STM32_CONSOLE); - port->irq = ret; + port->irq = irq; port->rs485_config = stm32_usart_config_rs485; ret = stm32_usart_init_rs485(port, pdev); -- cgit From 9159835a978f4092bf00a69b51256e69c961edb9 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 27 Jan 2021 17:42:22 +0100 Subject: vt: keyboard, use new API for keyboard_tasklet This converts the keyboard_tasklet to use the new API in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") The new API changes the argument passed to the callback function, but fortunately the argument isn't used so it is straight forward to use DECLARE_TASKLET_DISABLED() rather than DECLARE_TASKLET_DISABLED_OLD(). Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20210127164222.13220-1-kernel@esmil.dk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 9f2eaa104ebc..77638629c562 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -131,8 +131,8 @@ static const unsigned char max_vals[] = { static const int NR_TYPES = ARRAY_SIZE(max_vals); -static void kbd_bh(unsigned long dummy); -static DECLARE_TASKLET_DISABLED_OLD(keyboard_tasklet, kbd_bh); +static void kbd_bh(struct tasklet_struct *unused); +static DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh); static struct input_handler kbd_handler; static DEFINE_SPINLOCK(kbd_event_lock); @@ -1245,7 +1245,7 @@ void vt_kbd_con_stop(int console) * handle the scenario when keyboard handler is not registered yet * but we already getting updates from the VT to update led state. */ -static void kbd_bh(unsigned long dummy) +static void kbd_bh(struct tasklet_struct *unused) { unsigned int leds; unsigned long flags; -- cgit From 151db8c08c90405c14384bbdc5acb136bc42dbf8 Mon Sep 17 00:00:00 2001 From: "Ahmed S. Darwish" Date: Mon, 8 Feb 2021 19:16:14 +0100 Subject: vt_ioctl: Remove in_interrupt() check reset_vc() uses a "!in_interrupt()" conditional before resetting the palettes, which is a blocking operation. Since commit 8b6312f4dcc1e ("[PATCH] vt: refactor console SAK processing") all calls are invoked from a workqueue process context, with the blocking console lock always acquired. Remove the "!in_interrupt()" check. Signed-off-by: Ahmed S. Darwish Signed-off-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20210208181615.381861-2-bigeasy@linutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt_ioctl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 4a4cbd4a5f37..89aeaf3c1bca 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -930,8 +930,7 @@ void reset_vc(struct vc_data *vc) put_pid(vc->vt_pid); vc->vt_pid = NULL; vc->vt_newvt = -1; - if (!in_interrupt()) /* Via keyboard.c:SAK() - akpm */ - reset_palette(vc); + reset_palette(vc); } void vc_SAK(struct work_struct *work) -- cgit From a157270fbf37f822e1fa9e9faa8ed8c81da1eb28 Mon Sep 17 00:00:00 2001 From: "Ahmed S. Darwish" Date: Mon, 8 Feb 2021 19:16:15 +0100 Subject: serial: core: Remove BUG_ON(in_interrupt()) check The usage of in_interrupt() in drivers is phased out for various reasons. In both exported functions where BUG_ON(in_interrupt()) is invoked, there is a mutex_lock() afterwards. mutex_lock() contains a might_sleep() which will already trigger a stack trace if the target functions is called from atomic context. Remove the BUG_ON() and add a "Context: " in the kernel-doc instead. Signed-off-by: Ahmed S. Darwish Signed-off-by: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/20210208181615.381861-3-bigeasy@linutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 7dacdb6a8534..ba31e97d3d96 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2848,6 +2848,8 @@ static const struct attribute_group tty_dev_attr_group = { * @drv: pointer to the uart low level driver structure for this port * @uport: uart port structure to use for this port. * + * Context: task context, might sleep + * * This allows the driver to register its own uart_port structure * with the core driver. The main purpose is to allow the low * level uart drivers to expand uart_port, rather than having yet @@ -2861,8 +2863,6 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) struct device *tty_dev; int num_groups; - BUG_ON(in_interrupt()); - if (uport->line >= drv->nr) return -EINVAL; @@ -2951,6 +2951,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) * @drv: pointer to the uart low level driver structure for this port * @uport: uart port structure for this port * + * Context: task context, might sleep + * * This unhooks (and hangs up) the specified port structure from the * core driver. No further calls will be made to the low-level code * for this port. @@ -2963,8 +2965,6 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) struct tty_struct *tty; int ret = 0; - BUG_ON(in_interrupt()); - mutex_lock(&port_mutex); /* -- cgit