diff --git a/Documentation/devicetree/bindings/serial/amlogic,meson-uart.txt b/Documentation/devicetree/bindings/serial/amlogic,meson-uart.txt index 8ff65fa632fdedd739b005027e198051f62c8c34..c06c045126fc9070ca8f4faefc18b17a8de85ee6 100644 --- a/Documentation/devicetree/bindings/serial/amlogic,meson-uart.txt +++ b/Documentation/devicetree/bindings/serial/amlogic,meson-uart.txt @@ -21,7 +21,7 @@ Required properties: - interrupts : identifier to the device interrupt - clocks : a list of phandle + clock-specifier pairs, one for each entry in clock names. -- clocks-names : +- clock-names : * "xtal" for external xtal clock identifier * "pclk" for the bus core clock, either the clk81 clock or the gate clock * "baud" for the source of the baudrate generator, can be either the xtal diff --git a/Documentation/devicetree/bindings/serial/mvebu-uart.txt b/Documentation/devicetree/bindings/serial/mvebu-uart.txt index 2ae2fee7e023c01375f7fa09b9b21f7f812293c1..b7e0e32b9ac62c1029ba93998b8257ce71478de4 100644 --- a/Documentation/devicetree/bindings/serial/mvebu-uart.txt +++ b/Documentation/devicetree/bindings/serial/mvebu-uart.txt @@ -24,7 +24,7 @@ Required properties: - Must contain two elements for the extended variant of the IP (marvell,armada-3700-uart-ext): "uart-tx" and "uart-rx", respectively the UART TX interrupt and the UART RX interrupt. A - corresponding interrupts-names property must be defined. + corresponding interrupt-names property must be defined. - For backward compatibility reasons, a single element interrupts property is also supported for the standard variant of the IP, containing only the UART sum interrupt. This form is deprecated diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index ad962f4ec3aaffe73991118aabd78b9da3d5cb87..a006ea4d065f7cdd0781ef2ef1ab1ee35827c5da 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -17,6 +17,8 @@ Required properties: - "renesas,scifa-r8a7745" for R8A7745 (RZ/G1E) SCIFA compatible UART. - "renesas,scifb-r8a7745" for R8A7745 (RZ/G1E) SCIFB compatible UART. - "renesas,hscif-r8a7745" for R8A7745 (RZ/G1E) HSCIF compatible UART. + - "renesas,scif-r8a77470" for R8A77470 (RZ/G1C) SCIF compatible UART. + - "renesas,hscif-r8a77470" for R8A77470 (RZ/G1C) HSCIF compatible UART. - "renesas,scif-r8a7778" for R8A7778 (R-Car M1) SCIF compatible UART. - "renesas,scif-r8a7779" for R8A7779 (R-Car H1) SCIF compatible UART. - "renesas,scif-r8a7790" for R8A7790 (R-Car H2) SCIF compatible UART. diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 84aa9d6763753029f45c39483c9b84ca59299409..6da20b9688f7419aae4f806798eaa137c8c4f649 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -942,7 +942,7 @@ int __init early_init_dt_scan_chosen_stdout(void) int offset; const char *p, *q, *options = NULL; int l; - const struct earlycon_id *match; + const struct earlycon_id **p_match; const void *fdt = initial_boot_params; offset = fdt_path_offset(fdt, "/chosen"); @@ -969,7 +969,10 @@ int __init early_init_dt_scan_chosen_stdout(void) return 0; } - for (match = __earlycon_table; match < __earlycon_table_end; match++) { + for (p_match = __earlycon_table; p_match < __earlycon_table_end; + p_match++) { + const struct earlycon_id *match = *p_match; + if (!match->compatible[0]) continue; diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 3b3e1f6632d71c1aae24d690679b4b53673ce148..1dbe27c9946c16578a6ad0f82313c4adf797aef0 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -121,6 +121,9 @@ struct gsm_dlci { struct mutex mutex; /* Link layer */ + int mode; +#define DLCI_MODE_ABM 0 /* Normal Asynchronous Balanced Mode */ +#define DLCI_MODE_ADM 1 /* Asynchronous Disconnected Mode */ spinlock_t lock; /* Protects the internal state */ struct timer_list t1; /* Retransmit timer for SABM and UA */ int retries; @@ -1364,7 +1367,13 @@ static struct gsm_control *gsm_control_send(struct gsm_mux *gsm, ctrl->data = data; ctrl->len = clen; gsm->pending_cmd = ctrl; - gsm->cretries = gsm->n2; + + /* If DLCI0 is in ADM mode skip retries, it won't respond */ + if (gsm->dlci[0]->mode == DLCI_MODE_ADM) + gsm->cretries = 1; + else + gsm->cretries = gsm->n2; + mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); gsm_control_transmit(gsm, ctrl); spin_unlock_irqrestore(&gsm->control_lock, flags); @@ -1472,6 +1481,7 @@ static void gsm_dlci_t1(struct timer_list *t) if (debug & 8) pr_info("DLCI %d opening in ADM mode.\n", dlci->addr); + dlci->mode = DLCI_MODE_ADM; gsm_dlci_open(dlci); } else { gsm_dlci_close(dlci); @@ -2861,11 +2871,22 @@ static int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk) static int gsm_carrier_raised(struct tty_port *port) { struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); + struct gsm_mux *gsm = dlci->gsm; + /* Not yet open so no carrier info */ if (dlci->state != DLCI_OPEN) return 0; if (debug & 2) return 1; + + /* + * Basic mode with control channel in ADM mode may not respond + * to CMD_MSC at all and modem_rx is empty. + */ + if (gsm->encoding == 0 && gsm->dlci[0]->mode == DLCI_MODE_ADM && + !dlci->modem_rx) + return 1; + return dlci->modem_rx & TIOCM_CD; } diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index a24278380fec2a6a67b2513514f08ecd6688dfe2..22683393a0f2c3f7b6054d367823a3f99f4df103 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -169,7 +169,7 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match) */ int __init setup_earlycon(char *buf) { - const struct earlycon_id *match; + const struct earlycon_id **p_match; if (!buf || !buf[0]) return -EINVAL; @@ -177,7 +177,9 @@ int __init setup_earlycon(char *buf) if (early_con.flags & CON_ENABLED) return -EALREADY; - for (match = __earlycon_table; match < __earlycon_table_end; match++) { + for (p_match = __earlycon_table; p_match < __earlycon_table_end; + p_match++) { + const struct earlycon_id *match = *p_match; size_t len = strlen(match->name); if (strncmp(buf, match->name, len)) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 91f3a1a5cb7fa81ac82a2289e792fc5c908df9b2..c2fc6bef7a6f28a6fc3fe9f18782c555bdb35a35 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -316,7 +316,7 @@ static u32 imx_uart_readl(struct imx_port *sport, u32 offset) * differ from the value that was last written. As it only * clears after being set, reread conditionally. */ - if (sport->ucr2 & UCR2_SRST) + if (!(sport->ucr2 & UCR2_SRST)) sport->ucr2 = readl(sport->port.membase + offset); return sport->ucr2; break; @@ -1833,6 +1833,11 @@ static int imx_uart_rs485_config(struct uart_port *port, rs485conf->flags &= ~SER_RS485_ENABLED; if (rs485conf->flags & SER_RS485_ENABLED) { + /* Enable receiver if low-active RTS signal is requested */ + if (sport->have_rtscts && !sport->have_rtsgpio && + !(rs485conf->flags & SER_RS485_RTS_ON_SEND)) + rs485conf->flags |= SER_RS485_RX_DURING_TX; + /* disable transmitter */ ucr2 = imx_uart_readl(sport, UCR2); if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND) @@ -2265,6 +2270,18 @@ static int imx_uart_probe(struct platform_device *pdev) (!sport->have_rtscts && !sport->have_rtsgpio)) dev_err(&pdev->dev, "no RTS control, disabling rs485\n"); + /* + * If using the i.MX UART RTS/CTS control then the RTS (CTS_B) + * signal cannot be set low during transmission in case the + * receiver is off (limitation of the i.MX UART IP). + */ + if (sport->port.rs485.flags & SER_RS485_ENABLED && + sport->have_rtscts && !sport->have_rtsgpio && + (!(sport->port.rs485.flags & SER_RS485_RTS_ON_SEND) && + !(sport->port.rs485.flags & SER_RS485_RX_DURING_TX))) + dev_err(&pdev->dev, + "low-active RTS not possible when receiver is off, enabling receiver\n"); + imx_uart_rs485_config(&sport->port, &sport->port.rs485); /* Disable interrupts before requesting them */ diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 750e5645dc857c7ba63bd0a848e9f6055fac988c..f503fab1e2685f2451d540ed18d5f474a71581b4 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -495,7 +495,6 @@ static void mvebu_uart_set_termios(struct uart_port *port, termios->c_iflag |= old->c_iflag & ~(INPCK | IGNPAR); termios->c_cflag &= CREAD | CBAUD; termios->c_cflag |= old->c_cflag & ~(CREAD | CBAUD); - termios->c_lflag = old->c_lflag; } spin_unlock_irqrestore(&port->lock, flags); diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 65ff669373d4a0325ffd97a04f070ee3e389217a..a1b3eb04cb32c78f17ccd32698ced4a459091869 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1022,6 +1022,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) struct qcom_geni_serial_port *port; struct uart_port *uport; struct resource *res; + int irq; if (pdev->dev.of_node) line = of_alias_get_id(pdev->dev.of_node, "serial"); @@ -1061,11 +1062,12 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS; port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; - uport->irq = platform_get_irq(pdev, 0); - if (uport->irq < 0) { - dev_err(&pdev->dev, "Failed to get IRQ %d\n", uport->irq); - return uport->irq; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Failed to get IRQ %d\n", irq); + return irq; } + uport->irq = irq; uport->private_data = &qcom_geni_console_driver; platform_set_drvdata(pdev, port); diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index abcb4d09a2d866d05b7af3c1ff5d3f68492514c1..bd72dd843338dc9a3aecaacaa04dc7b2fc2442db 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1181,7 +1181,7 @@ static int __init cdns_early_console_setup(struct earlycon_device *device, /* only set baud if specified on command line - otherwise * assume it has been initialized by a boot loader. */ - if (device->baud) { + if (port->uartclk && device->baud) { u32 cd = 0, bdiv = 0; u32 mr; int div8; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 63114ea35ec1b8f3620ec888155b929ed1915200..7c838b90a31d636865ce99a84466967d57b777aa 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2816,7 +2816,10 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) kref_init(&tty->kref); tty->magic = TTY_MAGIC; - tty_ldisc_init(tty); + if (tty_ldisc_init(tty)) { + kfree(tty); + return NULL; + } tty->session = NULL; tty->pgrp = NULL; mutex_init(&tty->legacy_mutex); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 050f4d650891763f96800244869821afb9d1586e..fb7329ab2b37a85f0682805d2fe996f6279df64b 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -176,12 +176,11 @@ static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) return ERR_CAST(ldops); } - ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); - if (ld == NULL) { - put_ldops(ldops); - return ERR_PTR(-ENOMEM); - } - + /* + * There is no way to handle allocation failure of only 16 bytes. + * Let's simplify error handling and save more memory. + */ + ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL | __GFP_NOFAIL); ld->ops = ldops; ld->tty = tty; @@ -527,19 +526,16 @@ static int tty_ldisc_failto(struct tty_struct *tty, int ld) static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) { /* There is an outstanding reference here so this is safe */ - old = tty_ldisc_get(tty, old->ops->num); - WARN_ON(IS_ERR(old)); - tty->ldisc = old; - tty_set_termios_ldisc(tty, old->ops->num); - if (tty_ldisc_open(tty, old) < 0) { - tty_ldisc_put(old); + if (tty_ldisc_failto(tty, old->ops->num) < 0) { + const char *name = tty_name(tty); + + pr_warn("Falling back ldisc for %s.\n", name); /* The traditional behaviour is to fall back to N_TTY, we want to avoid falling back to N_NULL unless we have no choice to avoid the risk of breaking anything */ if (tty_ldisc_failto(tty, N_TTY) < 0 && tty_ldisc_failto(tty, N_NULL) < 0) - panic("Couldn't open N_NULL ldisc for %s.", - tty_name(tty)); + panic("Couldn't open N_NULL ldisc for %s.", name); } } @@ -824,12 +820,13 @@ EXPORT_SYMBOL_GPL(tty_ldisc_release); * the tty structure is not completely set up when this call is made. */ -void tty_ldisc_init(struct tty_struct *tty) +int tty_ldisc_init(struct tty_struct *tty) { struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); if (IS_ERR(ld)) - panic("n_tty: init_tty"); + return PTR_ERR(ld); tty->ldisc = ld; + return 0; } /** diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 278841c75b97b0f55431e75c3a172a041f43f94b..af240573e48295687e4403b0bcc11875d5da2e82 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -188,7 +188,7 @@ #endif #ifdef CONFIG_SERIAL_EARLYCON -#define EARLYCON_TABLE() STRUCT_ALIGN(); \ +#define EARLYCON_TABLE() . = ALIGN(8); \ VMLINUX_SYMBOL(__earlycon_table) = .; \ KEEP(*(__earlycon_table)) \ VMLINUX_SYMBOL(__earlycon_table_end) = .; diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 1d356105f25a1680b05a6119fa1d9efba578b6b6..b4c9fda9d8335bd3b20611d9684645dce3f8532c 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -351,10 +351,10 @@ struct earlycon_id { char name[16]; char compatible[128]; int (*setup)(struct earlycon_device *, const char *options); -} __aligned(32); +}; -extern const struct earlycon_id __earlycon_table[]; -extern const struct earlycon_id __earlycon_table_end[]; +extern const struct earlycon_id *__earlycon_table[]; +extern const struct earlycon_id *__earlycon_table_end[]; #if defined(CONFIG_SERIAL_EARLYCON) && !defined(MODULE) #define EARLYCON_USED_OR_UNUSED __used @@ -362,12 +362,19 @@ extern const struct earlycon_id __earlycon_table_end[]; #define EARLYCON_USED_OR_UNUSED __maybe_unused #endif -#define OF_EARLYCON_DECLARE(_name, compat, fn) \ - static const struct earlycon_id __UNIQUE_ID(__earlycon_##_name) \ - EARLYCON_USED_OR_UNUSED __section(__earlycon_table) \ +#define _OF_EARLYCON_DECLARE(_name, compat, fn, unique_id) \ + static const struct earlycon_id unique_id \ + EARLYCON_USED_OR_UNUSED __initconst \ = { .name = __stringify(_name), \ .compatible = compat, \ - .setup = fn } + .setup = fn }; \ + static const struct earlycon_id EARLYCON_USED_OR_UNUSED \ + __section(__earlycon_table) \ + * const __PASTE(__p, unique_id) = &unique_id + +#define OF_EARLYCON_DECLARE(_name, compat, fn) \ + _OF_EARLYCON_DECLARE(_name, compat, fn, \ + __UNIQUE_ID(__earlycon_##_name)) #define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn) diff --git a/include/linux/tty.h b/include/linux/tty.h index 47f8af22f2168f0376d77068ecbc2df930ae8f79..1dd587ba6d882bb882b44680ec1acebb314c4364 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -701,7 +701,7 @@ extern int tty_unregister_ldisc(int disc); extern int tty_set_ldisc(struct tty_struct *tty, int disc); extern int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty); extern void tty_ldisc_release(struct tty_struct *tty); -extern void tty_ldisc_init(struct tty_struct *tty); +extern int __must_check tty_ldisc_init(struct tty_struct *tty); extern void tty_ldisc_deinit(struct tty_struct *tty); extern int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p, char *f, int count);