diff options
48 files changed, 1187 insertions, 646 deletions
diff --git a/Documentation/devicetree/bindings/serial/mediatek,uart.yaml b/Documentation/devicetree/bindings/serial/mediatek,uart.yaml new file mode 100644 index 000000000000..4ff27d6d4d5b --- /dev/null +++ b/Documentation/devicetree/bindings/serial/mediatek,uart.yaml @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/mediatek,uart.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek Universal Asynchronous Receiver/Transmitter (UART) + +maintainers: + - Matthias Brugger <matthias.bgg@gmail.com> + +allOf: + - $ref: serial.yaml# + +description: | + The MediaTek UART is based on the basic 8250 UART and compatible + with 16550A, with enhancements for high speed baud rates and + support for DMA. + +properties: + compatible: + oneOf: + - const: mediatek,mt6577-uart + - items: + - enum: + - mediatek,mt2701-uart + - mediatek,mt2712-uart + - mediatek,mt6580-uart + - mediatek,mt6582-uart + - mediatek,mt6589-uart + - mediatek,mt6755-uart + - mediatek,mt6765-uart + - mediatek,mt6779-uart + - mediatek,mt6795-uart + - mediatek,mt6797-uart + - mediatek,mt7622-uart + - mediatek,mt7623-uart + - mediatek,mt7629-uart + - mediatek,mt7986-uart + - mediatek,mt8127-uart + - mediatek,mt8135-uart + - mediatek,mt8173-uart + - mediatek,mt8183-uart + - mediatek,mt8186-uart + - mediatek,mt8192-uart + - mediatek,mt8195-uart + - mediatek,mt8516-uart + - const: mediatek,mt6577-uart + + reg: + description: The base address of the UART register bank + maxItems: 1 + + clocks: + minItems: 1 + items: + - description: The clock the baudrate is derived from + - description: The bus clock for register accesses + + clock-names: + minItems: 1 + items: + - const: baud + - const: bus + + dmas: + items: + - description: phandle to TX DMA + - description: phandle to RX DMA + + dma-names: + items: + - const: tx + - const: rx + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + description: + The UART interrupt and optionally the RX in-band wakeup interrupt. + minItems: 1 + items: + - const: uart + - const: wakeup + + pinctrl-0: true + pinctrl-1: true + + pinctrl-names: + minItems: 1 + items: + - const: default + - const: sleep + +required: + - compatible + - reg + - clocks + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + serial@11006000 { + compatible = "mediatek,mt6589-uart", "mediatek,mt6577-uart"; + reg = <0x11006000 0x400>; + interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_LOW>, + <GIC_SPI 52 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "uart", "wakeup"; + clocks = <&uart_clk>, <&bus_clk>; + clock-names = "baud", "bus"; + pinctrl-0 = <&uart_pin>; + pinctrl-1 = <&uart_pin_sleep>; + pinctrl-names = "default", "sleep"; + }; diff --git a/Documentation/devicetree/bindings/serial/mtk-uart.txt b/Documentation/devicetree/bindings/serial/mtk-uart.txt deleted file mode 100644 index 113b5d6a2245..000000000000 --- a/Documentation/devicetree/bindings/serial/mtk-uart.txt +++ /dev/null @@ -1,59 +0,0 @@ -* MediaTek Universal Asynchronous Receiver/Transmitter (UART) - -Required properties: -- compatible should contain: - * "mediatek,mt2701-uart" for MT2701 compatible UARTS - * "mediatek,mt2712-uart" for MT2712 compatible UARTS - * "mediatek,mt6580-uart" for MT6580 compatible UARTS - * "mediatek,mt6582-uart" for MT6582 compatible UARTS - * "mediatek,mt6589-uart" for MT6589 compatible UARTS - * "mediatek,mt6755-uart" for MT6755 compatible UARTS - * "mediatek,mt6765-uart" for MT6765 compatible UARTS - * "mediatek,mt6779-uart" for MT6779 compatible UARTS - * "mediatek,mt6795-uart" for MT6795 compatible UARTS - * "mediatek,mt6797-uart" for MT6797 compatible UARTS - * "mediatek,mt7622-uart" for MT7622 compatible UARTS - * "mediatek,mt7623-uart" for MT7623 compatible UARTS - * "mediatek,mt7629-uart" for MT7629 compatible UARTS - * "mediatek,mt7986-uart", "mediatek,mt6577-uart" for MT7986 compatible UARTS - * "mediatek,mt8127-uart" for MT8127 compatible UARTS - * "mediatek,mt8135-uart" for MT8135 compatible UARTS - * "mediatek,mt8173-uart" for MT8173 compatible UARTS - * "mediatek,mt8183-uart", "mediatek,mt6577-uart" for MT8183 compatible UARTS - * "mediatek,mt8186-uart", "mediatek,mt6577-uart" for MT8183 compatible UARTS - * "mediatek,mt8192-uart", "mediatek,mt6577-uart" for MT8192 compatible UARTS - * "mediatek,mt8195-uart", "mediatek,mt6577-uart" for MT8195 compatible UARTS - * "mediatek,mt8516-uart" for MT8516 compatible UARTS - * "mediatek,mt6577-uart" for MT6577 and all of the above - -- reg: The base address of the UART register bank. - -- interrupts: - index 0: an interrupt specifier for the UART controller itself - index 1: optional, an interrupt specifier with edge sensitivity on Rx pin to - support Rx in-band wake up. If one would like to use this feature, - one must create an addtional pinctrl to reconfigure Rx pin to normal - GPIO before suspend. - -- clocks : Must contain an entry for each entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names: - - "baud": The clock the baudrate is derived from - - "bus": The bus clock for register accesses (optional) - -For compatibility with older device trees an unnamed clock is used for the -baud clock if the baudclk does not exist. Do not use this for new designs. - -Example: - - uart0: serial@11006000 { - compatible = "mediatek,mt6589-uart", "mediatek,mt6577-uart"; - reg = <0x11006000 0x400>; - interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_LOW>, - <GIC_SPI 52 IRQ_TYPE_EDGE_FALLING>; - clocks = <&uart_clk>, <&bus_clk>; - clock-names = "baud", "bus"; - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&uart_pin>; - pinctrl-1 = <&uart_pin_sleep>; - }; diff --git a/Documentation/driver-api/serial/serial-rs485.rst b/Documentation/driver-api/serial/serial-rs485.rst index 6bc824f948f9..00b5d333acba 100644 --- a/Documentation/driver-api/serial/serial-rs485.rst +++ b/Documentation/driver-api/serial/serial-rs485.rst @@ -38,10 +38,14 @@ RS485 Serial Communications the values given by the device tree. Any driver for devices capable of working both as RS232 and RS485 should - implement the rs485_config callback in the uart_port structure. The - serial_core calls rs485_config to do the device specific part in response - to TIOCSRS485 and TIOCGRS485 ioctls (see below). The rs485_config callback - receives a pointer to struct serial_rs485. + implement the rs485_config callback and provide rs485_supported in the + uart_port structure. The serial core calls rs485_config to do the device + specific part in response to TIOCSRS485 ioctl (see below). The rs485_config + callback receives a pointer to a sanitizated serial_rs485 structure. The + serial_rs485 userspace provides is sanitized before calling rs485_config + using rs485_supported that indicates what RS485 features the driver supports + for the uart_port. TIOCGRS485 ioctl can be used to read back the + serial_rs485 structure matching to the current configuration. 4. Usage from user-level ======================== diff --git a/drivers/accessibility/braille/braille_console.c b/drivers/accessibility/braille/braille_console.c index fdc6b593f500..c4d54a5326b1 100644 --- a/drivers/accessibility/braille/braille_console.c +++ b/drivers/accessibility/braille/braille_console.c @@ -131,7 +131,7 @@ static void vc_refresh(struct vc_data *vc) for (i = 0; i < WIDTH; i++) { u16 glyph = screen_glyph(vc, 2 * (vc_x + i) + vc_y * vc->vc_size_row); - buf[i] = inverse_translate(vc, glyph, 1); + buf[i] = inverse_translate(vc, glyph, true); } braille_write(buf); } diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c index d726537fa16c..f52265293482 100644 --- a/drivers/accessibility/speakup/main.c +++ b/drivers/accessibility/speakup/main.c @@ -470,7 +470,7 @@ static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs) c |= 0x100; } - ch = inverse_translate(vc, c, 1); + ch = inverse_translate(vc, c, true); *attribs = (w & 0xff00) >> 8; } return ch; diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c index 31dceb5039b5..49907427a165 100644 --- a/drivers/tty/mips_ejtag_fdc.c +++ b/drivers/tty/mips_ejtag_fdc.c @@ -1222,7 +1222,7 @@ static void kgdbfdc_push_one(void) /* Construct a word from any data in buffer */ word = mips_ejtag_fdc_encode(bufs, &kgdbfdc_wbuflen, 1); - /* Relocate any remaining data to beginnning of buffer */ + /* Relocate any remaining data to beginning of buffer */ kgdbfdc_wbuflen -= word.bytes; for (i = 0; i < kgdbfdc_wbuflen; ++i) kgdbfdc_wbuf[i] = kgdbfdc_wbuf[i + word.bytes]; diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 640c9e871044..3afdd9033a9c 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -118,6 +118,9 @@ struct n_tty_data { size_t read_tail; size_t line_start; + /* # of chars looked ahead (to find software flow control chars) */ + size_t lookahead_count; + /* protected by output lock */ unsigned int column; unsigned int canon_column; @@ -333,6 +336,8 @@ static void reset_buffer_flags(struct n_tty_data *ldata) ldata->erasing = 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); ldata->push = 0; + + ldata->lookahead_count = 0; } static void n_tty_packet_mode_flush(struct tty_struct *tty) @@ -1225,12 +1230,30 @@ static bool n_tty_is_char_flow_ctrl(struct tty_struct *tty, unsigned char c) return c == START_CHAR(tty) || c == STOP_CHAR(tty); } -/* Returns true if c is consumed as flow-control character */ -static bool n_tty_receive_char_flow_ctrl(struct tty_struct *tty, unsigned char c) +/** + * n_tty_receive_char_flow_ctrl - receive flow control chars + * @tty: terminal device + * @c: character + * @lookahead_done: lookahead has processed this character already + * + * Receive and process flow control character actions. + * + * In case lookahead for flow control chars already handled the character in + * advance to the normal receive, the actions are skipped during normal + * receive. + * + * Returns true if @c is consumed as flow-control character, the character + * must not be treated as normal character. + */ +static bool n_tty_receive_char_flow_ctrl(struct tty_struct *tty, unsigned char c, + bool lookahead_done) { if (!n_tty_is_char_flow_ctrl(tty, c)) return false; + if (lookahead_done) + return true; + if (c == START_CHAR(tty)) { start_tty(tty); process_echoes(tty); @@ -1242,11 +1265,12 @@ static bool n_tty_receive_char_flow_ctrl(struct tty_struct *tty, unsigned char c return true; } -static void n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) +static void n_tty_receive_char_special(struct tty_struct *tty, unsigned char c, + bool lookahead_done) { struct n_tty_data *ldata = tty->disc_data; - if (I_IXON(tty) && n_tty_receive_char_flow_ctrl(tty, c)) + if (I_IXON(tty) && n_tty_receive_char_flow_ctrl(tty, c, lookahead_done)) return; if (L_ISIG(tty)) { @@ -1401,7 +1425,8 @@ static void n_tty_receive_char(struct tty_struct *tty, unsigned char c) put_tty_queue(c, ldata); } -static void n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c) +static void n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c, + bool lookahead_done) { if (I_ISTRIP(tty)) c &= 0x7f; @@ -1409,12 +1434,10 @@ static void n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c) c = tolower(c); if (I_IXON(tty)) { - if (c == STOP_CHAR(tty)) - stop_tty(tty); - else if (c == START_CHAR(tty) || - (tty->flow.stopped && !tty->flow.tco_stopped && I_IXANY(tty) && - c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && - c != SUSP_CHAR(tty))) { + if (!n_tty_receive_char_flow_ctrl(tty, c, lookahead_done) && + tty->flow.stopped && !tty->flow.tco_stopped && I_IXANY(tty) && + c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && + c != SUSP_CHAR(tty)) { start_tty(tty); process_echoes(tty); } @@ -1457,6 +1480,27 @@ n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag) n_tty_receive_char_flagged(tty, c, flag); } +/* Caller must ensure count > 0 */ +static void n_tty_lookahead_flow_ctrl(struct tty_struct *tty, const unsigned char *cp, + const unsigned char *fp, unsigned int count) +{ + struct n_tty_data *ldata = tty->disc_data; + unsigned char flag = TTY_NORMAL; + + ldata->lookahead_count += count; + + if (!I_IXON(tty)) + return; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) + n_tty_receive_char_flow_ctrl(tty, *cp, false); + cp++; + } +} + static void n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp, const char *fp, int count) @@ -1496,7 +1540,7 @@ n_tty_receive_buf_raw(struct tty_struct *tty, const unsigned char *cp, static void n_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp, - const char *fp, int count) + const char *fp, int count, bool lookahead_done) { char flag = TTY_NORMAL; @@ -1504,12 +1548,12 @@ n_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp, if (fp) flag = *fp++; if (likely(flag == TTY_NORMAL)) - n_tty_receive_char_closing(tty, *cp++); + n_tty_receive_char_closing(tty, *cp++, lookahead_done); } } static void n_tty_receive_buf_standard(struct tty_struct *tty, - const unsigned char *cp, const char *fp, int count) + const unsigned char *cp, const char *fp, int count, bool lookahead_done) { struct n_tty_data *ldata = tty->disc_data; char flag = TTY_NORMAL; @@ -1540,7 +1584,7 @@ static void n_tty_receive_buf_standard(struct tty_struct *tty, } if (test_bit(c, ldata->char_map)) - n_tty_receive_char_special(tty, c); + n_tty_receive_char_special(tty, c, lookahead_done); else n_tty_receive_char(tty, c); } @@ -1551,21 +1595,30 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, { struct n_tty_data *ldata = tty->disc_data; bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty)); + size_t la_count = min_t(size_t, ldata->lookahead_count, count); if (ldata->real_raw) n_tty_receive_buf_real_raw(tty, cp, fp, count); else if (ldata->raw || (L_EXTPROC(tty) && !preops)) n_tty_receive_buf_raw(tty, cp, fp, count); - else if (tty->closing && !L_EXTPROC(tty)) - n_tty_receive_buf_closing(tty, cp, fp, count); - else { - n_tty_receive_buf_standard(tty, cp, fp, count); + else if (tty->closing && !L_EXTPROC(tty)) { + if (la_count > 0) + n_tty_receive_buf_closing(tty, cp, fp, la_count, true); + if (count > la_count) + n_tty_receive_buf_closing(tty, cp, fp, count - la_count, false); + } else { + if (la_count > 0) + n_tty_receive_buf_standard(tty, cp, fp, la_count, true); + if (count > la_count) + n_tty_receive_buf_standard(tty, cp, fp, count - la_count, false); flush_echoes(tty); if (tty->ops->flush_chars) tty->ops->flush_chars(tty); } + ldata->lookahead_count -= la_count; + if (ldata->icanon && !L_EXTPROC(tty)) return; @@ -2446,6 +2499,7 @@ static struct tty_ldisc_ops n_tty_ops = { .receive_buf = n_tty_receive_buf, .write_wakeup = n_tty_write_wakeup, .receive_buf2 = n_tty_receive_buf2, + .lookahead_buf = n_tty_lookahead_flow_ctrl, }; /** diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 696030cfcb09..b120da57c61f 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -123,6 +123,26 @@ static inline void serial_out(struct uart_8250_port *up, int offset, int value) up->port.serial_out(&up->port, offset, value); } +/** + * serial_lsr_in - Read LSR register and preserve flags across reads + * @up: uart 8250 port + * + * Read LSR register and handle saving non-preserved flags across reads. + * The flags that are not preserved across reads are stored into + * up->lsr_saved_flags. + * + * Returns LSR value or'ed with the preserved flags (if any). + */ +static inline unsigned int serial_lsr_in(struct uart_8250_port *up) +{ + unsigned int lsr = up->lsr_saved_flags; + + lsr |= serial_in(up, UART_LSR); + up->lsr_saved_flags = lsr & LSR_SAVE_FLAGS; + + return lsr; +} + /* * For the 16C950 */ @@ -187,6 +207,7 @@ int serial8250_em485_config(struct uart_port *port, struct serial_rs485 *rs485); void serial8250_em485_start_tx(struct uart_8250_port *p); void serial8250_em485_stop_tx(struct uart_8250_port *p); void serial8250_em485_destroy(struct uart_8250_port *p); +extern struct serial_rs485 serial8250_em485_supported; /* MCR <-> TIOCM conversion */ static inline int serial8250_TIOCM_to_MCR(int tiocm) diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index 2a1226a78a0c..d9f1e618cfbd 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -108,6 +108,7 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev) up.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST | UPF_IOREMAP; up.port.rs485_config = serial8250_em485_config; + up.port.rs485_supported = &serial8250_em485_supported; up.rs485_start_tx = bcm2835aux_rs485_start_tx; up.rs485_stop_tx = bcm2835aux_rs485_stop_tx; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index cfbd2de0ca6e..90ddc8924811 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -276,8 +276,7 @@ static void serial8250_backup_timeout(struct timer_list *t) * the "Diva" UART used on the management processor on many HP * ia64 and parisc boxes. */ - lsr = serial_in(up, UART_LSR); - up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + lsr = serial_lsr_in(up); if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) && (lsr & UART_LSR_THRE)) { @@ -1004,6 +1003,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) uart->port.throttle = up->port.throttle; uart->port.unthrottle = up->port.unthrottle; uart->port.rs485_config = up->port.rs485_config; + uart->port.rs485_supported = up->port.rs485_supported; uart->port.rs485 = up->port.rs485; uart->rs485_start_tx = up->rs485_start_tx; uart->rs485_stop_tx = up->rs485_stop_tx; diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index f57bbd32ef11..4cc69bb612ab 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -122,12 +122,15 @@ static void dw8250_check_lcr(struct uart_port *p, int value) /* Returns once the transmitter is empty or we run out of retries */ static void dw8250_tx_wait_empty(struct uart_port *p) { + struct uart_8250_port *up = up_to_u8250p(p); unsigned int tries = 20000; unsigned int delay_threshold = tries - 1000; unsigned int lsr; while (tries--) { lsr = readb (p->membase + (UART_LSR << p->regshift)); + up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + if (lsr & UART_LSR_TEMT) break; @@ -253,7 +256,7 @@ static int dw8250_handle_irq(struct uart_port *p) */ if (!up->dma && rx_timeout) { spin_lock_irqsave(&p->lock, flags); - status = p->serial_in(p, UART_LSR); + status = serial_lsr_in(up); if (!(status & (UART_LSR_DR | UART_LSR_BI))) (void) p->serial_in(p, UART_RX); @@ -263,7 +266,7 @@ static int dw8250_handle_irq(struct uart_port *p) /* Manually stop the Rx DMA transfer when acting as flow controller */ if (quirks & DW_UART_QUIRK_IS_DMA_FC && up->dma && up->dma->rx_running && rx_timeout) { - status = p->serial_in(p, UART_LSR); + status = serial_lsr_in(up); if (status & (UART_LSR_DR | UART_LSR_BI)) { dw8250_writel_ext(p, RZN1_UART_RDMACR, 0); dw8250_writel_ext(p, DW_UART_DMASA, 1); diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c index fbabfdd8c7b8..c83e7eaf3877 100644 --- a/drivers/tty/serial/8250/8250_dwlib.c +++ b/drivers/tty/serial/8250/8250_dwlib.c @@ -93,9 +93,6 @@ static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485) tcr &= ~DW_UART_TCR_XFER_MODE; if (rs485->flags & SER_RS485_ENABLED) { - /* Clear unsupported flags. */ - rs485->flags &= SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | - SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND; tcr |= DW_UART_TCR_RS485_EN; if (rs485->flags & SER_RS485_RX_DURING_TX) { @@ -111,8 +108,6 @@ static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485) dw8250_writel_ext(p, DW_UART_DE_EN, 1); dw8250_writel_ext(p, DW_UART_RE_EN, 1); } else { - rs485->flags = 0; - tcr &= ~DW_UART_TCR_RS485_EN; } @@ -127,11 +122,6 @@ static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485) dw8250_writel_ext(p, DW_UART_TCR, tcr); - rs485->delay_rts_before_send = 0; - rs485->delay_rts_after_send = 0; - - p->rs485 = *rs485; - return 0; } @@ -149,6 +139,11 @@ static bool dw8250_detect_rs485_hw(struct uart_port *p) return reg; } +static const struct serial_rs485 dw8250_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND, +}; + void dw8250_setup_port(struct uart_port *p) { struct dw8250_port_data *pd = p->private_data; @@ -159,8 +154,10 @@ void dw8250_setup_port(struct uart_port *p) pd->hw_rs485_support = dw8250_detect_rs485_hw(p); if (pd->hw_rs485_support) { p->rs485_config = dw8250_rs485_config; + p->rs485_supported = &dw8250_rs485_supported; } else { p->rs485_config = serial8250_em485_config; + p->rs485_supported = &serial8250_em485_supported; up->rs485_start_tx = serial8250_em485_start_tx; up->rs485_stop_tx = serial8250_em485_stop_tx; } diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 7292917ac878..528779b40049 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -113,6 +113,7 @@ struct exar8250; struct exar8250_platform { int (*rs485_config)(struct uart_port *, struct serial_rs485 *); + const struct serial_rs485 *rs485_supported; int (*register_gpio)(struct pci_dev *, struct uart_8250_port *); void (*unregister_gpio)(struct uart_8250_port *); }; @@ -426,15 +427,18 @@ static int generic_rs485_config(struct uart_port *port, if (is_rs485) writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); - port->rs485 = *rs485; - return 0; } +static const struct serial_rs485 generic_rs485_supported = { + .flags = SER_RS485_ENABLED, +}; + static const struct exar8250_platform exar8250_default_platform = { .register_gpio = xr17v35x_register_gpio, .unregister_gpio = xr17v35x_unregister_gpio, .rs485_config = generic_rs485_config, + .rs485_supported = &generic_rs485_supported, }; static int iot2040_rs485_config(struct uart_port *port, @@ -470,6 +474,10 @@ static int iot2040_rs485_config(struct uart_port *port, return generic_rs485_config(port, rs485); } +static const struct serial_rs485 iot2040_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS, +}; + static const struct property_entry iot2040_gpio_properties[] = { PROPERTY_ENTRY_U32("exar,first-pin", 10), PROPERTY_ENTRY_U32("ngpios", 1), @@ -498,6 +506,7 @@ static int iot2040_register_gpio(struct pci_dev *pcidev, static const struct exar8250_platform iot2040_platform = { .rs485_config = iot2040_rs485_config, + .rs485_supported = &iot2040_rs485_supported, .register_gpio = iot2040_register_gpio, .unregister_gpio = xr17v35x_unregister_gpio, }; @@ -540,6 +549,7 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, port->port.uartclk = baud * 16; port->port.rs485_config = platform->rs485_config; + port->port.rs485_supported = platform->rs485_supported; /* * Setup the UART clock for the devices on expansion slot to diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index dba5950b8d0e..1fb86c73786c 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -206,19 +206,7 @@ static int fintek_8250_rs485_config(struct uart_port *port, if (!(rs485->flags & SER_RS485_RTS_ON_SEND) == !(rs485->flags & SER_RS485_RTS_AFTER_SEND)) return -EINVAL; - memset(rs485->padding, 0, sizeof(rs485->padding)); config |= RS485_URA; - } else { - memset(rs485, 0, sizeof(*rs485)); - } - - rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | - SER_RS485_RTS_AFTER_SEND; - - /* Only the first port supports delays */ - if (pdata->index) { - rs485->delay_rts_before_send = 0; - rs485->delay_rts_after_send = 0; } if (rs485->delay_rts_before_send) { @@ -241,8 +229,6 @@ static int fintek_8250_rs485_config(struct uart_port *port, sio_write_reg(pdata, RS485, config); fintek_8250_exit_key(pdata->base_port); - port->rs485 = *rs485; - return 0; } @@ -424,6 +410,17 @@ static int probe_setup_port(struct fintek_8250 *pdata, return -ENODEV; } +/* Only the first port supports delays */ +static const struct serial_rs485 fintek_8250_rs485_supported_port0 = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + +static const struct serial_rs485 fintek_8250_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, +}; + static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) { struct fintek_8250 *pdata = uart->port.private_data; @@ -435,6 +432,10 @@ static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) case CHIP_ID_F81866: case CHIP_ID_F81865: uart->port.rs485_config = fintek_8250_rs485_config; + if (!pdata->index) + uart->port.rs485_supported = &fintek_8250_rs485_supported_port0; + else + uart->port.rs485_supported = &fintek_8250_rs485_supported; break; default: /* No RS485 Auto direction functional */ diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c index 570e25d6f37e..3a1cb51cbc91 100644 --- a/drivers/tty/serial/8250/8250_lpc18xx.c +++ b/drivers/tty/serial/8250/8250_lpc18xx.c @@ -40,14 +40,6 @@ static int lpc18xx_rs485_config(struct uart_port *port, u32 rs485_dly_reg = 0; unsigned baud_clk; - if (rs485->flags & SER_RS485_ENABLED) - memset(rs485->padding, 0, sizeof(rs485->padding)); - else - memset(rs485, 0, sizeof(*rs485)); - - rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | - SER_RS485_RTS_AFTER_SEND; - if (rs485->flags & SER_RS485_ENABLED) { rs485_ctrl_reg |= LPC18XX_UART_RS485CTRL_NMMEN | LPC18XX_UART_RS485CTRL_DCTRL; @@ -73,14 +65,9 @@ static int lpc18xx_rs485_config(struct uart_port *port, / baud_clk; } - /* Delay RTS before send not supported */ - rs485->delay_rts_before_send = 0; - serial_out(up, LPC18XX_UART_RS485CTRL, rs485_ctrl_reg); serial_out(up, LPC18XX_UART_RS485DLY, rs485_dly_reg); - port->rs485 = *rs485; - return 0; } @@ -98,6 +85,12 @@ static void lpc18xx_uart_serial_out(struct uart_port *p, int offset, int value) writel(value, p->membase + offset); } +static const struct serial_rs485 lpc18xx_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, + .delay_rts_after_send = 1, + /* Delay RTS before send is not supported */ +}; + static int lpc18xx_serial_probe(struct platform_device *pdev) { struct lpc18xx_uart_data *data; @@ -168,6 +161,7 @@ static int lpc18xx_serial_probe(struct platform_device *pdev) uart.port.uartclk = clk_get_rate(data->clk_uart); uart.port.private_data = data; uart.port.rs485_config = lpc18xx_rs485_config; + uart.port.rs485_supported = &lpc18xx_rs485_supported; uart.port.serial_out = lpc18xx_uart_serial_out; uart.dma = &data->dma; diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 5a699a1aa79c..65cccd559db2 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -165,6 +165,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev, port->dev = &ofdev->dev; port->rs485_config = serial8250_em485_config; + port->rs485_supported = &serial8250_em485_supported; up->rs485_start_tx = serial8250_em485_start_tx; up->rs485_stop_tx = serial8250_em485_stop_tx; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index a17619db7939..b6d71268aa7d 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1562,16 +1562,6 @@ static int pci_fintek_rs485_config(struct uart_port *port, pci_read_config_byte(pci_dev, 0x40 + 8 * *index + 7, &setting); - if (!rs485) - rs485 = &port->rs485; - else if (rs485->flags & SER_RS485_ENABLED) - memset(rs485->padding, 0, sizeof(rs485->padding)); - else - memset(rs485, 0, sizeof(*rs485)); - - /* F81504/508/512 not support RTS delay before or after send */ - rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND; - if (rs485->flags & SER_RS485_ENABLED) { /* Enable RTS H/W control mode */ setting |= FINTEK_RTS_CONTROL_BY_HW; @@ -1583,9 +1573,6 @@ static int pci_fintek_rs485_config(struct uart_port *port, /* RTS driving low on TX */ setting |= FINTEK_RTS_INVERT; } - - rs485->delay_rts_after_send = 0; - rs485->delay_rts_before_send = 0; } else { /* Disable RTS H/W control mode */ setting &= ~(FINTEK_RTS_CONTROL_BY_HW | FINTEK_RTS_INVERT); @@ -1593,12 +1580,14 @@ static int pci_fintek_rs485_config(struct uart_port *port, pci_write_config_byte(pci_dev, 0x40 + 8 * *index + 7, setting); - if (rs485 != &port->rs485) - port->rs485 = *rs485; - return 0; } +static const struct serial_rs485 pci_fintek_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND, + /* F81504/508/512 does not support RTS delay before or after send */ +}; + static int pci_fintek_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) @@ -1618,6 +1607,7 @@ static int pci_fintek_setup(struct serial_private *priv, port->port.iotype = UPIO_PORT; port->port.iobase = iobase; port->port.rs485_config = pci_fintek_rs485_config; + port->port.rs485_supported = &pci_fintek_rs485_supported; data = devm_kzalloc(&pdev->dev, sizeof(u8), GFP_KERNEL); if (!data) @@ -1689,7 +1679,7 @@ static int pci_fintek_init(struct pci_dev *dev) * pciserial_resume_ports() */ port = serial8250_get_port(priv->line[i]); - pci_fintek_rs485_config(&port->port, NULL); + uart_rs485_config(&port->port); } else { /* First init without port data * force init to RS232 Mode diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 8f32fe9e149e..3e3d784aa628 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -647,6 +647,14 @@ void serial8250_em485_destroy(struct uart_8250_port *p) } EXPORT_SYMBOL_GPL(serial8250_em485_destroy); +struct serial_rs485 serial8250_em485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND | + SER_RS485_TERMINATE_BUS | SER_RS485_RX_DURING_TX, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; +EXPORT_SYMBOL_GPL(serial8250_em485_supported); + /** * serial8250_em485_config() - generic ->rs485_config() callback * @port: uart port @@ -667,13 +675,6 @@ int serial8250_em485_config(struct uart_port *port, struct serial_rs485 *rs485) rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; } - /* clamp the delays to [0, 100ms] */ - rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U); - rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U); - - memset(rs485->padding, 0, sizeof(rs485->padding)); - port->rs485 = *rs485; - gpiod_set_value(port->rs485_term_gpio, rs485->flags & SER_RS485_TERMINATE_BUS); @@ -681,15 +682,8 @@ int serial8250_em485_config(struct uart_port *port, struct serial_rs485 *rs485) * Both serial8250_em485_init() and serial8250_em485_destroy() * are idempotent. */ - if (rs485->flags & SER_RS485_ENABLED) { - int ret = serial8250_em485_init(up); - - if (ret) { - rs485->flags &= ~SER_RS485_ENABLED; - port->rs485.flags &= ~SER_RS485_ENABLED; - } - return ret; - } + if (rs485->flags & SER_RS485_ENABLED) + return serial8250_em485_init(up); serial8250_em485_destroy(up); return 0; @@ -1503,18 +1497,12 @@ static void __stop_tx_rs485(struct uart_8250_port *p, u64 stop_delay) } } -static inline void __do_stop_tx(struct uart_8250_port *p) -{ - if (serial8250_clear_THRI(p)) - serial8250_rpm_put_tx(p); -} - static inline void __stop_tx(struct uart_8250_port *p) { struct uart_8250_em485 *em485 = p->em485; if (em485) { - unsigned char lsr = serial_in(p, UART_LSR); + unsigned char lsr = serial_lsr_in(p); u64 stop_delay = 0; p->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; @@ -1544,7 +1532,9 @@ static inline void __stop_tx(struct uart_8250_port *p) __stop_tx_rs485(p, stop_delay); } - __do_stop_tx(p); + + if (serial8250_clear_THRI(p)) + serial8250_rpm_put_tx(p); } static void serial8250_stop_tx(struct uart_port *port) @@ -1573,10 +1563,8 @@ static inline void __start_tx(struct uart_port *port) if (serial8250_set_THRI(up)) { if (up->bugs & UART_BUG_TXEN) { - unsigned char lsr; + unsigned char lsr = serial_lsr_in(up); - lsr = serial_in(up, UART_LSR); - up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; if (lsr & UART_LSR_THRE) serial8250_tx_chars(up); } @@ -1616,7 +1604,8 @@ void serial8250_em485_start_tx(struct uart_8250_port *up) } EXPORT_SYMBOL_GPL(serial8250_em485_start_tx); -static inline void start_tx_rs485(struct uart_port *port) +/* Returns false, if start_tx_timer was setup to defer TX start */ +static bool start_tx_rs485(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); struct uart_8250_em485 *em485 = up->em485; @@ -1644,11 +1633,11 @@ static inline void start_tx_rs485(struct uart_port *port) em485->active_timer = &em485->start_tx_timer; start_hrtimer_ms(&em485->start_tx_timer, up->port.rs485.delay_rts_before_send); - return; + return false; } } - __start_tx(port); + return true; } static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t) @@ -1678,14 +1667,12 @@ static void serial8250_start_tx(struct uart_port *port) serial8250_rpm_get_tx(up); - if (em485 && - em485->active_timer == &em485->start_tx_timer) - return; - - if (em485) - start_tx_rs485(port); - else - __start_tx(port); + if (em485) { + if ((em485->active_timer == &em485->start_tx_timer) || + !start_tx_rs485(port)) + return; + } + __start_tx(port); } static void serial8250_throttle(struct uart_port *port) @@ -1792,9 +1779,11 @@ void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr) EXPORT_SYMBOL_GPL(serial8250_read_char); /* - * serial8250_rx_chars: processes according to the passed in LSR - * value, and returns the remaining LSR bits not handled - * by this Rx routine. + * serial8250_rx_chars - Read characters. The first LSR value must be passed in. + * + * Returns LSR bits. The caller should rely only on non-Rx related LSR bits + * (such as THRE) because the LSR value might come from an already consumed + * character. */ unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) { @@ -1926,7 +1915,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) spin_lock_irqsave(&port->lock, flags); - status = serial_port_in(port, UART_LSR); + status = serial_lsr_in(up); /* * If port is stopped and there are no error conditions in the @@ -2007,8 +1996,7 @@ static unsigned int serial8250_tx_empty(struct uart_port *port) serial8250_rpm_get(up); spin_lock_irqsave(&port->lock, flags); - lsr = serial_port_in(port, UART_LSR); - up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + lsr = serial_lsr_in(up); spin_unlock_irqrestore(&port->lock, flags); serial8250_rpm_put(up); @@ -2084,9 +2072,7 @@ static void wait_for_lsr(struct uart_8250_port *up, int bits) /* Wait up to 10ms for the character(s) to be sent. */ for (;;) { - status = serial_in(up, UART_LSR); - - up->lsr_saved_flags |= status & LSR_SAVE_FLAGS; + status = serial_lsr_in(up); if ((status & bits) == bits) break; @@ -3201,7 +3187,7 @@ static void serial8250_config_port(struct uart_port *port, int flags) autoconfig(up); if (port->rs485.flags & SER_RS485_ENABLED) - port->rs485_config(port, &port->rs485); + uart_rs485_config(port); /* if access method is AU, it is a 16550 with a quirk */ if (port->type == PORT_16550A && port->iotype == UPIO_AU) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index a452748c69b2..8a3ee1525d80 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -324,6 +324,7 @@ config SERIAL_MAX310X depends on SPI_MASTER select SERIAL_CORE select REGMAP_SPI if SPI_MASTER + select REGMAP_I2C if I2C help This selects support for an advanced UART from Maxim (Dallas). Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830. diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 97ef41cb2721..eccd66625d25 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2681,17 +2681,12 @@ static int pl011_find_free_port(void) static int pl011_get_rs485_mode(struct uart_amba_port *uap) { struct uart_port *port = &uap->port; - struct serial_rs485 *rs485 = &port->rs485; int ret; ret = uart_get_rs485_mode(port); if (ret) return ret; - /* clamp the delays to [0, 100ms] */ - rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U); - rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U); - return 0; } @@ -2751,6 +2746,13 @@ static int pl011_register_port(struct uart_amba_port *uap) return ret; } +static const struct serial_rs485 pl011_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND | + SER_RS485_RX_DURING_TX, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + static int pl011_probe(struct amba_device *dev, const struct amba_id *id) { struct uart_amba_port *uap; @@ -2777,6 +2779,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->port.irq = dev->irq[0]; uap->port.ops = &amba_pl011_pops; uap->port.rs485_config = pl011_rs485_config; + uap->port.rs485_supported = &pl011_rs485_supported; snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev)); ret = pl011_setup_port(&dev->dev, uap, &dev->res, portnr); diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 6269dbf93546..ab2c5b2a1ce8 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -702,6 +702,11 @@ static struct uart_driver ar933x_uart_driver = { .cons = NULL, /* filled in runtime */ }; +static const struct serial_rs485 ar933x_no_rs485 = {}; +static const struct serial_rs485 ar933x_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, +}; + static int ar933x_uart_probe(struct platform_device *pdev) { struct ar933x_uart_port *up; @@ -773,6 +778,7 @@ static int ar933x_uart_probe(struct platform_device *pdev) port->fifosize = AR933X_UART_FIFO_SIZE; port->ops = &ar933x_uart_ops; port->rs485_config = ar933x_config_rs485; + port->rs485_supported = &ar933x_rs485_supported; baud = ar933x_uart_get_baud(port->uartclk, AR933X_UART_MAX_SCALE, 1); up->min_baud = max_t(unsigned int, baud, AR933X_UART_MIN_BAUD); @@ -796,6 +802,7 @@ static int ar933x_uart_probe(struct platform_device *pdev) !up->rts_gpiod) { dev_err(&pdev->dev, "lacking rts-gpio, disabling RS485\n"); port->rs485.flags &= ~SER_RS485_ENABLED; + port->rs485_supported = &ar933x_no_rs485; } #ifdef CONFIG_SERIAL_AR933X_CONSOLE diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index dd1c7e4bd1c9..74dd1d3ac46f 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2473,6 +2473,12 @@ static const struct uart_ops atmel_pops = { #endif }; +static const struct serial_rs485 atmel_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND | SER_RS485_RX_DURING_TX, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + /* * Configure the port from the platform device resource info. */ @@ -2494,6 +2500,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, port->mapbase = mpdev->resource[0].start; port->irq = platform_get_irq(mpdev, 0); port->rs485_config = atmel_config_rs485; + port->rs485_supported = &atmel_rs485_supported; port->iso7816_config = atmel_config_iso7816; port->membase = NULL; diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 0d6e62f6bb07..d35414cb3e4e 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1365,11 +1365,6 @@ static int lpuart_config_rs485(struct uart_port *port, ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE); writeb(modem, sport->port.membase + UARTMODEM); - /* clear unsupported configurations */ - rs485->delay_rts_before_send = 0; - rs485->delay_rts_after_send = 0; - rs485->flags &= ~SER_RS485_RX_DURING_TX; - if (rs485->flags & SER_RS485_ENABLED) { /* Enable auto RS-485 RTS mode */ modem |= UARTMODEM_TXRTSE; @@ -1400,11 +1395,6 @@ static int lpuart32_config_rs485(struct uart_port *port, & ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE); lpuart32_write(&sport->port, modem, UARTMODIR); - /* clear unsupported configurations */ - rs485->delay_rts_before_send = 0; - rs485->delay_rts_after_send = 0; - rs485->flags &= ~SER_RS485_RX_DURING_TX; - if (rs485->flags & SER_RS485_ENABLED) { /* Enable auto RS-485 RTS mode */ modem |= UARTMODEM_TXRTSE; @@ -2621,6 +2611,11 @@ static struct uart_driver lpuart_reg = { .cons = LPUART_CONSOLE, }; +static const struct serial_rs485 lpuart_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, + /* delay_rts_* and RX_DURING_TX are not supported */ +}; + static int lpuart_probe(struct platform_device *pdev) { const struct lpuart_soc_data *sdata = of_device_get_match_data(&pdev->dev); @@ -2660,6 +2655,7 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.rs485_config = lpuart32_config_rs485; else sport->port.rs485_config = lpuart_config_rs485; + sport->port.rs485_supported = &lpuart_rs485_supported; sport->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(sport->ipg_clk)) { @@ -2717,14 +2713,7 @@ static int lpuart_probe(struct platform_device *pdev) if (ret) goto failed_get_rs485; - if (sport->port.rs485.flags & SER_RS485_RX_DURING_TX) - dev_err(&pdev->dev, "driver doesn't support RX during TX\n"); - - if (sport->port.rs485.delay_rts_before_send || - sport->port.rs485.delay_rts_after_send) - dev_err(&pdev->dev, "driver doesn't support RTS delays\n"); - - sport->port.rs485_config(&sport->port, &sport->port.rs485); + uart_rs485_config(&sport->port); ret = devm_request_irq(&pdev->dev, sport->port.irq, handler, 0, DRIVER_NAME, sport); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 30edb35a6a15..f4edde54175f 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1913,10 +1913,6 @@ static int imx_uart_rs485_config(struct uart_port *port, struct imx_port *sport = (struct imx_port *)port; u32 ucr2; - /* RTS is required to control the transmitter */ - if (!sport->have_rtscts && !sport->have_rtsgpio) - 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 && @@ -2200,6 +2196,14 @@ static enum hrtimer_restart imx_trigger_stop_tx(struct hrtimer *t) return HRTIMER_NORESTART; } +static const struct serial_rs485 imx_no_rs485 = {}; /* No RS485 if no RTS */ +static const struct serial_rs485 imx_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND | + SER_RS485_RX_DURING_TX, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + /* Default RX DMA buffer configuration */ #define RX_DMA_PERIODS 16 #define RX_DMA_PERIOD_LEN (PAGE_SIZE / 4) @@ -2279,6 +2283,11 @@ static int imx_uart_probe(struct platform_device *pdev) sport->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_IMX_CONSOLE); sport->port.ops = &imx_uart_pops; sport->port.rs485_config = imx_uart_rs485_config; + /* RTS is required to control the RS485 transmitter */ + if (sport->have_rtscts || sport->have_rtsgpio) + sport->port.rs485_supported = &imx_rs485_supported; + else + sport->port.rs485_supported = &imx_no_rs485; sport->port.flags = UPF_BOOT_AUTOCONF; timer_setup(&sport->timer, imx_uart_timeout, 0); @@ -2338,7 +2347,7 @@ static int imx_uart_probe(struct platform_device *pdev) 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); + uart_rs485_config(&sport->port); /* Disable interrupts before requesting them */ ucr1 = imx_uart_readl(sport, UCR1); diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index a0b6ea52d133..4915a786e315 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -14,6 +14,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/gpio/driver.h> +#include <linux/i2c.h> #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/property.h> @@ -72,7 +73,8 @@ #define MAX310X_GLOBALCMD_REG MAX310X_REG_1F /* Global Command (WO) */ /* Extended registers */ -#define MAX310X_REVID_EXTREG MAX310X_REG_05 /* Revision ID */ +#define MAX310X_SPI_REVID_EXTREG MAX310X_REG_05 /* Revision ID */ +#define MAX310X_I2C_REVID_EXTREG (0x25) /* Revision ID */ /* IRQ register bits */ #define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ @@ -245,7 +247,17 @@ #define MAX14830_BRGCFG_CLKDIS_BIT (1 << 6) /* Clock Disable */ #define MAX14830_REV_ID (0xb0) +struct max310x_if_cfg { + int (*extended_reg_enable)(struct device *dev, bool enable); + + unsigned int rev_id_reg; +}; + struct max310x_devtype { + struct { + unsigned short min; + unsigned short max; + } slave_addr; char name[9]; int nr; u8 mode1; @@ -258,9 +270,8 @@ struct max310x_one { struct work_struct tx_work; struct work_struct md_work; struct work_struct rs_work; + struct regmap *regmap; - u8 wr_header; - u8 rd_header; u8 rx_buf[MAX310X_FIFO_SIZE]; }; #define to_max310x_port(_port) \ @@ -268,6 +279,7 @@ struct max310x_one { struct max310x_port { const struct max310x_devtype *devtype; + const struct max310x_if_cfg *if_cfg; struct regmap *regmap; struct clk *clk; #ifdef CONFIG_GPIOLIB @@ -289,26 +301,26 @@ static DECLARE_BITMAP(max310x_lines, MAX310X_UART_NRMAX); static u8 max310x_port_read(struct uart_port *port, u8 reg) { - struct max310x_port *s = dev_get_drvdata(port->dev); + struct max310x_one *one = to_max310x_port(port); unsigned int val = 0; - regmap_read(s->regmap, port->iobase + reg, &val); + regmap_read(one->regmap, reg, &val); return val; } static void max310x_port_write(struct uart_port *port, u8 reg, u8 val) { - struct max310x_port *s = dev_get_drvdata(port->dev); + struct max310x_one *one = to_max310x_port(port); - regmap_write(s->regmap, port->iobase + reg, val); + regmap_write(one->regmap, reg, val); } static void max310x_port_update(struct uart_port *port, u8 reg, u8 mask, u8 val) { - struct max310x_port *s = dev_get_drvdata(port->dev); + struct max310x_one *one = to_max310x_port(port); - regmap_update_bits(s->regmap, port->iobase + reg, mask, val); + regmap_update_bits(one->regmap, reg, mask, val); } static int max3107_detect(struct device *dev) @@ -357,13 +369,12 @@ static int max3109_detect(struct device *dev) unsigned int val = 0; int ret; - ret = regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, - MAX310X_EXTREG_ENBL); + ret = s->if_cfg->extended_reg_enable(dev, true); if (ret) return ret; - regmap_read(s->regmap, MAX310X_REVID_EXTREG, &val); - regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, MAX310X_EXTREG_DSBL); + regmap_read(s->regmap, s->if_cfg->rev_id_reg, &val); + s->if_cfg->extended_reg_enable(dev, false); if (((val & MAX310x_REV_MASK) != MAX3109_REV_ID)) { dev_err(dev, "%s ID 0x%02x does not match\n", s->devtype->name, val); @@ -388,13 +399,12 @@ static int max14830_detect(struct device *dev) unsigned int val = 0; int ret; - ret = regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, - MAX310X_EXTREG_ENBL); + ret = s->if_cfg->extended_reg_enable(dev, true); if (ret) return ret; - regmap_read(s->regmap, MAX310X_REVID_EXTREG, &val); - regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, MAX310X_EXTREG_DSBL); + regmap_read(s->regmap, s->if_cfg->rev_id_reg, &val); + s->if_cfg->extended_reg_enable(dev, false); if (((val & MAX310x_REV_MASK) != MAX14830_REV_ID)) { dev_err(dev, "%s ID 0x%02x does not match\n", s->devtype->name, val); @@ -419,6 +429,10 @@ static const struct max310x_devtype max3107_devtype = { .mode1 = MAX310X_MODE1_AUTOSLEEP_BIT | MAX310X_MODE1_IRQSEL_BIT, .detect = max3107_detect, .power = max310x_power, + .slave_addr = { + .min = 0x2c, + .max = 0x2f, + }, }; static const struct max310x_devtype max3108_devtype = { @@ -427,6 +441,10 @@ static const struct max310x_devtype max3108_devtype = { .mode1 = MAX310X_MODE1_AUTOSLEEP_BIT, .detect = max3108_detect, .power = max310x_power, + .slave_addr = { + .min = 0x60, + .max = 0x6f, + }, }; static const struct max310x_devtype max3109_devtype = { @@ -435,6 +453,10 @@ static const struct max310x_devtype max3109_devtype = { .mode1 = MAX310X_MODE1_AUTOSLEEP_BIT, .detect = max3109_detect, .power = max310x_power, + .slave_addr = { + .min = 0x60, + .max = 0x6f, + }, }; static const struct max310x_devtype max14830_devtype = { @@ -443,11 +465,15 @@ static const struct max310x_devtype max14830_devtype = { .mode1 = MAX310X_MODE1_IRQSEL_BIT, .detect = max14830_detect, .power = max14830_power, + .slave_addr = { + .min = 0x60, + .max = 0x6f, + }, }; static bool max310x_reg_writeable(struct device *dev, unsigned int reg) { - switch (reg & 0x1f) { + switch (reg) { case MAX310X_IRQSTS_REG: case MAX310X_LSR_IRQSTS_REG: case MAX310X_SPCHR_IRQSTS_REG: @@ -464,7 +490,7 @@ static bool max310x_reg_writeable(struct device *dev, unsigned int reg) static bool max310x_reg_volatile(struct device *dev, unsigned int reg) { - switch (reg & 0x1f) { + switch (reg) { case MAX310X_RHR_REG: case MAX310X_IRQSTS_REG: case MAX310X_LSR_IRQSTS_REG: @@ -486,7 +512,7 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg) static bool max310x_reg_precious(struct device *dev, unsigned int reg) { - switch (reg & 0x1f) { + switch (reg) { case MAX310X_RHR_REG: case MAX310X_IRQSTS_REG: case MAX310X_SPCHR_IRQSTS_REG: @@ -624,31 +650,15 @@ static u32 max310x_set_ref_clk(struct device *dev, struct max310x_port *s, static void max310x_batch_write(struct uart_port *port, u8 *txbuf, unsigned int len) { struct max310x_one *one = to_max310x_port(port); - struct spi_transfer xfer[] = { - { - .tx_buf = &one->wr_header, - .len = sizeof(one->wr_header), - }, { - .tx_buf = txbuf, - .len = len, - } - }; - spi_sync_transfer(to_spi_device(port->dev), xfer, ARRAY_SIZE(xfer)); + + regmap_raw_write(one->regmap, MAX310X_THR_REG, txbuf, len); } static void max310x_batch_read(struct uart_port *port, u8 *rxbuf, unsigned int len) { struct max310x_one *one = to_max310x_port(port); - struct spi_transfer xfer[] = { - { - .tx_buf = &one->rd_header, - .len = sizeof(one->rd_header), - }, { - .rx_buf = rxbuf, - .len = len, - } - }; - spi_sync_transfer(to_spi_device(port->dev), xfer, ARRAY_SIZE(xfer)); + + regmap_raw_read(one->regmap, MAX310X_RHR_REG, rxbuf, len); } static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) @@ -1035,8 +1045,6 @@ static int max310x_rs485_config(struct uart_port *port, (rs485->delay_rts_after_send > 0x0f)) return -ERANGE; - rs485->flags &= SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX | - SER_RS485_ENABLED; port->rs485 = *rs485; schedule_work(&one->rs_work); @@ -1249,16 +1257,24 @@ static int max310x_gpio_set_config(struct gpio_chip *chip, unsigned int offset, } #endif +static const struct serial_rs485 max310x_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + static int max310x_probe(struct device *dev, const struct max310x_devtype *devtype, - struct regmap *regmap, int irq) + const struct max310x_if_cfg *if_cfg, + struct regmap *regmaps[], int irq) { int i, ret, fmin, fmax, freq; struct max310x_port *s; u32 uartclk = 0; bool xtal; - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + for (i = 0; i < devtype->nr; i++) + if (IS_ERR(regmaps[i])) + return PTR_ERR(regmaps[i]); /* Alloc port structure */ s = devm_kzalloc(dev, struct_size(s, p, devtype->nr), GFP_KERNEL); @@ -1305,8 +1321,9 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty goto out_clk; } - s->regmap = regmap; + s->regmap = regmaps[0]; s->devtype = devtype; + s->if_cfg = if_cfg; dev_set_drvdata(dev, s); /* Check device to ensure we are talking to what we expect */ @@ -1315,22 +1332,18 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty goto out_clk; for (i = 0; i < devtype->nr; i++) { - unsigned int offs = i << 5; - /* Reset port */ - regmap_write(s->regmap, MAX310X_MODE2_REG + offs, + regmap_write(regmaps[i], MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT); /* Clear port reset */ - regmap_write(s->regmap, MAX310X_MODE2_REG + offs, 0); + regmap_write(regmaps[i], MAX310X_MODE2_REG, 0); /* Wait for port startup */ do { - regmap_read(s->regmap, - MAX310X_BRGDIVLSB_REG + offs, &ret); + regmap_read(regmaps[i], MAX310X_BRGDIVLSB_REG, &ret); } while (ret != 0x01); - regmap_write(s->regmap, MAX310X_MODE1_REG + offs, - devtype->mode1); + regmap_write(regmaps[i], MAX310X_MODE1_REG, devtype->mode1); } uartclk = max310x_set_ref_clk(dev, s, freq, xtal); @@ -1353,11 +1366,14 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty s->p[i].port.fifosize = MAX310X_FIFO_SIZE; s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; s->p[i].port.iotype = UPIO_PORT; - s->p[i].port.iobase = i * 0x20; + s->p[i].port.iobase = i; s->p[i].port.membase = (void __iomem *)~0; s->p[i].port.uartclk = uartclk; s->p[i].port.rs485_config = max310x_rs485_config; + s->p[i].port.rs485_supported = &max310x_rs485_supported; s->p[i].port.ops = &max310x_ops; + s->p[i].regmap = regmaps[i]; + /* Disable all interrupts */ max310x_port_write(&s->p[i].port, MAX310X_IRQEN_REG, 0); /* Clear IRQ status register */ @@ -1368,10 +1384,6 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty INIT_WORK(&s->p[i].md_work, max310x_md_proc); /* Initialize queue for changing RS485 mode */ INIT_WORK(&s->p[i].rs_work, max310x_rs_proc); - /* Initialize SPI-transfer buffers */ - s->p[i].wr_header = (s->p[i].port.iobase + MAX310X_THR_REG) | - MAX310X_WRITE_BIT; - s->p[i].rd_header = (s->p[i].port.iobase + MAX310X_RHR_REG); /* Register port */ ret = uart_add_one_port(&max310x_uart, &s->p[i].port); @@ -1456,16 +1468,31 @@ static struct regmap_config regcfg = { .val_bits = 8, .write_flag_mask = MAX310X_WRITE_BIT, .cache_type = REGCACHE_RBTREE, + .max_register = MAX310X_REG_1F, .writeable_reg = max310x_reg_writeable, .volatile_reg = max310x_reg_volatile, .precious_reg = max310x_reg_precious, }; #ifdef CONFIG_SPI_MASTER +static int max310x_spi_extended_reg_enable(struct device *dev, bool enable) +{ + struct max310x_port *s = dev_get_drvdata(dev); + + return regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, + enable ? MAX310X_EXTREG_ENBL : MAX310X_EXTREG_DSBL); +} + +static const struct max310x_if_cfg __maybe_unused max310x_spi_if_cfg = { + .extended_reg_enable = max310x_spi_extended_reg_enable, + .rev_id_reg = MAX310X_SPI_REVID_EXTREG, +}; + static int max310x_spi_probe(struct spi_device *spi) { const struct max310x_devtype *devtype; - struct regmap *regmap; + struct regmap *regmaps[4]; + unsigned int i; int ret; /* Setup SPI bus */ @@ -1480,10 +1507,14 @@ static int max310x_spi_probe(struct spi_device *spi) if (!devtype) devtype = (struct max310x_devtype *)spi_get_device_id(spi)->driver_data; - regcfg.max_register = devtype->nr * 0x20 - 1; - regmap = devm_regmap_init_spi(spi, ®cfg); + for (i = 0; i < devtype->nr; i++) { + u8 port_mask = i * 0x20; + regcfg.read_flag_mask = port_mask; + regcfg.write_flag_mask = port_mask | MAX310X_WRITE_BIT; + regmaps[i] = devm_regmap_init_spi(spi, ®cfg); + } - return max310x_probe(&spi->dev, devtype, regmap, spi->irq); + return max310x_probe(&spi->dev, devtype, &max310x_spi_if_cfg, regmaps, spi->irq); } static void max310x_spi_remove(struct spi_device *spi) @@ -1512,6 +1543,97 @@ static struct spi_driver max310x_spi_driver = { }; #endif +#ifdef CONFIG_I2C +static int max310x_i2c_extended_reg_enable(struct device *dev, bool enable) +{ + return 0; +} + +static struct regmap_config regcfg_i2c = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .writeable_reg = max310x_reg_writeable, + .volatile_reg = max310x_reg_volatile, + .precious_reg = max310x_reg_precious, + .max_register = MAX310X_I2C_REVID_EXTREG, +}; + +static const struct max310x_if_cfg max310x_i2c_if_cfg = { + .extended_reg_enable = max310x_i2c_extended_reg_enable, + .rev_id_reg = MAX310X_I2C_REVID_EXTREG, +}; + +static unsigned short max310x_i2c_slave_addr(unsigned short addr, + unsigned int nr) +{ + /* + * For MAX14830 and MAX3109, the slave address depends on what the + * A0 and A1 pins are tied to. + * See Table I2C Address Map of the datasheet. + * Based on that table, the following formulas were determined. + * UART1 - UART0 = 0x10 + * UART2 - UART1 = 0x20 + 0x10 + * UART3 - UART2 = 0x10 + */ + + addr -= nr * 0x10; + + if (nr >= 2) + addr -= 0x20; + + return addr; +} + +static int max310x_i2c_probe(struct i2c_client *client) +{ + const struct max310x_devtype *devtype = + device_get_match_data(&client->dev); + struct i2c_client *port_client; + struct regmap *regmaps[4]; + unsigned int i; + u8 port_addr; + + if (client->addr < devtype->slave_addr.min || + client->addr > devtype->slave_addr.max) + return dev_err_probe(&client->dev, -EINVAL, + "Slave addr 0x%x outside of range [0x%x, 0x%x]\n", + client->addr, devtype->slave_addr.min, + devtype->slave_addr.max); + + regmaps[0] = devm_regmap_init_i2c(client, ®cfg_i2c); + + for (i = 1; i < devtype->nr; i++) { + port_addr = max310x_i2c_slave_addr(client->addr, i); + port_client = devm_i2c_new_dummy_device(&client->dev, + client->adapter, + port_addr); + + regmaps[i] = devm_regmap_init_i2c(port_client, ®cfg_i2c); + } + + return max310x_probe(&client->dev, devtype, &max310x_i2c_if_cfg, + regmaps, client->irq); +} + +static int max310x_i2c_remove(struct i2c_client *client) +{ + max310x_remove(&client->dev); + + return 0; +} + +static struct i2c_driver max310x_i2c_driver = { + .driver = { + .name = MAX310X_NAME, + .of_match_table = max310x_dt_ids, + .pm = &max310x_pm_ops, + }, + .probe_new = max310x_i2c_probe, + .remove = max310x_i2c_remove, +}; +#endif + static int __init max310x_uart_init(void) { int ret; @@ -1525,15 +1647,35 @@ static int __init max310x_uart_init(void) #ifdef CONFIG_SPI_MASTER ret = spi_register_driver(&max310x_spi_driver); if (ret) - uart_unregister_driver(&max310x_uart); + goto err_spi_register; +#endif + +#ifdef CONFIG_I2C + ret = i2c_add_driver(&max310x_i2c_driver); + if (ret) + goto err_i2c_register; +#endif + + return 0; + +#ifdef CONFIG_I2C +err_i2c_register: + spi_unregister_driver(&max310x_spi_driver); #endif +err_spi_register: + uart_unregister_driver(&max310x_uart); + return ret; } module_init(max310x_uart_init); static void __exit max310x_uart_exit(void) { +#ifdef CONFIG_I2C + i2c_del_driver(&max310x_i2c_driver); +#endif + #ifdef CONFIG_SPI_MASTER spi_unregister_driver(&max310x_spi_driver); #endif diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index 2aec62b5d6c4..036f178e3d66 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -448,11 +448,14 @@ static int mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) } writeb(mr1, port->membase + MCFUART_UMR); writeb(mr2, port->membase + MCFUART_UMR); - port->rs485 = *rs485; return 0; } +static const struct serial_rs485 mcf_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND, +}; + /****************************************************************************/ /* @@ -502,6 +505,7 @@ int __init early_mcf_setup(struct mcf_platform_uart *platp) port->uartclk = MCF_BUSCLK; port->flags = UPF_BOOT_AUTOCONF; port->rs485_config = mcf_config_rs485; + port->rs485_supported = &mcf_rs485_supported; port->ops = &mcf_uart_ops; } @@ -629,6 +633,7 @@ static int mcf_probe(struct platform_device *pdev) port->ops = &mcf_uart_ops; port->flags = UPF_BOOT_AUTOCONF; port->rs485_config = mcf_config_rs485; + port->rs485_supported = &mcf_rs485_supported; port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MCF_CONSOLE); uart_add_one_port(&mcf_driver, port); diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 46f4d4cacb6e..98622c35d896 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1559,6 +1559,13 @@ static int serial_omap_probe_rs485(struct uart_omap_port *up, return 0; } +static const struct serial_rs485 serial_omap_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND | + SER_RS485_RX_DURING_TX, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + static int serial_omap_probe(struct platform_device *pdev) { struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev); @@ -1636,6 +1643,7 @@ static int serial_omap_probe(struct platform_device *pdev) up->port.flags = omap_up_info->flags; up->port.uartclk = omap_up_info->uartclk; up->port.rs485_config = serial_omap_config_rs485; + up->port.rs485_supported = &serial_omap_rs485_supported; if (!up->port.uartclk) { up->port.uartclk = DEFAULT_CLK_SPEED; dev_warn(&pdev->dev, diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index b399aac530fe..f418f1de66b3 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -503,7 +503,7 @@ static int pic32_uart_startup(struct uart_port *port) if (!sport->irq_fault_name) { dev_err(port->dev, "%s: kasprintf err!", __func__); ret = -ENOMEM; - goto out_done; + goto out_disable_clk; } irq_set_status_flags(sport->irq_fault, IRQ_NOAUTOEN); ret = request_irq(sport->irq_fault, pic32_uart_fault_interrupt, @@ -579,6 +579,8 @@ out_r: out_f: free_irq(sport->irq_fault, port); kfree(sport->irq_fault_name); +out_disable_clk: + clk_disable_unprepare(sport->clk); out_done: return ret; } diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 3133446e806c..f63257b8e872 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -52,7 +52,6 @@ #ifdef CONFIG_PPC_PMAC #include <asm/machdep.h> #include <asm/pmac_feature.h> -#include <asm/dbdma.h> #include <asm/macio.h> #else #include <linux/platform_device.h> diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 8472bf70477c..2ceecaa4a478 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1143,7 +1143,6 @@ static int sc16is7xx_config_rs485(struct uart_port *port, return -EINVAL; } - port->rs485 = *rs485; one->config.flags |= SC16IS7XX_RECONF_RS485; kthread_queue_work(&s->kworker, &one->reg_work); @@ -1354,6 +1353,12 @@ static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip, } #endif +static const struct serial_rs485 sc16is7xx_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, /* Not supported but keep returning -EINVAL */ +}; + static int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, struct regmap *regmap, int irq) @@ -1456,6 +1461,7 @@ static int sc16is7xx_probe(struct device *dev, s->p[i].port.iotype = UPIO_PORT; s->p[i].port.uartclk = freq; s->p[i].port.rs485_config = sc16is7xx_config_rs485; + s->p[i].port.rs485_supported = &sc16is7xx_rs485_supported; s->p[i].port.ops = &sc16is7xx_ops; s->p[i].old_mctrl = 0; s->p[i].port.line = sc16is7xx_alloc_line(); diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index d942ab152f5a..101fb585e6f9 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -441,7 +441,7 @@ static char tegra_uart_decode_rx_error(struct tegra_uart_port *tup, if (unlikely(lsr & TEGRA_UART_LSR_ANY)) { if (lsr & UART_LSR_OE) { - /* Overrrun error */ + /* Overrun error */ flag = TTY_OVERRUN; tup->uport.icount.overrun++; dev_dbg(tup->uport.dev, "Got overrun errors\n"); @@ -1080,7 +1080,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) tup->rx_in_progress = 1; /* - * Enable IE_RXS for the receive status interrupts like line errros. + * Enable IE_RXS for the receive status interrupts like line errors. * Enable IE_RX_TIMEOUT to get the bytes which cannot be DMA'd. * * EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 338ebadfd44b..1368b0ef7d7f 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1276,6 +1276,97 @@ static int uart_get_icount(struct tty_struct *tty, return 0; } +#define SER_RS485_LEGACY_FLAGS (SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | \ + SER_RS485_RTS_AFTER_SEND | SER_RS485_RX_DURING_TX | \ + SER_RS485_TERMINATE_BUS) + +static int uart_check_rs485_flags(struct uart_port *port, struct serial_rs485 *rs485) +{ + u32 flags = rs485->flags; + + /* Don't return -EINVAL for unsupported legacy flags */ + flags &= ~SER_RS485_LEGACY_FLAGS; + + /* + * For any bit outside of the legacy ones that is not supported by + * the driver, return -EINVAL. + */ + if (flags & ~port->rs485_supported->flags) + return -EINVAL; + + return 0; +} + +static void uart_sanitize_serial_rs485(struct uart_port *port, struct serial_rs485 *rs485) +{ + u32 supported_flags = port->rs485_supported->flags; + + if (!(rs485->flags & SER_RS485_ENABLED)) { + memset(rs485, 0, sizeof(*rs485)); + return; + } + + /* pick sane settings if the user hasn't */ + if ((supported_flags & (SER_RS485_RTS_ON_SEND|SER_RS485_RTS_AFTER_SEND)) && + !(rs485->flags & SER_RS485_RTS_ON_SEND) == + !(rs485->flags & SER_RS485_RTS_AFTER_SEND)) { + dev_warn_ratelimited(port->dev, + "%s (%d): invalid RTS setting, using RTS_ON_SEND instead\n", + port->name, port->line); + rs485->flags |= SER_RS485_RTS_ON_SEND; + rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; + supported_flags |= SER_RS485_RTS_ON_SEND|SER_RS485_RTS_AFTER_SEND; + } + + if (!port->rs485_supported->delay_rts_before_send) { + if (rs485->delay_rts_before_send) { + dev_warn_ratelimited(port->dev, + "%s (%d): RTS delay before sending not supported\n", + port->name, port->line); + } + rs485->delay_rts_before_send = 0; + } else if (rs485->delay_rts_before_send > RS485_MAX_RTS_DELAY) { + rs485->delay_rts_before_send = RS485_MAX_RTS_DELAY; + dev_warn_ratelimited(port->dev, + "%s (%d): RTS delay before sending clamped to %u ms\n", + port->name, port->line, rs485->delay_rts_before_send); + } + + if (!port->rs485_supported->delay_rts_after_send) { + if (rs485->delay_rts_after_send) { + dev_warn_ratelimited(port->dev, + "%s (%d): RTS delay after sending not supported\n", + port->name, port->line); + } + rs485->delay_rts_after_send = 0; + } else if (rs485->delay_rts_after_send > RS485_MAX_RTS_DELAY) { + rs485->delay_rts_after_send = RS485_MAX_RTS_DELAY; + dev_warn_ratelimited(port->dev, + "%s (%d): RTS delay after sending clamped to %u ms\n", + port->name, port->line, rs485->delay_rts_after_send); + } + + rs485->flags &= supported_flags; + + /* Return clean padding area to userspace */ + memset(rs485->padding, 0, sizeof(rs485->padding)); +} + +int uart_rs485_config(struct uart_port *port) +{ + struct serial_rs485 *rs485 = &port->rs485; + int ret; + + uart_sanitize_serial_rs485(port, rs485); + + ret = port->rs485_config(port, rs485); + if (ret) + memset(rs485, 0, sizeof(*rs485)); + + return ret; +} +EXPORT_SYMBOL_GPL(uart_rs485_config); + static int uart_get_rs485_config(struct uart_port *port, struct serial_rs485 __user *rs485) { @@ -1305,31 +1396,10 @@ static int uart_set_rs485_config(struct uart_port *port, if (copy_from_user(&rs485, rs485_user, sizeof(*rs485_user))) return -EFAULT; - /* pick sane settings if the user hasn't */ - if (!(rs485.flags & SER_RS485_RTS_ON_SEND) == - !(rs485.flags & SER_RS485_RTS_AFTER_SEND)) { - dev_warn_ratelimited(port->dev, - "%s (%d): invalid RTS setting, using RTS_ON_SEND instead\n", - port->name, port->line); - rs485.flags |= SER_RS485_RTS_ON_SEND; - rs485.flags &= ~SER_RS485_RTS_AFTER_SEND; - } - - if (rs485.delay_rts_before_send > RS485_MAX_RTS_DELAY) { - rs485.delay_rts_before_send = RS485_MAX_RTS_DELAY; - dev_warn_ratelimited(port->dev, - "%s (%d): RTS delay before sending clamped to %u ms\n", - port->name, port->line, rs485.delay_rts_before_send); - } - - if (rs485.delay_rts_after_send > RS485_MAX_RTS_DELAY) { - rs485.delay_rts_after_send = RS485_MAX_RTS_DELAY; - dev_warn_ratelimited(port->dev, - "%s (%d): RTS delay after sending clamped to %u ms\n", - port->name, port->line, rs485.delay_rts_after_send); - } - /* Return clean padding area to userspace */ - memset(rs485.padding, 0, sizeof(rs485.padding)); + ret = uart_check_rs485_flags(port, &rs485); + if (ret) + return ret; + uart_sanitize_serial_rs485(port, &rs485); spin_lock_irqsave(&port->lock, flags); ret = port->rs485_config(port, &rs485); diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index b7b44f4050d4..db3dd9731ee1 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -1375,6 +1375,13 @@ static void stm32_usart_deinit_port(struct stm32_port *stm32port) clk_disable_unprepare(stm32port->clk); } +static const struct serial_rs485 stm32_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND | + SER_RS485_RX_DURING_TX, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + static int stm32_usart_init_port(struct stm32_port *stm32port, struct platform_device *pdev) { @@ -1394,6 +1401,7 @@ static int stm32_usart_init_port(struct stm32_port *stm32port, port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_STM32_CONSOLE); port->irq = irq; port->rs485_config = stm32_usart_config_rs485; + port->rs485_supported = &stm32_rs485_supported; ret = stm32_usart_init_rs485(port, pdev); if (ret) diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index bfa431a8e690..754fa43670cc 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -5,6 +5,7 @@ #include <linux/types.h> #include <linux/errno.h> +#include <linux/minmax.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> @@ -104,6 +105,7 @@ static void tty_buffer_reset(struct tty_buffer *p, size_t size) p->size = size; p->next = NULL; p->commit = 0; + p->lookahead = 0; p->read = 0; p->flags = 0; } @@ -234,6 +236,7 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld) buf->head = next; } buf->head->read = buf->head->commit; + buf->head->lookahead = buf->head->read; if (ld && ld->ops->flush_buffer) ld->ops->flush_buffer(tty); @@ -276,13 +279,15 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size, if (n != NULL) { n->flags = flags; buf->tail = n; - /* paired w/ acquire in flush_to_ldisc(); ensures - * flush_to_ldisc() sees buffer data. + /* + * Paired w/ acquire in flush_to_ldisc() and lookahead_bufs() + * ensures they see all buffer data. */ smp_store_release(&b->commit, b->used); - /* paired w/ acquire in flush_to_ldisc(); ensures the - * latest commit value can be read before the head is - * advanced to the next buffer + /* + * Paired w/ acquire in flush_to_ldisc() and lookahead_bufs() + * ensures the latest commit value can be read before the head + * is advanced to the next buffer. */ smp_store_release(&b->next, n); } else if (change) @@ -459,6 +464,40 @@ int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p, } EXPORT_SYMBOL_GPL(tty_ldisc_receive_buf); +static void lookahead_bufs(struct tty_port *port, struct tty_buffer *head) +{ + head->lookahead = max(head->lookahead, head->read); + + while (head) { + struct tty_buffer *next; + unsigned char *p, *f = NULL; + unsigned int count; + + /* + * Paired w/ release in __tty_buffer_request_room(); + * ensures commit value read is not stale if the head + * is advancing to the next buffer. + */ + next = smp_load_acquire(&head->next); + /* + * Paired w/ release in __tty_buffer_request_room() or in + * tty_buffer_flush(); ensures we see the committed buffer data. + */ + count = smp_load_acquire(&head->commit) - head->lookahead; + if (!count) { + head = next; + continue; + } + + p = char_buf_ptr(head, head->lookahead); + if (~head->flags & TTYB_NORMAL) + f = flag_buf_ptr(head, head->lookahead); + + port->client_ops->lookahead_buf(port, p, f, count); + head->lookahead += count; + } +} + static int receive_buf(struct tty_port *port, struct tty_buffer *head, int count) { @@ -496,7 +535,7 @@ static void flush_to_ldisc(struct work_struct *work) while (1) { struct tty_buffer *head = buf->head; struct tty_buffer *next; - int count; + int count, rcvd; /* Ldisc or user is trying to gain exclusive access */ if (atomic_read(&buf->priority)) @@ -519,10 +558,12 @@ static void flush_to_ldisc(struct work_struct *work) continue; } - count = receive_buf(port, head, count); - if (!count) + rcvd = receive_buf(port, head, count); + head->read += rcvd; + if (rcvd < count) + lookahead_bufs(port, head); + if (!rcvd) break; - head->read += count; if (need_resched()) cond_resched(); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 8fec1d8648f5..82a8855981f7 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1663,7 +1663,7 @@ void tty_kclose(struct tty_struct *tty) */ tty_ldisc_release(tty); - /* Wait for pending work before tty destruction commmences */ + /* Wait for pending work before tty destruction commences */ tty_flush_works(tty); tty_debug_hangup(tty, "freeing structure\n"); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 880608a65773..dce08a6d7b5e 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -43,6 +43,26 @@ static int tty_port_default_receive_buf(struct tty_port *port, return ret; } +static void tty_port_default_lookahead_buf(struct tty_port *port, const unsigned char *p, + const unsigned char *f, unsigned int count) +{ + struct tty_struct *tty; + struct tty_ldisc *disc; + + tty = READ_ONCE(port->itty); + if (!tty) + return; + + disc = tty_ldisc_ref(tty); + if (!disc) + return; + + if (disc->ops->lookahead_buf) + disc->ops->lookahead_buf(disc->tty, p, f, count); + + tty_ldisc_deref(disc); +} + static void tty_port_default_wakeup(struct tty_port *port) { struct tty_struct *tty = tty_port_tty_get(port); @@ -55,6 +75,7 @@ static void tty_port_default_wakeup(struct tty_port *port) const struct tty_port_client_operations tty_port_default_client_ops = { .receive_buf = tty_port_default_receive_buf, + .lookahead_buf = tty_port_default_lookahead_buf, .write_wakeup = tty_port_default_wakeup, }; EXPORT_SYMBOL_GPL(tty_port_default_client_ops); diff --git a/drivers/tty/vt/Makefile b/drivers/tty/vt/Makefile index fe30ce512819..b3dfe9d5717e 100644 --- a/drivers/tty/vt/Makefile +++ b/drivers/tty/vt/Makefile @@ -30,6 +30,6 @@ $(obj)/defkeymap.o: $(obj)/defkeymap.c ifdef GENERATE_KEYMAP $(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map - loadkeys --mktable $< > $@ + loadkeys --mktable --unicode $< > $@ endif diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index d815ac98b39e..fff97ae87e00 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -38,7 +38,7 @@ static unsigned short translations[][256] = { /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ - { + [LAT1_MAP] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, @@ -71,9 +71,9 @@ static unsigned short translations[][256] = { 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff - }, + }, /* VT100 graphics mapped to Unicode */ - { + [GRAF_MAP] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, @@ -108,8 +108,8 @@ static unsigned short translations[][256] = { 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff }, /* IBM Codepage 437 mapped to Unicode */ - { - 0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, + [IBMPC_MAP] = { + 0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c, 0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc, @@ -141,9 +141,9 @@ static unsigned short translations[][256] = { 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 - }, + }, /* User mapping -- default to codes for direct font mapping */ - { + [USER_MAP] = { 0xf000, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007, 0xf008, 0xf009, 0xf00a, 0xf00b, 0xf00c, 0xf00d, 0xf00e, 0xf00f, 0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017, @@ -184,30 +184,51 @@ static unsigned short translations[][256] = { #define MAX_GLYPH 512 /* Max possible glyph value */ -static int inv_translate[MAX_NR_CONSOLES]; +static enum translation_map inv_translate[MAX_NR_CONSOLES]; + +#define UNI_DIRS 32U +#define UNI_DIR_ROWS 32U +#define UNI_ROW_GLYPHS 64U + +#define UNI_DIR(uni) ( (uni) >> 11) +#define UNI_ROW(uni) (((uni) & GENMASK(10, 6)) >> 6) +#define UNI_GLYPH(uni) ( (uni) & GENMASK( 5, 0)) +#define UNI(dir, row, glyph) (((dir) << 11) | ((row) << 6) | (glyph)) -struct uni_pagedir { - u16 **uni_pgdir[32]; +/** + * struct uni_pagedict -- unicode directory + * + * @uni_pgdir: 32*32*64 table with glyphs + * @refcount: reference count of this structure + * @sum: checksum + * @inverse_translations: best-effort inverse mapping + * @inverse_trans_unicode: best-effort inverse mapping to unicode + */ +struct uni_pagedict { + u16 **uni_pgdir[UNI_DIRS]; unsigned long refcount; unsigned long sum; - unsigned char *inverse_translations[4]; + unsigned char *inverse_translations[LAST_MAP + 1]; u16 *inverse_trans_unicode; }; -static struct uni_pagedir *dflt; +static struct uni_pagedict *dflt; -static void set_inverse_transl(struct vc_data *conp, struct uni_pagedir *p, int i) +static void set_inverse_transl(struct vc_data *conp, struct uni_pagedict *p, + enum translation_map m) { int j, glyph; - unsigned short *t = translations[i]; + unsigned short *t = translations[m]; unsigned char *q; - - if (!p) return; - q = p->inverse_translations[i]; + + if (!p) + return; + q = p->inverse_translations[m]; if (!q) { - q = p->inverse_translations[i] = kmalloc(MAX_GLYPH, GFP_KERNEL); - if (!q) return; + q = p->inverse_translations[m] = kmalloc(MAX_GLYPH, GFP_KERNEL); + if (!q) + return; } memset(q, 0, MAX_GLYPH); @@ -215,47 +236,47 @@ static void set_inverse_transl(struct vc_data *conp, struct uni_pagedir *p, int glyph = conv_uni_to_pc(conp, t[j]); if (glyph >= 0 && glyph < MAX_GLYPH && q[glyph] < 32) { /* prefer '-' above SHY etc. */ - q[glyph] = j; + q[glyph] = j; } } } static void set_inverse_trans_unicode(struct vc_data *conp, - struct uni_pagedir *p) + struct uni_pagedict *p) { - int i, j, k, glyph; - u16 **p1, *p2; - u16 *q; + unsigned int d, r, g; + u16 *inv; - if (!p) return; - q = p->inverse_trans_unicode; - if (!q) { - q = p->inverse_trans_unicode = - kmalloc_array(MAX_GLYPH, sizeof(u16), GFP_KERNEL); - if (!q) + if (!p) + return; + + inv = p->inverse_trans_unicode; + if (!inv) { + inv = p->inverse_trans_unicode = kmalloc_array(MAX_GLYPH, + sizeof(*inv), GFP_KERNEL); + if (!inv) return; } - memset(q, 0, MAX_GLYPH * sizeof(u16)); + memset(inv, 0, MAX_GLYPH * sizeof(*inv)); - for (i = 0; i < 32; i++) { - p1 = p->uni_pgdir[i]; - if (!p1) + for (d = 0; d < UNI_DIRS; d++) { + u16 **dir = p->uni_pgdir[d]; + if (!dir) continue; - for (j = 0; j < 32; j++) { - p2 = p1[j]; - if (!p2) + for (r = 0; r < UNI_DIR_ROWS; r++) { + u16 *row = dir[r]; + if (!row) continue; - for (k = 0; k < 64; k++) { - glyph = p2[k]; - if (glyph >= 0 && glyph < MAX_GLYPH - && q[glyph] < 32) - q[glyph] = (i << 11) + (j << 6) + k; + for (g = 0; g < UNI_ROW_GLYPHS; g++) { + u16 glyph = row[g]; + if (glyph < MAX_GLYPH && inv[glyph] < 32) + inv[glyph] = UNI(d, r, g); } } } } -unsigned short *set_translate(int m, struct vc_data *vc) +unsigned short *set_translate(enum translation_map m, struct vc_data *vc) { inv_translate[vc->vc_num] = m; return translations[m]; @@ -268,37 +289,38 @@ unsigned short *set_translate(int m, struct vc_data *vc) * was active. * Still, it is now possible to a certain extent to cut and paste non-ASCII. */ -u16 inverse_translate(const struct vc_data *conp, int glyph, int use_unicode) +u16 inverse_translate(const struct vc_data *conp, u16 glyph, bool use_unicode) { - struct uni_pagedir *p; - int m; - if (glyph < 0 || glyph >= MAX_GLYPH) + struct uni_pagedict *p; + enum translation_map m; + + if (glyph >= MAX_GLYPH) return 0; - else { - p = *conp->vc_uni_pagedir_loc; - if (!p) + + p = *conp->vc_uni_pagedir_loc; + if (!p) + return glyph; + + if (use_unicode) { + if (!p->inverse_trans_unicode) return glyph; - else if (use_unicode) { - if (!p->inverse_trans_unicode) - return glyph; - else - return p->inverse_trans_unicode[glyph]; - } else { - m = inv_translate[conp->vc_num]; - if (!p->inverse_translations[m]) - return glyph; - else - return p->inverse_translations[m][glyph]; - } + + return p->inverse_trans_unicode[glyph]; } + + m = inv_translate[conp->vc_num]; + if (!p->inverse_translations[m]) + return glyph; + + return p->inverse_translations[m][glyph]; } EXPORT_SYMBOL_GPL(inverse_translate); static void update_user_maps(void) { int i; - struct uni_pagedir *p, *q = NULL; - + struct uni_pagedict *p, *q = NULL; + for (i = 0; i < MAX_NR_CONSOLES; i++) { if (!vc_cons_allocated(i)) continue; @@ -321,15 +343,15 @@ static void update_user_maps(void) */ int con_set_trans_old(unsigned char __user * arg) { - int i; unsigned short inbuf[E_TABSZ]; - unsigned char ubuf[E_TABSZ]; - - if (copy_from_user(ubuf, arg, E_TABSZ)) - return -EFAULT; + unsigned int i; + unsigned char ch; - for (i = 0; i < E_TABSZ ; i++) - inbuf[i] = UNI_DIRECT_BASE | ubuf[i]; + for (i = 0; i < ARRAY_SIZE(inbuf); i++) { + if (get_user(ch, &arg[i])) + return -EFAULT; + inbuf[i] = UNI_DIRECT_BASE | ch; + } console_lock(); memcpy(translations[USER_MAP], inbuf, sizeof(inbuf)); @@ -381,7 +403,7 @@ int con_get_trans_new(ushort __user * arg) } /* - * Unicode -> current font conversion + * Unicode -> current font conversion * * A font has at most 512 chars, usually 256. * But one font position may represent several Unicode chars. @@ -393,33 +415,36 @@ int con_get_trans_new(ushort __user * arg) extern u8 dfont_unicount[]; /* Defined in console_defmap.c */ extern u16 dfont_unitable[]; -static void con_release_unimap(struct uni_pagedir *p) +static void con_release_unimap(struct uni_pagedict *dict) { - u16 **p1; - int i, j; - - if (p == dflt) dflt = NULL; - for (i = 0; i < 32; i++) { - p1 = p->uni_pgdir[i]; - if (p1 != NULL) { - for (j = 0; j < 32; j++) - kfree(p1[j]); - kfree(p1); + unsigned int d, r; + + if (dict == dflt) + dflt = NULL; + + for (d = 0; d < UNI_DIRS; d++) { + u16 **dir = dict->uni_pgdir[d]; + if (dir != NULL) { + for (r = 0; r < UNI_DIR_ROWS; r++) + kfree(dir[r]); + kfree(dir); } - p->uni_pgdir[i] = NULL; + dict->uni_pgdir[d] = NULL; } - for (i = 0; i < 4; i++) { - kfree(p->inverse_translations[i]); - p->inverse_translations[i] = NULL; + + for (r = 0; r < ARRAY_SIZE(dict->inverse_translations); r++) { + kfree(dict->inverse_translations[r]); + dict->inverse_translations[r] = NULL; } - kfree(p->inverse_trans_unicode); - p->inverse_trans_unicode = NULL; + + kfree(dict->inverse_trans_unicode); + dict->inverse_trans_unicode = NULL; } /* Caller must hold the console lock */ void con_free_unimap(struct vc_data *vc) { - struct uni_pagedir *p; + struct uni_pagedict *p; p = *vc->vc_uni_pagedir_loc; if (!p) @@ -430,41 +455,42 @@ void con_free_unimap(struct vc_data *vc) con_release_unimap(p); kfree(p); } - -static int con_unify_unimap(struct vc_data *conp, struct uni_pagedir *p) + +static int con_unify_unimap(struct vc_data *conp, struct uni_pagedict *dict1) { - int i, j, k; - struct uni_pagedir *q; - - for (i = 0; i < MAX_NR_CONSOLES; i++) { - if (!vc_cons_allocated(i)) + struct uni_pagedict *dict2; + unsigned int cons, d, r; + + for (cons = 0; cons < MAX_NR_CONSOLES; cons++) { + if (!vc_cons_allocated(cons)) continue; - q = *vc_cons[i].d->vc_uni_pagedir_loc; - if (!q || q == p || q->sum != p->sum) + dict2 = *vc_cons[cons].d->vc_uni_pagedir_loc; + if (!dict2 || dict2 == dict1 || dict2->sum != dict1->sum) continue; - for (j = 0; j < 32; j++) { - u16 **p1, **q1; - p1 = p->uni_pgdir[j]; q1 = q->uni_pgdir[j]; - if (!p1 && !q1) + for (d = 0; d < UNI_DIRS; d++) { + u16 **dir1 = dict1->uni_pgdir[d]; + u16 **dir2 = dict2->uni_pgdir[d]; + if (!dir1 && !dir2) continue; - if (!p1 || !q1) + if (!dir1 || !dir2) break; - for (k = 0; k < 32; k++) { - if (!p1[k] && !q1[k]) + for (r = 0; r < UNI_DIR_ROWS; r++) { + if (!dir1[r] && !dir2[r]) continue; - if (!p1[k] || !q1[k]) + if (!dir1[r] || !dir2[r]) break; - if (memcmp(p1[k], q1[k], 64*sizeof(u16))) + if (memcmp(dir1[r], dir2[r], UNI_ROW_GLYPHS * + sizeof(*dir1[r]))) break; } - if (k < 32) + if (r < UNI_DIR_ROWS) break; } - if (j == 32) { - q->refcount++; - *conp->vc_uni_pagedir_loc = q; - con_release_unimap(p); - kfree(p); + if (d == UNI_DIRS) { + dict2->refcount++; + *conp->vc_uni_pagedir_loc = dict2; + con_release_unimap(dict1); + kfree(dict1); return 1; } } @@ -472,55 +498,68 @@ static int con_unify_unimap(struct vc_data *conp, struct uni_pagedir *p) } static int -con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos) +con_insert_unipair(struct uni_pagedict *p, u_short unicode, u_short fontpos) { - int i, n; - u16 **p1, *p2; - - p1 = p->uni_pgdir[n = unicode >> 11]; - if (!p1) { - p1 = p->uni_pgdir[n] = kmalloc_array(32, sizeof(u16 *), - GFP_KERNEL); - if (!p1) return -ENOMEM; - for (i = 0; i < 32; i++) - p1[i] = NULL; + u16 **dir, *row; + unsigned int n; + + n = UNI_DIR(unicode); + dir = p->uni_pgdir[n]; + if (!dir) { + dir = p->uni_pgdir[n] = kcalloc(UNI_DIR_ROWS, sizeof(*dir), + GFP_KERNEL); + if (!dir) + return -ENOMEM; } - p2 = p1[n = (unicode >> 6) & 0x1f]; - if (!p2) { - p2 = p1[n] = kmalloc_array(64, sizeof(u16), GFP_KERNEL); - if (!p2) return -ENOMEM; - memset(p2, 0xff, 64*sizeof(u16)); /* No glyphs for the characters (yet) */ + n = UNI_ROW(unicode); + row = dir[n]; + if (!row) { + row = dir[n] = kmalloc_array(UNI_ROW_GLYPHS, sizeof(*row), + GFP_KERNEL); + if (!row) + return -ENOMEM; + /* No glyphs for the characters (yet) */ + memset(row, 0xff, UNI_ROW_GLYPHS * sizeof(*row)); } - p2[unicode & 0x3f] = fontpos; - + row[UNI_GLYPH(unicode)] = fontpos; + p->sum += (fontpos << 20U) + unicode; return 0; } +static int con_allocate_new(struct vc_data *vc) +{ + struct uni_pagedict *new, *old = *vc->vc_uni_pagedir_loc; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return -ENOMEM; + + new->refcount = 1; + *vc->vc_uni_pagedir_loc = new; + + if (old) + old->refcount--; + + return 0; +} + /* Caller must hold the lock */ static int con_do_clear_unimap(struct vc_data *vc) { - struct uni_pagedir *p, *q; + struct uni_pagedict *old = *vc->vc_uni_pagedir_loc; + + if (!old || old->refcount > 1) + return con_allocate_new(vc); + + if (old == dflt) + dflt = NULL; + old->sum = 0; + con_release_unimap(old); - p = *vc->vc_uni_pagedir_loc; - if (!p || --p->refcount) { - q = kzalloc(sizeof(*p), GFP_KERNEL); - if (!q) { - if (p) - p->refcount++; - return -ENOMEM; - } - q->refcount=1; - *vc->vc_uni_pagedir_loc = q; - } else { - if (p == dflt) dflt = NULL; - p->refcount++; - p->sum = 0; - con_release_unimap(p); - } return 0; } @@ -532,91 +571,93 @@ int con_clear_unimap(struct vc_data *vc) console_unlock(); return ret; } - + +static struct uni_pagedict *con_unshare_unimap(struct vc_data *vc, + struct uni_pagedict *old) +{ + struct uni_pagedict *new; + unsigned int d, r, g; + int ret; + u16 uni = 0; + + ret = con_allocate_new(vc); + if (ret) + return ERR_PTR(ret); + + new = *vc->vc_uni_pagedir_loc; + + /* + * uni_pgdir is a 32*32*64 table with rows allocated when its first + * entry is added. The unicode value must still be incremented for + * empty rows. We are copying entries from "old" to "new". + */ + for (d = 0; d < UNI_DIRS; d++) { + u16 **dir = old->uni_pgdir[d]; + if (!dir) { + /* Account for empty table */ + uni += UNI_DIR_ROWS * UNI_ROW_GLYPHS; + continue; + } + + for (r = 0; r < UNI_DIR_ROWS; r++) { + u16 *row = dir[r]; + if (!row) { + /* Account for row of 64 empty entries */ + uni += UNI_ROW_GLYPHS; + continue; + } + + for (g = 0; g < UNI_ROW_GLYPHS; g++, uni++) { + if (row[g] == 0xffff) + continue; + /* + * Found one, copy entry for unicode uni with + * fontpos value row[g]. + */ + ret = con_insert_unipair(new, uni, row[g]); + if (ret) { + old->refcount++; + *vc->vc_uni_pagedir_loc = old; + con_release_unimap(new); + kfree(new); + return ERR_PTR(ret); + } + } + } + } + + return new; +} + int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) { - int err = 0, err1, i; - struct uni_pagedir *p, *q; + int err = 0, err1; + struct uni_pagedict *dict; struct unipair *unilist, *plist; if (!ct) return 0; - unilist = vmemdup_user(list, array_size(sizeof(struct unipair), ct)); + unilist = vmemdup_user(list, array_size(sizeof(*unilist), ct)); if (IS_ERR(unilist)) return PTR_ERR(unilist); console_lock(); /* Save original vc_unipagdir_loc in case we allocate a new one */ - p = *vc->vc_uni_pagedir_loc; - - if (!p) { + dict = *vc->vc_uni_pagedir_loc; + if (!dict) { err = -EINVAL; - goto out_unlock; } - - if (p->refcount > 1) { - int j, k; - u16 **p1, *p2, l; - - err1 = con_do_clear_unimap(vc); - if (err1) { - err = err1; + + if (dict->refcount > 1) { + dict = con_unshare_unimap(vc, dict); + if (IS_ERR(dict)) { + err = PTR_ERR(dict); goto out_unlock; } - - /* - * Since refcount was > 1, con_clear_unimap() allocated a - * a new uni_pagedir for this vc. Re: p != q - */ - q = *vc->vc_uni_pagedir_loc; - - /* - * uni_pgdir is a 32*32*64 table with rows allocated - * when its first entry is added. The unicode value must - * still be incremented for empty rows. We are copying - * entries from "p" (old) to "q" (new). - */ - l = 0; /* unicode value */ - for (i = 0; i < 32; i++) { - p1 = p->uni_pgdir[i]; - if (p1) - for (j = 0; j < 32; j++) { - p2 = p1[j]; - if (p2) { - for (k = 0; k < 64; k++, l++) - if (p2[k] != 0xffff) { - /* - * Found one, copy entry for unicode - * l with fontpos value p2[k]. - */ - err1 = con_insert_unipair(q, l, p2[k]); - if (err1) { - p->refcount++; - *vc->vc_uni_pagedir_loc = p; - con_release_unimap(q); - kfree(q); - err = err1; - goto out_unlock; - } - } - } else { - /* Account for row of 64 empty entries */ - l += 64; - } - } - else - /* Account for empty table */ - l += 32 * 64; - } - - /* - * Finished copying font table, set vc_uni_pagedir to new table - */ - p = q; - } else if (p == dflt) { + } else if (dict == dflt) { dflt = NULL; } @@ -624,20 +665,20 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) * Insert user specified unicode pairs into new table. */ for (plist = unilist; ct; ct--, plist++) { - err1 = con_insert_unipair(p, plist->unicode, plist->fontpos); + err1 = con_insert_unipair(dict, plist->unicode, plist->fontpos); if (err1) err = err1; } - + /* * Merge with fontmaps of any other virtual consoles. */ - if (con_unify_unimap(vc, p)) + if (con_unify_unimap(vc, dict)) goto out_unlock; - for (i = 0; i <= 3; i++) - set_inverse_transl(vc, p, i); /* Update inverse translations */ - set_inverse_trans_unicode(vc, p); + for (enum translation_map m = FIRST_MAP; m <= LAST_MAP; m++) + set_inverse_transl(vc, dict, m); + set_inverse_trans_unicode(vc, dict); out_unlock: console_unlock(); @@ -652,55 +693,56 @@ out_unlock: * Loads the unimap for the hardware font, as defined in uni_hash.tbl. * The representation used was the most compact I could come up * with. This routine is executed at video setup, and when the - * PIO_FONTRESET ioctl is called. + * PIO_FONTRESET ioctl is called. * * The caller must hold the console lock */ int con_set_default_unimap(struct vc_data *vc) { - int i, j, err = 0, err1; - u16 *q; - struct uni_pagedir *p; + struct uni_pagedict *dict; + unsigned int fontpos, count; + int err = 0, err1; + u16 *dfont; if (dflt) { - p = *vc->vc_uni_pagedir_loc; - if (p == dflt) + dict = *vc->vc_uni_pagedir_loc; + if (dict == dflt) return 0; dflt->refcount++; *vc->vc_uni_pagedir_loc = dflt; - if (p && !--p->refcount) { - con_release_unimap(p); - kfree(p); + if (dict && !--dict->refcount) { + con_release_unimap(dict); + kfree(dict); } return 0; } - + /* The default font is always 256 characters */ err = con_do_clear_unimap(vc); if (err) return err; - - p = *vc->vc_uni_pagedir_loc; - q = dfont_unitable; - - for (i = 0; i < 256; i++) - for (j = dfont_unicount[i]; j; j--) { - err1 = con_insert_unipair(p, *(q++), i); + + dict = *vc->vc_uni_pagedir_loc; + dfont = dfont_unitable; + + for (fontpos = 0; fontpos < 256U; fontpos++) + for (count = dfont_unicount[fontpos]; count; count--) { + err1 = con_insert_unipair(dict, *(dfont++), fontpos); if (err1) err = err1; } - - if (con_unify_unimap(vc, p)) { + + if (con_unify_unimap(vc, dict)) { dflt = *vc->vc_uni_pagedir_loc; return err; } - for (i = 0; i <= 3; i++) - set_inverse_transl(vc, p, i); /* Update all inverse translations */ - set_inverse_trans_unicode(vc, p); - dflt = p; + for (enum translation_map m = FIRST_MAP; m <= LAST_MAP; m++) + set_inverse_transl(vc, dict, m); + set_inverse_trans_unicode(vc, dict); + dflt = dict; return err; } EXPORT_SYMBOL(con_set_default_unimap); @@ -714,16 +756,16 @@ EXPORT_SYMBOL(con_set_default_unimap); */ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc) { - struct uni_pagedir *q; + struct uni_pagedict *src; if (!*src_vc->vc_uni_pagedir_loc) return -EINVAL; if (*dst_vc->vc_uni_pagedir_loc == *src_vc->vc_uni_pagedir_loc) return 0; con_free_unimap(dst_vc); - q = *src_vc->vc_uni_pagedir_loc; - q->refcount++; - *dst_vc->vc_uni_pagedir_loc = q; + src = *src_vc->vc_uni_pagedir_loc; + src->refcount++; + *dst_vc->vc_uni_pagedir_loc = src; return 0; } EXPORT_SYMBOL(con_copy_unimap); @@ -734,46 +776,53 @@ EXPORT_SYMBOL(con_copy_unimap); * Read the console unicode data for this console. Called from the ioctl * handlers. */ -int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list) +int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, + struct unipair __user *list) { - int i, j, k, ret = 0; ushort ect; - u16 **p1, *p2; - struct uni_pagedir *p; + struct uni_pagedict *dict; struct unipair *unilist; + unsigned int d, r, g; + int ret = 0; - unilist = kvmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); + unilist = kvmalloc_array(ct, sizeof(*unilist), GFP_KERNEL); if (!unilist) return -ENOMEM; console_lock(); ect = 0; - if (*vc->vc_uni_pagedir_loc) { - p = *vc->vc_uni_pagedir_loc; - for (i = 0; i < 32; i++) { - p1 = p->uni_pgdir[i]; - if (p1) - for (j = 0; j < 32; j++) { - p2 = *(p1++); - if (p2) - for (k = 0; k < 64; k++, p2++) { - if (*p2 >= MAX_GLYPH) - continue; - if (ect < ct) { - unilist[ect].unicode = - (i<<11)+(j<<6)+k; - unilist[ect].fontpos = *p2; - } - ect++; + dict = *vc->vc_uni_pagedir_loc; + if (!dict) + goto unlock; + + for (d = 0; d < UNI_DIRS; d++) { + u16 **dir = dict->uni_pgdir[d]; + if (!dir) + continue; + + for (r = 0; r < UNI_DIR_ROWS; r++) { + u16 *row = dir[r]; + if (!row) + continue; + + for (g = 0; g < UNI_ROW_GLYPHS; g++, row++) { + if (*row >= MAX_GLYPH) + continue; + if (ect < ct) { + unilist[ect].unicode = UNI(d, r, g); + unilist[ect].fontpos = *row; } + ect++; } } } +unlock: console_unlock(); - if (copy_to_user(list, unilist, min(ect, ct) * sizeof(struct unipair))) + if (copy_to_user(list, unilist, min(ect, ct) * sizeof(*unilist))) + ret = -EFAULT; + if (put_user(ect, uct)) ret = -EFAULT; - put_user(ect, uct); kvfree(unilist); return ret ? ret : (ect <= ct) ? 0 : -ENOMEM; } @@ -798,20 +847,18 @@ u32 conv_8bit_to_uni(unsigned char c) int conv_uni_to_8bit(u32 uni) { int c; - for (c = 0; c < 0x100; c++) + for (c = 0; c < ARRAY_SIZE(translations[USER_MAP]); c++) if (translations[USER_MAP][c] == uni || (translations[USER_MAP][c] == (c | 0xf000) && uni == c)) return c; return -1; } -int -conv_uni_to_pc(struct vc_data *conp, long ucs) +int conv_uni_to_pc(struct vc_data *conp, long ucs) { - int h; - u16 **p1, *p2; - struct uni_pagedir *p; - + struct uni_pagedict *dict; + u16 **dir, *row, glyph; + /* Only 16-bit codes supported at this time */ if (ucs > 0xffff) return -4; /* Not found */ @@ -826,17 +873,24 @@ conv_uni_to_pc(struct vc_data *conp, long ucs) */ else if ((ucs & ~UNI_DIRECT_MASK) == UNI_DIRECT_BASE) return ucs & UNI_DIRECT_MASK; - - if (!*conp->vc_uni_pagedir_loc) + + dict = *conp->vc_uni_pagedir_loc; + if (!dict) return -3; - p = *conp->vc_uni_pagedir_loc; - if ((p1 = p->uni_pgdir[ucs >> 11]) && - (p2 = p1[(ucs >> 6) & 0x1f]) && - (h = p2[ucs & 0x3f]) < MAX_GLYPH) - return h; + dir = dict->uni_pgdir[UNI_DIR(ucs)]; + if (!dir) + return -4; - return -4; /* not found */ + row = dir[UNI_ROW(ucs)]; + if (!row) + return -4; + + glyph = row[UNI_GLYPH(ucs)]; + if (glyph >= MAX_GLYPH) + return -4; + + return glyph; } /* @@ -844,11 +898,11 @@ conv_uni_to_pc(struct vc_data *conp, long ucs) * initialized. It must be possible to call kmalloc(..., GFP_KERNEL) * from this function, hence the call from sys_setup. */ -void __init +void __init console_map_init(void) { int i; - + for (i = 0; i < MAX_NR_CONSOLES; i++) if (vc_cons_allocated(i) && !*vc_cons[i].d->vc_uni_pagedir_loc) con_set_default_unimap(vc_cons[i].d); diff --git a/drivers/tty/vt/defkeymap.c_shipped b/drivers/tty/vt/defkeymap.c_shipped index 094d95bf0005..0c043e4f292e 100644 --- a/drivers/tty/vt/defkeymap.c_shipped +++ b/drivers/tty/vt/defkeymap.c_shipped @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -/* Do not edit this file! It was automatically generated by */ -/* loadkeys --mktable defkeymap.map > defkeymap.c */ +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable --unicode defkeymap.map > defkeymap.c */ #include <linux/types.h> #include <linux/keyboard.h> @@ -139,7 +139,7 @@ static unsigned short ctrl_alt_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -ushort *key_maps[MAX_NR_KEYMAPS] = { +unsigned short *key_maps[MAX_NR_KEYMAPS] = { plain_map, shift_map, altgr_map, NULL, ctrl_map, shift_ctrl_map, NULL, NULL, alt_map, NULL, NULL, NULL, diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index f7755e73696e..6ef22f01cc51 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -68,7 +68,8 @@ sel_pos(int n, bool unicode) { if (unicode) return screen_glyph_unicode(vc_sel.cons, n / 2); - return inverse_translate(vc_sel.cons, screen_glyph(vc_sel.cons, n), 0); + return inverse_translate(vc_sel.cons, screen_glyph(vc_sel.cons, n), + false); } /** diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index f8c87c4d7399..c718b0d01e3d 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3939,7 +3939,7 @@ static ssize_t show_bind(struct device *dev, struct device_attribute *attr, bind = con_is_bound(con->con); console_unlock(); - return snprintf(buf, PAGE_SIZE, "%i\n", bind); + return sysfs_emit(buf, "%i\n", bind); } static ssize_t show_name(struct device *dev, struct device_attribute *attr, @@ -3947,7 +3947,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute *attr, { struct con_driver *con = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s %s\n", + return sysfs_emit(buf, "%s %s\n", (con->flag & CON_DRIVER_FLAG_MODULE) ? "(M)" : "(S)", con->desc); @@ -4741,7 +4741,7 @@ u32 screen_glyph_unicode(const struct vc_data *vc, int n) if (uniscr) return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols]; - return inverse_translate(vc, screen_glyph(vc, n * 2), 1); + return inverse_translate(vc, screen_glyph(vc, n * 2), true); } EXPORT_SYMBOL_GPL(screen_glyph_unicode); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 576612f18d59..058a78b8dbcf 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -75,7 +75,7 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines); static int vgacon_set_origin(struct vc_data *c); static void vgacon_save_screen(struct vc_data *c); static void vgacon_invert_region(struct vc_data *c, u16 * p, int count); -static struct uni_pagedir *vgacon_uni_pagedir; +static struct uni_pagedict *vgacon_uni_pagedir; static int vgacon_refcount; /* Description of the hardware situation */ @@ -342,7 +342,7 @@ static const char *vgacon_startup(void) static void vgacon_init(struct vc_data *c, int init) { - struct uni_pagedir *p; + struct uni_pagedict *p; /* * We cannot be loaded as a module, therefore init will be 1 diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h index d5b9c8d40c18..f75033f0277f 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -17,7 +17,7 @@ #include <linux/vt.h> #include <linux/workqueue.h> -struct uni_pagedir; +struct uni_pagedict; struct uni_screen; #define NPAR 16 @@ -157,8 +157,8 @@ struct vc_data { unsigned int vc_bell_duration; /* Console bell duration */ unsigned short vc_cur_blink_ms; /* Cursor blink duration */ struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */ - struct uni_pagedir *vc_uni_pagedir; - struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */ + struct uni_pagedict *vc_uni_pagedir; + struct uni_pagedict **vc_uni_pagedir_loc; /* [!] Location of uni_pagedict variable for this console */ struct uni_screen *vc_uni_screen; /* unicode screen content */ /* additional information is in vt_kern.h */ }; diff --git a/include/linux/consolemap.h b/include/linux/consolemap.h index bcfce748c9d8..c35db4896c37 100644 --- a/include/linux/consolemap.h +++ b/include/linux/consolemap.h @@ -7,30 +7,56 @@ #ifndef __LINUX_CONSOLEMAP_H__ #define __LINUX_CONSOLEMAP_H__ -#define LAT1_MAP 0 -#define GRAF_MAP 1 -#define IBMPC_MAP 2 -#define USER_MAP 3 +enum translation_map { + LAT1_MAP, + GRAF_MAP, + IBMPC_MAP, + USER_MAP, + + FIRST_MAP = LAT1_MAP, + LAST_MAP = USER_MAP, +}; #include <linux/types.h> -#ifdef CONFIG_CONSOLE_TRANSLATIONS struct vc_data; -extern u16 inverse_translate(const struct vc_data *conp, int glyph, - int use_unicode); -extern unsigned short *set_translate(int m, struct vc_data *vc); -extern int conv_uni_to_pc(struct vc_data *conp, long ucs); -extern u32 conv_8bit_to_uni(unsigned char c); -extern int conv_uni_to_8bit(u32 uni); +#ifdef CONFIG_CONSOLE_TRANSLATIONS +u16 inverse_translate(const struct vc_data *conp, u16 glyph, bool use_unicode); +unsigned short *set_translate(enum translation_map m, struct vc_data *vc); +int conv_uni_to_pc(struct vc_data *conp, long ucs); +u32 conv_8bit_to_uni(unsigned char c); +int conv_uni_to_8bit(u32 uni); void console_map_init(void); #else -#define inverse_translate(conp, glyph, uni) ((uint16_t)glyph) -#define set_translate(m, vc) ((unsigned short *)NULL) -#define conv_uni_to_pc(conp, ucs) ((int) (ucs > 0xff ? -1: ucs)) -#define conv_8bit_to_uni(c) ((uint32_t)(c)) -#define conv_uni_to_8bit(c) ((int) ((c) & 0xff)) -#define console_map_init(c) do { ; } while (0) +static inline u16 inverse_translate(const struct vc_data *conp, u16 glyph, + bool use_unicode) +{ + return glyph; +} + +static inline unsigned short *set_translate(enum translation_map m, + struct vc_data *vc) +{ + return NULL; +} + +static inline int conv_uni_to_pc(struct vc_data *conp, long ucs) +{ + return ucs > 0xff ? -1 : ucs; +} + +static inline u32 conv_8bit_to_uni(unsigned char c) +{ + return c; +} + +static inline int conv_uni_to_8bit(u32 uni) +{ + return uni & 0xff; +} + +static inline void console_map_init(void) { } #endif /* CONFIG_CONSOLE_TRANSLATIONS */ #endif /* __LINUX_CONSOLEMAP_H__ */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 657a0fc68a3f..8032ffa741ed 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -255,6 +255,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; + const struct serial_rs485 *rs485_supported; /* Supported mask for serial_rs485 */ struct gpio_desc *rs485_term_gpio; /* enable RS485 bus termination */ struct serial_iso7816 iso7816; void *private_data; /* generic platform data pointer */ @@ -593,4 +594,5 @@ static inline int uart_handle_break(struct uart_port *port) !((cflag) & CLOCAL)) int uart_get_rs485_mode(struct uart_port *port); +int uart_rs485_config(struct uart_port *port); #endif /* LINUX_SERIAL_CORE_H */ diff --git a/include/linux/tty_buffer.h b/include/linux/tty_buffer.h index 3b9d77604291..1796648c2907 100644 --- a/include/linux/tty_buffer.h +++ b/include/linux/tty_buffer.h @@ -15,6 +15,7 @@ struct tty_buffer { int used; int size; int commit; + int lookahead; /* Lazy update on recv, can become less than "read" */ int read; int flags; /* Data points here */ diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index e85002b56752..33678e1936f6 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -186,6 +186,18 @@ int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass, * indicate all data received is %TTY_NORMAL. If assigned, prefer this * function for automatic flow control. * + * @lookahead_buf: [DRV] ``void ()(struct tty_struct *tty, + * const unsigned char *cp, const char *fp, int count) + * + * This function is called by the low-level tty driver for characters + * not eaten by ->receive_buf() or ->receive_buf2(). It is useful for + * processing high-priority characters such as software flow-control + * characters that could otherwise get stuck into the intermediate + * buffer until tty has room to receive them. Ldisc must be able to + * handle later a ->receive_buf() or ->receive_buf2() call for the + * same characters (e.g. by skipping the actions for high-priority + * characters already handled by ->lookahead_buf()). + * * @owner: module containting this ldisc (for reference counting) * * This structure defines the interface between the tty line discipline @@ -229,6 +241,8 @@ struct tty_ldisc_ops { void (*dcd_change)(struct tty_struct *tty, unsigned int status); int (*receive_buf2)(struct tty_struct *tty, const unsigned char *cp, const char *fp, int count); + void (*lookahead_buf)(struct tty_struct *tty, const unsigned char *cp, + const unsigned char *fp, unsigned int count); struct module *owner; }; diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h index 58e9619116b7..fa3c3bdaa234 100644 --- a/include/linux/tty_port.h +++ b/include/linux/tty_port.h @@ -40,6 +40,8 @@ struct tty_port_operations { struct tty_port_client_operations { int (*receive_buf)(struct tty_port *port, const unsigned char *, const unsigned char *, size_t); + void (*lookahead_buf)(struct tty_port *port, const unsigned char *cp, + const unsigned char *fp, unsigned int count); void (*write_wakeup)(struct tty_port *port); }; |