The "KT" serial port has another use case for a "received break" quirk, so before adding another special case to the 8250 core take this opportunity to push such quirks out of the core and into a uart_port op. Stephen says: "If the callback function is to no longer live in 8250.c itself, arch/arm/mach-tegra/devices.c isn't logically a good place to put it, and that file will be going away once we get rid of all the board files and move solely to device tree." ...so since 8250_pci.c houses all the quirks for pci serial devices this quirk is similarly housed in of_serial.c. Once the open firmware conversion completes the infrastructure details (CONFIG_TEGRA_SERIAL, include/linux/of_serial.h, and the export) can all be removed to make this self contained to of_serial.c. Cc: Nhan H Mai <nhan.h.mai@xxxxxxxxx> Cc: Colin Cross <ccross@xxxxxxxxxxx> Cc: Olof Johansson <olof@xxxxxxxxx> Cc: Stephen Warren <swarren@xxxxxxxxxx> Cc: Grant Likely <grant.likely@xxxxxxxxxxxx> Acked-by: Sudhakar Mamillapalli <sudhakar@xxxxxx> Reported-by: Alan Cox <alan@xxxxxxxxxxxxxxxxxxx> Acked-by: Alan Cox <alan@xxxxxxxxxxxxxxx> Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx> --- arch/arm/configs/tegra_defconfig | 1 + arch/arm/mach-tegra/board-harmony.c | 4 ++++ arch/arm/mach-tegra/board-paz00.c | 5 +++++ arch/arm/mach-tegra/board-seaboard.c | 4 ++++ arch/arm/mach-tegra/board-trimslice.c | 4 ++++ arch/arm/mach-tegra/devices.h | 1 - drivers/tty/serial/8250/8250.c | 34 +++------------------------------ drivers/tty/serial/Kconfig | 8 ++++++++ drivers/tty/serial/of_serial.c | 29 +++++++++++++++++++++++++++- include/linux/of_serial.h | 17 +++++++++++++++++ include/linux/serial_8250.h | 1 + include/linux/serial_core.h | 5 +++++ 12 files changed, 80 insertions(+), 33 deletions(-) create mode 100644 include/linux/of_serial.h diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig index 351d670..de7288a 100644 --- a/arch/arm/configs/tegra_defconfig +++ b/arch/arm/configs/tegra_defconfig @@ -97,6 +97,7 @@ CONFIG_INPUT_EVDEV=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_TEGRA=y # CONFIG_HW_RANDOM is not set CONFIG_I2C=y # CONFIG_I2C_COMPAT is not set diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c index c00aadb..b8ceac3 100644 --- a/arch/arm/mach-tegra/board-harmony.c +++ b/arch/arm/mach-tegra/board-harmony.c @@ -19,6 +19,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/serial_8250.h> +#include <linux/of_serial.h> #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/pda_power.h> @@ -52,6 +53,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTD, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, @@ -115,7 +117,9 @@ static void __init harmony_i2c_init(void) } static struct platform_device *harmony_devices[] __initdata = { +#if IS_ENABLED(CONFIG_SERIAL_TEGRA) &debug_uart, +#endif &tegra_sdhci_device1, &tegra_sdhci_device2, &tegra_sdhci_device4, diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c index 330afdf..1113dab 100644 --- a/arch/arm/mach-tegra/board-paz00.c +++ b/arch/arm/mach-tegra/board-paz00.c @@ -21,6 +21,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/serial_8250.h> +#include <linux/of_serial.h> #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/gpio_keys.h> @@ -55,6 +56,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTA, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, @@ -65,6 +67,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTC, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, @@ -142,7 +145,9 @@ static struct platform_device gpio_keys_device = { }; static struct platform_device *paz00_devices[] __initdata = { +#if IS_ENABLED(CONFIG_SERIAL_TEGRA) &debug_uart, +#endif &tegra_sdhci_device4, &tegra_sdhci_device1, &wifi_rfkill_device, diff --git a/arch/arm/mach-tegra/board-seaboard.c b/arch/arm/mach-tegra/board-seaboard.c index d669847..59a30ab 100644 --- a/arch/arm/mach-tegra/board-seaboard.c +++ b/arch/arm/mach-tegra/board-seaboard.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/serial_8250.h> +#include <linux/of_serial.h> #include <linux/i2c.h> #include <linux/delay.h> #include <linux/input.h> @@ -47,6 +48,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { /* Memory and IRQ filled in before registration */ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, @@ -145,7 +147,9 @@ static struct platform_device seaboard_audio_device = { }; static struct platform_device *seaboard_devices[] __initdata = { +#if IS_ENABLED(CONFIG_SERIAL_TEGRA) &debug_uart, +#endif &tegra_pmu_device, &tegra_sdhci_device4, &tegra_sdhci_device3, diff --git a/arch/arm/mach-tegra/board-trimslice.c b/arch/arm/mach-tegra/board-trimslice.c index cd52820..b156f55 100644 --- a/arch/arm/mach-tegra/board-trimslice.c +++ b/arch/arm/mach-tegra/board-trimslice.c @@ -22,6 +22,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/serial_8250.h> +#include <linux/of_serial.h> #include <linux/io.h> #include <linux/i2c.h> #include <linux/gpio.h> @@ -48,6 +49,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTA, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, @@ -81,7 +83,9 @@ static struct platform_device trimslice_audio_device = { }; static struct platform_device *trimslice_devices[] __initdata = { +#if IS_ENABLED(CONFIG_SERIAL_TEGRA) &debug_uart, +#endif &tegra_sdhci_device1, &tegra_sdhci_device4, &tegra_i2s_device1, diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h index ec45567..6e5f852 100644 --- a/arch/arm/mach-tegra/devices.h +++ b/arch/arm/mach-tegra/devices.h @@ -53,5 +53,4 @@ extern struct platform_device tegra_i2s_device1; extern struct platform_device tegra_i2s_device2; extern struct platform_device tegra_das_device; extern struct platform_device tegra_pcm_device; - #endif diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 5c27f7e..cbd94c3 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -1332,27 +1332,6 @@ static void serial8250_enable_ms(struct uart_port *port) } /* - * Clear the Tegra rx fifo after a break - * - * FIXME: This needs to become a port specific callback once we have a - * framework for this - */ -static void clear_rx_fifo(struct uart_8250_port *up) -{ - unsigned int status, tmout = 10000; - do { - status = serial_in(up, UART_LSR); - if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) - status = serial_in(up, UART_RX); - else - break; - if (--tmout == 0) - break; - udelay(1); - } while (1); -} - -/* * serial8250_rx_chars: processes according to the passed in LSR * value, and returns the remaining LSR bits not handled * by this Rx routine. @@ -1386,20 +1365,10 @@ serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) up->lsr_saved_flags = 0; if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { - /* - * For statistics only - */ if (lsr & UART_LSR_BI) { lsr &= ~(UART_LSR_FE | UART_LSR_PE); port->icount.brk++; /* - * If tegra port then clear the rx fifo to - * accept another break/character. - */ - if (port->type == PORT_TEGRA) - clear_rx_fifo(up); - - /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask @@ -3037,6 +3006,7 @@ static int __devinit serial8250_probe(struct platform_device *dev) port.serial_in = p->serial_in; port.serial_out = p->serial_out; port.handle_irq = p->handle_irq; + port.handle_break = p->handle_break; port.set_termios = p->set_termios; port.pm = p->pm; port.dev = &dev->dev; @@ -3209,6 +3179,8 @@ int serial8250_register_port(struct uart_port *port) uart->port.set_termios = port->set_termios; if (port->pm) uart->port.pm = port->pm; + if (port->handle_break) + uart->port.handle_break = port->handle_break; if (serial8250_isa_config != NULL) serial8250_isa_config(0, &uart->port, diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 665beb6..33fa6ec 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -380,6 +380,14 @@ config SERIAL_PXA_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) +# FIXME remove this option when Tegra completes conversion to open firmware +config SERIAL_TEGRA + bool "Tegra serial port support" + depends on SERIAL_OF_PLATFORM=y + help + If you have a machine based on NVIDIA Tegra you can enable its + onboard serial ports by enabling this option. + config SERIAL_SA1100 bool "SA1100 serial port support" depends on ARM && ARCH_SA1100 diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 4621cf9..5a3b3f6 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -12,8 +12,10 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/serial_core.h> #include <linux/serial_8250.h> +#include <linux/serial_reg.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> @@ -168,6 +170,31 @@ static int of_platform_serial_remove(struct platform_device *ofdev) return 0; } +#if IS_ENABLED(CONFIG_SERIAL_TEGRA) +void tegra_serial_handle_break(struct uart_port *p) +{ + unsigned int status, tmout = 10000; + + do { + status = p->serial_in(p, UART_LSR); + if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) + status = p->serial_in(p, UART_RX); + else + break; + if (--tmout == 0) + break; + udelay(1); + } while (1); +} +/* FIXME remove this export when tegra finishes conversion to open firmware */ +EXPORT_SYMBOL_GPL(tegra_serial_handle_break); +#endif + +static void tegra_setup_quirk(struct uart_port *port) +{ + port->handle_break = tegra_serial_handle_break; +} + /* * contiguous OF_ number space for serial port types so the * __sinfo array does not have holes, and to allow cases of: @@ -192,7 +219,7 @@ static struct of_serial_info __refdata __sinfo[] = { [OF_PORT_16550] = { .type = PORT_16550, }, [OF_PORT_16750] = { .type = PORT_16750, }, [OF_PORT_16850] = { .type = PORT_16850, }, - [OF_PORT_TEGRA] = { .type = PORT_TEGRA, }, + [OF_PORT_TEGRA] = { .type = PORT_TEGRA, .setup = tegra_setup_quirk, }, [OF_PORT_NWPSERIAL] = { .type = PORT_NWPSERIAL, }, [OF_PORT_UNKNOWN] = { .type = PORT_UNKNOWN, }, }; diff --git a/include/linux/of_serial.h b/include/linux/of_serial.h new file mode 100644 index 0000000..2035d96 --- /dev/null +++ b/include/linux/of_serial.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_OF_SERIAL_H +#define __LINUX_OF_SERIAL_H + +/* + * FIXME remove this file when tegra finishes conversion to open firmware, + * expectation is that all quirks will then be self-contained in + * drivers/tty/serial/of_serial.c. + */ +#if IS_ENABLED(CONFIG_SERIAL_TEGRA) +extern void tegra_serial_handle_break(struct uart_port *port); +#else +static inline void tegra_serial_handle_break(struct uart_port *port) +{ +} +#endif + +#endif /* __LINUX_OF_SERIAL */ diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 8f012f8..a522fd9 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -38,6 +38,7 @@ struct plat_serial8250_port { int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned old); + void (*handle_break)(struct uart_port *); }; /* diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 2db407a..65db992 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -310,6 +310,7 @@ struct uart_port { int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned int old); + void (*handle_break)(struct uart_port *); unsigned int irq; /* irq number */ unsigned long irqflags; /* irq flags */ unsigned int uartclk; /* base uart clock */ @@ -533,6 +534,10 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) static inline int uart_handle_break(struct uart_port *port) { struct uart_state *state = port->state; + + if (port->handle_break) + port->handle_break(port); + #ifdef SUPPORT_SYSRQ if (port->cons && port->cons->index == port->line) { if (!port->sysrq) { -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html