From 027d7dacf73273dbe07a75b2ef5579616f17272c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:43 +0100 Subject: TTY: serial, cleanup include file There are some functions (uart_handle_dcd_change, _handle_cts_change, _insert_char) which are big enough to not be inlined. So move them from .h to .c. We need to export them so that modules can actually use them. They will be even bigger when we introduce tty refcounting to them. While at it, cleanup the "Proud member of Uglyhacks'R'US". It means, define uart_handle_sysrq_char only when SUPPORT_SYSRQ is set. Otherwise define it as a macro. This is needed for some arm driver where the second parameter is undefined if expanded. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 99 ++++++--------------------------------------- 1 file changed, 12 insertions(+), 87 deletions(-) (limited to 'include') diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index eadf33d0abba..945e02cae614 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -483,10 +483,19 @@ static inline int uart_tx_stopped(struct uart_port *port) /* * The following are helper functions for the low level drivers. */ + +extern void uart_handle_dcd_change(struct uart_port *uport, + unsigned int status); +extern void uart_handle_cts_change(struct uart_port *uport, + unsigned int status); + +extern void uart_insert_char(struct uart_port *port, unsigned int status, + unsigned int overrun, unsigned int ch, unsigned int flag); + +#ifdef SUPPORT_SYSRQ static inline int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) { -#ifdef SUPPORT_SYSRQ if (port->sysrq) { if (ch && time_before(jiffies, port->sysrq)) { handle_sysrq(ch); @@ -495,11 +504,10 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) } port->sysrq = 0; } -#endif return 0; } -#ifndef SUPPORT_SYSRQ -#define uart_handle_sysrq_char(port,ch) uart_handle_sysrq_char(port, 0) +#else +#define uart_handle_sysrq_char(port,ch) ({ (void)port; 0; }) #endif /* @@ -522,89 +530,6 @@ static inline int uart_handle_break(struct uart_port *port) return 0; } -/** - * uart_handle_dcd_change - handle a change of carrier detect state - * @uport: uart_port structure for the open port - * @status: new carrier detect status, nonzero if active - */ -static inline void -uart_handle_dcd_change(struct uart_port *uport, unsigned int status) -{ - struct uart_state *state = uport->state; - struct tty_port *port = &state->port; - struct tty_ldisc *ld = tty_ldisc_ref(port->tty); - struct pps_event_time ts; - - if (ld && ld->ops->dcd_change) - pps_get_ts(&ts); - - uport->icount.dcd++; -#ifdef CONFIG_HARD_PPS - if ((uport->flags & UPF_HARDPPS_CD) && status) - hardpps(); -#endif - - if (port->flags & ASYNC_CHECK_CD) { - if (status) - wake_up_interruptible(&port->open_wait); - else if (port->tty) - tty_hangup(port->tty); - } - - if (ld && ld->ops->dcd_change) - ld->ops->dcd_change(port->tty, status, &ts); - if (ld) - tty_ldisc_deref(ld); -} - -/** - * uart_handle_cts_change - handle a change of clear-to-send state - * @uport: uart_port structure for the open port - * @status: new clear to send status, nonzero if active - */ -static inline void -uart_handle_cts_change(struct uart_port *uport, unsigned int status) -{ - struct tty_port *port = &uport->state->port; - struct tty_struct *tty = port->tty; - - uport->icount.cts++; - - if (port->flags & ASYNC_CTS_FLOW) { - if (tty->hw_stopped) { - if (status) { - tty->hw_stopped = 0; - uport->ops->start_tx(uport); - uart_write_wakeup(uport); - } - } else { - if (!status) { - tty->hw_stopped = 1; - uport->ops->stop_tx(uport); - } - } - } -} - -#include - -static inline void -uart_insert_char(struct uart_port *port, unsigned int status, - unsigned int overrun, unsigned int ch, unsigned int flag) -{ - struct tty_struct *tty = port->state->port.tty; - - if ((status & port->ignore_status_mask & ~overrun) == 0) - tty_insert_flip_char(tty, ch, flag); - - /* - * Overrun is special. Since it's reported immediately, - * it doesn't affect the current character. - */ - if (status & ~port->ignore_status_mask & overrun) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); -} - /* * UART_ENABLE_MS - determine if port should enable modem status irqs */ -- cgit v1.2.3 From 448ac154c957c4580531fa0c8f2045816fe2f0e7 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 22 Nov 2011 13:41:24 -0800 Subject: serial/8250_pci: setup-quirk workaround for the kt serial controller Workaround dropped notifications in the iir register. Prevent reads coincident with new interrupt notifications by reading the iir at most once per interrupt. Reported-by: Nhan H Mai Signed-off-by: Dan Williams Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 4 +++- drivers/tty/serial/8250_pci.c | 17 ++++++++++++++++- include/linux/serial_core.h | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index eeadf1b8e093..3a8e5bfe17e2 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1619,11 +1619,13 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) do { struct uart_8250_port *up; struct uart_port *port; + bool skip; up = list_entry(l, struct uart_8250_port, list); port = &up->port; + skip = pass_counter && up->port.flags & UPF_IIR_ONCE; - if (port->handle_irq(port)) { + if (!skip && port->handle_irq(port)) { handled = 1; end = NULL; } else if (end == NULL) diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 825937a5f210..8742ef5be6ba 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -1092,6 +1092,14 @@ static int skip_tx_en_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +static int kt_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + port->flags |= UPF_IIR_ONCE; + return skip_tx_en_setup(priv, board, port, idx); +} + static int pci_eg20t_init(struct pci_dev *dev) { #if defined(CONFIG_SERIAL_PCH_UART) || defined(CONFIG_SERIAL_PCH_UART_MODULE) @@ -1110,7 +1118,6 @@ pci_xr17c154_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } -/* This should be in linux/pci_ids.h */ #define PCI_VENDOR_ID_SBSMODULARIO 0x124B #define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B #define PCI_DEVICE_ID_OCTPRO 0x0001 @@ -1136,6 +1143,7 @@ pci_xr17c154_setup(struct serial_private *priv, #define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 #define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6 #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 +#define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -1220,6 +1228,13 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = ce4100_serial_setup, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_PATSBURG_KT, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = kt_serial_setup, + }, /* * ITE */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 945e02cae614..b67305e3ad57 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -351,6 +351,7 @@ struct uart_port { #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) #define UPF_EXAR_EFR ((__force upf_t) (1 << 25)) +#define UPF_IIR_ONCE ((__force upf_t) (1 << 26)) /* The exact UART type is known and should not be probed. */ #define UPF_FIXED_TYPE ((__force upf_t) (1 << 27)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) -- cgit v1.2.3 From 3986fb2ba67bb30cac18b0cff48c88d69ad37681 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 4 Dec 2011 18:42:20 -0500 Subject: serial: export the key functions for an 8250 IRQ handler For drivers that need to construct their own IRQ handler, the three components are seen in the current handle_port -- i.e. Rx, Tx and modem_status. Make these exported symbols so that "almost" 8250 UARTs can construct their own IRQ handler with these shared components, while working around their own unique errata issues. The function names are given a serial8250 prefix, since they are now entering the global namespace. Signed-off-by: Paul Gortmaker Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 29 +++++++++++++++-------------- include/linux/serial_8250.h | 4 ++++ 2 files changed, 19 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 5274228fa03c..9c0396fa3915 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1300,8 +1300,6 @@ static void serial8250_stop_tx(struct uart_port *port) } } -static void transmit_chars(struct uart_8250_port *up); - static void serial8250_start_tx(struct uart_port *port) { struct uart_8250_port *up = @@ -1318,7 +1316,7 @@ static void serial8250_start_tx(struct uart_port *port) if ((up->port.type == PORT_RM9000) ? (lsr & UART_LSR_THRE) : (lsr & UART_LSR_TEMT)) - transmit_chars(up); + serial8250_tx_chars(up); } } @@ -1376,12 +1374,12 @@ static void clear_rx_fifo(struct uart_8250_port *up) } /* - * receive_chars: processes according to the passed in LSR + * serial8250_rx_chars: processes according to the passed in LSR * value, and returns the remaining LSR bits not handled * by this Rx routine. */ -static unsigned char -receive_chars(struct uart_8250_port *up, unsigned char lsr) +unsigned char +serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) { struct tty_struct *tty = up->port.state->port.tty; unsigned char ch; @@ -1462,8 +1460,9 @@ ignore_char: spin_lock(&up->port.lock); return lsr; } +EXPORT_SYMBOL_GPL(serial8250_rx_chars); -static void transmit_chars(struct uart_8250_port *up) +void serial8250_tx_chars(struct uart_8250_port *up) { struct circ_buf *xmit = &up->port.state->xmit; int count; @@ -1500,8 +1499,9 @@ static void transmit_chars(struct uart_8250_port *up) if (uart_circ_empty(xmit)) __stop_tx(up); } +EXPORT_SYMBOL_GPL(serial8250_tx_chars); -static unsigned int check_modem_status(struct uart_8250_port *up) +unsigned int serial8250_modem_status(struct uart_8250_port *up) { unsigned int status = serial_in(up, UART_MSR); @@ -1523,6 +1523,7 @@ static unsigned int check_modem_status(struct uart_8250_port *up) return status; } +EXPORT_SYMBOL_GPL(serial8250_modem_status); /* * This handles the interrupt from one port. @@ -1539,10 +1540,10 @@ static void serial8250_handle_port(struct uart_8250_port *up) DEBUG_INTR("status = %x...", status); if (status & (UART_LSR_DR | UART_LSR_BI)) - status = receive_chars(up, status); - check_modem_status(up); + status = serial8250_rx_chars(up, status); + serial8250_modem_status(up); if (status & UART_LSR_THRE) - transmit_chars(up); + serial8250_tx_chars(up); spin_unlock_irqrestore(&up->port.lock, flags); } @@ -1782,7 +1783,7 @@ static void serial8250_backup_timeout(unsigned long data) } if (!(iir & UART_IIR_NO_INT)) - transmit_chars(up); + serial8250_tx_chars(up); if (is_real_interrupt(up->port.irq)) serial_out(up, UART_IER, ier); @@ -1816,7 +1817,7 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) unsigned int status; unsigned int ret; - status = check_modem_status(up); + status = serial8250_modem_status(up); ret = 0; if (status & UART_MSR_DCD) @@ -2863,7 +2864,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) * while processing with interrupts off. */ if (up->msr_saved_flags) - check_modem_status(up); + serial8250_modem_status(up); if (locked) spin_unlock(&up->port.lock); diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 1f05bbeac01e..b44034eca123 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -66,6 +66,7 @@ enum { * dependent on the 8250 driver. */ struct uart_port; +struct uart_8250_port; int serial8250_register_port(struct uart_port *); void serial8250_unregister_port(int line); @@ -82,6 +83,9 @@ extern void serial8250_do_set_termios(struct uart_port *port, extern void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); int serial8250_handle_irq(struct uart_port *port, unsigned int iir); +unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr); +void serial8250_tx_chars(struct uart_8250_port *up); +unsigned int serial8250_modem_status(struct uart_8250_port *up); extern void serial8250_set_isa_configurator(void (*v) (int port, struct uart_port *up, -- cgit v1.2.3 From 9deaa53ac7fa373623123aa4f18828dd62292b1a Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 4 Dec 2011 18:42:23 -0500 Subject: serial: add irq handler for Freescale 16550 errata. Sending a break on the SOC UARTs found in some MPC83xx/85xx/86xx chips seems to cause a short lived IRQ storm (/proc/interrupts typically shows somewhere between 300 and 1500 events). Unfortunately this renders SysRQ over the serial console completely inoperable. The suggested workaround in the errata is to read the Rx register, wait one character period, and then read the Rx register again. We achieve this by tracking the old LSR value, and on the subsequent interrupt event after a break, we don't read LSR, instead we just read the RBR again and return immediately. The "fsl,ns16550" is used in the compatible field of the serial device to mark UARTs known to have this issue. Thanks to Scott Wood for providing the errata data which led to a much cleaner fix. Signed-off-by: Paul Gortmaker Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/legacy_serial.c | 3 ++ drivers/tty/serial/8250_fsl.c | 63 +++++++++++++++++++++++++++++++++++++ drivers/tty/serial/Kconfig | 5 +++ drivers/tty/serial/Makefile | 1 + include/linux/serial_8250.h | 1 + 5 files changed, 73 insertions(+) create mode 100644 drivers/tty/serial/8250_fsl.c (limited to 'include') diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index c7b5afeecaf2..3fea3689527e 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -441,6 +441,9 @@ static void __init fixup_port_irq(int index, return; port->irq = virq; + + if (of_device_is_compatible(np, "fsl,ns16550")) + port->handle_irq = fsl8250_handle_irq; } static void __init fixup_port_pio(int index, diff --git a/drivers/tty/serial/8250_fsl.c b/drivers/tty/serial/8250_fsl.c new file mode 100644 index 000000000000..f4d3c47b88e8 --- /dev/null +++ b/drivers/tty/serial/8250_fsl.c @@ -0,0 +1,63 @@ +#include +#include + +#include "8250.h" + +/* + * Freescale 16550 UART "driver", Copyright (C) 2011 Paul Gortmaker. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This isn't a full driver; it just provides an alternate IRQ + * handler to deal with an errata. Everything else is just + * using the bog standard 8250 support. + * + * We follow code flow of serial8250_default_handle_irq() but add + * a check for a break and insert a dummy read on the Rx for the + * immediately following IRQ event. + * + * We re-use the already existing "bug handling" lsr_saved_flags + * field to carry the "what we just did" information from the one + * IRQ event to the next one. + */ + +int fsl8250_handle_irq(struct uart_port *port) +{ + unsigned char lsr, orig_lsr; + unsigned long flags; + unsigned int iir; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + spin_lock_irqsave(&up->port.lock, flags); + + iir = port->serial_in(port, UART_IIR); + if (iir & UART_IIR_NO_INT) { + spin_unlock_irqrestore(&up->port.lock, flags); + return 0; + } + + /* This is the WAR; if last event was BRK, then read and return */ + if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { + up->lsr_saved_flags &= ~UART_LSR_BI; + port->serial_in(port, UART_RX); + spin_unlock_irqrestore(&up->port.lock, flags); + return 1; + } + + lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); + + if (lsr & (UART_LSR_DR | UART_LSR_BI)) + lsr = serial8250_rx_chars(up, lsr); + + serial8250_modem_status(up); + + if (lsr & UART_LSR_THRE) + serial8250_tx_chars(up); + + up->lsr_saved_flags = orig_lsr; + spin_unlock_irqrestore(&up->port.lock, flags); + return 1; +} diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 705d2dc39c9a..fee9e04f42e7 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -97,6 +97,11 @@ config SERIAL_8250_PNP This builds standard PNP serial support. You may be able to disable this feature if you only need legacy serial support. +config SERIAL_8250_FSL + bool + depends on SERIAL_8250 && PPC + default PPC + config SERIAL_8250_HP300 tristate depends on SERIAL_8250 && HP300 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index af57089ddb67..75eadb8d7178 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o +obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index b44034eca123..8f012f8ac8e9 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -82,6 +82,7 @@ extern void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old); extern void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); +extern int fsl8250_handle_irq(struct uart_port *port); int serial8250_handle_irq(struct uart_port *port, unsigned int iir); unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr); void serial8250_tx_chars(struct uart_8250_port *up); -- cgit v1.2.3