From 525ba62c96abec9130b8edb877d1128864cee01c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Thu, 9 Nov 2017 08:05:53 -0800 Subject: serdev: Introduce devm_serdev_device_open() Add code implementing managed version of serdev_device_open() for serdev device drivers that "open" the device during driver's lifecycle only once (e.g. opened in .probe() and closed in .remove()). Cc: linux-kernel@vger.kernel.org Cc: linux-serial@vger.kernel.org Cc: Rob Herring Cc: cphealy@gmail.com Cc: Guenter Roeck Cc: Lucas Stach Cc: Nikita Yushchenko Cc: Lee Jones Cc: Greg Kroah-Hartman Cc: Pavel Machek Cc: Andy Shevchenko Cc: Johan Hovold Cc: Sebastian Reichel Acked-by: Rob Herring Reviewed-by: Sebastian Reichel Reviewed-by: Guenter Roeck Signed-off-by: Andrey Smirnov Signed-off-by: Greg Kroah-Hartman --- include/linux/serdev.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/serdev.h b/include/linux/serdev.h index e69402d4a8ae..9929063bd45d 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -193,6 +193,7 @@ static inline int serdev_controller_receive_buf(struct serdev_controller *ctrl, int serdev_device_open(struct serdev_device *); void serdev_device_close(struct serdev_device *); +int devm_serdev_device_open(struct device *, struct serdev_device *); unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int); void serdev_device_set_flow_control(struct serdev_device *, bool); int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t); -- cgit v1.2.3-70-g09d2 From 10f9e033c1e0e8563699b5839a2ff7f565dd72b2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 3 Nov 2017 15:30:54 +0100 Subject: serdev: document driver callbacks Amend the driver-callback kerneldoc with calling context and expected return values. Note that this is based on the requirements and characteristics of the tty-port controller implementation which receives data in workqueue context and whose write_wakeup callback must not sleep. Also note that while the receive_buf callback returns an integer, the returned value is still expected to be non-negative (and no greater than the buffer-size argument). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- include/linux/serdev.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 9929063bd45d..d7a9713e9883 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -27,8 +27,10 @@ struct serdev_device; /** * struct serdev_device_ops - Callback operations for a serdev device - * @receive_buf: Function called with data received from device. - * @write_wakeup: Function called when ready to transmit more data. + * @receive_buf: Function called with data received from device; + * returns number of bytes accepted; may sleep. + * @write_wakeup: Function called when ready to transmit more data; must + * not sleep. */ struct serdev_device_ops { int (*receive_buf)(struct serdev_device *, const unsigned char *, size_t); -- cgit v1.2.3-70-g09d2 From 743f93f822be1b54f3f9bd53d13f02192e65ce0b Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 24 Nov 2017 23:26:40 +0100 Subject: serial: Make retrieval of rs485 properties platform-agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit ef838a81dd4d ("serial: Add common rs485 device tree parsing function") consolidated retrieval of rs485 OF properties in a common helper function but did not #ifdef it to CONFIG_OF. The function is therefore included on ACPI platforms as well even though it's not used. On the other hand ACPI platforms with rs485 do exist (e.g. Siemens IOT2040) and they may leverage _DSD to store rs485 properties. Likewise, UART platform devices instantiated from an MFD should be able to specify rs485 properties. In fact, the tty subsystem maintainer had asked for a "generic" function during review of commit ef838a81dd4d: https://marc.info/?l=linux-serial&m=150143441725194&w=4 Thus, instead of constraining the helper to OF platforms, make it platform-agnostic by converting it to device_property_*() functions and renaming it accordingly. In imx.c, move the invocation of uart_get_rs485_mode() from serial_imx_probe_dt() to serial_imx_probe() so that it also gets called for non-OF devices. In omap-serial.c, move its invocation further up within serial_omap_probe_rs485() so that the RTS polarity can be overridden with the driver-specific "rs485-rts-active-high" property once we introduce a generic "rs485-rts-active-low" property. Cc: Jan Kiszka Cc: Richard Genoud Cc: Sascha Hauer Signed-off-by: Lukas Wunner Acked-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 2 +- drivers/tty/serial/fsl_lpuart.c | 2 +- drivers/tty/serial/imx.c | 4 ++-- drivers/tty/serial/omap-serial.c | 4 ++-- drivers/tty/serial/serial_core.c | 15 ++++++++------- include/linux/serial_core.h | 6 +----- 6 files changed, 15 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index efa25611ca0c..df46a9e88c34 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2345,7 +2345,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, atmel_init_property(atmel_port, pdev); atmel_set_ops(port); - of_get_rs485_mode(pdev->dev.of_node, &port->rs485); + uart_get_rs485_mode(&pdev->dev, &port->rs485); port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 1c4d3f387138..38e76f95490e 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -2206,7 +2206,7 @@ static int lpuart_probe(struct platform_device *pdev) if (ret) goto failed_attach_port; - of_get_rs485_mode(np, &sport->port.rs485); + uart_get_rs485_mode(&pdev->dev, &sport->port.rs485); if (sport->port.rs485.flags & SER_RS485_RX_DURING_TX) { dev_err(&pdev->dev, "driver doesn't support RX during TX\n"); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 16311a24b483..aca3b7dbd09c 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1999,8 +1999,6 @@ static int serial_imx_probe_dt(struct imx_port *sport, if (of_get_property(np, "rts-gpios", NULL)) sport->have_rtsgpio = 1; - of_get_rs485_mode(np, &sport->port.rs485); - return 0; } #else @@ -2093,6 +2091,8 @@ static int serial_imx_probe(struct platform_device *pdev) return ret; } + uart_get_rs485_mode(&pdev->dev, &sport->port.rs485); + /* Disable interrupts before requesting them */ reg = readl_relaxed(sport->port.membase + UCR1); reg &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN | diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 53d59e9b944a..227347548fff 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1611,6 +1611,8 @@ static int serial_omap_probe_rs485(struct uart_omap_port *up, if (!np) return 0; + uart_get_rs485_mode(up->dev, rs485conf); + if (of_property_read_bool(np, "rs485-rts-active-high")) rs485conf->flags |= SER_RS485_RTS_ON_SEND; else @@ -1632,8 +1634,6 @@ static int serial_omap_probe_rs485(struct uart_omap_port *up, up->rts_gpio = -EINVAL; } - of_get_rs485_mode(np, rs485conf); - return 0; } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 854995e1cae7..a59184a7afb0 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -3013,19 +3013,20 @@ EXPORT_SYMBOL(uart_add_one_port); EXPORT_SYMBOL(uart_remove_one_port); /** - * of_get_rs485_mode() - Implement parsing rs485 properties - * @np: uart node + * uart_get_rs485_mode() - retrieve rs485 properties for given uart + * @dev: uart device * @rs485conf: output parameter * * This function implements the device tree binding described in * Documentation/devicetree/bindings/serial/rs485.txt. */ -void of_get_rs485_mode(struct device_node *np, struct serial_rs485 *rs485conf) +void uart_get_rs485_mode(struct device *dev, struct serial_rs485 *rs485conf) { u32 rs485_delay[2]; int ret; - ret = of_property_read_u32_array(np, "rs485-rts-delay", rs485_delay, 2); + ret = device_property_read_u32_array(dev, "rs485-rts-delay", + rs485_delay, 2); if (!ret) { rs485conf->delay_rts_before_send = rs485_delay[0]; rs485conf->delay_rts_after_send = rs485_delay[1]; @@ -3040,13 +3041,13 @@ void of_get_rs485_mode(struct device_node *np, struct serial_rs485 *rs485conf) */ rs485conf->flags &= ~(SER_RS485_RX_DURING_TX | SER_RS485_ENABLED); - if (of_property_read_bool(np, "rs485-rx-during-tx")) + if (device_property_read_bool(dev, "rs485-rx-during-tx")) rs485conf->flags |= SER_RS485_RX_DURING_TX; - if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) + if (device_property_read_bool(dev, "linux,rs485-enabled-at-boot-time")) rs485conf->flags |= SER_RS485_ENABLED; } -EXPORT_SYMBOL_GPL(of_get_rs485_mode); +EXPORT_SYMBOL_GPL(uart_get_rs485_mode); MODULE_DESCRIPTION("Serial driver core"); MODULE_LICENSE("GPL"); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 37b044e78333..b60b225c0a59 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -501,9 +501,5 @@ static inline int uart_handle_break(struct uart_port *port) (cflag) & CRTSCTS || \ !((cflag) & CLOCAL)) -/* - * Common device tree parsing helpers - */ -void of_get_rs485_mode(struct device_node *np, struct serial_rs485 *rs485conf); - +void uart_get_rs485_mode(struct device *dev, struct serial_rs485 *rs485conf); #endif /* LINUX_SERIAL_CORE_H */ -- cgit v1.2.3-70-g09d2 From 0f646b63a1b2ede7885e45cf6e986128e001f91b Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 28 Dec 2017 14:07:07 +0100 Subject: serial: core: Make uart_parse_options take const char* argument The pointed string is never modified from within uart_parse_options, so it should be marked as const in the function prototype. Signed-off-by: Paul Cercueil Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 5 +++-- include/linux/serial_core.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index f57969de2f1c..2148883db66d 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1955,9 +1955,10 @@ EXPORT_SYMBOL_GPL(uart_parse_earlycon); * eg: 115200n8r */ void -uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow) +uart_parse_options(const char *options, int *baud, int *parity, + int *bits, int *flow) { - char *s = options; + const char *s = options; *baud = simple_strtoul(s, NULL, 10); while (*s >= '0' && *s <= '9') diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index b60b225c0a59..4c310c34ddad 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -387,7 +387,7 @@ struct uart_port *uart_get_console(struct uart_port *ports, int nr, struct console *c); int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, char **options); -void uart_parse_options(char *options, int *baud, int *parity, int *bits, +void uart_parse_options(const char *options, int *baud, int *parity, int *bits, int *flow); int uart_set_options(struct uart_port *port, struct console *co, int baud, int parity, int bits, int flow); -- cgit v1.2.3-70-g09d2 From b027e2298bd588d6fa36ed2eda97447fb3eac078 Mon Sep 17 00:00:00 2001 From: Gaurav Kohli Date: Tue, 23 Jan 2018 13:16:34 +0530 Subject: tty: fix data race between tty_init_dev and flush of buf There can be a race, if receive_buf call comes before tty initialization completes in n_tty_open and tty->disc_data may be NULL. CPU0 CPU1 ---- ---- 000|n_tty_receive_buf_common() n_tty_open() -001|n_tty_receive_buf2() tty_ldisc_open.isra.3() -002|tty_ldisc_receive_buf(inline) tty_ldisc_setup() Using ldisc semaphore lock in tty_init_dev till disc_data initializes completely. Signed-off-by: Gaurav Kohli Reviewed-by: Alan Cox Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 8 +++++++- drivers/tty/tty_ldisc.c | 4 ++-- include/linux/tty.h | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index a6ca634411e1..688bd25aa6b0 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1323,6 +1323,9 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n", __func__, tty->driver->name); + retval = tty_ldisc_lock(tty, 5 * HZ); + if (retval) + goto err_release_lock; tty->port->itty = tty; /* @@ -1333,6 +1336,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) retval = tty_ldisc_setup(tty, tty->link); if (retval) goto err_release_tty; + tty_ldisc_unlock(tty); /* Return the tty locked so that it cannot vanish under the caller */ return tty; @@ -1345,9 +1349,11 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: - tty_unlock(tty); + tty_ldisc_unlock(tty); tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n", retval, idx); +err_release_lock: + tty_unlock(tty); release_tty(tty, idx); return ERR_PTR(retval); } diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 24ec5c7e6b20..4e7946c0484b 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -337,7 +337,7 @@ static inline void __tty_ldisc_unlock(struct tty_struct *tty) ldsem_up_write(&tty->ldisc_sem); } -static int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) +int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) { int ret; @@ -348,7 +348,7 @@ static int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) return 0; } -static void tty_ldisc_unlock(struct tty_struct *tty) +void tty_ldisc_unlock(struct tty_struct *tty) { clear_bit(TTY_LDISC_HALTED, &tty->flags); __tty_ldisc_unlock(tty); diff --git a/include/linux/tty.h b/include/linux/tty.h index 7ac8ba208b1f..0a6c71e0ad01 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -405,6 +405,8 @@ extern const char *tty_name(const struct tty_struct *tty); extern struct tty_struct *tty_kopen(dev_t device); extern void tty_kclose(struct tty_struct *tty); extern int tty_dev_name_to_number(const char *name, dev_t *number); +extern int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout); +extern void tty_ldisc_unlock(struct tty_struct *tty); #else static inline void tty_kref_put(struct tty_struct *tty) { } -- cgit v1.2.3-70-g09d2