From 4bb1ce2350a598502b23088b169e16b43d4bc639 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 3 Sep 2018 15:10:49 +0200 Subject: serial: uartps: console_setup() can't be placed to init section When console device is rebinded, console_setup() is called again. But marking it as __init means that function will be clear after boot is complete. If console device is binded again console_setup() is not found and error "Unable to handle kernel paging request at virtual address" is reported. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index a48f19b1b88f..fdb984b9eb28 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1217,7 +1217,7 @@ static void cdns_uart_console_write(struct console *co, const char *s, * * Return: 0 on success, negative errno otherwise. */ -static int __init cdns_uart_console_setup(struct console *co, char *options) +static int cdns_uart_console_setup(struct console *co, char *options) { struct uart_port *port = console_port; -- cgit v1.2.3 From 77ec669f257b2d71a641a5c67fc9ca826a8e227e Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 3 Sep 2018 15:10:50 +0200 Subject: serial: uartps: Do not initialize field to zero again Writing zero and NULLs to already initialized fields is not needed. Remove this additional writes. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index fdb984b9eb28..edb9c2aeee72 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1503,15 +1503,12 @@ static int cdns_uart_probe(struct platform_device *pdev) /* At this point, we've got an empty uart_port struct, initialize it */ spin_lock_init(&port->lock); - port->membase = NULL; - port->irq = 0; port->type = PORT_UNKNOWN; port->iotype = UPIO_MEM32; port->flags = UPF_BOOT_AUTOCONF; port->ops = &cdns_uart_ops; port->fifosize = CDNS_UART_FIFO_SIZE; port->line = id; - port->dev = NULL; /* * Register the port. -- cgit v1.2.3 From 4b9d33c6a30688344a3e95179654ea31b07f59b7 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Mon, 3 Sep 2018 15:10:51 +0200 Subject: serial: uartps: Fix suspend functionality The driver's suspend/resume functions were buggy. If UART node contains any child node in the DT and the child is established a communication path with the parent UART. The relevant /dev/ttyPS* node will be not available for other operations. If the driver is trying to do any operations like suspend/resume without checking the tty->dev status it leads to the kernel crash/hang. This patch fix this issue by call the device_may_wake() with the generic parameter of type struct device. in the uart suspend and resume paths. It also fixes a race condition in the uart suspend path(i.e uart_suspend_port() should be called at the end of cdns_uart_suspend API this path updates the same) Signed-off-by: Nava kishore Manne Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 41 +++++++++++--------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index edb9c2aeee72..251d061c00fe 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1273,24 +1273,11 @@ static struct uart_driver cdns_uart_uart_driver = { static int cdns_uart_suspend(struct device *device) { struct uart_port *port = dev_get_drvdata(device); - struct tty_struct *tty; - struct device *tty_dev; - int may_wake = 0; - - /* Get the tty which could be NULL so don't assume it's valid */ - tty = tty_port_tty_get(&port->state->port); - if (tty) { - tty_dev = tty->dev; - may_wake = device_may_wakeup(tty_dev); - tty_kref_put(tty); - } + int may_wake; - /* - * Call the API provided in serial_core.c file which handles - * the suspend. - */ - uart_suspend_port(&cdns_uart_uart_driver, port); - if (!(console_suspend_enabled && !may_wake)) { + may_wake = device_may_wakeup(device); + + if (console_suspend_enabled && may_wake) { unsigned long flags = 0; spin_lock_irqsave(&port->lock, flags); @@ -1305,7 +1292,11 @@ static int cdns_uart_suspend(struct device *device) spin_unlock_irqrestore(&port->lock, flags); } - return 0; + /* + * Call the API provided in serial_core.c file which handles + * the suspend. + */ + return uart_suspend_port(&cdns_uart_uart_driver, port); } /** @@ -1319,17 +1310,9 @@ static int cdns_uart_resume(struct device *device) struct uart_port *port = dev_get_drvdata(device); unsigned long flags = 0; u32 ctrl_reg; - struct tty_struct *tty; - struct device *tty_dev; - int may_wake = 0; - - /* Get the tty which could be NULL so don't assume it's valid */ - tty = tty_port_tty_get(&port->state->port); - if (tty) { - tty_dev = tty->dev; - may_wake = device_may_wakeup(tty_dev); - tty_kref_put(tty); - } + int may_wake; + + may_wake = device_may_wakeup(device); if (console_suspend_enabled && !may_wake) { struct cdns_uart *cdns_uart = port->private_data; -- cgit v1.2.3 From 46a460f0150ab4751d19923ce1199e787ee21879 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 3 Sep 2018 15:10:52 +0200 Subject: serial: uartps: Do not use static struct uart_driver out of probe() cdns_uart_suspend()/resume() and remove() are using static reference to struct uart_driver. Assign this reference to private data structure as preparation step for dynamic struct uart_driver allocation. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 251d061c00fe..12d6033df144 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -180,6 +180,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * @port: Pointer to the UART port * @uartclk: Reference clock * @pclk: APB clock + * @cdns_uart_driver: Pointer to UART driver * @baud: Current baud rate * @clk_rate_change_nb: Notifier block for clock changes * @quirks: Flags for RXBS support. @@ -188,6 +189,7 @@ struct cdns_uart { struct uart_port *port; struct clk *uartclk; struct clk *pclk; + struct uart_driver *cdns_uart_driver; unsigned int baud; struct notifier_block clk_rate_change_nb; u32 quirks; @@ -1273,6 +1275,7 @@ static struct uart_driver cdns_uart_uart_driver = { static int cdns_uart_suspend(struct device *device) { struct uart_port *port = dev_get_drvdata(device); + struct cdns_uart *cdns_uart = port->private_data; int may_wake; may_wake = device_may_wakeup(device); @@ -1296,7 +1299,7 @@ static int cdns_uart_suspend(struct device *device) * Call the API provided in serial_core.c file which handles * the suspend. */ - return uart_suspend_port(&cdns_uart_uart_driver, port); + return uart_suspend_port(cdns_uart->cdns_uart_driver, port); } /** @@ -1308,6 +1311,7 @@ static int cdns_uart_suspend(struct device *device) static int cdns_uart_resume(struct device *device) { struct uart_port *port = dev_get_drvdata(device); + struct cdns_uart *cdns_uart = port->private_data; unsigned long flags = 0; u32 ctrl_reg; int may_wake; @@ -1315,8 +1319,6 @@ static int cdns_uart_resume(struct device *device) may_wake = device_may_wakeup(device); if (console_suspend_enabled && !may_wake) { - struct cdns_uart *cdns_uart = port->private_data; - clk_enable(cdns_uart->pclk); clk_enable(cdns_uart->uartclk); @@ -1350,7 +1352,7 @@ static int cdns_uart_resume(struct device *device) spin_unlock_irqrestore(&port->lock, flags); } - return uart_resume_port(&cdns_uart_uart_driver, port); + return uart_resume_port(cdns_uart->cdns_uart_driver, port); } #endif /* ! CONFIG_PM_SLEEP */ static int __maybe_unused cdns_runtime_suspend(struct device *dev) @@ -1414,6 +1416,8 @@ static int cdns_uart_probe(struct platform_device *pdev) if (!port) return -ENOMEM; + cdns_uart_data->cdns_uart_driver = &cdns_uart_uart_driver; + match = of_match_node(cdns_uart_of_match, pdev->dev.of_node); if (match && match->data) { const struct cdns_platform_data *data = match->data; @@ -1571,7 +1575,7 @@ static int cdns_uart_remove(struct platform_device *pdev) clk_notifier_unregister(cdns_uart_data->uartclk, &cdns_uart_data->clk_rate_change_nb); #endif - rc = uart_remove_one_port(&cdns_uart_uart_driver, port); + rc = uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port); port->mapbase = 0; clk_disable_unprepare(cdns_uart_data->uartclk); clk_disable_unprepare(cdns_uart_data->pclk); -- cgit v1.2.3 From 14090ad1805f93418f69d2f661b6b9bb130b7824 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 3 Sep 2018 15:10:53 +0200 Subject: serial: uartps: Move alias reading higher in probe() This cosmetic change is done only for having next patch much easier to read. Moving id setup higher in probe is not affecting any usage of this driver and it also simplify error path. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 12d6033df144..dff39b424662 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1416,6 +1416,16 @@ static int cdns_uart_probe(struct platform_device *pdev) if (!port) return -ENOMEM; + /* Look for a serialN alias */ + id = of_alias_get_id(pdev->dev.of_node, "serial"); + if (id < 0) + id = 0; + + if (id >= CDNS_UART_NR_PORTS) { + dev_err(&pdev->dev, "Cannot get uart_port structure\n"); + return -ENODEV; + } + cdns_uart_data->cdns_uart_driver = &cdns_uart_uart_driver; match = of_match_node(cdns_uart_of_match, pdev->dev.of_node); @@ -1477,16 +1487,6 @@ static int cdns_uart_probe(struct platform_device *pdev) &cdns_uart_data->clk_rate_change_nb)) dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); #endif - /* Look for a serialN alias */ - id = of_alias_get_id(pdev->dev.of_node, "serial"); - if (id < 0) - id = 0; - - if (id >= CDNS_UART_NR_PORTS) { - dev_err(&pdev->dev, "Cannot get uart_port structure\n"); - rc = -ENODEV; - goto err_out_notif_unreg; - } /* At this point, we've got an empty uart_port struct, initialize it */ spin_lock_init(&port->lock); @@ -1545,7 +1545,6 @@ err_out_pm_disable: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); -err_out_notif_unreg: #ifdef CONFIG_COMMON_CLK clk_notifier_unregister(cdns_uart_data->uartclk, &cdns_uart_data->clk_rate_change_nb); -- cgit v1.2.3 From e4bbb5194ea3bc77ab843fe01c9f243cf8c6c6b2 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 3 Sep 2018 15:10:54 +0200 Subject: serial: uartps: Move register to probe based on run time detection Register uart driver in probe to be able to register one device with unique major/minor separately. Also calculate number of instances of this driver to be able to call uart_unregister_driver() when there is no instance. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 42 +++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index dff39b424662..382f11c6720c 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1394,6 +1394,9 @@ static const struct of_device_id cdns_uart_of_match[] = { }; MODULE_DEVICE_TABLE(of, cdns_uart_of_match); +/* Temporary variable for storing number of instances */ +static int instances; + /** * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure @@ -1426,6 +1429,14 @@ static int cdns_uart_probe(struct platform_device *pdev) return -ENODEV; } + if (!cdns_uart_uart_driver.state) { + rc = uart_register_driver(&cdns_uart_uart_driver); + if (rc < 0) { + dev_err(&pdev->dev, "Failed to register driver\n"); + return rc; + } + } + cdns_uart_data->cdns_uart_driver = &cdns_uart_uart_driver; match = of_match_node(cdns_uart_of_match, pdev->dev.of_node); @@ -1443,7 +1454,8 @@ static int cdns_uart_probe(struct platform_device *pdev) } if (IS_ERR(cdns_uart_data->pclk)) { dev_err(&pdev->dev, "pclk clock not found.\n"); - return PTR_ERR(cdns_uart_data->pclk); + rc = PTR_ERR(cdns_uart_data->pclk); + goto err_out_unregister_driver; } cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk"); @@ -1454,13 +1466,14 @@ static int cdns_uart_probe(struct platform_device *pdev) } if (IS_ERR(cdns_uart_data->uartclk)) { dev_err(&pdev->dev, "uart_clk clock not found.\n"); - return PTR_ERR(cdns_uart_data->uartclk); + rc = PTR_ERR(cdns_uart_data->uartclk); + goto err_out_unregister_driver; } rc = clk_prepare_enable(cdns_uart_data->pclk); if (rc) { dev_err(&pdev->dev, "Unable to enable pclk clock.\n"); - return rc; + goto err_out_unregister_driver; } rc = clk_prepare_enable(cdns_uart_data->uartclk); if (rc) { @@ -1539,6 +1552,7 @@ static int cdns_uart_probe(struct platform_device *pdev) console_port = NULL; #endif + instances++; return 0; err_out_pm_disable: @@ -1553,7 +1567,9 @@ err_out_clk_disable: clk_disable_unprepare(cdns_uart_data->uartclk); err_out_clk_dis_pclk: clk_disable_unprepare(cdns_uart_data->pclk); - +err_out_unregister_driver: + if (!instances) + uart_unregister_driver(cdns_uart_data->cdns_uart_driver); return rc; } @@ -1581,6 +1597,8 @@ static int cdns_uart_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); + if (!--instances) + uart_unregister_driver(cdns_uart_data->cdns_uart_driver); return rc; } @@ -1596,28 +1614,14 @@ static struct platform_driver cdns_uart_platform_driver = { static int __init cdns_uart_init(void) { - int retval = 0; - - /* Register the cdns_uart driver with the serial core */ - retval = uart_register_driver(&cdns_uart_uart_driver); - if (retval) - return retval; - /* Register the platform driver */ - retval = platform_driver_register(&cdns_uart_platform_driver); - if (retval) - uart_unregister_driver(&cdns_uart_uart_driver); - - return retval; + return platform_driver_register(&cdns_uart_platform_driver); } static void __exit cdns_uart_exit(void) { /* Unregister the platform driver */ platform_driver_unregister(&cdns_uart_platform_driver); - - /* Unregister the cdns_uart driver */ - uart_unregister_driver(&cdns_uart_uart_driver); } arch_initcall(cdns_uart_init); -- cgit v1.2.3 From 10a5315b47b09ea3c8e69e5bb5b51a829fe84028 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 3 Sep 2018 15:10:55 +0200 Subject: serial: uartps: Fill struct uart_driver in probe() This is preparation step for dynamic port allocation without CDNS_UART_NR_PORTS macro. Fill the structure only once at probe. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 382f11c6720c..945d252b93e3 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1100,6 +1100,8 @@ static const struct uart_ops cdns_uart_ops = { #endif }; +static struct uart_driver cdns_uart_uart_driver; + #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE /** * cdns_uart_console_putchar - write the character to the FIFO buffer @@ -1240,8 +1242,6 @@ static int cdns_uart_console_setup(struct console *co, char *options) return uart_set_options(port, co, baud, parity, bits, flow); } -static struct uart_driver cdns_uart_uart_driver; - static struct console cdns_uart_console = { .name = CDNS_UART_TTY_NAME, .write = cdns_uart_console_write, @@ -1253,18 +1253,6 @@ static struct console cdns_uart_console = { }; #endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */ -static struct uart_driver cdns_uart_uart_driver = { - .owner = THIS_MODULE, - .driver_name = CDNS_UART_NAME, - .dev_name = CDNS_UART_TTY_NAME, - .major = CDNS_UART_MAJOR, - .minor = CDNS_UART_MINOR, - .nr = CDNS_UART_NR_PORTS, -#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE - .cons = &cdns_uart_console, -#endif -}; - #ifdef CONFIG_PM_SLEEP /** * cdns_uart_suspend - suspend event @@ -1430,6 +1418,16 @@ static int cdns_uart_probe(struct platform_device *pdev) } if (!cdns_uart_uart_driver.state) { + cdns_uart_uart_driver.owner = THIS_MODULE; + cdns_uart_uart_driver.driver_name = CDNS_UART_NAME; + cdns_uart_uart_driver.dev_name = CDNS_UART_TTY_NAME; + cdns_uart_uart_driver.major = CDNS_UART_MAJOR; + cdns_uart_uart_driver.minor = CDNS_UART_MINOR; + cdns_uart_uart_driver.nr = CDNS_UART_NR_PORTS; +#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE + cdns_uart_uart_driver.cons = &cdns_uart_console; +#endif + rc = uart_register_driver(&cdns_uart_uart_driver); if (rc < 0) { dev_err(&pdev->dev, "Failed to register driver\n"); -- cgit v1.2.3 From 427c8ae9bebc484080b00523b434c98c23135088 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 3 Sep 2018 15:10:56 +0200 Subject: serial: uartps: Change logic how console_port is setup Change logic how console_port is setup by using CON_ENABLED flag instead of index. There will be unique cdns_uart_console() structures that's why code can't use id for console_port assignment. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 945d252b93e3..654a54a32f10 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1533,7 +1533,7 @@ static int cdns_uart_probe(struct platform_device *pdev) * If register_console() don't assign value, then console_port pointer * is cleanup. */ - if (cdns_uart_uart_driver.cons->index == -1) + if (!console_port) console_port = port; #endif @@ -1546,7 +1546,8 @@ static int cdns_uart_probe(struct platform_device *pdev) #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE /* This is not port which is used for console that's why clean it up */ - if (cdns_uart_uart_driver.cons->index == -1) + if (console_port == port && + !(cdns_uart_uart_driver.cons->flags & CON_ENABLED)) console_port = NULL; #endif @@ -1595,6 +1596,12 @@ static int cdns_uart_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); + +#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE + if (console_port == port) + console_port = NULL; +#endif + if (!--instances) uart_unregister_driver(cdns_uart_data->cdns_uart_driver); return rc; -- cgit v1.2.3 From 024ca329bfb9a948f76eaff3243e21b7e70182f2 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 3 Sep 2018 15:10:57 +0200 Subject: serial: uartps: Register own uart console and driver structures Every instance is registering own struct console and struct uart_driver with minor number which corresponds to alias ID (or 0 now) and with 1 uart port. The same alias ID is saved to tty_driver->name_base which is key field for creating ttyPSX name. Because name_base and minor number are setup already there is no need to setup any port->line number because 0 is the right value. Unfortunately this driver is setting up major number to 0 for using dynamic assignment and kernel is allocating different major numbers for every instance instead of using the same major and different minor number. ~# ls -la /dev/ttyPS* crw------- 1 root root 252, 0 Jan 1 03:36 /dev/ttyPS0 crw--w---- 1 root root 253, 1 Jan 1 00:00 /dev/ttyPS1 When major number is not 0. For example 252 then major/minor combinations are in expected form ~# ls -la /dev/ttyPS* crw------- 1 root root 252, 0 Jan 1 04:04 /dev/ttyPS0 crw--w---- 1 root root 252, 1 Jan 1 00:00 /dev/ttyPS1 Driver is not freeing struct cdns_uart_console in case that instance is not used as console. The reason is that console is incorrectly unregistred and "console [0] disabled" message will be shown. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 93 ++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 654a54a32f10..2f1df9d8bc78 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -30,7 +30,6 @@ #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" #define CDNS_UART_MAJOR 0 /* use dynamic node allocation */ -#define CDNS_UART_MINOR 0 /* works best with devtmpfs */ #define CDNS_UART_NR_PORTS 2 #define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ #define CDNS_UART_REGISTER_SPACE 0x1000 @@ -1100,8 +1099,6 @@ static const struct uart_ops cdns_uart_ops = { #endif }; -static struct uart_driver cdns_uart_uart_driver; - #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE /** * cdns_uart_console_putchar - write the character to the FIFO buffer @@ -1241,16 +1238,6 @@ static int cdns_uart_console_setup(struct console *co, char *options) return uart_set_options(port, co, baud, parity, bits, flow); } - -static struct console cdns_uart_console = { - .name = CDNS_UART_TTY_NAME, - .write = cdns_uart_console_write, - .device = uart_console_device, - .setup = cdns_uart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, /* Specified on the cmdline (e.g. console=ttyPS ) */ - .data = &cdns_uart_uart_driver, -}; #endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */ #ifdef CONFIG_PM_SLEEP @@ -1382,9 +1369,6 @@ static const struct of_device_id cdns_uart_of_match[] = { }; MODULE_DEVICE_TABLE(of, cdns_uart_of_match); -/* Temporary variable for storing number of instances */ -static int instances; - /** * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure @@ -1398,6 +1382,11 @@ static int cdns_uart_probe(struct platform_device *pdev) struct resource *res; struct cdns_uart *cdns_uart_data; const struct of_device_id *match; + struct uart_driver *cdns_uart_uart_driver; + char *driver_name; +#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE + struct console *cdns_uart_console; +#endif cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data), GFP_KERNEL); @@ -1407,6 +1396,12 @@ static int cdns_uart_probe(struct platform_device *pdev) if (!port) return -ENOMEM; + cdns_uart_uart_driver = devm_kzalloc(&pdev->dev, + sizeof(*cdns_uart_uart_driver), + GFP_KERNEL); + if (!cdns_uart_uart_driver) + return -ENOMEM; + /* Look for a serialN alias */ id = of_alias_get_id(pdev->dev.of_node, "serial"); if (id < 0) @@ -1417,25 +1412,50 @@ static int cdns_uart_probe(struct platform_device *pdev) return -ENODEV; } - if (!cdns_uart_uart_driver.state) { - cdns_uart_uart_driver.owner = THIS_MODULE; - cdns_uart_uart_driver.driver_name = CDNS_UART_NAME; - cdns_uart_uart_driver.dev_name = CDNS_UART_TTY_NAME; - cdns_uart_uart_driver.major = CDNS_UART_MAJOR; - cdns_uart_uart_driver.minor = CDNS_UART_MINOR; - cdns_uart_uart_driver.nr = CDNS_UART_NR_PORTS; + /* There is a need to use unique driver name */ + driver_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%d", + CDNS_UART_NAME, id); + if (!driver_name) + return -ENOMEM; + + cdns_uart_uart_driver->owner = THIS_MODULE; + cdns_uart_uart_driver->driver_name = driver_name; + cdns_uart_uart_driver->dev_name = CDNS_UART_TTY_NAME; + cdns_uart_uart_driver->major = CDNS_UART_MAJOR; + cdns_uart_uart_driver->minor = id; + cdns_uart_uart_driver->nr = 1; + #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE - cdns_uart_uart_driver.cons = &cdns_uart_console; + cdns_uart_console = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_console), + GFP_KERNEL); + if (!cdns_uart_console) + return -ENOMEM; + + strncpy(cdns_uart_console->name, CDNS_UART_TTY_NAME, + sizeof(cdns_uart_console->name)); + cdns_uart_console->index = id; + cdns_uart_console->write = cdns_uart_console_write; + cdns_uart_console->device = uart_console_device; + cdns_uart_console->setup = cdns_uart_console_setup; + cdns_uart_console->flags = CON_PRINTBUFFER; + cdns_uart_console->data = cdns_uart_uart_driver; + cdns_uart_uart_driver->cons = cdns_uart_console; #endif - rc = uart_register_driver(&cdns_uart_uart_driver); - if (rc < 0) { - dev_err(&pdev->dev, "Failed to register driver\n"); - return rc; - } + rc = uart_register_driver(cdns_uart_uart_driver); + if (rc < 0) { + dev_err(&pdev->dev, "Failed to register driver\n"); + return rc; } - cdns_uart_data->cdns_uart_driver = &cdns_uart_uart_driver; + cdns_uart_data->cdns_uart_driver = cdns_uart_uart_driver; + + /* + * Setting up proper name_base needs to be done after uart + * registration because tty_driver structure is not filled. + * name_base is 0 by default. + */ + cdns_uart_uart_driver->tty_driver->name_base = id; match = of_match_node(cdns_uart_of_match, pdev->dev.of_node); if (match && match->data) { @@ -1506,7 +1526,6 @@ static int cdns_uart_probe(struct platform_device *pdev) port->flags = UPF_BOOT_AUTOCONF; port->ops = &cdns_uart_ops; port->fifosize = CDNS_UART_FIFO_SIZE; - port->line = id; /* * Register the port. @@ -1537,7 +1556,7 @@ static int cdns_uart_probe(struct platform_device *pdev) console_port = port; #endif - rc = uart_add_one_port(&cdns_uart_uart_driver, port); + rc = uart_add_one_port(cdns_uart_uart_driver, port); if (rc) { dev_err(&pdev->dev, "uart_add_one_port() failed; err=%i\n", rc); @@ -1547,11 +1566,10 @@ static int cdns_uart_probe(struct platform_device *pdev) #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE /* This is not port which is used for console that's why clean it up */ if (console_port == port && - !(cdns_uart_uart_driver.cons->flags & CON_ENABLED)) + !(cdns_uart_uart_driver->cons->flags & CON_ENABLED)) console_port = NULL; #endif - instances++; return 0; err_out_pm_disable: @@ -1567,8 +1585,8 @@ err_out_clk_disable: err_out_clk_dis_pclk: clk_disable_unprepare(cdns_uart_data->pclk); err_out_unregister_driver: - if (!instances) - uart_unregister_driver(cdns_uart_data->cdns_uart_driver); + uart_unregister_driver(cdns_uart_data->cdns_uart_driver); + return rc; } @@ -1602,8 +1620,7 @@ static int cdns_uart_remove(struct platform_device *pdev) console_port = NULL; #endif - if (!--instances) - uart_unregister_driver(cdns_uart_data->cdns_uart_driver); + uart_unregister_driver(cdns_uart_data->cdns_uart_driver); return rc; } -- cgit v1.2.3 From bed25ac0e2b6ab8f9aed2d20bc9c3a2037311800 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 3 Sep 2018 15:10:58 +0200 Subject: serial: uartps: Move Port ID to device data structure Record port ID in device data structure to be have it connected to certain instance. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 2f1df9d8bc78..0443edc1ad78 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -181,6 +181,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * @pclk: APB clock * @cdns_uart_driver: Pointer to UART driver * @baud: Current baud rate + * @id: Port ID * @clk_rate_change_nb: Notifier block for clock changes * @quirks: Flags for RXBS support. */ @@ -190,6 +191,7 @@ struct cdns_uart { struct clk *pclk; struct uart_driver *cdns_uart_driver; unsigned int baud; + int id; struct notifier_block clk_rate_change_nb; u32 quirks; }; @@ -1377,7 +1379,7 @@ MODULE_DEVICE_TABLE(of, cdns_uart_of_match); */ static int cdns_uart_probe(struct platform_device *pdev) { - int rc, id, irq; + int rc, irq; struct uart_port *port; struct resource *res; struct cdns_uart *cdns_uart_data; @@ -1403,18 +1405,18 @@ static int cdns_uart_probe(struct platform_device *pdev) return -ENOMEM; /* Look for a serialN alias */ - id = of_alias_get_id(pdev->dev.of_node, "serial"); - if (id < 0) - id = 0; + cdns_uart_data->id = of_alias_get_id(pdev->dev.of_node, "serial"); + if (cdns_uart_data->id < 0) + cdns_uart_data->id = 0; - if (id >= CDNS_UART_NR_PORTS) { + if (cdns_uart_data->id >= CDNS_UART_NR_PORTS) { dev_err(&pdev->dev, "Cannot get uart_port structure\n"); return -ENODEV; } /* There is a need to use unique driver name */ driver_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%d", - CDNS_UART_NAME, id); + CDNS_UART_NAME, cdns_uart_data->id); if (!driver_name) return -ENOMEM; @@ -1422,7 +1424,7 @@ static int cdns_uart_probe(struct platform_device *pdev) cdns_uart_uart_driver->driver_name = driver_name; cdns_uart_uart_driver->dev_name = CDNS_UART_TTY_NAME; cdns_uart_uart_driver->major = CDNS_UART_MAJOR; - cdns_uart_uart_driver->minor = id; + cdns_uart_uart_driver->minor = cdns_uart_data->id; cdns_uart_uart_driver->nr = 1; #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE @@ -1433,7 +1435,7 @@ static int cdns_uart_probe(struct platform_device *pdev) strncpy(cdns_uart_console->name, CDNS_UART_TTY_NAME, sizeof(cdns_uart_console->name)); - cdns_uart_console->index = id; + cdns_uart_console->index = cdns_uart_data->id; cdns_uart_console->write = cdns_uart_console_write; cdns_uart_console->device = uart_console_device; cdns_uart_console->setup = cdns_uart_console_setup; @@ -1455,7 +1457,7 @@ static int cdns_uart_probe(struct platform_device *pdev) * registration because tty_driver structure is not filled. * name_base is 0 by default. */ - cdns_uart_uart_driver->tty_driver->name_base = id; + cdns_uart_uart_driver->tty_driver->name_base = cdns_uart_data->id; match = of_match_node(cdns_uart_of_match, pdev->dev.of_node); if (match && match->data) { -- cgit v1.2.3 From 6ac1b91f346f7c489ef2a582b0c8c085fc51aabb Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 3 Sep 2018 15:05:37 +0200 Subject: serial: uartps: Enable automatic flow control Enable automatic flow control which should ensure that there is no mainteinance in connection for zcu100 BT case. Signed-off-by: Michal Simek Acked-by: Nava kishore Manne Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 0443edc1ad78..71c032744dae 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1006,13 +1006,12 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) val = readl(port->membase + CDNS_UART_MODEMCR); mode_reg = readl(port->membase + CDNS_UART_MR); - val &= ~(CDNS_UART_MODEMCR_RTS | CDNS_UART_MODEMCR_DTR); + val &= ~(CDNS_UART_MODEMCR_RTS | CDNS_UART_MODEMCR_DTR | + CDNS_UART_MODEMCR_FCM); mode_reg &= ~CDNS_UART_MR_CHMODE_MASK; - if (mctrl & TIOCM_RTS) - val |= CDNS_UART_MODEMCR_RTS; - if (mctrl & TIOCM_DTR) - val |= CDNS_UART_MODEMCR_DTR; + if (mctrl & TIOCM_RTS || mctrl & TIOCM_DTR) + val |= CDNS_UART_MODEMCR_FCM; if (mctrl & TIOCM_LOOP) mode_reg |= CDNS_UART_MR_CHMODE_L_LOOP; else -- cgit v1.2.3 From 1bd54d851f50dea6af30c3e6ff4f3e9aab5558f9 Mon Sep 17 00:00:00 2001 From: He Zhe Date: Fri, 17 Aug 2018 22:42:28 +0800 Subject: kgdboc: Passing ekgdboc to command line causes panic kgdboc_option_setup does not check input argument before passing it to strlen. The argument would be a NULL pointer if "ekgdboc", without its value, is set in command line and thus cause the following panic. PANIC: early exception 0xe3 IP 10:ffffffff8fbbb620 error 0 cr2 0x0 [ 0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 4.18-rc8+ #1 [ 0.000000] RIP: 0010:strlen+0x0/0x20 ... [ 0.000000] Call Trace [ 0.000000] ? kgdboc_option_setup+0x9/0xa0 [ 0.000000] ? kgdboc_early_init+0x6/0x1b [ 0.000000] ? do_early_param+0x4d/0x82 [ 0.000000] ? parse_args+0x212/0x330 [ 0.000000] ? rdinit_setup+0x26/0x26 [ 0.000000] ? parse_early_options+0x20/0x23 [ 0.000000] ? rdinit_setup+0x26/0x26 [ 0.000000] ? parse_early_param+0x2d/0x39 [ 0.000000] ? setup_arch+0x2f7/0xbf4 [ 0.000000] ? start_kernel+0x5e/0x4c2 [ 0.000000] ? load_ucode_bsp+0x113/0x12f [ 0.000000] ? secondary_startup_64+0xa5/0xb0 This patch adds a check to prevent the panic. Cc: stable@vger.kernel.org Cc: jason.wessel@windriver.com Cc: gregkh@linuxfoundation.org Cc: jslaby@suse.com Signed-off-by: He Zhe Reviewed-by: Daniel Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/kgdboc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index b4ba2b1dab76..f4d0ef695225 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -130,6 +130,11 @@ static void kgdboc_unregister_kbd(void) static int kgdboc_option_setup(char *opt) { + if (!opt) { + pr_err("kgdboc: config string not provided\n"); + return -EINVAL; + } + if (strlen(opt) >= MAX_CONFIG_LEN) { printk(KERN_ERR "kgdboc: config string too long\n"); return -ENOSPC; -- cgit v1.2.3 From 39724d56a0a83d8e3822913583b50d62996800cc Mon Sep 17 00:00:00 2001 From: He Zhe Date: Fri, 17 Aug 2018 22:42:29 +0800 Subject: kgdboc: Change printk to the right fashion pr_* is preferred according to scripts/checkpatch.pl. Cc: jason.wessel@windriver.com Cc: gregkh@linuxfoundation.org Cc: jslaby@suse.com Signed-off-by: He Zhe Acked-by: Daniel Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/kgdboc.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index f4d0ef695225..371357d5e216 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -8,6 +8,9 @@ * * 2007-2008 (c) Jason Wessel - Wind River Systems, Inc. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -131,12 +134,12 @@ static void kgdboc_unregister_kbd(void) static int kgdboc_option_setup(char *opt) { if (!opt) { - pr_err("kgdboc: config string not provided\n"); + pr_err("config string not provided\n"); return -EINVAL; } if (strlen(opt) >= MAX_CONFIG_LEN) { - printk(KERN_ERR "kgdboc: config string too long\n"); + pr_err("config string too long\n"); return -ENOSPC; } strcpy(config, opt); @@ -253,7 +256,7 @@ static int param_set_kgdboc_var(const char *kmessage, int len = strlen(kmessage); if (len >= MAX_CONFIG_LEN) { - printk(KERN_ERR "kgdboc: config string too long\n"); + pr_err("config string too long\n"); return -ENOSPC; } @@ -264,8 +267,7 @@ static int param_set_kgdboc_var(const char *kmessage, } if (kgdb_connected) { - printk(KERN_ERR - "kgdboc: Cannot reconfigure while KGDB is connected.\n"); + pr_err("Cannot reconfigure while KGDB is connected.\n"); return -EBUSY; } -- cgit v1.2.3 From 2dd453168643d9475028cd867c57e65956a0f7f9 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Mon, 10 Sep 2018 16:20:14 -0700 Subject: kgdboc: Fix restrict error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's an error when compiled with restrict: drivers/tty/serial/kgdboc.c: In function ‘configure_kgdboc’: drivers/tty/serial/kgdboc.c:137:2: error: ‘strcpy’ source argument is the same as destination [-Werror=restrict] strcpy(config, opt); ^~~~~~~~~~~~~~~~~~~ As the error implies, this is from trying to use config as both source and destination. Drop the call to the function where config is the argument since nothing else happens in the function. Signed-off-by: Laura Abbott Reviewed-by: Daniel Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/kgdboc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 371357d5e216..e9a83bb5bee5 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -162,15 +162,13 @@ static int configure_kgdboc(void) { struct tty_driver *p; int tty_line = 0; - int err; + int err = -ENODEV; char *cptr = config; struct console *cons; - err = kgdboc_option_setup(config); - if (err || !strlen(config) || isspace(config[0])) + if (!strlen(config) || isspace(config[0])) goto noconfig; - err = -ENODEV; kgdboc_io_ops.is_console = 0; kgdb_tty_driver = NULL; -- cgit v1.2.3 From 9d7c249a1ef9bf0d5696df14e6bc067004f16979 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 2 Aug 2018 13:14:32 +0200 Subject: serial: 8250: drop the printk from serial8250_interrupt() The printk() in serial8250_interrupt() was once hidden behind a debug macro in commit f4f653e9875e5 ("serial: 8250, disable "too much work" messages") and reverted back in commit 12de375ec493a ("Revert "serial: 8250, disable "too much work" messages""). This was introduced first in 0.99.13k with the "serial" driver itself (and called pass_number with a limit of 64 and no print). In 1.1.13 it was renamed to pass_counter and the printk was behind #if 0. In 1.1.94 the limit of 64 was increased to 256 and hidden behind RS_ISR_PASS_LIMIT. With this change the #if 0 turned into #if 1. It slowly become what we have today with a loop limit of 512. Usually, that printk isn't hit. However on KVM with a busy UART and overloaded host it might happen. It is also likely with threaded interrupts and a task which preempts the interrupt handler. If the UART has (legitimate) work to do and we break out of the loop, nothing changes: the interrupt is most likely already pending in the interrupt controller and we end up in the handler anyway. This printk is hardly helping. Older kernels also had a comment saying that a bad configuration might lead to this but I don't see how that should happen because a wrongly configured interrupt number would let the handler leave "early" with IRQ_NONE and the spurious detected will handle that (weill since 2.6.11, before that we had no spurious detector). In that case, we would never loop that often here. This loop looks like an optimisation in order to pull the bytes from the FIFO which were received while we were already here instead of waiting for the interrupt. This might have been a good idea while the CPUs were slow and FIFOs small. There are other serial driver in tree, like the amba-pl*, which also have this kind of a loop but without the printk (and were based on this driver). Remove the printk which might trigger in otherwise valid situtations. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 8fe3d0ed229e..94f3e1c64490 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -130,12 +130,8 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) l = l->next; - if (l == i->head && pass_counter++ > PASS_LIMIT) { - /* If we hit this, we're dead. */ - printk_ratelimited(KERN_ERR - "serial8250: too much work for irq%d\n", irq); + if (l == i->head && pass_counter++ > PASS_LIMIT) break; - } } while (l != end); spin_unlock(&i->lock); -- cgit v1.2.3 From ea42d7a67a9e2b2e780eacbf9b2c3848c9ce2a0f Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Mon, 6 Aug 2018 14:22:11 +0530 Subject: tty: serial: uartlite: Enable clocks at probe At probe the uartlite is getting configured. Enable the clocks before assiging uart and disable after probe is done. Signed-off-by: Shubhrajyoti Datta Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/uartlite.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 98d3eadd2fd0..69411b6cc2a2 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -776,13 +776,17 @@ static int ulite_probe(struct platform_device *pdev) pdata->clk = NULL; } - ret = clk_prepare(pdata->clk); + ret = clk_prepare_enable(pdata->clk); if (ret) { dev_err(&pdev->dev, "Failed to prepare clock\n"); return ret; } - return ulite_assign(&pdev->dev, id, res->start, irq, pdata); + ret = ulite_assign(&pdev->dev, id, res->start, irq, pdata); + + clk_disable(pdata->clk); + + return ret; } static int ulite_remove(struct platform_device *pdev) -- cgit v1.2.3 From 415b43bdb00890f5698ceff3d3531d2daa98c5a1 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Mon, 6 Aug 2018 14:22:12 +0530 Subject: tty: serial: uartlite: Move uart register to probe Move uart register to probe. This is in preparation of removing the hardcoding of number of uarts. Signed-off-by: Shubhrajyoti Datta Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/uartlite.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 69411b6cc2a2..77da5652fc60 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -782,6 +782,15 @@ static int ulite_probe(struct platform_device *pdev) return ret; } + if (!ulite_uart_driver.state) { + dev_dbg(&pdev->dev, "uartlite: calling uart_register_driver()\n"); + ret = uart_register_driver(&ulite_uart_driver); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register driver\n"); + return ret; + } + } + ret = ulite_assign(&pdev->dev, id, res->start, irq, pdata); clk_disable(pdata->clk); @@ -817,25 +826,9 @@ static struct platform_driver ulite_platform_driver = { static int __init ulite_init(void) { - int ret; - - pr_debug("uartlite: calling uart_register_driver()\n"); - ret = uart_register_driver(&ulite_uart_driver); - if (ret) - goto err_uart; pr_debug("uartlite: calling platform_driver_register()\n"); - ret = platform_driver_register(&ulite_platform_driver); - if (ret) - goto err_plat; - - return 0; - -err_plat: - uart_unregister_driver(&ulite_uart_driver); -err_uart: - pr_err("registering uartlite driver failed: err=%i\n", ret); - return ret; + return platform_driver_register(&ulite_platform_driver); } static void __exit ulite_exit(void) -- cgit v1.2.3 From 5f6825d1cef747f6677a833708d698c7df3c1bc8 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Mon, 6 Aug 2018 14:22:13 +0530 Subject: tty: serial: uartlite: remove console_init register_console is called twice once from uart_add_one_port -> uart_configure_port remove the double call Signed-off-by: Shubhrajyoti Datta Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/uartlite.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 77da5652fc60..f8538e2d6c9c 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -541,14 +541,6 @@ static struct console ulite_console = { .data = &ulite_uart_driver, }; -static int __init ulite_console_init(void) -{ - register_console(&ulite_console); - return 0; -} - -console_initcall(ulite_console_init); - static void early_uartlite_putc(struct uart_port *port, int c) { /* -- cgit v1.2.3 From deeb33e8fdd834770f75996c18153453d5af6c50 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Mon, 6 Aug 2018 14:22:14 +0530 Subject: tty: serial: uartlite: Use dynamic array for console port Driver console functions are using pointer to static array with fixed size. There can be only one serial console at the time which is found by register_console(). register_console() is filling cons->index to port->line value. Signed-off-by: Shubhrajyoti Datta Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/uartlite.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index f8538e2d6c9c..f0344adc86db 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -55,6 +55,11 @@ #define ULITE_CONTROL_RST_RX 0x02 #define ULITE_CONTROL_IE 0x10 +/* Static pointer to console port */ +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE +static struct uart_port *console_port; +#endif + struct uartlite_data { const struct uartlite_reg_ops *reg_ops; struct clk *clk; @@ -472,7 +477,7 @@ static void ulite_console_putchar(struct uart_port *port, int ch) static void ulite_console_write(struct console *co, const char *s, unsigned int count) { - struct uart_port *port = &ulite_ports[co->index]; + struct uart_port *port = console_port; unsigned long flags; unsigned int ier; int locked = 1; @@ -506,10 +511,8 @@ static int ulite_console_setup(struct console *co, char *options) int parity = 'n'; int flow = 'n'; - if (co->index < 0 || co->index >= ULITE_NR_UARTS) - return -EINVAL; - port = &ulite_ports[co->index]; + port = console_port; /* Has the device been initialized yet? */ if (!port->mapbase) { @@ -652,6 +655,17 @@ static int ulite_assign(struct device *dev, int id, u32 base, int irq, dev_set_drvdata(dev, port); +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE + /* + * If console hasn't been found yet try to assign this port + * because it is required to be assigned for console setup function. + * If register_console() don't assign value, then console_port pointer + * is cleanup. + */ + if (ulite_uart_driver.cons->index == -1) + console_port = port; +#endif + /* Register the port */ rc = uart_add_one_port(&ulite_uart_driver, port); if (rc) { @@ -661,6 +675,12 @@ static int ulite_assign(struct device *dev, int id, u32 base, int irq, return rc; } +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE + /* This is not port which is used for console that's why clean it up */ + if (ulite_uart_driver.cons->index == -1) + console_port = NULL; +#endif + return 0; } -- cgit v1.2.3 From c974991d2620419fe21508fc4529014369d16df7 Mon Sep 17 00:00:00 2001 From: jun qian Date: Mon, 27 Aug 2018 07:49:04 -0700 Subject: tty:serial:imx: use spin_lock instead of spin_lock_irqsave in isr Before the program enters the uart ISR, the local interrupt has been disabled by the system, so it's not appropriate to use spin_lock_irqsave interface in the ISR. Signed-off-by: jun qian Reviewed-by: Barry Song <21cnbao@gmail.com> Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 239c0fa2e981..3069ee93583e 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -706,27 +706,25 @@ static irqreturn_t imx_uart_rtsint(int irq, void *dev_id) { struct imx_port *sport = dev_id; u32 usr1; - unsigned long flags; - spin_lock_irqsave(&sport->port.lock, flags); + spin_lock(&sport->port.lock); imx_uart_writel(sport, USR1_RTSD, USR1); usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS; uart_handle_cts_change(&sport->port, !!usr1); wake_up_interruptible(&sport->port.state->port.delta_msr_wait); - spin_unlock_irqrestore(&sport->port.lock, flags); + spin_unlock(&sport->port.lock); return IRQ_HANDLED; } static irqreturn_t imx_uart_txint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned long flags; - spin_lock_irqsave(&sport->port.lock, flags); + spin_lock(&sport->port.lock); imx_uart_transmit_buffer(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); + spin_unlock(&sport->port.lock); return IRQ_HANDLED; } @@ -735,9 +733,8 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id) struct imx_port *sport = dev_id; unsigned int rx, flg, ignored = 0; struct tty_port *port = &sport->port.state->port; - unsigned long flags; - spin_lock_irqsave(&sport->port.lock, flags); + spin_lock(&sport->port.lock); while (imx_uart_readl(sport, USR2) & USR2_RDR) { u32 usr2; @@ -797,7 +794,7 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id) } out: - spin_unlock_irqrestore(&sport->port.lock, flags); + spin_unlock(&sport->port.lock); tty_flip_buffer_push(port); return IRQ_HANDLED; } @@ -903,13 +900,11 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) } if (usr1 & USR1_DTRD) { - unsigned long flags; - imx_uart_writel(sport, USR1_DTRD, USR1); - spin_lock_irqsave(&sport->port.lock, flags); + spin_lock(&sport->port.lock); imx_uart_mctrl_check(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); + spin_unlock(&sport->port.lock); ret = IRQ_HANDLED; } -- cgit v1.2.3 From 07b5e16e9830878291bc83df70e87cdb0833517c Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 5 Sep 2018 09:24:26 +0800 Subject: tty: serial: imx: add lock for registers save/restore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In noirq suspend/resume stage with no_console_suspend enabled, imx_uart_console_write() may be called to print out log_buf message by printk(), so there will be race condition between imx_uart_console_write() and imx_uart_save/restore_context(), need to add lock to protect the registers save/restore operations. Signed-off-by: Anson Huang Acked-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 3069ee93583e..2634272ad0fb 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -2371,8 +2371,13 @@ static int imx_uart_remove(struct platform_device *pdev) static void imx_uart_restore_context(struct imx_port *sport) { - if (!sport->context_saved) + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + if (!sport->context_saved) { + spin_unlock_irqrestore(&sport->port.lock, flags); return; + } imx_uart_writel(sport, sport->saved_reg[4], UFCR); imx_uart_writel(sport, sport->saved_reg[5], UESC); @@ -2385,11 +2390,15 @@ static void imx_uart_restore_context(struct imx_port *sport) imx_uart_writel(sport, sport->saved_reg[2], UCR3); imx_uart_writel(sport, sport->saved_reg[3], UCR4); sport->context_saved = false; + spin_unlock_irqrestore(&sport->port.lock, flags); } static void imx_uart_save_context(struct imx_port *sport) { + unsigned long flags; + /* Save necessary regs */ + spin_lock_irqsave(&sport->port.lock, flags); sport->saved_reg[0] = imx_uart_readl(sport, UCR1); sport->saved_reg[1] = imx_uart_readl(sport, UCR2); sport->saved_reg[2] = imx_uart_readl(sport, UCR3); @@ -2401,6 +2410,7 @@ static void imx_uart_save_context(struct imx_port *sport) sport->saved_reg[8] = imx_uart_readl(sport, UBMR); sport->saved_reg[9] = imx_uart_readl(sport, IMX21_UTS); sport->context_saved = true; + spin_unlock_irqrestore(&sport->port.lock, flags); } static void imx_uart_enable_wakeup(struct imx_port *sport, bool on) -- cgit v1.2.3 From fcfed1be53e014b9d707ca4520bb1591c2700f2e Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 5 Sep 2018 09:24:27 +0800 Subject: tty: serial: imx: add pinctrl sleep/default mode switch for suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some i.MX SoCs' low power mode, such as i.MX7D's LPSR(low power state retention), UART iomux settings will be lost, need to add pinctrl sleep/default mode switch during suspend/resume to make sure UART iomux settings are correct after resume. Signed-off-by: Anson Huang Acked-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 2634272ad0fb..4341589e0aab 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -2444,6 +2445,8 @@ static int imx_uart_suspend_noirq(struct device *dev) clk_disable(sport->clk_ipg); + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -2452,6 +2455,8 @@ static int imx_uart_resume_noirq(struct device *dev) struct imx_port *sport = dev_get_drvdata(dev); int ret; + pinctrl_pm_select_default_state(dev); + ret = clk_enable(sport->clk_ipg); if (ret) return ret; -- cgit v1.2.3 From 10c63443b74d1ef5c1b3bb104a9e6e40dc2437ff Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Aug 2018 14:54:03 +0200 Subject: Revert "serial: sh-sci: Remove SCIx_RZ_SCIFA_REGTYPE" This reverts commit 7acece71a517cad83a0842a94d94c13f271b680c. Signed-off-by: Geert Uytterhoeven Acked-by: Chris Brandt Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 31 +++++++++++++++++++++++++++++++ include/linux/serial_sci.h | 1 + 2 files changed, 32 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index ac4424bf6b13..5d42c9a63001 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -291,6 +291,33 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .error_clear = SCIF_ERROR_CLEAR, }, + /* + * The "SCIFA" that is in RZ/T and RZ/A2. + * It looks like a normal SCIF with FIFO data, but with a + * compressed address space. Also, the break out of interrupts + * are different: ERI/BRI, RXI, TXI, TEI, DRI. + */ + [SCIx_RZ_SCIFA_REGTYPE] = { + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x02, 8 }, + [SCSCR] = { 0x04, 16 }, + [SCxTDR] = { 0x06, 8 }, + [SCxSR] = { 0x08, 16 }, + [SCxRDR] = { 0x0A, 8 }, + [SCFCR] = { 0x0C, 16 }, + [SCFDR] = { 0x0E, 16 }, + [SCSPTR] = { 0x10, 16 }, + [SCLSR] = { 0x12, 16 }, + }, + .fifosize = 16, + .overrun_reg = SCLSR, + .overrun_mask = SCLSR_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCIF_DEFAULT_ERROR_MASK, + .error_clear = SCIF_ERROR_CLEAR, + }, + /* * Common SH-3 SCIF definitions. */ @@ -3110,6 +3137,10 @@ static const struct of_device_id of_sci_match[] = { .compatible = "renesas,scif-r7s72100", .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH2_SCIF_FIFODATA_REGTYPE), }, + { + .compatible = "renesas,scif-r7s9210", + .data = SCI_OF_DATA(PORT_SCIF, SCIx_RZ_SCIFA_REGTYPE), + }, /* Family-specific types */ { .compatible = "renesas,rcar-gen1-scif", diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index c0e795d95477..1c89611e0e06 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -36,6 +36,7 @@ enum { SCIx_SH4_SCIF_FIFODATA_REGTYPE, SCIx_SH7705_SCIF_REGTYPE, SCIx_HSCIF_REGTYPE, + SCIx_RZ_SCIFA_REGTYPE, SCIx_NR_REGTYPES, }; -- cgit v1.2.3 From a1c2fd7e1098ea49ff31785ad970311d64e5904e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Aug 2018 14:54:04 +0200 Subject: Revert "serial: sh-sci: Allow for compressed SCIF address" This reverts commit 2d4dd0da45401c7ae7332b4d1eb7bbb1348edde9. This broke earlycon on all Renesas ARM platforms using a SCIF port for the serial console (R-Car, RZ/A1, RZ/G1, RZ/G2 SoCs), due to an incorrect value of port->regshift. Signed-off-by: Geert Uytterhoeven Acked-by: Chris Brandt Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 5d42c9a63001..ab3f6e91853d 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -346,15 +346,15 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { [SCIx_SH4_SCIF_REGTYPE] = { .regs = { [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x02, 8 }, - [SCSCR] = { 0x04, 16 }, - [SCxTDR] = { 0x06, 8 }, - [SCxSR] = { 0x08, 16 }, - [SCxRDR] = { 0x0a, 8 }, - [SCFCR] = { 0x0c, 16 }, - [SCFDR] = { 0x0e, 16 }, - [SCSPTR] = { 0x10, 16 }, - [SCLSR] = { 0x12, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCSPTR] = { 0x20, 16 }, + [SCLSR] = { 0x24, 16 }, }, .fifosize = 16, .overrun_reg = SCLSR, @@ -2837,7 +2837,7 @@ static int sci_init_single(struct platform_device *dev, { struct uart_port *port = &sci_port->port; const struct resource *res; - unsigned int i, regtype; + unsigned int i; int ret; sci_port->cfg = p; @@ -2874,7 +2874,6 @@ static int sci_init_single(struct platform_device *dev, if (unlikely(sci_port->params == NULL)) return -EINVAL; - regtype = sci_port->params - sci_port_params; switch (p->type) { case PORT_SCIFB: sci_port->rx_trigger = 48; @@ -2929,10 +2928,6 @@ static int sci_init_single(struct platform_device *dev, port->regshift = 1; } - if (regtype == SCIx_SH4_SCIF_REGTYPE) - if (sci_port->reg_size >= 0x20) - port->regshift = 1; - /* * The UART port needs an IRQ value, so we peg this to the RX IRQ * for the multi-IRQ ports, which is where we are primarily -- cgit v1.2.3 From 3d8b43ad9c0cf023dd12458f23250c1b86b21e4e Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Mon, 17 Sep 2018 13:26:23 -0500 Subject: serial: sh-sci: Add earlycon for R7S9210 Since the register offsets are different for RZ/A2 SCIF, we need to declare a separate string for it. Signed-off-by: Chris Brandt Reviewed-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index ab3f6e91853d..426241da2e44 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3414,6 +3414,12 @@ static int __init scif_early_console_setup(struct earlycon_device *device, { return early_console_setup(device, PORT_SCIF); } +static int __init rzscifa_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + port_cfg.regtype = SCIx_RZ_SCIFA_REGTYPE; + return early_console_setup(device, PORT_SCIF); +} static int __init scifa_early_console_setup(struct earlycon_device *device, const char *opt) { @@ -3432,6 +3438,7 @@ static int __init hscif_early_console_setup(struct earlycon_device *device, OF_EARLYCON_DECLARE(sci, "renesas,sci", sci_early_console_setup); OF_EARLYCON_DECLARE(scif, "renesas,scif", scif_early_console_setup); +OF_EARLYCON_DECLARE(scif, "renesas,scif-r7s9210", rzscifa_early_console_setup); OF_EARLYCON_DECLARE(scifa, "renesas,scifa", scifa_early_console_setup); OF_EARLYCON_DECLARE(scifb, "renesas,scifb", scifb_early_console_setup); OF_EARLYCON_DECLARE(hscif, "renesas,hscif", hscif_early_console_setup); -- cgit v1.2.3 From 1ff3652bc7111df26b5807037f624be294cf69d5 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 13 Sep 2018 10:21:25 +0200 Subject: serial: samsung: Enable baud clock for UART reset procedure in resume Ensure that the baud clock is also enabled for UART register writes in driver resume. On Exynos5433 SoC this is needed to avoid external abort issue. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 2f8fa184aafa..da1bd4bba8a9 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1941,7 +1941,11 @@ static int s3c24xx_serial_resume(struct device *dev) if (port) { clk_prepare_enable(ourport->clk); + if (!IS_ERR(ourport->baudclk)) + clk_prepare_enable(ourport->baudclk); s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port)); + if (!IS_ERR(ourport->baudclk)) + clk_disable_unprepare(ourport->baudclk); clk_disable_unprepare(ourport->clk); uart_resume_port(&s3c24xx_uart_drv, port); @@ -1964,7 +1968,11 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) if (rx_enabled(port)) uintm &= ~S3C64XX_UINTM_RXD_MSK; clk_prepare_enable(ourport->clk); + if (!IS_ERR(ourport->baudclk)) + clk_prepare_enable(ourport->baudclk); wr_regl(port, S3C64XX_UINTM, uintm); + if (!IS_ERR(ourport->baudclk)) + clk_disable_unprepare(ourport->baudclk); clk_disable_unprepare(ourport->clk); } } -- cgit v1.2.3 From c886751465b8e312389d91446b76a00f45a79276 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Mon, 27 Aug 2018 20:03:02 -0500 Subject: serial: 8250_omap: Make 8250_omap driver driver depend on ARCH_K3 Allow 8250 omap serial driver to be used for K3 platforms. Signed-off-by: Lokesh Vutla Signed-off-by: Nishanth Menon Acked-by: Tony Lindgren Acked-by: Vignesh R Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index f005eaf8bc57..15c2c5463835 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -375,7 +375,7 @@ config SERIAL_8250_RT288X config SERIAL_8250_OMAP tristate "Support for OMAP internal UART (8250 based driver)" - depends on SERIAL_8250 && ARCH_OMAP2PLUS + depends on SERIAL_8250 && (ARCH_OMAP2PLUS || ARCH_K3) help If you have a machine based on an Texas Instruments OMAP CPU you can enable its onboard serial ports by enabling this option. -- cgit v1.2.3 From 20464f3a9b46ef882b9fdf140dcc7ff6a6db8d4c Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 17 Sep 2018 11:33:40 -0700 Subject: serial: sprd: Remove unused structure Remove the unused reg_backup structure. Signed-off-by: Baolin Wang Acked-by: Chunyan Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 828f1143859c..1b0e3fbe546a 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -95,19 +95,8 @@ #define SPRD_IMSR_BREAK_DETECT BIT(7) #define SPRD_IMSR_TIMEOUT BIT(13) -struct reg_backup { - u32 ien; - u32 ctrl0; - u32 ctrl1; - u32 ctrl2; - u32 clkd0; - u32 clkd1; - u32 dspwait; -}; - struct sprd_uart_port { struct uart_port port; - struct reg_backup reg_bak; char name[16]; }; -- cgit v1.2.3 From 2b5a997386b0594e671a32c7e429cf59ac8fc54c Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 17 Sep 2018 11:33:41 -0700 Subject: serial: sprd: Use readable macros instead of magic number Define readable macros instead of magic number to make code more readable. Signed-off-by: Baolin Wang Acked-by: Chunyan Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 1b0e3fbe546a..e18d8afc88ff 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -45,6 +45,8 @@ /* data number in TX and RX fifo */ #define SPRD_STS1 0x000C +#define SPRD_RX_FIFO_CNT_MASK GENMASK(7, 0) +#define SPRD_TX_FIFO_CNT_MASK GENMASK(15, 8) /* interrupt enable register and its BITs */ #define SPRD_IEN 0x0010 @@ -82,11 +84,15 @@ /* fifo threshold register */ #define SPRD_CTL2 0x0020 #define THLD_TX_EMPTY 0x40 +#define THLD_TX_EMPTY_SHIFT 8 #define THLD_RX_FULL 0x40 /* config baud rate register */ #define SPRD_CLKD0 0x0024 +#define SPRD_CLKD0_MASK GENMASK(15, 0) #define SPRD_CLKD1 0x0028 +#define SPRD_CLKD1_MASK GENMASK(20, 16) +#define SPRD_CLKD1_SHIFT 16 /* interrupt mask status register */ #define SPRD_IMSR 0x002C @@ -115,7 +121,7 @@ static inline void serial_out(struct uart_port *port, int offset, int value) static unsigned int sprd_tx_empty(struct uart_port *port) { - if (serial_in(port, SPRD_STS1) & 0xff00) + if (serial_in(port, SPRD_STS1) & SPRD_TX_FIFO_CNT_MASK) return 0; else return TIOCSER_TEMT; @@ -213,7 +219,8 @@ static inline void sprd_rx(struct uart_port *port) struct tty_port *tty = &port->state->port; unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; - while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { + while ((serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK) && + max_count--) { lsr = serial_in(port, SPRD_LSR); ch = serial_in(port, SPRD_RXD); flag = TTY_NORMAL; @@ -303,16 +310,17 @@ static int sprd_startup(struct uart_port *port) struct sprd_uart_port *sp; unsigned long flags; - serial_out(port, SPRD_CTL2, ((THLD_TX_EMPTY << 8) | THLD_RX_FULL)); + serial_out(port, SPRD_CTL2, + THLD_TX_EMPTY << THLD_TX_EMPTY_SHIFT | THLD_RX_FULL); /* clear rx fifo */ timeout = SPRD_TIMEOUT; - while (timeout-- && serial_in(port, SPRD_STS1) & 0x00ff) + while (timeout-- && serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK) serial_in(port, SPRD_RXD); /* clear tx fifo */ timeout = SPRD_TIMEOUT; - while (timeout-- && serial_in(port, SPRD_STS1) & 0xff00) + while (timeout-- && serial_in(port, SPRD_STS1) & SPRD_TX_FIFO_CNT_MASK) cpu_relax(); /* clear interrupt */ @@ -433,10 +441,11 @@ static void sprd_set_termios(struct uart_port *port, } /* clock divider bit0~bit15 */ - serial_out(port, SPRD_CLKD0, quot & 0xffff); + serial_out(port, SPRD_CLKD0, quot & SPRD_CLKD0_MASK); /* clock divider bit16~bit20 */ - serial_out(port, SPRD_CLKD1, (quot & 0x1f0000) >> 16); + serial_out(port, SPRD_CLKD1, + (quot & SPRD_CLKD1_MASK) >> SPRD_CLKD1_SHIFT); serial_out(port, SPRD_LCR, lcr); fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; serial_out(port, SPRD_CTL1, fc); @@ -510,7 +519,7 @@ static void wait_for_xmitr(struct uart_port *port) if (--tmout == 0) break; udelay(1); - } while (status & 0xff00); + } while (status & SPRD_TX_FIFO_CNT_MASK); } static void sprd_console_putchar(struct uart_port *port, int ch) -- cgit v1.2.3 From 262d3dc00730f7a9d835faeca46689d750177e55 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 17 Sep 2018 11:33:42 -0700 Subject: serial: sprd: Remove unnecessary resource validation The devm_ioremap_resource() will valid the resources, thus remove the unnecessary resource validation in the driver. Signed-off-by: Baolin Wang Acked-by: Chunyan Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index e18d8afc88ff..03b0cd45f1ed 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -710,15 +710,12 @@ static int sprd_probe(struct platform_device *pdev) up->uartclk = clk_get_rate(clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "not provide mem resource\n"); - return -ENODEV; - } - up->mapbase = res->start; up->membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(up->membase)) return PTR_ERR(up->membase); + up->mapbase = res->start; + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "not provide irq resource: %d\n", irq); -- cgit v1.2.3 From dd22161eef8b19930aa4439722940f44e2aff521 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 17 Sep 2018 11:33:43 -0700 Subject: serial: sprd: Change 'int' to 'unsigned int' The register offset value should be 'unsigned int' type. Moreover, prefer 'unsigned int' to bare use of 'unsigned'. Signed-off-by: Baolin Wang Acked-by: Chunyan Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 03b0cd45f1ed..8d5c9cd6024c 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -109,12 +109,14 @@ struct sprd_uart_port { static struct sprd_uart_port *sprd_port[UART_NR_MAX]; static int sprd_ports_num; -static inline unsigned int serial_in(struct uart_port *port, int offset) +static inline unsigned int serial_in(struct uart_port *port, + unsigned int offset) { return readl_relaxed(port->membase + offset); } -static inline void serial_out(struct uart_port *port, int offset, int value) +static inline void serial_out(struct uart_port *port, unsigned int offset, + int value) { writel_relaxed(value, port->membase + offset); } @@ -598,8 +600,7 @@ static void sprd_putc(struct uart_port *port, int c) writeb(c, port->membase + SPRD_TXD); } -static void sprd_early_write(struct console *con, const char *s, - unsigned n) +static void sprd_early_write(struct console *con, const char *s, unsigned int n) { struct earlycon_device *dev = con->data; -- cgit v1.2.3 From d2de9601eb12e7c5b7519cf155cf613322211b54 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 17 Sep 2018 11:33:44 -0700 Subject: serial: sprd: Fix the indentation issue Make the macros' definition and code have the same correct indentation. Signed-off-by: Baolin Wang Acked-by: Chunyan Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 46 +++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 8d5c9cd6024c..4287ca305b6b 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -68,24 +68,24 @@ #define SPRD_LCR_DATA_LEN6 0x4 #define SPRD_LCR_DATA_LEN7 0x8 #define SPRD_LCR_DATA_LEN8 0xc -#define SPRD_LCR_PARITY (BIT(0) | BIT(1)) +#define SPRD_LCR_PARITY (BIT(0) | BIT(1)) #define SPRD_LCR_PARITY_EN 0x2 #define SPRD_LCR_EVEN_PAR 0x0 #define SPRD_LCR_ODD_PAR 0x1 /* control register 1 */ -#define SPRD_CTL1 0x001C +#define SPRD_CTL1 0x001C #define RX_HW_FLOW_CTL_THLD BIT(6) #define RX_HW_FLOW_CTL_EN BIT(7) #define TX_HW_FLOW_CTL_EN BIT(8) #define RX_TOUT_THLD_DEF 0x3E00 -#define RX_HFC_THLD_DEF 0x40 +#define RX_HFC_THLD_DEF 0x40 /* fifo threshold register */ #define SPRD_CTL2 0x0020 -#define THLD_TX_EMPTY 0x40 +#define THLD_TX_EMPTY 0x40 #define THLD_TX_EMPTY_SHIFT 8 -#define THLD_RX_FULL 0x40 +#define THLD_RX_FULL 0x40 /* config baud rate register */ #define SPRD_CLKD0 0x0024 @@ -95,11 +95,11 @@ #define SPRD_CLKD1_SHIFT 16 /* interrupt mask status register */ -#define SPRD_IMSR 0x002C -#define SPRD_IMSR_RX_FIFO_FULL BIT(0) +#define SPRD_IMSR 0x002C +#define SPRD_IMSR_RX_FIFO_FULL BIT(0) #define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) -#define SPRD_IMSR_BREAK_DETECT BIT(7) -#define SPRD_IMSR_TIMEOUT BIT(13) +#define SPRD_IMSR_BREAK_DETECT BIT(7) +#define SPRD_IMSR_TIMEOUT BIT(13) struct sprd_uart_port { struct uart_port port; @@ -229,7 +229,7 @@ static inline void sprd_rx(struct uart_port *port) port->icount.rx++; if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | - SPRD_LSR_FE | SPRD_LSR_OE)) + SPRD_LSR_FE | SPRD_LSR_OE)) if (handle_lsr_errors(port, &lsr, &flag)) continue; if (uart_handle_sysrq_char(port, ch)) @@ -292,8 +292,8 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) if (ims & SPRD_IMSR_TIMEOUT) serial_out(port, SPRD_ICLR, SPRD_ICLR_TIMEOUT); - if (ims & (SPRD_IMSR_RX_FIFO_FULL | - SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT)) + if (ims & (SPRD_IMSR_RX_FIFO_FULL | SPRD_IMSR_BREAK_DETECT | + SPRD_IMSR_TIMEOUT)) sprd_rx(port); if (ims & SPRD_IMSR_TX_FIFO_EMPTY) @@ -333,7 +333,7 @@ static int sprd_startup(struct uart_port *port) sp = container_of(port, struct sprd_uart_port, port); snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line); ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq, - IRQF_SHARED, sp->name, port); + IRQF_SHARED, sp->name, port); if (ret) { dev_err(port->dev, "fail to request serial irq %d, ret=%d\n", port->irq, ret); @@ -361,8 +361,8 @@ static void sprd_shutdown(struct uart_port *port) } static void sprd_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) + struct ktermios *termios, + struct ktermios *old) { unsigned int baud, quot; unsigned int lcr = 0, fc; @@ -480,8 +480,7 @@ static void sprd_config_port(struct uart_port *port, int flags) port->type = PORT_SPRD; } -static int sprd_verify_port(struct uart_port *port, - struct serial_struct *ser) +static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser) { if (ser->type != PORT_SPRD) return -EINVAL; @@ -531,7 +530,7 @@ static void sprd_console_putchar(struct uart_port *port, int ch) } static void sprd_console_write(struct console *co, const char *s, - unsigned int count) + unsigned int count) { struct uart_port *port = &sprd_port[co->index]->port; int locked = 1; @@ -594,7 +593,7 @@ static void sprd_putc(struct uart_port *port, int c) unsigned int timeout = SPRD_TIMEOUT; while (timeout-- && - !(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER)) + !(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER)) cpu_relax(); writeb(c, port->membase + SPRD_TXD); @@ -607,9 +606,8 @@ static void sprd_early_write(struct console *con, const char *s, unsigned int n) uart_console_write(&dev->port, s, n, sprd_putc); } -static int __init sprd_early_console_setup( - struct earlycon_device *device, - const char *opt) +static int __init sprd_early_console_setup(struct earlycon_device *device, + const char *opt) { if (!device->port.membase) return -ENODEV; @@ -691,8 +689,8 @@ static int sprd_probe(struct platform_device *pdev) index = sprd_probe_dt_alias(index, &pdev->dev); - sprd_port[index] = devm_kzalloc(&pdev->dev, - sizeof(*sprd_port[index]), GFP_KERNEL); + sprd_port[index] = devm_kzalloc(&pdev->dev, sizeof(*sprd_port[index]), + GFP_KERNEL); if (!sprd_port[index]) return -ENOMEM; -- cgit v1.2.3 From 5963e8a3122471cadfe0eba41c4ceaeaa5c8bb4d Mon Sep 17 00:00:00 2001 From: Anton Vasilyev Date: Tue, 7 Aug 2018 13:59:05 +0300 Subject: serial: mxs-auart: Fix potential infinite loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the error path of mxs_auart_request_gpio_irq() is performed backward iterating with index i of enum type. Underline enum type may be unsigned char. In this case check (--i >= 0) will be always true and error handling goes into infinite loop. The patch changes the check so that it is valid for signed and unsigned types. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Anton Vasilyev Acked-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 76aa289652f7..27235a526cce 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1634,8 +1634,9 @@ static int mxs_auart_request_gpio_irq(struct mxs_auart_port *s) /* * If something went wrong, rollback. + * Be careful: i may be unsigned. */ - while (err && (--i >= 0)) + while (err && (i-- > 0)) if (irq[i] >= 0) free_irq(irq[i], s); -- cgit v1.2.3 From 2843cbb5d3c467a85112f004a8a9345370760520 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 23 Aug 2018 23:30:27 +0200 Subject: tty: serial: qcom_geni_serial: Drop useless check for dev.of_node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With gcc 4.1.2: drivers/tty/serial/qcom_geni_serial.c: In function ‘qcom_geni_serial_probe’: drivers/tty/serial/qcom_geni_serial.c:1261: warning: ‘drv’ may be used uninitialized in this function Indeed, if dev.of_node is NULL, drv will be used uninitialized, and dereferenced in uart_add_one_port(). However, as this driver supports DT only, dev.of_node will always be valid. Hence remove the useless check for dev.of_node, killing the warning as a side effect. Fixes: 8a8a66a1a18a1dbd ("tty: serial: qcom_geni_serial: Add support for flow control") Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 29ec34387246..1cfbb6745a8c 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1263,14 +1263,12 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) if (of_device_is_compatible(pdev->dev.of_node, "qcom,geni-debug-uart")) console = true; - if (pdev->dev.of_node) { - if (console) { - drv = &qcom_geni_console_driver; - line = of_alias_get_id(pdev->dev.of_node, "serial"); - } else { - drv = &qcom_geni_uart_driver; - line = of_alias_get_id(pdev->dev.of_node, "hsuart"); - } + if (console) { + drv = &qcom_geni_console_driver; + line = of_alias_get_id(pdev->dev.of_node, "serial"); + } else { + drv = &qcom_geni_uart_driver; + line = of_alias_get_id(pdev->dev.of_node, "hsuart"); } port = get_port_from_line(line, console); -- cgit v1.2.3 From c362272bdea32bf048d6916b0a2dc485eb9cf787 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 5 Sep 2018 13:11:46 -0700 Subject: tty: serial: qcom_geni_serial: Fix serial when not used as console If you've got the "console" serial port setup to use just as a UART (AKA there is no "console=ttyMSMX" on the kernel command line) then certain initialization is skipped. When userspace later tries to do something with the port then things go boom (specifically, on my system, some sort of exception hit that caused the system to reboot itself w/ no error messages). Let's cleanup / refactor the init so that we always run the same init code regardless of whether we're using the console. To make this work, we make rely on qcom_geni_serial_pm doing its job to turn resources on. For the record, here is a trace of the order of things (after this patch) when console= is specified on the command line and we have an agetty on the port: qcom_geni_serial_pm: 4 (undefined) => 0 (on) qcom_geni_console_setup qcom_geni_serial_port_setup qcom_geni_serial_console_write qcom_geni_serial_startup qcom_geni_serial_start_tx ...and here is the order of things (after this patch) when console= is _NOT_ specified on the command line and we have an agetty port: qcom_geni_serial_pm: 4 => 0 qcom_geni_serial_pm: 0 => 3 qcom_geni_serial_pm: 3 => 0 qcom_geni_serial_startup qcom_geni_serial_port_setup qcom_geni_serial_pm: 0 => 3 qcom_geni_serial_pm: 3 => 0 qcom_geni_serial_startup qcom_geni_serial_start_tx Fixes: c4f528795d1a ("tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP") Signed-off-by: Douglas Anderson Reviewed-by: Matthias Kaehlcke Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 55 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 1cfbb6745a8c..e80d50723a75 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -851,6 +851,23 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport, uport); unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; + u32 proto; + + if (uart_console(uport)) + port->tx_bytes_pw = 1; + else + port->tx_bytes_pw = 4; + port->rx_bytes_pw = RX_BYTES_PW; + + proto = geni_se_read_proto(&port->se); + if (proto != GENI_SE_UART) { + dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto); + return -ENXIO; + } + + qcom_geni_serial_stop_rx(uport); + + get_tx_fifo_size(port); set_rfr_wm(port); writel_relaxed(rxstale, uport->membase + SE_UART_RX_STALE_CNT); @@ -874,30 +891,19 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) return -ENOMEM; } port->setup = true; + return 0; } static int qcom_geni_serial_startup(struct uart_port *uport) { int ret; - u32 proto; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); scnprintf(port->name, sizeof(port->name), "qcom_serial_%s%d", (uart_console(uport) ? "console" : "uart"), uport->line); - if (!uart_console(uport)) { - port->tx_bytes_pw = 4; - port->rx_bytes_pw = RX_BYTES_PW; - } - proto = geni_se_read_proto(&port->se); - if (proto != GENI_SE_UART) { - dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto); - return -ENXIO; - } - - get_tx_fifo_size(port); if (!port->setup) { ret = qcom_geni_serial_port_setup(uport); if (ret) @@ -1056,6 +1062,7 @@ static int __init qcom_geni_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; + int ret; if (co->index >= GENI_UART_CONS_PORTS || co->index < 0) return -ENXIO; @@ -1071,21 +1078,10 @@ static int __init qcom_geni_console_setup(struct console *co, char *options) if (unlikely(!uport->membase)) return -ENXIO; - if (geni_se_resources_on(&port->se)) { - dev_err(port->se.dev, "Error turning on resources\n"); - return -ENXIO; - } - - if (unlikely(geni_se_read_proto(&port->se) != GENI_SE_UART)) { - geni_se_resources_off(&port->se); - return -ENXIO; - } - if (!port->setup) { - port->tx_bytes_pw = 1; - port->rx_bytes_pw = RX_BYTES_PW; - qcom_geni_serial_stop_rx(uport); - qcom_geni_serial_port_setup(uport); + ret = qcom_geni_serial_port_setup(uport); + if (ret) + return ret; } if (options) @@ -1203,11 +1199,12 @@ static void qcom_geni_serial_pm(struct uart_port *uport, { struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + /* If we've never been called, treat it as off */ + if (old_state == UART_PM_STATE_UNDEFINED) + old_state = UART_PM_STATE_OFF; + if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) geni_se_resources_on(&port->se); - else if (!uart_console(uport) && (new_state == UART_PM_STATE_ON && - old_state == UART_PM_STATE_UNDEFINED)) - geni_se_resources_on(&port->se); else if (new_state == UART_PM_STATE_OFF && old_state == UART_PM_STATE_ON) geni_se_resources_off(&port->se); -- cgit v1.2.3 From a27d938251ef40c43db81af16fc26b2cec181d4d Mon Sep 17 00:00:00 2001 From: John Garry Date: Thu, 30 Aug 2018 17:08:50 +0800 Subject: serial: 8250_of: Fix for lack of interrupt support In commit c58caaab3bf8 ("serial: 8250: of: Defer probe on missing IRQ"), a check was added for the UART driver being probed prior to the parent IRQ controller. Unfortunately this breaks certain boards which have no interrupt support, like Huawei D03. Indeed, the 8250 DT bindings state that interrupts should be supported - not must. To fix, switch from irq_of_parse_and_map() to of_irq_get(), which does relay whether the IRQ host controller domain is not ready, i.e. defer probe, instead of assuming it. Fixes: c58caaab3bf8 ("serial: 8250: of: Defer probe on missing IRQ") Signed-off-by: John Garry Reviewed-by: Rob Herring Reviewed-by: Alexander Sverdlin Tested-by: Alexander Sverdlin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_of.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index af8beefe9b5c..877fd7f8a8ed 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -58,7 +58,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev, struct resource resource; struct device_node *np = ofdev->dev.of_node; u32 clk, spd, prop; - int ret; + int ret, irq; memset(port, 0, sizeof *port); @@ -143,21 +143,27 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (ret >= 0) port->line = ret; - port->irq = irq_of_parse_and_map(np, 0); - if (!port->irq) { - ret = -EPROBE_DEFER; - goto err_unprepare; + irq = of_irq_get(np, 0); + if (irq < 0) { + if (irq == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_unprepare; + } + /* IRQ support not mandatory */ + irq = 0; } + port->irq = irq; + info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL); if (IS_ERR(info->rst)) { ret = PTR_ERR(info->rst); - goto err_dispose; + goto err_unprepare; } ret = reset_control_deassert(info->rst); if (ret) - goto err_dispose; + goto err_unprepare; port->type = type; port->uartclk = clk; @@ -184,8 +190,6 @@ static int of_platform_serial_setup(struct platform_device *ofdev, port->handle_irq = fsl8250_handle_irq; return 0; -err_dispose: - irq_dispose_mapping(port->irq); err_unprepare: clk_disable_unprepare(info->clk); err_pmruntime: -- cgit v1.2.3 From fff10721d8b819c12266620c2981a05dbeb72175 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 27 Aug 2018 20:52:47 -0500 Subject: tty: Convert to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: linux-serial@vger.kernel.org Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/tty/ehv_bytechan.c | 12 ++++++------ drivers/tty/serial/cpm_uart/cpm_uart_core.c | 8 ++++---- drivers/tty/serial/pmac_zilog.c | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index eea4049b5dcc..769e0a5d1dfc 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -128,8 +128,8 @@ static int find_console_handle(void) */ iprop = of_get_property(np, "hv-handle", NULL); if (!iprop) { - pr_err("ehv-bc: no 'hv-handle' property in %s node\n", - np->name); + pr_err("ehv-bc: no 'hv-handle' property in %pOFn node\n", + np); return 0; } stdout_bc = be32_to_cpu(*iprop); @@ -661,8 +661,8 @@ static int ehv_bc_tty_probe(struct platform_device *pdev) iprop = of_get_property(np, "hv-handle", NULL); if (!iprop) { - dev_err(&pdev->dev, "no 'hv-handle' property in %s node\n", - np->name); + dev_err(&pdev->dev, "no 'hv-handle' property in %pOFn node\n", + np); return -ENODEV; } @@ -682,8 +682,8 @@ static int ehv_bc_tty_probe(struct platform_device *pdev) bc->rx_irq = irq_of_parse_and_map(np, 0); bc->tx_irq = irq_of_parse_and_map(np, 1); if ((bc->rx_irq == NO_IRQ) || (bc->tx_irq == NO_IRQ)) { - dev_err(&pdev->dev, "no 'interrupts' property in %s node\n", - np->name); + dev_err(&pdev->dev, "no 'interrupts' property in %pOFn node\n", + np); ret = -ENODEV; goto error; } diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 24a5f05e769b..ea7204d75022 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1151,8 +1151,8 @@ static int cpm_uart_init_port(struct device_node *np, if (!pinfo->clk) { data = of_get_property(np, "fsl,cpm-brg", &len); if (!data || len != 4) { - printk(KERN_ERR "CPM UART %s has no/invalid " - "fsl,cpm-brg property.\n", np->name); + printk(KERN_ERR "CPM UART %pOFn has no/invalid " + "fsl,cpm-brg property.\n", np); return -EINVAL; } pinfo->brg = *data; @@ -1160,8 +1160,8 @@ static int cpm_uart_init_port(struct device_node *np, data = of_get_property(np, "fsl,cpm-command", &len); if (!data || len != 4) { - printk(KERN_ERR "CPM UART %s has no/invalid " - "fsl,cpm-command property.\n", np->name); + printk(KERN_ERR "CPM UART %pOFn has no/invalid " + "fsl,cpm-command property.\n", np); return -EINVAL; } pinfo->command = *data; diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 3d21790d961e..a4ec22d1f214 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1566,9 +1566,9 @@ static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match) * to work around bugs in ancient Apple device-trees */ if (macio_request_resources(uap->dev, "pmac_zilog")) - printk(KERN_WARNING "%s: Failed to request resource" + printk(KERN_WARNING "%pOFn: Failed to request resource" ", port still active\n", - uap->node->name); + uap->node); else uap->flags |= PMACZILOG_FLAG_RSRC_REQUESTED; -- cgit v1.2.3 From f6aa5beb45be27968a4df90176ca36dfc4363d37 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 3 Sep 2018 02:44:52 +0200 Subject: serial: 8250: Fix clearing FIFOs in RS485 mode again The 8250 FIFOs indeed need to be cleared after stopping transmission in RS485 mode without SER_RS485_RX_DURING_TX flag set. But there are two problems with the approach taken by the previous patch from Fixes tag. First, serial8250_clear_fifos() should clear fifos, but what it really does is it enables the FIFOs unconditionally if present, clears them and then sets the FCR register to zero, which effectively disables the FIFOs. In case the FIFO is disabled, enabling it and clearing it makes no sense and in fact can trigger misbehavior of the 8250 core. Moreover, the FCR register may contain other FIFO configuration bits which may not be writable unconditionally and writing them incorrectly can trigger misbehavior of the 8250 core too. (ie. AM335x UART swallows the first byte and retransmits the last byte twice because of this FCR write). Second, serial8250_clear_and_reinit_fifos() completely reloads the FCR, but what really has to happen at the end of the RS485 transmission is clearing of the FIFOs and nothing else. This patch repairs serial8250_clear_fifos() so that it really only clears the FIFOs by operating on FCR[2:1] bits and leaves all the other bits alone. It also undoes serial8250_clear_and_reinit_fifos() from __do_stop_tx_rs485() as serial8250_clear_fifos() is sufficient. Signed-off-by: Marek Vasut Fixes: 2bed8a8e7072 ("Clearing FIFOs in RS485 emulation mode causes subsequent transmits to break") Cc: Daniel Jedrychowski Cc: Greg Kroah-Hartman Cc: stable # let it bake a bit before merging Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 3f779d25ec0c..f776b3eafb96 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -552,11 +552,30 @@ static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) */ static void serial8250_clear_fifos(struct uart_8250_port *p) { + unsigned char fcr; + unsigned char clr_mask = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; + if (p->capabilities & UART_CAP_FIFO) { - serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_out(p, UART_FCR, 0); + /* + * Make sure to avoid changing FCR[7:3] and ENABLE_FIFO bits. + * In case ENABLE_FIFO is not set, there is nothing to flush + * so just return. Furthermore, on certain implementations of + * the 8250 core, the FCR[7:3] bits may only be changed under + * specific conditions and changing them if those conditions + * are not met can have nasty side effects. One such core is + * the 8250-omap present in TI AM335x. + */ + fcr = serial_in(p, UART_FCR); + + /* FIFO is not enabled, there's nothing to clear. */ + if (!(fcr & UART_FCR_ENABLE_FIFO)) + return; + + fcr |= clr_mask; + serial_out(p, UART_FCR, fcr); + + fcr &= ~clr_mask; + serial_out(p, UART_FCR, fcr); } } @@ -1448,7 +1467,7 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p) * Enable previously disabled RX interrupts. */ if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) { - serial8250_clear_and_reinit_fifos(p); + serial8250_clear_fifos(p); p->ier |= UART_IER_RLSI | UART_IER_RDI; serial_port_out(&p->port, UART_IER, p->ier); -- cgit v1.2.3 From 8344498721059754e09d30fe255a12dab8fb03ef Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 12 Sep 2018 15:31:55 +0100 Subject: sc16is7xx: Fix for multi-channel stall The SC16IS752 is a dual-channel device. The two channels are largely independent, but the IRQ signals are wired together as an open-drain, active low signal which will be driven low while either of the channels requires attention, which can be for significant periods of time until operations complete and the interrupt can be acknowledged. In that respect it is should be treated as a true level-sensitive IRQ. The kernel, however, needs to be able to exit interrupt context in order to use I2C or SPI to access the device registers (which may involve sleeping). Therefore the interrupt needs to be masked out or paused in some way. The usual way to manage sleeping from within an interrupt handler is to use a threaded interrupt handler - a regular interrupt routine does the minimum amount of work needed to triage the interrupt before waking the interrupt service thread. If the threaded IRQ is marked as IRQF_ONESHOT the kernel will automatically mask out the interrupt until the thread runs to completion. The sc16is7xx driver used to use a threaded IRQ, but a patch switched to using a kthread_worker in order to set realtime priorities on the handler thread and for other optimisations. The end result is non-threaded IRQ that schedules some work then returns IRQ_HANDLED, making the kernel think that all IRQ processing has completed. The work-around to prevent a constant stream of interrupts is to mark the interrupt as edge-sensitive rather than level-sensitive, but interpreting an active-low source as a falling-edge source requires care to prevent a total cessation of interrupts. Whereas an edge-triggering source will generate a new edge for every interrupt condition a level-triggering source will keep the signal at the interrupting level until it no longer requires attention; in other words, the host won't see another edge until all interrupt conditions are cleared. It is therefore vital that the interrupt handler does not exit with an outstanding interrupt condition, otherwise the kernel will not receive another interrupt unless some other operation causes the interrupt state on the device to be cleared. The existing sc16is7xx driver has a very simple interrupt "thread" (kthread_work job) that processes interrupts on each channel in turn until there are no more. If both channels are active and the first channel starts interrupting while the handler for the second channel is running then it will not be detected and an IRQ stall ensues. This could be handled easily if there was a shared IRQ status register, or a convenient way to determine if the IRQ had been deasserted for any length of time, but both appear to be lacking. Avoid this problem (or at least make it much less likely to happen) by reducing the granularity of per-channel interrupt processing to one condition per iteration, only exiting the overall loop when both channels are no longer interrupting. Signed-off-by: Phil Elwell Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 243c96025053..47b41159a8bc 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -657,7 +657,7 @@ static void sc16is7xx_handle_tx(struct uart_port *port) uart_write_wakeup(port); } -static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) +static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) { struct uart_port *port = &s->p[portno].port; @@ -666,7 +666,7 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); if (iir & SC16IS7XX_IIR_NO_INT_BIT) - break; + return false; iir &= SC16IS7XX_IIR_ID_MASK; @@ -688,16 +688,23 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) port->line, iir); break; } - } while (1); + } while (0); + return true; } static void sc16is7xx_ist(struct kthread_work *ws) { struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work); - int i; - for (i = 0; i < s->devtype->nr_uart; ++i) - sc16is7xx_port_irq(s, i); + while (1) { + bool keep_polling = false; + int i; + + for (i = 0; i < s->devtype->nr_uart; ++i) + keep_polling |= sc16is7xx_port_irq(s, i); + if (!keep_polling) + break; + } } static irqreturn_t sc16is7xx_irq(int irq, void *dev_id) -- cgit v1.2.3 From 30ec514d440cf2c472c8e4b0079af2c731f71a3e Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 12 Sep 2018 15:31:56 +0100 Subject: sc16is7xx: Fix for "Unexpected interrupt: 8" The SC16IS752 has an Enhanced Feature Register which is aliased at the same address as the Interrupt Identification Register; accessing it requires that a magic value is written to the Line Configuration Register. If an interrupt is raised while the EFR is mapped in then the ISR won't be able to access the IIR, leading to the "Unexpected interrupt" error messages. Avoid the problem by claiming a mutex around accesses to the EFR register, also claiming the mutex in the interrupt handler work item (this is equivalent to disabling interrupts to interlock against a non-threaded interrupt handler). See: https://github.com/raspberrypi/linux/issues/2529 Signed-off-by: Phil Elwell Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 47b41159a8bc..268098681856 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -328,6 +328,7 @@ struct sc16is7xx_port { struct kthread_worker kworker; struct task_struct *kworker_task; struct kthread_work irq_work; + struct mutex efr_lock; struct sc16is7xx_one p[0]; }; @@ -499,6 +500,21 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) div /= 4; } + /* In an amazing feat of design, the Enhanced Features Register shares + * the address of the Interrupt Identification Register, and is + * switched in by writing a magic value (0xbf) to the Line Control + * Register. Any interrupt firing during this time will see the EFR + * where it expects the IIR to be, leading to "Unexpected interrupt" + * messages. + * + * Prevent this possibility by claiming a mutex while accessing the + * EFR, and claiming the same mutex from within the interrupt handler. + * This is similar to disabling the interrupt, but that doesn't work + * because the bulk of the interrupt processing is run as a workqueue + * job in thread context. + */ + mutex_lock(&s->efr_lock); + lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG); /* Open the LCR divisors for configuration */ @@ -514,6 +530,8 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) /* Put LCR back to the normal mode */ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + mutex_unlock(&s->efr_lock); + sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, SC16IS7XX_MCR_CLKSEL_BIT, prescaler); @@ -696,6 +714,8 @@ static void sc16is7xx_ist(struct kthread_work *ws) { struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work); + mutex_lock(&s->efr_lock); + while (1) { bool keep_polling = false; int i; @@ -705,6 +725,8 @@ static void sc16is7xx_ist(struct kthread_work *ws) if (!keep_polling) break; } + + mutex_unlock(&s->efr_lock); } static irqreturn_t sc16is7xx_irq(int irq, void *dev_id) @@ -899,6 +921,9 @@ static void sc16is7xx_set_termios(struct uart_port *port, if (!(termios->c_cflag & CREAD)) port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK; + /* As above, claim the mutex while accessing the EFR. */ + mutex_lock(&s->efr_lock); + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_CONF_MODE_B); @@ -920,6 +945,8 @@ static void sc16is7xx_set_termios(struct uart_port *port, /* Update LCR register */ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + mutex_unlock(&s->efr_lock); + /* Get baud rate generator configuration */ baud = uart_get_baud_rate(port, termios, old, port->uartclk / 16 / 4 / 0xffff, @@ -1185,6 +1212,7 @@ static int sc16is7xx_probe(struct device *dev, s->regmap = regmap; s->devtype = devtype; dev_set_drvdata(dev, s); + mutex_init(&s->efr_lock); kthread_init_worker(&s->kworker); kthread_init_work(&s->irq_work, sc16is7xx_ist); -- cgit v1.2.3 From 863299001b39280b3be9f5c627d23debe66aa71b Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 12 Sep 2018 17:50:42 +1000 Subject: tty_port: Remove incorrect whitespace after comments Currently there are a bunch of kernel-doc function comments that have a line of whitespace after the comment and before the function they comment - this is incorrect, there should be no whitespace here. Remove incorrect whitespace between comment and associated function. Signed-off-by: Tobin C. Harding Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 25d736880013..cb6075096a5b 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -279,7 +279,6 @@ EXPORT_SYMBOL(tty_port_put); * Return a refcount protected tty instance or NULL if the port is not * associated with a tty (eg due to close or hangup) */ - struct tty_struct *tty_port_tty_get(struct tty_port *port) { unsigned long flags; @@ -300,7 +299,6 @@ EXPORT_SYMBOL(tty_port_tty_get); * Associate the port and tty pair. Manages any internal refcounts. * Pass NULL to deassociate a port */ - void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) { unsigned long flags; @@ -343,7 +341,6 @@ out: * * Caller holds tty lock. */ - void tty_port_hangup(struct tty_port *port) { struct tty_struct *tty; @@ -399,7 +396,6 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); * to hide some internal details. This will eventually become entirely * internal to the tty port. */ - int tty_port_carrier_raised(struct tty_port *port) { if (port->ops->carrier_raised == NULL) @@ -416,7 +412,6 @@ EXPORT_SYMBOL(tty_port_carrier_raised); * to hide some internal details. This will eventually become entirely * internal to the tty port. */ - void tty_port_raise_dtr_rts(struct tty_port *port) { if (port->ops->dtr_rts) @@ -432,7 +427,6 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts); * to hide some internal details. This will eventually become entirely * internal to the tty port. */ - void tty_port_lower_dtr_rts(struct tty_port *port) { if (port->ops->dtr_rts) @@ -464,7 +458,6 @@ EXPORT_SYMBOL(tty_port_lower_dtr_rts); * NB: May drop and reacquire tty lock when blocking, so tty and tty_port * may have changed state (eg., may have been hung up). */ - int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty, struct file *filp) { -- cgit v1.2.3 From feacbecb39db098cd9f6d728dcb00d753a3f36ed Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 7 Sep 2018 15:19:06 +0200 Subject: TTY: tty_buffer, warn on leaks When we leak some tty buffer, warn about that. For that we need to account the memory used also in the tty_buffer_free_all function. On other locations, the accounting is handled correctly. Note that we do not account the free list, as that was accounted in tty_buffer_free before put on the free list. I have been using this patch for ages, so let's see if anybody else encounters any issues. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index c996b6859c5e..76151c002858 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -118,9 +118,12 @@ void tty_buffer_free_all(struct tty_port *port) struct tty_bufhead *buf = &port->buf; struct tty_buffer *p, *next; struct llist_node *llist; + unsigned int freed = 0; + int still_used; while ((p = buf->head) != NULL) { buf->head = p->next; + freed += p->size; if (p->size > 0) kfree(p); } @@ -132,7 +135,9 @@ void tty_buffer_free_all(struct tty_port *port) buf->head = &buf->sentinel; buf->tail = &buf->sentinel; - atomic_set(&buf->mem_used, 0); + still_used = atomic_xchg(&buf->mem_used, 0); + WARN(still_used != freed, "we still have not freed %d bytes!", + still_used - freed); } /** -- cgit v1.2.3 From 1cd25cbb2fedbc777f3a8c3cb1ba69b645aeaa64 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Wed, 19 Sep 2018 18:59:01 -0700 Subject: kgdboc: Fix warning with module build After 2dd453168643 ("kgdboc: Fix restrict error"), kgdboc_option_setup is now only used when built in, resulting in a warning when compiled as a module: drivers/tty/serial/kgdboc.c:134:12: warning: 'kgdboc_option_setup' defined but not used [-Wunused-function] static int kgdboc_option_setup(char *opt) ^~~~~~~~~~~~~~~~~~~ Move the function under the appropriate ifdef for builtin only. Fixes: 2dd453168643 ("kgdboc: Fix restrict error") Reported-by: Stephen Rothwell Signed-off-by: Laura Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/kgdboc.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index e9a83bb5bee5..baeeeaec3f03 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -131,24 +131,6 @@ static void kgdboc_unregister_kbd(void) #define kgdboc_restore_input() #endif /* ! CONFIG_KDB_KEYBOARD */ -static int kgdboc_option_setup(char *opt) -{ - if (!opt) { - pr_err("config string not provided\n"); - return -EINVAL; - } - - if (strlen(opt) >= MAX_CONFIG_LEN) { - pr_err("config string too long\n"); - return -ENOSPC; - } - strcpy(config, opt); - - return 0; -} - -__setup("kgdboc=", kgdboc_option_setup); - static void cleanup_kgdboc(void) { if (kgdb_unregister_nmi_console()) @@ -316,6 +298,25 @@ static struct kgdb_io kgdboc_io_ops = { }; #ifdef CONFIG_KGDB_SERIAL_CONSOLE +static int kgdboc_option_setup(char *opt) +{ + if (!opt) { + pr_err("config string not provided\n"); + return -EINVAL; + } + + if (strlen(opt) >= MAX_CONFIG_LEN) { + pr_err("config string too long\n"); + return -ENOSPC; + } + strcpy(config, opt); + + return 0; +} + +__setup("kgdboc=", kgdboc_option_setup); + + /* This is only available if kgdboc is a built in for early debugging */ static int __init kgdboc_early_init(char *opt) { -- cgit v1.2.3 From 13b4353bb05568c9fa3e98df0bf95dd1478c14b7 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 20 Sep 2018 01:59:16 +0000 Subject: tty: serial: remove set but not used variable 'error' Fixes gcc '-Wunused-but-set-variable' warning: drivers/tty/serial/pmac_zilog.c: In function 'pmz_receive_chars': drivers/tty/serial/pmac_zilog.c:222:30: warning: variable 'error' set but not used [-Wunused-but-set-variable] Signed-off-by: YueHaibing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pmac_zilog.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index a4ec22d1f214..a9d40988e1c8 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -219,7 +219,7 @@ static void pmz_interrupt_control(struct uart_pmac_port *uap, int enable) static bool pmz_receive_chars(struct uart_pmac_port *uap) { struct tty_port *port; - unsigned char ch, r1, drop, error, flag; + unsigned char ch, r1, drop, flag; int loops = 0; /* Sanity check, make sure the old bug is no longer happening */ @@ -231,7 +231,6 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) port = &uap->port.state->port; while (1) { - error = 0; drop = 0; r1 = read_zsreg(uap, R1); @@ -273,7 +272,6 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) uap->port.icount.rx++; if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | BRK_ABRT)) { - error = 1; if (r1 & BRK_ABRT) { pmz_debug("pmz: got break !\n"); r1 &= ~(PAR_ERR | CRC_ERR); -- cgit v1.2.3 From b1078c355d76769b5ddefc67d143fbd9b6e52c05 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 20 Sep 2018 13:41:52 +0200 Subject: of: base: Introduce of_alias_get_alias_list() to check alias IDs The function travels the lookup table to record alias ids for the given device match structures and alias stem. This function will be used by serial drivers to check if requested alias is allocated or free to use. Signed-off-by: Michal Simek Reviewed-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/of/base.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of.h | 10 ++++++++++ 2 files changed, 62 insertions(+) (limited to 'drivers') diff --git a/drivers/of/base.c b/drivers/of/base.c index 74eaedd5b860..33011b88ed3f 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) "OF: " fmt +#include #include #include #include @@ -1942,6 +1943,57 @@ int of_alias_get_id(struct device_node *np, const char *stem) } EXPORT_SYMBOL_GPL(of_alias_get_id); +/** + * of_alias_get_alias_list - Get alias list for the given device driver + * @matches: Array of OF device match structures to search in + * @stem: Alias stem of the given device_node + * @bitmap: Bitmap field pointer + * @nbits: Maximum number of alias ID which can be recorded it bitmap + * + * The function travels the lookup table to record alias ids for the given + * device match structures and alias stem. + * + * Return: 0 or -ENOSYS when !CONFIG_OF + */ +int of_alias_get_alias_list(const struct of_device_id *matches, + const char *stem, unsigned long *bitmap, + unsigned int nbits) +{ + struct alias_prop *app; + + /* Zero bitmap field to make sure that all the time it is clean */ + bitmap_zero(bitmap, nbits); + + mutex_lock(&of_mutex); + pr_debug("%s: Looking for stem: %s\n", __func__, stem); + list_for_each_entry(app, &aliases_lookup, link) { + pr_debug("%s: stem: %s, id: %d\n", + __func__, app->stem, app->id); + + if (strcmp(app->stem, stem) != 0) { + pr_debug("%s: stem comparison doesn't passed %s\n", + __func__, app->stem); + continue; + } + + if (app->id >= nbits) { + pr_debug("%s: ID %d greater then bitmap field %d\n", + __func__, app->id, nbits); + continue; + } + + if (of_match_node(matches, app->np)) { + pr_debug("%s: Allocated ID %d\n", __func__, app->id); + set_bit(app->id, bitmap); + } + /* Alias exist but it not compatible with matches */ + } + mutex_unlock(&of_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(of_alias_get_alias_list); + /** * of_alias_get_highest_id - Get highest alias id for the given stem * @stem: Alias stem to be examined diff --git a/include/linux/of.h b/include/linux/of.h index 99b0ebf49632..d51457b40725 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -392,6 +392,9 @@ extern int of_phandle_iterator_args(struct of_phandle_iterator *it, extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); extern int of_alias_get_id(struct device_node *np, const char *stem); extern int of_alias_get_highest_id(const char *stem); +extern int of_alias_get_alias_list(const struct of_device_id *matches, + const char *stem, unsigned long *bitmap, + unsigned int nbits); extern int of_machine_is_compatible(const char *compat); @@ -893,6 +896,13 @@ static inline int of_alias_get_highest_id(const char *stem) return -ENOSYS; } +static inline int of_alias_get_alias_list(const struct of_device_id *matches, + const char *stem, unsigned long *bitmap, + unsigned int nbits) +{ + return -ENOSYS; +} + static inline int of_machine_is_compatible(const char *compat) { return 0; -- cgit v1.2.3 From ae1cca3fa3478be92948dbbcd722390272032ade Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 20 Sep 2018 13:41:53 +0200 Subject: serial: uartps: Change uart ID port allocation For IPs which have alias algorightm all the time using that alias and minor number. It means serial20 alias ends up as ttyPS20. If alias is not setup for probed IP instance the first unused position is used but that needs to be checked if it is really empty because another instance doesn't need to be probed at that time. of_alias_get_alias_list() fills alias bitmap which exactly shows which ID is free. If alias pointing to different not compatible IP, it is free to use. cdns_get_id() call is placed below structure allocation to simplify error path. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 109 ++++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 71c032744dae..f77200a0f461 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -30,7 +30,6 @@ #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" #define CDNS_UART_MAJOR 0 /* use dynamic node allocation */ -#define CDNS_UART_NR_PORTS 2 #define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ #define CDNS_UART_REGISTER_SPACE 0x1000 @@ -1370,6 +1369,88 @@ static const struct of_device_id cdns_uart_of_match[] = { }; MODULE_DEVICE_TABLE(of, cdns_uart_of_match); +/* + * Maximum number of instances without alias IDs but if there is alias + * which target "< MAX_UART_INSTANCES" range this ID can't be used. + */ +#define MAX_UART_INSTANCES 32 + +/* Stores static aliases list */ +static DECLARE_BITMAP(alias_bitmap, MAX_UART_INSTANCES); +static int alias_bitmap_initialized; + +/* Stores actual bitmap of allocated IDs with alias IDs together */ +static DECLARE_BITMAP(bitmap, MAX_UART_INSTANCES); +/* Protect bitmap operations to have unique IDs */ +static DEFINE_MUTEX(bitmap_lock); + +static int cdns_get_id(struct platform_device *pdev) +{ + int id, ret; + + mutex_lock(&bitmap_lock); + + /* Alias list is stable that's why get alias bitmap only once */ + if (!alias_bitmap_initialized) { + ret = of_alias_get_alias_list(cdns_uart_of_match, "serial", + alias_bitmap, MAX_UART_INSTANCES); + if (ret) + return ret; + + alias_bitmap_initialized++; + } + + /* Make sure that alias ID is not taken by instance without alias */ + bitmap_or(bitmap, bitmap, alias_bitmap, MAX_UART_INSTANCES); + + dev_dbg(&pdev->dev, "Alias bitmap: %*pb\n", + MAX_UART_INSTANCES, bitmap); + + /* Look for a serialN alias */ + id = of_alias_get_id(pdev->dev.of_node, "serial"); + if (id < 0) { + dev_warn(&pdev->dev, + "No serial alias passed. Using the first free id\n"); + + /* + * Start with id 0 and check if there is no serial0 alias + * which points to device which is compatible with this driver. + * If alias exists then try next free position. + */ + id = 0; + + for (;;) { + dev_info(&pdev->dev, "Checking id %d\n", id); + id = find_next_zero_bit(bitmap, MAX_UART_INSTANCES, id); + + /* No free empty instance */ + if (id == MAX_UART_INSTANCES) { + dev_err(&pdev->dev, "No free ID\n"); + mutex_unlock(&bitmap_lock); + return -EINVAL; + } + + dev_dbg(&pdev->dev, "The empty id is %d\n", id); + /* Check if ID is empty */ + if (!test_and_set_bit(id, bitmap)) { + /* Break the loop if bit is taken */ + dev_dbg(&pdev->dev, + "Selected ID %d allocation passed\n", + id); + break; + } + dev_dbg(&pdev->dev, + "Selected ID %d allocation failed\n", id); + /* if taking bit fails then try next one */ + id++; + } + } + + mutex_unlock(&bitmap_lock); + + return id; +} + /** * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure @@ -1403,21 +1484,17 @@ static int cdns_uart_probe(struct platform_device *pdev) if (!cdns_uart_uart_driver) return -ENOMEM; - /* Look for a serialN alias */ - cdns_uart_data->id = of_alias_get_id(pdev->dev.of_node, "serial"); + cdns_uart_data->id = cdns_get_id(pdev); if (cdns_uart_data->id < 0) - cdns_uart_data->id = 0; - - if (cdns_uart_data->id >= CDNS_UART_NR_PORTS) { - dev_err(&pdev->dev, "Cannot get uart_port structure\n"); - return -ENODEV; - } + return cdns_uart_data->id; /* There is a need to use unique driver name */ driver_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%d", CDNS_UART_NAME, cdns_uart_data->id); - if (!driver_name) - return -ENOMEM; + if (!driver_name) { + rc = -ENOMEM; + goto err_out_id; + } cdns_uart_uart_driver->owner = THIS_MODULE; cdns_uart_uart_driver->driver_name = driver_name; @@ -1446,7 +1523,7 @@ static int cdns_uart_probe(struct platform_device *pdev) rc = uart_register_driver(cdns_uart_uart_driver); if (rc < 0) { dev_err(&pdev->dev, "Failed to register driver\n"); - return rc; + goto err_out_id; } cdns_uart_data->cdns_uart_driver = cdns_uart_uart_driver; @@ -1587,7 +1664,10 @@ err_out_clk_dis_pclk: clk_disable_unprepare(cdns_uart_data->pclk); err_out_unregister_driver: uart_unregister_driver(cdns_uart_data->cdns_uart_driver); - +err_out_id: + mutex_lock(&bitmap_lock); + clear_bit(cdns_uart_data->id, bitmap); + mutex_unlock(&bitmap_lock); return rc; } @@ -1610,6 +1690,9 @@ static int cdns_uart_remove(struct platform_device *pdev) #endif rc = uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port); port->mapbase = 0; + mutex_lock(&bitmap_lock); + clear_bit(cdns_uart_data->id, bitmap); + mutex_unlock(&bitmap_lock); clk_disable_unprepare(cdns_uart_data->uartclk); clk_disable_unprepare(cdns_uart_data->pclk); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From c550f01c810f2197c98e6e3103f81797f5e063be Mon Sep 17 00:00:00 2001 From: Steve Sakoman Date: Thu, 20 Sep 2018 09:20:34 -1000 Subject: serial:serial_core: Allow use of CTS for PPS line discipline Add a "pps_4wire" file to serial ports in sysfs in case the kernel is configured with CONFIG_PPS_CLIENT_LDISC. Writing 1 to the file enables the use of CTS instead of DCD for PPS signal input. This is necessary in case a serial port is not completely wired. Though this affects PPS processing the patch is against the serial core as the source of the serial port PPS event dispatching has to be modified. Furthermore it should be possible to modify the source of serial port PPS event dispatching before changing the line discipline. Signed-off-by: Andreas Steinmetz Signed-off-by: Steve Sakoman Tested-by: Steve Sakoman Tested-by: Eric Gallimore Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-tty | 9 +++++ drivers/tty/serial/serial_core.c | 70 ++++++++++++++++++++++++++++++++++++- include/linux/serial_core.h | 3 +- 3 files changed, 80 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-tty b/Documentation/ABI/testing/sysfs-tty index 9eb3c2b6b040..441105a75d1f 100644 --- a/Documentation/ABI/testing/sysfs-tty +++ b/Documentation/ABI/testing/sysfs-tty @@ -154,3 +154,12 @@ Description: device specification. For example, when user sets 7bytes on 16550A, which has 1/4/8/14 bytes trigger, the RX trigger is automatically changed to 4 bytes. + +What: /sys/class/tty/ttyS0/pps_4wire +Date: September 2018 +Contact: Steve Sakoman +Description: + Shows/sets "4 wire" mode for the PPS input to the serial driver. + For fully implemented serial ports PPS is normally provided + on the DCD line. For partial "4 wire" implementations CTS is + used instead of DCD. diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 80bb56facfb6..ed0133395cc7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2664,6 +2664,57 @@ static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift); } +static ssize_t pps_4wire_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tty_port *port = dev_get_drvdata(dev); + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport; + int mode = 0; + + mutex_lock(&port->mutex); + uport = uart_port_check(state); + if (!uport) + goto out; + + mode = uport->pps_4wire; + +out: + mutex_unlock(&port->mutex); + return sprintf(buf, mode ? "yes\n" : "no\n"); +} + +static ssize_t pps_4wire_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct tty_port *port = dev_get_drvdata(dev); + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport; + bool mode; + int ret; + + if (!count) + return -EINVAL; + + ret = kstrtobool(buf, &mode); + if (ret < 0) + return ret; + + mutex_lock(&port->mutex); + uport = uart_port_check(state); + if (!uport) + goto out; + + spin_lock_irq(&uport->lock); + uport->pps_4wire = mode; + spin_unlock_irq(&uport->lock); + +out: + mutex_unlock(&port->mutex); + return count; +} +static DEVICE_ATTR_RW(pps_4wire); + static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL); static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL); static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL); @@ -2692,6 +2743,7 @@ static struct attribute *tty_dev_attrs[] = { &dev_attr_io_type.attr, &dev_attr_iomem_base.attr, &dev_attr_iomem_reg_shift.attr, + &dev_attr_pps_4wire.attr, NULL, }; @@ -2748,6 +2800,9 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) goto out; } + /* assert that pps handling is done via DCD as default */ + uport->pps_4wire = 0; + /* * If this port is a console, then the spinlock is already * initialised. @@ -2923,7 +2978,7 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) lockdep_assert_held_once(&uport->lock); - if (tty) { + if (tty && !uport->pps_4wire) { ld = tty_ldisc_ref(tty); if (ld) { if (ld->ops->dcd_change) @@ -2952,8 +3007,21 @@ EXPORT_SYMBOL_GPL(uart_handle_dcd_change); */ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) { + struct tty_port *port = &uport->state->port; + struct tty_struct *tty = port->tty; + struct tty_ldisc *ld; + lockdep_assert_held_once(&uport->lock); + if (tty && uport->pps_4wire) { + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->ops->dcd_change) + ld->ops->dcd_change(tty, status); + tty_ldisc_deref(ld); + } + } + uport->icount.cts++; if (uart_softcts_mode(uport)) { diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 406edae44ca3..079793e5d3fa 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -255,7 +255,8 @@ struct uart_port { struct device *dev; /* parent device */ unsigned char hub6; /* this should be in the 8250 driver */ unsigned char suspended; - unsigned char unused[2]; + unsigned char pps_4wire; /* CTS instead of DCD */ + unsigned char unused; const char *name; /* port name */ struct attribute_group *attr_group; /* port specific attributes */ const struct attribute_group **tty_groups; /* all attributes (serial core use only) */ -- cgit v1.2.3 From ad8c0eaa0a418ae8ef3f9217638bb86439399eac Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 26 Sep 2018 14:58:47 +0200 Subject: tty/serial_core: add ISO7816 infrastructure Add the ISO7816 ioctl and associated accessors and data structure. Drivers can then use this common implementation to handle ISO7816 (smart cards). Signed-off-by: Nicolas Ferre [ludovic.desroches@microchip.com: squash and rebase, removal of gpios, checkpatch fixes] Signed-off-by: Ludovic Desroches Signed-off-by: Greg Kroah-Hartman --- Documentation/serial/serial-iso7816.txt | 83 +++++++++++++++++++++++++++++++++ arch/alpha/include/uapi/asm/ioctls.h | 2 + arch/mips/include/uapi/asm/ioctls.h | 2 + arch/parisc/include/uapi/asm/ioctls.h | 2 + arch/powerpc/include/uapi/asm/ioctls.h | 2 + arch/sh/include/uapi/asm/ioctls.h | 2 + arch/sparc/include/uapi/asm/ioctls.h | 2 + arch/xtensa/include/uapi/asm/ioctls.h | 2 + drivers/tty/serial/serial_core.c | 60 ++++++++++++++++++++++++ include/linux/serial_core.h | 3 ++ include/uapi/asm-generic/ioctls.h | 2 + include/uapi/linux/serial.h | 17 +++++++ 12 files changed, 179 insertions(+) create mode 100644 Documentation/serial/serial-iso7816.txt (limited to 'drivers') diff --git a/Documentation/serial/serial-iso7816.txt b/Documentation/serial/serial-iso7816.txt new file mode 100644 index 000000000000..3193d24a2b0f --- /dev/null +++ b/Documentation/serial/serial-iso7816.txt @@ -0,0 +1,83 @@ + ISO7816 SERIAL COMMUNICATIONS + +1. INTRODUCTION + + ISO/IEC7816 is a series of standards specifying integrated circuit cards (ICC) + also known as smart cards. + +2. HARDWARE-RELATED CONSIDERATIONS + + Some CPUs/UARTs (e.g., Microchip AT91) contain a built-in mode capable of + handling communication with a smart card. + + For these microcontrollers, the Linux driver should be made capable of + working in both modes, and proper ioctls (see later) should be made + available at user-level to allow switching from one mode to the other, and + vice versa. + +3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL + + The Linux kernel provides the serial_iso7816 structure (see [1]) to handle + ISO7816 communications. This data structure is used to set and configure + ISO7816 parameters in ioctls. + + Any driver for devices capable of working both as RS232 and ISO7816 should + implement the iso7816_config callback in the uart_port structure. The + serial_core calls iso7816_config to do the device specific part in response + to TIOCGISO7816 and TIOCSISO7816 ioctls (see below). The iso7816_config + callback receives a pointer to struct serial_iso7816. + +4. USAGE FROM USER-LEVEL + + From user-level, ISO7816 configuration can be get/set using the previous + ioctls. For instance, to set ISO7816 you can use the following code: + + #include + + /* Include definition for ISO7816 ioctls: TIOCSISO7816 and TIOCGISO7816 */ + #include + + /* Open your specific device (e.g., /dev/mydevice): */ + int fd = open ("/dev/mydevice", O_RDWR); + if (fd < 0) { + /* Error handling. See errno. */ + } + + struct serial_iso7816 iso7816conf; + + /* Reserved fields as to be zeroed */ + memset(&iso7816conf, 0, sizeof(iso7816conf)); + + /* Enable ISO7816 mode: */ + iso7816conf.flags |= SER_ISO7816_ENABLED; + + /* Select the protocol: */ + /* T=0 */ + iso7816conf.flags |= SER_ISO7816_T(0); + /* or T=1 */ + iso7816conf.flags |= SER_ISO7816_T(1); + + /* Set the guard time: */ + iso7816conf.tg = 2; + + /* Set the clock frequency*/ + iso7816conf.clk = 3571200; + + /* Set transmission factors: */ + iso7816conf.sc_fi = 372; + iso7816conf.sc_di = 1; + + if (ioctl(fd_usart, TIOCSISO7816, &iso7816conf) < 0) { + /* Error handling. See errno. */ + } + + /* Use read() and write() syscalls here... */ + + /* Close the device when finished: */ + if (close (fd) < 0) { + /* Error handling. See errno. */ + } + +5. REFERENCES + + [1] include/uapi/linux/serial.h diff --git a/arch/alpha/include/uapi/asm/ioctls.h b/arch/alpha/include/uapi/asm/ioctls.h index 3729d92d3fa8..1e9121c9b3c7 100644 --- a/arch/alpha/include/uapi/asm/ioctls.h +++ b/arch/alpha/include/uapi/asm/ioctls.h @@ -102,6 +102,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/mips/include/uapi/asm/ioctls.h b/arch/mips/include/uapi/asm/ioctls.h index 890245a9f0c4..16aa8a766aec 100644 --- a/arch/mips/include/uapi/asm/ioctls.h +++ b/arch/mips/include/uapi/asm/ioctls.h @@ -93,6 +93,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) /* I hope the range from 0x5480 on is free ... */ #define TIOCSCTTY 0x5480 /* become controlling tty */ diff --git a/arch/parisc/include/uapi/asm/ioctls.h b/arch/parisc/include/uapi/asm/ioctls.h index aafb1c0ca0af..82d1148c6379 100644 --- a/arch/parisc/include/uapi/asm/ioctls.h +++ b/arch/parisc/include/uapi/asm/ioctls.h @@ -62,6 +62,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 diff --git a/arch/powerpc/include/uapi/asm/ioctls.h b/arch/powerpc/include/uapi/asm/ioctls.h index 41b1a5c15734..2c145da3b774 100644 --- a/arch/powerpc/include/uapi/asm/ioctls.h +++ b/arch/powerpc/include/uapi/asm/ioctls.h @@ -102,6 +102,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/sh/include/uapi/asm/ioctls.h b/arch/sh/include/uapi/asm/ioctls.h index cc62f6f98103..11866d4f60e1 100644 --- a/arch/sh/include/uapi/asm/ioctls.h +++ b/arch/sh/include/uapi/asm/ioctls.h @@ -95,6 +95,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */ #define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */ diff --git a/arch/sparc/include/uapi/asm/ioctls.h b/arch/sparc/include/uapi/asm/ioctls.h index 2df52711e170..7fd2f5873c9e 100644 --- a/arch/sparc/include/uapi/asm/ioctls.h +++ b/arch/sparc/include/uapi/asm/ioctls.h @@ -27,6 +27,8 @@ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGRS485 _IOR('T', 0x41, struct serial_rs485) #define TIOCSRS485 _IOWR('T', 0x42, struct serial_rs485) +#define TIOCGISO7816 _IOR('T', 0x43, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x44, struct serial_iso7816) /* Note that all the ioctls that are not available in Linux have a * double underscore on the front to: a) avoid some programs to diff --git a/arch/xtensa/include/uapi/asm/ioctls.h b/arch/xtensa/include/uapi/asm/ioctls.h index ec43609cbfc5..6d4a87296c95 100644 --- a/arch/xtensa/include/uapi/asm/ioctls.h +++ b/arch/xtensa/include/uapi/asm/ioctls.h @@ -107,6 +107,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define TIOCSERCONFIG _IO('T', 83) #define TIOCSERGWILD _IOR('T', 84, int) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index ed0133395cc7..0a4e6eeb5ff3 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1308,6 +1308,58 @@ static int uart_set_rs485_config(struct uart_port *port, return 0; } +static int uart_get_iso7816_config(struct uart_port *port, + struct serial_iso7816 __user *iso7816) +{ + unsigned long flags; + struct serial_iso7816 aux; + + if (!port->iso7816_config) + return -ENOIOCTLCMD; + + spin_lock_irqsave(&port->lock, flags); + aux = port->iso7816; + spin_unlock_irqrestore(&port->lock, flags); + + if (copy_to_user(iso7816, &aux, sizeof(aux))) + return -EFAULT; + + return 0; +} + +static int uart_set_iso7816_config(struct uart_port *port, + struct serial_iso7816 __user *iso7816_user) +{ + struct serial_iso7816 iso7816; + int i, ret; + unsigned long flags; + + if (!port->iso7816_config) + return -ENOIOCTLCMD; + + if (copy_from_user(&iso7816, iso7816_user, sizeof(*iso7816_user))) + return -EFAULT; + + /* + * There are 5 words reserved for future use. Check that userspace + * doesn't put stuff in there to prevent breakages in the future. + */ + for (i = 0; i < 5; i++) + if (iso7816.reserved[i]) + return -EINVAL; + + spin_lock_irqsave(&port->lock, flags); + ret = port->iso7816_config(port, &iso7816); + spin_unlock_irqrestore(&port->lock, flags); + if (ret) + return ret; + + if (copy_to_user(iso7816_user, &port->iso7816, sizeof(port->iso7816))) + return -EFAULT; + + return 0; +} + /* * Called via sys_ioctl. We can use spin_lock_irq() here. */ @@ -1392,6 +1444,14 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) case TIOCSRS485: ret = uart_set_rs485_config(uport, uarg); break; + + case TIOCSISO7816: + ret = uart_set_iso7816_config(state->uart_port, uarg); + break; + + case TIOCGISO7816: + ret = uart_get_iso7816_config(state->uart_port, uarg); + break; default: if (uport->ops->ioctl) ret = uport->ops->ioctl(uport, cmd, arg); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 079793e5d3fa..4e2ba4894dcc 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -144,6 +144,8 @@ struct uart_port { void (*handle_break)(struct uart_port *); int (*rs485_config)(struct uart_port *, struct serial_rs485 *rs485); + int (*iso7816_config)(struct uart_port *, + struct serial_iso7816 *iso7816); unsigned int irq; /* irq number */ unsigned long irqflags; /* irq flags */ unsigned int uartclk; /* base uart clock */ @@ -261,6 +263,7 @@ struct uart_port { struct attribute_group *attr_group; /* port specific attributes */ const struct attribute_group **tty_groups; /* all attributes (serial core use only) */ struct serial_rs485 rs485; + struct serial_iso7816 iso7816; void *private_data; /* generic platform data pointer */ }; diff --git a/include/uapi/asm-generic/ioctls.h b/include/uapi/asm-generic/ioctls.h index 040651735662..cdc9f4ca8c27 100644 --- a/include/uapi/asm-generic/ioctls.h +++ b/include/uapi/asm-generic/ioctls.h @@ -79,6 +79,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h index 3fdd0dee8b41..93eb3c496ff1 100644 --- a/include/uapi/linux/serial.h +++ b/include/uapi/linux/serial.h @@ -132,4 +132,21 @@ struct serial_rs485 { are a royal PITA .. */ }; +/* + * Serial interface for controlling ISO7816 settings on chips with suitable + * support. Set with TIOCSISO7816 and get with TIOCGISO7816 if supported by + * your platform. + */ +struct serial_iso7816 { + __u32 flags; /* ISO7816 feature flags */ +#define SER_ISO7816_ENABLED (1 << 0) +#define SER_ISO7816_T_PARAM (0x0f << 4) +#define SER_ISO7816_T(t) (((t) & 0x0f) << 4) + __u32 tg; + __u32 sc_fi; + __u32 sc_di; + __u32 clk; + __u32 reserved[5]; +}; + #endif /* _UAPI_LINUX_SERIAL_H */ -- cgit v1.2.3 From 377fedd1866ae3979e4fe36193475b8acbc82784 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 26 Sep 2018 14:58:48 +0200 Subject: tty/serial: atmel: add ISO7816 support When mode is set in atmel_config_iso7816() we backup last RS232 mode for coming back to this mode if requested. Also allow setup of T=0 and T=1 parameter and basic support in set_termios function as well. Signed-off-by: Nicolas Ferre [ludovic.desroches@microchip.com: rebase, add check on fidi ratio, checkpatch fixes] Signed-off-by: Ludovic Desroches Acked-by: Richard Genoud Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 190 +++++++++++++++++++++++++++++++++++--- drivers/tty/serial/atmel_serial.h | 3 +- 2 files changed, 181 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 267d4d1de3f8..05147fe24343 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -147,6 +148,8 @@ struct atmel_uart_port { struct circ_buf rx_ring; struct mctrl_gpios *gpios; + u32 backup_mode; /* MR saved during iso7816 operations */ + u32 backup_brgr; /* BRGR saved during iso7816 operations */ unsigned int tx_done_mask; u32 fifo_size; u32 rts_high; @@ -163,6 +166,10 @@ struct atmel_uart_port { unsigned int pending_status; spinlock_t lock_suspended; + /* ISO7816 */ + unsigned int fidi_min; + unsigned int fidi_max; + #ifdef CONFIG_PM struct { u32 cr; @@ -361,6 +368,127 @@ static int atmel_config_rs485(struct uart_port *port, return 0; } +static unsigned int atmel_calc_cd(struct uart_port *port, + struct serial_iso7816 *iso7816conf) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int cd; + u64 mck_rate; + + mck_rate = (u64)clk_get_rate(atmel_port->clk); + do_div(mck_rate, iso7816conf->clk); + cd = mck_rate; + return cd; +} + +static unsigned int atmel_calc_fidi(struct uart_port *port, + struct serial_iso7816 *iso7816conf) +{ + u64 fidi = 0; + + if (iso7816conf->sc_fi && iso7816conf->sc_di) { + fidi = (u64)iso7816conf->sc_fi; + do_div(fidi, iso7816conf->sc_di); + } + return (u32)fidi; +} + +/* Enable or disable the iso7816 support */ +/* Called with interrupts disabled */ +static int atmel_config_iso7816(struct uart_port *port, + struct serial_iso7816 *iso7816conf) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int mode; + unsigned int cd, fidi; + int ret = 0; + + /* Disable interrupts */ + atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); + + mode = atmel_uart_readl(port, ATMEL_US_MR); + + if (iso7816conf->flags & SER_ISO7816_ENABLED) { + mode &= ~ATMEL_US_USMODE; + + if (iso7816conf->tg > 255) { + dev_err(port->dev, "ISO7816: Timeguard exceeding 255\n"); + memset(iso7816conf, 0, sizeof(struct serial_iso7816)); + ret = -EINVAL; + goto err_out; + } + + if ((iso7816conf->flags & SER_ISO7816_T_PARAM) + == SER_ISO7816_T(0)) { + mode |= ATMEL_US_USMODE_ISO7816_T0 | ATMEL_US_DSNACK; + } else if ((iso7816conf->flags & SER_ISO7816_T_PARAM) + == SER_ISO7816_T(1)) { + mode |= ATMEL_US_USMODE_ISO7816_T1 | ATMEL_US_INACK; + } else { + dev_err(port->dev, "ISO7816: Type not supported\n"); + memset(iso7816conf, 0, sizeof(struct serial_iso7816)); + ret = -EINVAL; + goto err_out; + } + + mode &= ~(ATMEL_US_USCLKS | ATMEL_US_NBSTOP | ATMEL_US_PAR); + + /* select mck clock, and output */ + mode |= ATMEL_US_USCLKS_MCK | ATMEL_US_CLKO; + /* set parity for normal/inverse mode + max iterations */ + mode |= ATMEL_US_PAR_EVEN | ATMEL_US_NBSTOP_1 | ATMEL_US_MAX_ITER(3); + + cd = atmel_calc_cd(port, iso7816conf); + fidi = atmel_calc_fidi(port, iso7816conf); + if (fidi == 0) { + dev_warn(port->dev, "ISO7816 fidi = 0, Generator generates no signal\n"); + } else if (fidi < atmel_port->fidi_min + || fidi > atmel_port->fidi_max) { + dev_err(port->dev, "ISO7816 fidi = %u, value not supported\n", fidi); + memset(iso7816conf, 0, sizeof(struct serial_iso7816)); + ret = -EINVAL; + goto err_out; + } + + if (!(port->iso7816.flags & SER_ISO7816_ENABLED)) { + /* port not yet in iso7816 mode: store configuration */ + atmel_port->backup_mode = atmel_uart_readl(port, ATMEL_US_MR); + atmel_port->backup_brgr = atmel_uart_readl(port, ATMEL_US_BRGR); + } + + atmel_uart_writel(port, ATMEL_US_TTGR, iso7816conf->tg); + atmel_uart_writel(port, ATMEL_US_BRGR, cd); + atmel_uart_writel(port, ATMEL_US_FIDI, fidi); + + atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXEN); + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY | ATMEL_US_NACK | ATMEL_US_ITERATION; + } else { + dev_dbg(port->dev, "Setting UART back to RS232\n"); + /* back to last RS232 settings */ + mode = atmel_port->backup_mode; + memset(iso7816conf, 0, sizeof(struct serial_iso7816)); + atmel_uart_writel(port, ATMEL_US_TTGR, 0); + atmel_uart_writel(port, ATMEL_US_BRGR, atmel_port->backup_brgr); + atmel_uart_writel(port, ATMEL_US_FIDI, 0x174); + + if (atmel_use_pdc_tx(port)) + atmel_port->tx_done_mask = ATMEL_US_ENDTX | + ATMEL_US_TXBUFE; + else + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } + + port->iso7816 = *iso7816conf; + + atmel_uart_writel(port, ATMEL_US_MR, mode); + +err_out: + /* Enable interrupts */ + atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask); + + return ret; +} + /* * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. */ @@ -480,8 +608,9 @@ static void atmel_stop_tx(struct uart_port *port) /* Disable interrupts */ atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); - if ((port->rs485.flags & SER_RS485_ENABLED) && - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) + if (((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || + port->iso7816.flags & SER_ISO7816_ENABLED) atmel_start_rx(port); } @@ -499,8 +628,9 @@ static void atmel_start_tx(struct uart_port *port) return; if (atmel_use_pdc_tx(port) || atmel_use_dma_tx(port)) - if ((port->rs485.flags & SER_RS485_ENABLED) && - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) + if (((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || + port->iso7816.flags & SER_ISO7816_ENABLED) atmel_stop_rx(port); if (atmel_use_pdc_tx(port)) @@ -798,8 +928,9 @@ static void atmel_complete_tx_dma(void *arg) */ if (!uart_circ_empty(xmit)) atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx); - else if ((port->rs485.flags & SER_RS485_ENABLED) && - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { + else if (((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || + port->iso7816.flags & SER_ISO7816_ENABLED) { /* DMA done, stop TX, start RX for RS485 */ atmel_start_rx(port); } @@ -1282,6 +1413,9 @@ atmel_handle_status(struct uart_port *port, unsigned int pending, wake_up_interruptible(&port->state->port.delta_msr_wait); } } + + if (pending & (ATMEL_US_NACK | ATMEL_US_ITERATION)) + dev_dbg(port->dev, "ISO7816 ERROR (0x%08x)\n", pending); } /* @@ -1374,8 +1508,9 @@ static void atmel_tx_pdc(struct uart_port *port) atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask); } else { - if ((port->rs485.flags & SER_RS485_ENABLED) && - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { + if (((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || + port->iso7816.flags & SER_ISO7816_ENABLED) { /* DMA done, stop TX, start RX for RS485 */ atmel_start_rx(port); } @@ -1727,6 +1862,22 @@ static void atmel_get_ip_name(struct uart_port *port) atmel_port->has_frac_baudrate = true; atmel_port->has_hw_timer = true; atmel_port->rtor = ATMEL_US_RTOR; + version = atmel_uart_readl(port, ATMEL_US_VERSION); + switch (version) { + case 0x814: /* sama5d2 */ + /* fall through */ + case 0x701: /* sama5d4 */ + atmel_port->fidi_min = 3; + atmel_port->fidi_max = 65535; + break; + case 0x502: /* sam9x5, sama5d3 */ + atmel_port->fidi_min = 3; + atmel_port->fidi_max = 2047; + break; + default: + atmel_port->fidi_min = 1; + atmel_port->fidi_max = 2047; + } } else if (name == dbgu_uart) { dev_dbg(port->dev, "Dbgu or uart without hw timer\n"); } else { @@ -2100,6 +2251,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, atmel_uart_writel(port, ATMEL_US_TTGR, port->rs485.delay_rts_after_send); mode |= ATMEL_US_USMODE_RS485; + } else if (port->iso7816.flags & SER_ISO7816_ENABLED) { + atmel_uart_writel(port, ATMEL_US_TTGR, port->iso7816.tg); + /* select mck clock, and output */ + mode |= ATMEL_US_USCLKS_MCK | ATMEL_US_CLKO; + /* set max iterations */ + mode |= ATMEL_US_MAX_ITER(3); + if ((port->iso7816.flags & SER_ISO7816_T_PARAM) + == SER_ISO7816_T(0)) + mode |= ATMEL_US_USMODE_ISO7816_T0; + else + mode |= ATMEL_US_USMODE_ISO7816_T1; } else if (termios->c_cflag & CRTSCTS) { /* RS232 with hardware handshake (RTS/CTS) */ if (atmel_use_fifo(port) && @@ -2176,7 +2338,8 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, } quot = cd | fp << ATMEL_US_FP_OFFSET; - atmel_uart_writel(port, ATMEL_US_BRGR, quot); + if (!(port->iso7816.flags & SER_ISO7816_ENABLED)) + atmel_uart_writel(port, ATMEL_US_BRGR, quot); atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN); atmel_port->tx_stopped = false; @@ -2357,6 +2520,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, port->mapbase = mpdev->resource[0].start; port->irq = mpdev->resource[1].start; port->rs485_config = atmel_config_rs485; + port->iso7816_config = atmel_config_iso7816; port->membase = NULL; memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring)); @@ -2380,8 +2544,12 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, /* only enable clock when USART is in use */ } - /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ - if (port->rs485.flags & SER_RS485_ENABLED) + /* + * Use TXEMPTY for interrupt when rs485 or ISO7816 else TXRDY or + * ENDTX|TXBUFE + */ + if (port->rs485.flags & SER_RS485_ENABLED || + port->iso7816.flags & SER_ISO7816_ENABLED) atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; else if (atmel_use_pdc_tx(port)) { port->fifosize = PDC_BUFFER_SIZE; diff --git a/drivers/tty/serial/atmel_serial.h b/drivers/tty/serial/atmel_serial.h index ba3a2437cde4..d811d4f2d0c0 100644 --- a/drivers/tty/serial/atmel_serial.h +++ b/drivers/tty/serial/atmel_serial.h @@ -78,7 +78,8 @@ #define ATMEL_US_OVER BIT(19) /* Oversampling Mode */ #define ATMEL_US_INACK BIT(20) /* Inhibit Non Acknowledge */ #define ATMEL_US_DSNACK BIT(21) /* Disable Successive NACK */ -#define ATMEL_US_MAX_ITER GENMASK(26, 24) /* Max Iterations */ +#define ATMEL_US_MAX_ITER_MASK GENMASK(26, 24) /* Max Iterations */ +#define ATMEL_US_MAX_ITER(n) (((n) << 24) & ATMEL_US_MAX_ITER_MASK) #define ATMEL_US_FILTER BIT(28) /* Infrared Receive Line Filter */ #define ATMEL_US_IER 0x08 /* Interrupt Enable Register */ -- cgit v1.2.3 From 823f4e53f0f2beef4ae43b0a8b33e27b0761b840 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 25 Sep 2018 14:23:21 +0000 Subject: serial: uartps: Fix missing unlock on error in cdns_get_id() Add the missing unlock before return from function cdns_get_id() in the error handling case. Fixes: ae1cca3fa347 ("serial: uartps: Change uart ID port allocation") Signed-off-by: Wei Yongjun Reviewed-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index f77200a0f461..0e3dae461f71 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1394,8 +1394,10 @@ static int cdns_get_id(struct platform_device *pdev) if (!alias_bitmap_initialized) { ret = of_alias_get_alias_list(cdns_uart_of_match, "serial", alias_bitmap, MAX_UART_INSTANCES); - if (ret) + if (ret) { + mutex_unlock(&bitmap_lock); return ret; + } alias_bitmap_initialized++; } -- cgit v1.2.3 From 2fd8e454189d580bcfc198fee60e51655945b986 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 28 Sep 2018 11:05:07 +0900 Subject: serial: 8250_uniphier: remove unused "fifo-size" property The FIFO size of the UART devices is 64 on almost all UniPhier SoCs with the exception Pro4TV SoC (MN2WS0235), which used 128 FIFO size. However, Pro4TV SoC was never upstreamed, and out of production. So, this property has never been used in a useful way. Let's remove old unused code. Signed-off-by: Masahiro Yamada Reviewed-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/uniphier-uart.txt | 4 ---- drivers/tty/serial/8250/8250_uniphier.c | 10 +--------- 2 files changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/serial/uniphier-uart.txt b/Documentation/devicetree/bindings/serial/uniphier-uart.txt index 0b3892a7a528..811c479b373d 100644 --- a/Documentation/devicetree/bindings/serial/uniphier-uart.txt +++ b/Documentation/devicetree/bindings/serial/uniphier-uart.txt @@ -6,9 +6,6 @@ Required properties: - interrupts: a single interrupt specifier. - clocks: phandle to the input clock. -Optional properties: -- fifo-size: the RX/TX FIFO size. Defaults to 64 if not specified. - Example: aliases { serial0 = &serial0; @@ -19,5 +16,4 @@ Example: reg = <0x54006800 0x40>; interrupts = <0 33 4>; clocks = <&uart_clk>; - fifo-size = <64>; }; diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 28d88ccf5a0c..d292654422b2 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -12,9 +12,6 @@ #include "8250.h" -/* Most (but not all) of UniPhier UART devices have 64-depth FIFO. */ -#define UNIPHIER_UART_DEFAULT_FIFO_SIZE 64 - /* * This hardware is similar to 8250, but its register map is a bit different: * - MMIO32 (regshift = 2) @@ -185,12 +182,6 @@ static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port, port->uartclk = clk_get_rate(priv->clk); - /* Check for fifo size */ - if (of_property_read_u32(np, "fifo-size", &prop) == 0) - port->fifosize = prop; - else - port->fifosize = UNIPHIER_UART_DEFAULT_FIFO_SIZE; - return 0; } @@ -241,6 +232,7 @@ static int uniphier_uart_probe(struct platform_device *pdev) up.port.type = PORT_16550A; up.port.iotype = UPIO_MEM32; + up.port.fifosize = 64; up.port.regshift = UNIPHIER_UART_REGSHIFT; up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE; up.capabilities = UART_CAP_FIFO; -- cgit v1.2.3 From aca70d19c8e56a871e9a68c9b6b940656666b56a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 28 Sep 2018 11:05:08 +0900 Subject: serial: 8250_uniphier: flatten probe function Currently, the DT-related settings are split out to uniphier_of_serial_setup(), but it turned out to be not nice. The next commit will add a DT property, but it will not fit in the helper. Merge the helper into the probe function. Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_uniphier.c | 49 ++++++++++++--------------------- 1 file changed, 17 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index d292654422b2..1028c02c5d83 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -155,36 +155,6 @@ static void uniphier_serial_dl_write(struct uart_8250_port *up, int value) writel(value, up->port.membase + UNIPHIER_UART_DLR); } -static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port, - struct uniphier8250_priv *priv) -{ - int ret; - u32 prop; - struct device_node *np = dev->of_node; - - ret = of_alias_get_id(np, "serial"); - if (ret < 0) { - dev_err(dev, "failed to get alias id\n"); - return ret; - } - port->line = ret; - - /* Get clk rate through clk driver */ - priv->clk = devm_clk_get(dev, NULL); - if (IS_ERR(priv->clk)) { - dev_err(dev, "failed to get clock\n"); - return PTR_ERR(priv->clk); - } - - ret = clk_prepare_enable(priv->clk); - if (ret < 0) - return ret; - - port->uartclk = clk_get_rate(priv->clk); - - return 0; -} - static int uniphier_uart_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -217,9 +187,24 @@ static int uniphier_uart_probe(struct platform_device *pdev) memset(&up, 0, sizeof(up)); - ret = uniphier_of_serial_setup(dev, &up.port, priv); - if (ret < 0) + ret = of_alias_get_id(dev->of_node, "serial"); + if (ret < 0) { + dev_err(dev, "failed to get alias id\n"); return ret; + } + up.port.line = ret; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get clock\n"); + return PTR_ERR(priv->clk); + } + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + up.port.uartclk = clk_get_rate(priv->clk); spin_lock_init(&priv->atomic_write_lock); -- cgit v1.2.3 From aad2d4952d24d8910d9fc64da9107df0fb780a09 Mon Sep 17 00:00:00 2001 From: Dai Okamura Date: Fri, 28 Sep 2018 11:05:09 +0900 Subject: serial: 8250_uniphier: add auto-flow-control support Add selective auto-flow-control support for UniPhier serial driver. Signed-off-by: Dai Okamura Signed-off-by: Masahiro Yamada Reviewed-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/uniphier-uart.txt | 3 +++ drivers/tty/serial/8250/8250_uniphier.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/serial/uniphier-uart.txt b/Documentation/devicetree/bindings/serial/uniphier-uart.txt index 811c479b373d..7a1bf02bb869 100644 --- a/Documentation/devicetree/bindings/serial/uniphier-uart.txt +++ b/Documentation/devicetree/bindings/serial/uniphier-uart.txt @@ -6,6 +6,9 @@ Required properties: - interrupts: a single interrupt specifier. - clocks: phandle to the input clock. +Optional properties: +-auto-flow-control: enable automatic flow control support. + Example: aliases { serial0 = &serial0; diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 1028c02c5d83..164ba133437a 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -222,6 +222,9 @@ static int uniphier_uart_probe(struct platform_device *pdev) up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE; up.capabilities = UART_CAP_FIFO; + if (of_property_read_bool(dev->of_node, "auto-flow-control")) + up.capabilities |= UART_CAP_AFE; + up.port.serial_in = uniphier_serial_in; up.port.serial_out = uniphier_serial_out; up.dl_read = uniphier_serial_dl_read; -- cgit v1.2.3 From 817e9bc8cc04e5c70592525b96812c46c1aa1c46 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 4 Oct 2018 09:57:23 -0700 Subject: Revert "serial:serial_core: Allow use of CTS for PPS line discipline" This reverts commit c550f01c810f2197c98e6e3103f81797f5e063be. Turns out the samsung tty driver is mucking around in the "unused" port fields and this patch breaks that code :( So we need to fix that driver up before this can be accepted. Reported-by: Stephen Rothwell Cc: Steve Sakoman Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-tty | 9 ----- drivers/tty/serial/serial_core.c | 70 +------------------------------------ include/linux/serial_core.h | 3 +- 3 files changed, 2 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-tty b/Documentation/ABI/testing/sysfs-tty index 441105a75d1f..9eb3c2b6b040 100644 --- a/Documentation/ABI/testing/sysfs-tty +++ b/Documentation/ABI/testing/sysfs-tty @@ -154,12 +154,3 @@ Description: device specification. For example, when user sets 7bytes on 16550A, which has 1/4/8/14 bytes trigger, the RX trigger is automatically changed to 4 bytes. - -What: /sys/class/tty/ttyS0/pps_4wire -Date: September 2018 -Contact: Steve Sakoman -Description: - Shows/sets "4 wire" mode for the PPS input to the serial driver. - For fully implemented serial ports PPS is normally provided - on the DCD line. For partial "4 wire" implementations CTS is - used instead of DCD. diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0a4e6eeb5ff3..70402cdb4d8c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2724,57 +2724,6 @@ static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift); } -static ssize_t pps_4wire_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct tty_port *port = dev_get_drvdata(dev); - struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport; - int mode = 0; - - mutex_lock(&port->mutex); - uport = uart_port_check(state); - if (!uport) - goto out; - - mode = uport->pps_4wire; - -out: - mutex_unlock(&port->mutex); - return sprintf(buf, mode ? "yes\n" : "no\n"); -} - -static ssize_t pps_4wire_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct tty_port *port = dev_get_drvdata(dev); - struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport; - bool mode; - int ret; - - if (!count) - return -EINVAL; - - ret = kstrtobool(buf, &mode); - if (ret < 0) - return ret; - - mutex_lock(&port->mutex); - uport = uart_port_check(state); - if (!uport) - goto out; - - spin_lock_irq(&uport->lock); - uport->pps_4wire = mode; - spin_unlock_irq(&uport->lock); - -out: - mutex_unlock(&port->mutex); - return count; -} -static DEVICE_ATTR_RW(pps_4wire); - static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL); static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL); static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL); @@ -2803,7 +2752,6 @@ static struct attribute *tty_dev_attrs[] = { &dev_attr_io_type.attr, &dev_attr_iomem_base.attr, &dev_attr_iomem_reg_shift.attr, - &dev_attr_pps_4wire.attr, NULL, }; @@ -2860,9 +2808,6 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) goto out; } - /* assert that pps handling is done via DCD as default */ - uport->pps_4wire = 0; - /* * If this port is a console, then the spinlock is already * initialised. @@ -3038,7 +2983,7 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) lockdep_assert_held_once(&uport->lock); - if (tty && !uport->pps_4wire) { + if (tty) { ld = tty_ldisc_ref(tty); if (ld) { if (ld->ops->dcd_change) @@ -3067,21 +3012,8 @@ EXPORT_SYMBOL_GPL(uart_handle_dcd_change); */ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) { - struct tty_port *port = &uport->state->port; - struct tty_struct *tty = port->tty; - struct tty_ldisc *ld; - lockdep_assert_held_once(&uport->lock); - if (tty && uport->pps_4wire) { - ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->ops->dcd_change) - ld->ops->dcd_change(tty, status); - tty_ldisc_deref(ld); - } - } - uport->icount.cts++; if (uart_softcts_mode(uport)) { diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 4e2ba4894dcc..047fa67d039b 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -257,8 +257,7 @@ struct uart_port { struct device *dev; /* parent device */ unsigned char hub6; /* this should be in the 8250 driver */ unsigned char suspended; - unsigned char pps_4wire; /* CTS instead of DCD */ - unsigned char unused; + unsigned char unused[2]; const char *name; /* port name */ struct attribute_group *attr_group; /* port specific attributes */ const struct attribute_group **tty_groups; /* all attributes (serial core use only) */ -- cgit v1.2.3 From 8ebfe885c65ec82a0b1e955cf99ed62664e84414 Mon Sep 17 00:00:00 2001 From: Lance Roy Date: Thu, 4 Oct 2018 00:14:04 -0700 Subject: TTY: sn_console: Replace spin_is_locked() with spin_trylock() sn_sal_console_write() used spin_is_locked() + spin_lock() to get achieve the same thing as a spin_trylock(), so simplify it by using that instead. This is also a step towards possibly removing spin_is_locked(). Signed-off-by: Lance Roy Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sn_console.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c index 42b9aded4eb1..fe9170731c16 100644 --- a/drivers/tty/serial/sn_console.c +++ b/drivers/tty/serial/sn_console.c @@ -888,7 +888,7 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) /* somebody really wants this output, might be an * oops, kdb, panic, etc. make sure they get it. */ - if (spin_is_locked(&port->sc_port.lock)) { + if (!spin_trylock_irqsave(&port->sc_port.lock, flags)) { int lhead = port->sc_port.state->xmit.head; int ltail = port->sc_port.state->xmit.tail; int counter, got_lock = 0; @@ -905,13 +905,11 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) */ for (counter = 0; counter < 150; mdelay(125), counter++) { - if (!spin_is_locked(&port->sc_port.lock) - || stole_lock) { - if (!stole_lock) { - spin_lock_irqsave(&port->sc_port.lock, - flags); - got_lock = 1; - } + if (stole_lock) + break; + + if (spin_trylock_irqsave(&port->sc_port.lock, flags)) { + got_lock = 1; break; } else { /* still locked */ @@ -938,7 +936,6 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); } else { stole_lock = 0; - spin_lock_irqsave(&port->sc_port.lock, flags); sn_transmit_chars(port, 1); spin_unlock_irqrestore(&port->sc_port.lock, flags); -- cgit v1.2.3 From 3bc3206e1c0f3f8891b544df02caae0e6d844f7d Mon Sep 17 00:00:00 2001 From: Vabhav Sharma Date: Wed, 10 Oct 2018 03:56:16 +0530 Subject: serial: fsl_lpuart: Remove the alias node dependence Numbering the ttyLPn space should not depend on the generic name "serial". If don't add the alias node like:"serial0 = &lpuart0;", then lpuart will probe failed: [ 0.773410] fsl-lpuart 2950000.serial: failed to get alias id, errno -19 So remove the alias node dependence, and add the support for allocate the line port automatically. Signed-off-by: Yuan Yao Signed-off-by: Vabhav Sharma Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 3f8d1274fc85..00c220e4f43c 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -232,6 +232,8 @@ /* IMX lpuart has four extra unused regs located at the beginning */ #define IMX_REG_OFF 0x10 +static DEFINE_IDA(fsl_lpuart_ida); + struct lpuart_port { struct uart_port port; struct clk *clk; @@ -2143,8 +2145,11 @@ static int lpuart_probe(struct platform_device *pdev) ret = of_alias_get_id(np, "serial"); if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); - return ret; + ret = ida_simple_get(&fsl_lpuart_ida, 0, UART_NR, GFP_KERNEL); + if (ret < 0) { + dev_err(&pdev->dev, "port line is full, add device failed\n"); + return ret; + } } if (ret >= ARRAY_SIZE(lpuart_ports)) { dev_err(&pdev->dev, "serial%d out of range\n", ret); @@ -2246,6 +2251,8 @@ static int lpuart_remove(struct platform_device *pdev) uart_remove_one_port(&lpuart_reg, &sport->port); + ida_simple_remove(&fsl_lpuart_ida, sport->port.line); + clk_disable_unprepare(sport->clk); if (sport->dma_tx_chan) @@ -2384,6 +2391,7 @@ static int __init lpuart_serial_init(void) static void __exit lpuart_serial_exit(void) { + ida_destroy(&fsl_lpuart_ida); platform_driver_unregister(&lpuart_driver); uart_unregister_driver(&lpuart_reg); } -- cgit v1.2.3 From c9a8e5fce009e3c601a43c49ea9dbcb25d1ffac5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 4 Oct 2018 11:06:13 -0700 Subject: tty: wipe buffer. After we are done with the tty buffer, zero it out. Reported-by: aszlig Tested-by: Milan Broz Tested-by: Daniel Zatovic Tested-by: aszlig Cc: Willy Tarreau Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 76151c002858..77070c2d1240 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -473,11 +473,15 @@ receive_buf(struct tty_port *port, struct tty_buffer *head, int count) { unsigned char *p = char_buf_ptr(head, head->read); char *f = NULL; + int n; if (~head->flags & TTYB_NORMAL) f = flag_buf_ptr(head, head->read); - return port->client_ops->receive_buf(port, p, f, count); + n = port->client_ops->receive_buf(port, p, f, count); + if (n > 0) + memset(p, 0, n); + return n; } /** -- cgit v1.2.3 From b97b3d9fb57860a60592859e332de7759fd54c2e Mon Sep 17 00:00:00 2001 From: Greg KH Date: Thu, 4 Oct 2018 11:06:14 -0700 Subject: tty: wipe buffer if not echoing data If we are not echoing the data to userspace or the console is in icanon mode, then perhaps it is a "secret" so we should wipe it once we are done with it. This mirrors the logic that the audit code has. Reported-by: aszlig Tested-by: Milan Broz Tested-by: Daniel Zatovic Tested-by: aszlig Cc: Willy Tarreau Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 431742201709..3ad460219fd6 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -152,17 +152,28 @@ static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i) return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; } +/* If we are not echoing the data, perhaps this is a secret so erase it */ +static void zero_buffer(struct tty_struct *tty, u8 *buffer, int size) +{ + bool icanon = !!L_ICANON(tty); + bool no_echo = !L_ECHO(tty); + + if (icanon && no_echo) + memset(buffer, 0x00, size); +} + static int tty_copy_to_user(struct tty_struct *tty, void __user *to, size_t tail, size_t n) { struct n_tty_data *ldata = tty->disc_data; size_t size = N_TTY_BUF_SIZE - tail; - const void *from = read_buf_addr(ldata, 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; to += size; @@ -171,7 +182,9 @@ static int tty_copy_to_user(struct tty_struct *tty, void __user *to, } tty_audit_add_data(tty, from, n); - return copy_to_user(to, from, n); + uncopied = copy_to_user(to, from, n); + zero_buffer(tty, from, n - uncopied); + return uncopied; } /** @@ -1960,11 +1973,12 @@ static int copy_from_read_buf(struct tty_struct *tty, n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail); n = min(*nr, n); if (n) { - const unsigned char *from = read_buf_addr(ldata, tail); + unsigned char *from = read_buf_addr(ldata, tail); retval = copy_to_user(*b, from, n); n -= retval; is_eof = n == 1 && *from == EOF_CHAR(tty); tty_audit_add_data(tty, from, n); + zero_buffer(tty, from, n); smp_store_release(&ldata->read_tail, ldata->read_tail + n); /* Turn single EOF into zero-length read */ if (L_EXTPROC(tty) && ldata->icanon && is_eof && -- cgit v1.2.3 From 33a1a7be198657c8ca26ad406c4d2a89b7162bcc Mon Sep 17 00:00:00 2001 From: Miles Chen Date: Mon, 8 Oct 2018 10:39:17 +0800 Subject: tty: check name length in tty_find_polling_driver() The issue is found by a fuzzing test. If tty_find_polling_driver() recevies an incorrect input such as ',,' or '0b', the len becomes 0 and strncmp() always return 0. In this case, a null p->ops->poll_init() is called and it causes a kernel panic. Fix this by checking name length against zero in tty_find_polling_driver(). $echo ,, > /sys/module/kgdboc/parameters/kgdboc [ 20.804451] WARNING: CPU: 1 PID: 104 at drivers/tty/serial/serial_core.c:457 uart_get_baud_rate+0xe8/0x190 [ 20.804917] Modules linked in: [ 20.805317] CPU: 1 PID: 104 Comm: sh Not tainted 4.19.0-rc7ajb #8 [ 20.805469] Hardware name: linux,dummy-virt (DT) [ 20.805732] pstate: 20000005 (nzCv daif -PAN -UAO) [ 20.805895] pc : uart_get_baud_rate+0xe8/0x190 [ 20.806042] lr : uart_get_baud_rate+0xc0/0x190 [ 20.806476] sp : ffffffc06acff940 [ 20.806676] x29: ffffffc06acff940 x28: 0000000000002580 [ 20.806977] x27: 0000000000009600 x26: 0000000000009600 [ 20.807231] x25: ffffffc06acffad0 x24: 00000000ffffeff0 [ 20.807576] x23: 0000000000000001 x22: 0000000000000000 [ 20.807807] x21: 0000000000000001 x20: 0000000000000000 [ 20.808049] x19: ffffffc06acffac8 x18: 0000000000000000 [ 20.808277] x17: 0000000000000000 x16: 0000000000000000 [ 20.808520] x15: ffffffffffffffff x14: ffffffff00000000 [ 20.808757] x13: ffffffffffffffff x12: 0000000000000001 [ 20.809011] x11: 0101010101010101 x10: ffffff880d59ff5f [ 20.809292] x9 : ffffff880d59ff5e x8 : ffffffc06acffaf3 [ 20.809549] x7 : 0000000000000000 x6 : ffffff880d59ff5f [ 20.809803] x5 : 0000000080008001 x4 : 0000000000000003 [ 20.810056] x3 : ffffff900853e6b4 x2 : dfffff9000000000 [ 20.810693] x1 : ffffffc06acffad0 x0 : 0000000000000cb0 [ 20.811005] Call trace: [ 20.811214] uart_get_baud_rate+0xe8/0x190 [ 20.811479] serial8250_do_set_termios+0xe0/0x6f4 [ 20.811719] serial8250_set_termios+0x48/0x54 [ 20.811928] uart_set_options+0x138/0x1bc [ 20.812129] uart_poll_init+0x114/0x16c [ 20.812330] tty_find_polling_driver+0x158/0x200 [ 20.812545] configure_kgdboc+0xbc/0x1bc [ 20.812745] param_set_kgdboc_var+0xb8/0x150 [ 20.812960] param_attr_store+0xbc/0x150 [ 20.813160] module_attr_store+0x40/0x58 [ 20.813364] sysfs_kf_write+0x8c/0xa8 [ 20.813563] kernfs_fop_write+0x154/0x290 [ 20.813764] vfs_write+0xf0/0x278 [ 20.813951] __arm64_sys_write+0x84/0xf4 [ 20.814400] el0_svc_common+0xf4/0x1dc [ 20.814616] el0_svc_handler+0x98/0xbc [ 20.814804] el0_svc+0x8/0xc [ 20.822005] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 [ 20.826913] Mem abort info: [ 20.827103] ESR = 0x84000006 [ 20.827352] Exception class = IABT (current EL), IL = 16 bits [ 20.827655] SET = 0, FnV = 0 [ 20.827855] EA = 0, S1PTW = 0 [ 20.828135] user pgtable: 4k pages, 39-bit VAs, pgdp = (____ptrval____) [ 20.828484] [0000000000000000] pgd=00000000aadee003, pud=00000000aadee003, pmd=0000000000000000 [ 20.829195] Internal error: Oops: 84000006 [#1] SMP [ 20.829564] Modules linked in: [ 20.829890] CPU: 1 PID: 104 Comm: sh Tainted: G W 4.19.0-rc7ajb #8 [ 20.830545] Hardware name: linux,dummy-virt (DT) [ 20.830829] pstate: 60000085 (nZCv daIf -PAN -UAO) [ 20.831174] pc : (null) [ 20.831457] lr : serial8250_do_set_termios+0x358/0x6f4 [ 20.831727] sp : ffffffc06acff9b0 [ 20.831936] x29: ffffffc06acff9b0 x28: ffffff9008d7c000 [ 20.832267] x27: ffffff900969e16f x26: 0000000000000000 [ 20.832589] x25: ffffff900969dfb0 x24: 0000000000000000 [ 20.832906] x23: ffffffc06acffad0 x22: ffffff900969e160 [ 20.833232] x21: 0000000000000000 x20: ffffffc06acffac8 [ 20.833559] x19: ffffff900969df90 x18: 0000000000000000 [ 20.833878] x17: 0000000000000000 x16: 0000000000000000 [ 20.834491] x15: ffffffffffffffff x14: ffffffff00000000 [ 20.834821] x13: ffffffffffffffff x12: 0000000000000001 [ 20.835143] x11: 0101010101010101 x10: ffffff880d59ff5f [ 20.835467] x9 : ffffff880d59ff5e x8 : ffffffc06acffaf3 [ 20.835790] x7 : 0000000000000000 x6 : ffffff880d59ff5f [ 20.836111] x5 : c06419717c314100 x4 : 0000000000000007 [ 20.836419] x3 : 0000000000000000 x2 : 0000000000000000 [ 20.836732] x1 : 0000000000000001 x0 : ffffff900969df90 [ 20.837100] Process sh (pid: 104, stack limit = 0x(____ptrval____)) [ 20.837396] Call trace: [ 20.837566] (null) [ 20.837816] serial8250_set_termios+0x48/0x54 [ 20.838089] uart_set_options+0x138/0x1bc [ 20.838570] uart_poll_init+0x114/0x16c [ 20.838834] tty_find_polling_driver+0x158/0x200 [ 20.839119] configure_kgdboc+0xbc/0x1bc [ 20.839380] param_set_kgdboc_var+0xb8/0x150 [ 20.839658] param_attr_store+0xbc/0x150 [ 20.839920] module_attr_store+0x40/0x58 [ 20.840183] sysfs_kf_write+0x8c/0xa8 [ 20.840183] sysfs_kf_write+0x8c/0xa8 [ 20.840440] kernfs_fop_write+0x154/0x290 [ 20.840702] vfs_write+0xf0/0x278 [ 20.840942] __arm64_sys_write+0x84/0xf4 [ 20.841209] el0_svc_common+0xf4/0x1dc [ 20.841471] el0_svc_handler+0x98/0xbc [ 20.841713] el0_svc+0x8/0xc [ 20.842057] Code: bad PC value [ 20.842764] ---[ end trace a8835d7de79aaadf ]--- [ 20.843134] Kernel panic - not syncing: Fatal exception [ 20.843515] SMP: stopping secondary CPUs [ 20.844289] Kernel Offset: disabled [ 20.844634] CPU features: 0x0,21806002 [ 20.844857] Memory Limit: none [ 20.845172] ---[ end Kernel panic - not syncing: Fatal exception ]--- Signed-off-by: Miles Chen Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 5e5da9acaf0a..252eef2c32f9 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -408,7 +408,7 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) mutex_lock(&tty_mutex); /* Search through the tty devices to look for a match */ list_for_each_entry(p, &tty_drivers, tty_drivers) { - if (strncmp(name, p->name, len) != 0) + if (!len || strncmp(name, p->name, len) != 0) continue; stp = str; if (*stp == ',') -- cgit v1.2.3 From 2088cfd882d0403609bdf426e9b24372fe1b8337 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 8 Oct 2018 14:17:19 +0200 Subject: serial: uartps: Do not allow use aliases >= MAX_UART_INSTANCES Aliases >= MAX_UART_INSTANCES is no problem to find out and use but in error path is necessary skip clearing bits in bitmap to ensure that only bits in allocated bitmap are handled and nothing beyond that. Without this patch when for example serial90 alias is used then in error patch bit 90 is clear in 32bit wide bitmap. Fixes: ae1cca3fa347 ("serial: uartps: Change uart ID port allocation") Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 0e3dae461f71..c3f6cce300aa 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1668,7 +1668,8 @@ err_out_unregister_driver: uart_unregister_driver(cdns_uart_data->cdns_uart_driver); err_out_id: mutex_lock(&bitmap_lock); - clear_bit(cdns_uart_data->id, bitmap); + if (cdns_uart_data->id < MAX_UART_INSTANCES) + clear_bit(cdns_uart_data->id, bitmap); mutex_unlock(&bitmap_lock); return rc; } @@ -1693,7 +1694,8 @@ static int cdns_uart_remove(struct platform_device *pdev) rc = uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port); port->mapbase = 0; mutex_lock(&bitmap_lock); - clear_bit(cdns_uart_data->id, bitmap); + if (cdns_uart_data->id < MAX_UART_INSTANCES) + clear_bit(cdns_uart_data->id, bitmap); mutex_unlock(&bitmap_lock); clk_disable_unprepare(cdns_uart_data->uartclk); clk_disable_unprepare(cdns_uart_data->pclk); -- cgit v1.2.3 From c58a3ae58bce99d20fdbc5d97beecf31cc19f3dd Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Fri, 12 Oct 2018 15:47:49 +0200 Subject: serial: sh-sci: do not warn if DMA transfers are not supported Not all (H)SCIF devices support DMA, and failure to set it up is not normally a cause for concern. This patch demotes the associated warning to debug output. Inspired by BSP patch "sci: sh-sci: Fix transfer sequence of unsupport DMA transfer" (6beb1f98d3bd30) by Hiromitsu Yamasaki. Signed-off-by: Ulrich Hecht Reviewed-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 426241da2e44..ff6ba6d86cd8 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1516,7 +1516,7 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port, chan = dma_request_slave_channel(port->dev, dir == DMA_MEM_TO_DEV ? "tx" : "rx"); if (!chan) { - dev_warn(port->dev, "dma_request_slave_channel failed\n"); + dev_dbg(port->dev, "dma_request_slave_channel failed\n"); return NULL; } -- cgit v1.2.3 From 7acf79b6b2160540af87f47a55d7e3e5637ddeb5 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 8 Oct 2018 13:58:47 +0200 Subject: of: base: Fix english spelling in of_alias_get_alias_list() Fix english spelling in of_alias_get_alias_list(). Reported-by: Geert Uytterhoeven Reported-by: Randy Dunlap Fixes: b1078c355d76 ("of: base: Introduce of_alias_get_alias_list() to check alias IDs") Signed-off-by: Michal Simek Acked-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/of/base.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/of/base.c b/drivers/of/base.c index 33011b88ed3f..908de45f966b 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1948,7 +1948,7 @@ EXPORT_SYMBOL_GPL(of_alias_get_id); * @matches: Array of OF device match structures to search in * @stem: Alias stem of the given device_node * @bitmap: Bitmap field pointer - * @nbits: Maximum number of alias ID which can be recorded it bitmap + * @nbits: Maximum number of alias IDs which can be recorded in bitmap * * The function travels the lookup table to record alias ids for the given * device match structures and alias stem. @@ -1971,7 +1971,7 @@ int of_alias_get_alias_list(const struct of_device_id *matches, __func__, app->stem, app->id); if (strcmp(app->stem, stem) != 0) { - pr_debug("%s: stem comparison doesn't passed %s\n", + pr_debug("%s: stem comparison didn't pass %s\n", __func__, app->stem); continue; } @@ -1986,7 +1986,7 @@ int of_alias_get_alias_list(const struct of_device_id *matches, pr_debug("%s: Allocated ID %d\n", __func__, app->id); set_bit(app->id, bitmap); } - /* Alias exist but it not compatible with matches */ + /* Alias exists but is not compatible with matches */ } mutex_unlock(&of_mutex); -- cgit v1.2.3 From 59eaeba63a171127a90bb76187536ba66076af40 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 12 Oct 2018 07:43:11 +0200 Subject: of: base: Change logic in of_alias_get_alias_list() Check compatible string first before setting up bit in bitmap to also cover cases that allocated bitfield is not big enough. Show warning about it but let driver to continue to work with allocated bitfield to keep at least some devices (included console which is commonly close to serial0) to work. Fixes: b1078c355d76 ("of: base: Introduce of_alias_get_alias_list() to check alias IDs") Fixes: ae1cca3fa347 ("serial: uartps: Change uart ID port allocation") Signed-off-by: Michal Simek Reviewed-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/of/base.c | 22 ++++++++++++---------- drivers/tty/serial/xilinx_uartps.c | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/of/base.c b/drivers/of/base.c index 908de45f966b..6418205a05f5 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1953,13 +1953,15 @@ EXPORT_SYMBOL_GPL(of_alias_get_id); * The function travels the lookup table to record alias ids for the given * device match structures and alias stem. * - * Return: 0 or -ENOSYS when !CONFIG_OF + * Return: 0 or -ENOSYS when !CONFIG_OF or + * -EOVERFLOW if alias ID is greater then allocated nbits */ int of_alias_get_alias_list(const struct of_device_id *matches, const char *stem, unsigned long *bitmap, unsigned int nbits) { struct alias_prop *app; + int ret = 0; /* Zero bitmap field to make sure that all the time it is clean */ bitmap_zero(bitmap, nbits); @@ -1976,21 +1978,21 @@ int of_alias_get_alias_list(const struct of_device_id *matches, continue; } - if (app->id >= nbits) { - pr_debug("%s: ID %d greater then bitmap field %d\n", - __func__, app->id, nbits); - continue; - } - if (of_match_node(matches, app->np)) { pr_debug("%s: Allocated ID %d\n", __func__, app->id); - set_bit(app->id, bitmap); + + if (app->id >= nbits) { + pr_warn("%s: ID %d >= than bitmap field %d\n", + __func__, app->id, nbits); + ret = -EOVERFLOW; + } else { + set_bit(app->id, bitmap); + } } - /* Alias exists but is not compatible with matches */ } mutex_unlock(&of_mutex); - return 0; + return ret; } EXPORT_SYMBOL_GPL(of_alias_get_alias_list); diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index c3f6cce300aa..57c66d2c3471 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1394,7 +1394,7 @@ static int cdns_get_id(struct platform_device *pdev) if (!alias_bitmap_initialized) { ret = of_alias_get_alias_list(cdns_uart_of_match, "serial", alias_bitmap, MAX_UART_INSTANCES); - if (ret) { + if (ret && ret != -EOVERFLOW) { mutex_unlock(&bitmap_lock); return ret; } -- cgit v1.2.3