diff options
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/8250.c | 4 | ||||
-rw-r--r-- | drivers/serial/8250_pnp.c | 2 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 105 | ||||
-rw-r--r-- | drivers/serial/Makefile | 3 | ||||
-rw-r--r-- | drivers/serial/altera_jtaguart.c | 504 | ||||
-rw-r--r-- | drivers/serial/altera_uart.c | 570 | ||||
-rw-r--r-- | drivers/serial/amba-pl011.c | 6 | ||||
-rw-r--r-- | drivers/serial/atmel_serial.c | 207 | ||||
-rw-r--r-- | drivers/serial/bfin_sport_uart.c | 209 | ||||
-rw-r--r-- | drivers/serial/bfin_sport_uart.h | 27 | ||||
-rw-r--r-- | drivers/serial/imx.c | 10 | ||||
-rw-r--r-- | drivers/serial/kgdboc.c | 94 | ||||
-rw-r--r-- | drivers/serial/mpc52xx_uart.c | 35 | ||||
-rw-r--r-- | drivers/serial/mpsc.c | 1 | ||||
-rw-r--r-- | drivers/serial/pmac_zilog.c | 4 | ||||
-rw-r--r-- | drivers/serial/serial_cs.c | 36 | ||||
-rw-r--r-- | drivers/serial/serial_ks8695.c | 1 | ||||
-rw-r--r-- | drivers/serial/sh-sci.c | 195 | ||||
-rw-r--r-- | drivers/serial/sunzilog.c | 50 | ||||
-rw-r--r-- | drivers/serial/timbuart.c | 25 | ||||
-rw-r--r-- | drivers/serial/uartlite.c | 32 |
21 files changed, 1833 insertions, 287 deletions
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 2b1ea3d4c4f..891e1dd65f2 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1891,8 +1891,8 @@ static int serial8250_get_poll_char(struct uart_port *port) struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned char lsr = serial_inp(up, UART_LSR); - while (!(lsr & UART_LSR_DR)) - lsr = serial_inp(up, UART_LSR); + if (!(lsr & UART_LSR_DR)) + return NO_POLL_CHAR; return serial_inp(up, UART_RX); } diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c index 24485cc62ff..4822cb50cd0 100644 --- a/drivers/serial/8250_pnp.c +++ b/drivers/serial/8250_pnp.c @@ -348,6 +348,8 @@ static const struct pnp_device_id pnp_dev_table[] = { { "FUJ02E6", 0 }, /* Fujitsu Wacom 2FGT Tablet PC device */ { "FUJ02E7", 0 }, + /* Fujitsu Wacom 1FGT Tablet PC device */ + { "FUJ02E9", 0 }, /* * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in * disguise) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index f55c49475a8..8b23165bc5d 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -518,12 +518,13 @@ config SERIAL_S3C2412 Serial port support for the Samsung S3C2412 and S3C2413 SoC config SERIAL_S3C2440 - tristate "Samsung S3C2440/S3C2442 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442) + tristate "Samsung S3C2440/S3C2442/S3C2416 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442 || CPU_S3C2416) default y if CPU_S3C2440 default y if CPU_S3C2442 + select SERIAL_SAMSUNG_UARTS_4 if CPU_S3C2416 help - Serial port support for the Samsung S3C2440 and S3C2442 SoC + Serial port support for the Samsung S3C2440, S3C2416 and S3C2442 SoC config SERIAL_S3C24A0 tristate "Samsung S3C24A0 Serial port support" @@ -533,21 +534,13 @@ config SERIAL_S3C24A0 Serial port support for the Samsung S3C24A0 SoC config SERIAL_S3C6400 - tristate "Samsung S3C6400/S3C6410/S5P6440 Seria port support" - depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440) + tristate "Samsung S3C6400/S3C6410/S5P6440/S5PC100 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440 || CPU_S5PC100) select SERIAL_SAMSUNG_UARTS_4 default y help - Serial port support for the Samsung S3C6400, S3C6410 and S5P6440 - SoCs - -config SERIAL_S5PC100 - tristate "Samsung S5PC100 Serial port support" - depends on SERIAL_SAMSUNG && CPU_S5PC100 - select SERIAL_SAMSUNG_UARTS_4 - default y - help - Serial port support for the Samsung S5PC100 SoCs + Serial port support for the Samsung S3C6400, S3C6410, S5P6440 + and S5PC100 SoCs config SERIAL_S5PV210 tristate "Samsung S5PV210 Serial port support" @@ -1430,8 +1423,8 @@ config SERIAL_SC26XX_CONSOLE Support for Console on SC2681/SC2692 serial ports. config SERIAL_BFIN_SPORT - tristate "Blackfin SPORT emulate UART (EXPERIMENTAL)" - depends on BLACKFIN && EXPERIMENTAL + tristate "Blackfin SPORT emulate UART" + depends on BLACKFIN select SERIAL_CORE help Enable SPORT emulate UART on Blackfin series. @@ -1446,28 +1439,52 @@ config SERIAL_BFIN_SPORT_CONSOLE config SERIAL_BFIN_SPORT0_UART bool "Enable UART over SPORT0" - depends on SERIAL_BFIN_SPORT && !(BF542 || BF542M || BF544 || BF544M) + depends on SERIAL_BFIN_SPORT && !(BF542 || BF544) help Enable UART over SPORT0 +config SERIAL_BFIN_SPORT0_UART_CTSRTS + bool "Enable UART over SPORT0 hardware flow control" + depends on SERIAL_BFIN_SPORT0_UART + help + Enable hardware flow control in the driver. + config SERIAL_BFIN_SPORT1_UART bool "Enable UART over SPORT1" depends on SERIAL_BFIN_SPORT help Enable UART over SPORT1 +config SERIAL_BFIN_SPORT1_UART_CTSRTS + bool "Enable UART over SPORT1 hardware flow control" + depends on SERIAL_BFIN_SPORT1_UART + help + Enable hardware flow control in the driver. + config SERIAL_BFIN_SPORT2_UART bool "Enable UART over SPORT2" depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) help Enable UART over SPORT2 +config SERIAL_BFIN_SPORT2_UART_CTSRTS + bool "Enable UART over SPORT2 hardware flow control" + depends on SERIAL_BFIN_SPORT2_UART + help + Enable hardware flow control in the driver. + config SERIAL_BFIN_SPORT3_UART bool "Enable UART over SPORT3" depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) help Enable UART over SPORT3 +config SERIAL_BFIN_SPORT3_UART_CTSRTS + bool "Enable UART over SPORT3 hardware flow control" + depends on SERIAL_BFIN_SPORT3_UART + help + Enable hardware flow control in the driver. + config SERIAL_TIMBERDALE tristate "Support for timberdale UART" select SERIAL_CORE @@ -1506,4 +1523,56 @@ config SERIAL_GRLIB_GAISLER_APBUART_CONSOLE help Support for running a console on the GRLIB APBUART +config SERIAL_ALTERA_JTAGUART + tristate "Altera JTAG UART support" + select SERIAL_CORE + help + This driver supports the Altera JTAG UART port. + +config SERIAL_ALTERA_JTAGUART_CONSOLE + bool "Altera JTAG UART console support" + depends on SERIAL_ALTERA_JTAGUART=y + select SERIAL_CORE_CONSOLE + help + Enable a Altera JTAG UART port to be the system console. + +config SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS + bool "Bypass output when no connection" + depends on SERIAL_ALTERA_JTAGUART_CONSOLE + select SERIAL_CORE_CONSOLE + help + Bypass console output and keep going even if there is no + JTAG terminal connection with the host. + +config SERIAL_ALTERA_UART + tristate "Altera UART support" + select SERIAL_CORE + help + This driver supports the Altera softcore UART port. + +config SERIAL_ALTERA_UART_MAXPORTS + int "Maximum number of Altera UART ports" + depends on SERIAL_ALTERA_UART + default 4 + help + This setting lets you define the maximum number of the Altera + UART ports. The usual default varies from board to board, and + this setting is a way of catering for that. + +config SERIAL_ALTERA_UART_BAUDRATE + int "Default baudrate for Altera UART ports" + depends on SERIAL_ALTERA_UART + default 115200 + help + This setting lets you define what the default baudrate is for the + Altera UART ports. The usual default varies from board to board, + and this setting is a way of catering for that. + +config SERIAL_ALTERA_UART_CONSOLE + bool "Altera UART console support" + depends on SERIAL_ALTERA_UART=y + select SERIAL_CORE_CONSOLE + help + Enable a Altera UART port to be the system console. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 6aa4723b74e..208a85572c3 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -44,7 +44,6 @@ obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o -obj-$(CONFIG_SERIAL_S5PC100) += s3c6400.o obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o @@ -83,3 +82,5 @@ obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o +obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o +obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o diff --git a/drivers/serial/altera_jtaguart.c b/drivers/serial/altera_jtaguart.c new file mode 100644 index 00000000000..f9b49b5ff5e --- /dev/null +++ b/drivers/serial/altera_jtaguart.c @@ -0,0 +1,504 @@ +/* + * altera_jtaguart.c -- Altera JTAG UART driver + * + * Based on mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com> + * (C) Copyright 2008, Thomas Chou <thomas@wytron.com.tw> + * (C) Copyright 2010, Tobias Klauser <tklauser@distanz.ch> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/altera_jtaguart.h> + +#define DRV_NAME "altera_jtaguart" + +/* + * Altera JTAG UART register definitions according to the Altera JTAG UART + * datasheet: http://www.altera.com/literature/hb/nios2/n2cpu_nii51009.pdf + */ + +#define ALTERA_JTAGUART_SIZE 8 + +#define ALTERA_JTAGUART_DATA_REG 0 + +#define ALTERA_JTAGUART_DATA_DATA_MSK 0x000000FF +#define ALTERA_JTAGUART_DATA_RVALID_MSK 0x00008000 +#define ALTERA_JTAGUART_DATA_RAVAIL_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_DATA_RAVAIL_OFF 16 + +#define ALTERA_JTAGUART_CONTROL_REG 4 + +#define ALTERA_JTAGUART_CONTROL_RE_MSK 0x00000001 +#define ALTERA_JTAGUART_CONTROL_WE_MSK 0x00000002 +#define ALTERA_JTAGUART_CONTROL_RI_MSK 0x00000100 +#define ALTERA_JTAGUART_CONTROL_RI_OFF 8 +#define ALTERA_JTAGUART_CONTROL_WI_MSK 0x00000200 +#define ALTERA_JTAGUART_CONTROL_AC_MSK 0x00000400 +#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_CONTROL_WSPACE_OFF 16 + +/* + * Local per-uart structure. + */ +struct altera_jtaguart { + struct uart_port port; + unsigned int sigs; /* Local copy of line sigs */ + unsigned long imr; /* Local IMR mirror */ +}; + +static unsigned int altera_jtaguart_tx_empty(struct uart_port *port) +{ + return (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) ? TIOCSER_TEMT : 0; +} + +static unsigned int altera_jtaguart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void altera_jtaguart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ +} + +static void altera_jtaguart_start_tx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr |= ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_stop_tx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_stop_rx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr &= ~ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_break_ctl(struct uart_port *port, int break_state) +{ +} + +static void altera_jtaguart_enable_ms(struct uart_port *port) +{ +} + +static void altera_jtaguart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + /* Just copy the old termios settings back */ + if (old) + tty_termios_copy_hw(termios, old); +} + +static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + unsigned long status; + + while ((status = readl(port->membase + ALTERA_JTAGUART_DATA_REG)) & + ALTERA_JTAGUART_DATA_RVALID_MSK) { + ch = status & ALTERA_JTAGUART_DATA_DATA_MSK; + flag = TTY_NORMAL; + port->icount.rx++; + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, 0, 0, ch, flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned int pending, count; + + if (port->x_char) { + /* Send special char - probably flow control */ + writel(port->x_char, port->membase + ALTERA_JTAGUART_DATA_REG); + port->x_char = 0; + port->icount.tx++; + return; + } + + pending = uart_circ_chars_pending(xmit); + if (pending > 0) { + count = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) >> + ALTERA_JTAGUART_CONTROL_WSPACE_OFF; + if (count > pending) + count = pending; + if (count > 0) { + pending -= count; + while (count--) { + writel(xmit->buf[xmit->tail], + port->membase + ALTERA_JTAGUART_DATA_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + if (pending < WAKEUP_CHARS) + uart_write_wakeup(port); + } + } + + if (pending == 0) { + pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + } +} + +static irqreturn_t altera_jtaguart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned int isr; + + isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >> + ALTERA_JTAGUART_CONTROL_RI_OFF) & pp->imr; + + spin_lock(&port->lock); + + if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK) + altera_jtaguart_rx_chars(pp); + if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK) + altera_jtaguart_tx_chars(pp); + + spin_unlock(&port->lock); + + return IRQ_RETVAL(isr); +} + +static void altera_jtaguart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ALTERA_JTAGUART; + + /* Clear mask, so no surprise interrupts. */ + writel(0, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static int altera_jtaguart_startup(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned long flags; + int ret; + + ret = request_irq(port->irq, altera_jtaguart_interrupt, IRQF_DISABLED, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void altera_jtaguart_shutdown(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +static const char *altera_jtaguart_type(struct uart_port *port) +{ + return (port->type == PORT_ALTERA_JTAGUART) ? "Altera JTAG UART" : NULL; +} + +static int altera_jtaguart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void altera_jtaguart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int altera_jtaguart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != PORT_ALTERA_JTAGUART) + return -EINVAL; + return 0; +} + +/* + * Define the basic serial functions we support. + */ +static struct uart_ops altera_jtaguart_ops = { + .tx_empty = altera_jtaguart_tx_empty, + .get_mctrl = altera_jtaguart_get_mctrl, + .set_mctrl = altera_jtaguart_set_mctrl, + .start_tx = altera_jtaguart_start_tx, + .stop_tx = altera_jtaguart_stop_tx, + .stop_rx = altera_jtaguart_stop_rx, + .enable_ms = altera_jtaguart_enable_ms, + .break_ctl = altera_jtaguart_break_ctl, + .startup = altera_jtaguart_startup, + .shutdown = altera_jtaguart_shutdown, + .set_termios = altera_jtaguart_set_termios, + .type = altera_jtaguart_type, + .request_port = altera_jtaguart_request_port, + .release_port = altera_jtaguart_release_port, + .config_port = altera_jtaguart_config_port, + .verify_port = altera_jtaguart_verify_port, +}; + +#define ALTERA_JTAGUART_MAXPORTS 1 +static struct altera_jtaguart altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS]; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) + +int __init early_altera_jtaguart_setup(struct altera_jtaguart_platform_uart + *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_jtaguart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_JTAGUART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->flags = ASYNC_BOOT_AUTOCONF; + port->ops = &altera_jtaguart_ops; + } + + return 0; +} + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) +static void altera_jtaguart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; + unsigned long status; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while (((status = readl(port->membase + ALTERA_JTAGUART_CONTROL_REG)) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { + if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) { + spin_unlock_irqrestore(&port->lock, flags); + return; /* no connection activity */ + } + spin_unlock_irqrestore(&port->lock, flags); + cpu_relax(); + spin_lock_irqsave(&port->lock, flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); + spin_unlock_irqrestore(&port->lock, flags); +} +#else +static void altera_jtaguart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while ((readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { + spin_unlock_irqrestore(&port->lock, flags); + cpu_relax(); + spin_lock_irqsave(&port->lock, flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); + spin_unlock_irqrestore(&port->lock, flags); +} +#endif + +static void altera_jtaguart_console_write(struct console *co, const char *s, + unsigned int count) +{ + for (; count; count--, s++) { + altera_jtaguart_console_putc(co, *s); + if (*s == '\n') + altera_jtaguart_console_putc(co, '\r'); + } +} + +static int __init altera_jtaguart_console_setup(struct console *co, + char *options) +{ + struct uart_port *port; + + if (co->index < 0 || co->index >= ALTERA_JTAGUART_MAXPORTS) + return -EINVAL; + port = &altera_jtaguart_ports[co->index].port; + if (port->membase == 0) + return -ENODEV; + return 0; +} + +static struct uart_driver altera_jtaguart_driver; + +static struct console altera_jtaguart_console = { + .name = "ttyJ", + .write = altera_jtaguart_console_write, + .device = uart_console_device, + .setup = altera_jtaguart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &altera_jtaguart_driver, +}; + +static int __init altera_jtaguart_console_init(void) +{ + register_console(&altera_jtaguart_console); + return 0; +} + +console_initcall(altera_jtaguart_console_init); + +#define ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console) + +#else + +#define ALTERA_JTAGUART_CONSOLE NULL + +#endif /* CONFIG_ALTERA_JTAGUART_CONSOLE */ + +static struct uart_driver altera_jtaguart_driver = { + .owner = THIS_MODULE, + .driver_name = "altera_jtaguart", + .dev_name = "ttyJ", + .major = ALTERA_JTAGUART_MAJOR, + .minor = ALTERA_JTAGUART_MINOR, + .nr = ALTERA_JTAGUART_MAXPORTS, + .cons = ALTERA_JTAGUART_CONSOLE, +}; + +static int __devinit altera_jtaguart_probe(struct platform_device *pdev) +{ + struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_jtaguart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_JTAGUART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->ops = &altera_jtaguart_ops; + port->flags = ASYNC_BOOT_AUTOCONF; + + uart_add_one_port(&altera_jtaguart_driver, port); + } + + return 0; +} + +static int __devexit altera_jtaguart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS; i++) { + port = &altera_jtaguart_ports[i].port; + if (port) + uart_remove_one_port(&altera_jtaguart_driver, port); + } + + return 0; +} + +static struct platform_driver altera_jtaguart_platform_driver = { + .probe = altera_jtaguart_probe, + .remove = __devexit_p(altera_jtaguart_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init altera_jtaguart_init(void) +{ + int rc; + + rc = uart_register_driver(&altera_jtaguart_driver); + if (rc) + return rc; + rc = platform_driver_register(&altera_jtaguart_platform_driver); + if (rc) { + uart_unregister_driver(&altera_jtaguart_driver); + return rc; + } + return 0; +} + +static void __exit altera_jtaguart_exit(void) +{ + platform_driver_unregister(&altera_jtaguart_platform_driver); + uart_unregister_driver(&altera_jtaguart_driver); +} + +module_init(altera_jtaguart_init); +module_exit(altera_jtaguart_exit); + +MODULE_DESCRIPTION("Altera JTAG UART driver"); +MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/serial/altera_uart.c b/drivers/serial/altera_uart.c new file mode 100644 index 00000000000..bcee156d2f2 --- /dev/null +++ b/drivers/serial/altera_uart.c @@ -0,0 +1,570 @@ +/* + * altera_uart.c -- Altera UART driver + * + * Based on mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com> + * (C) Copyright 2008, Thomas Chou <thomas@wytron.com.tw> + * (C) Copyright 2010, Tobias Klauser <tklauser@distanz.ch> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/altera_uart.h> + +#define DRV_NAME "altera_uart" + +/* + * Altera UART register definitions according to the Nios UART datasheet: + * http://www.altera.com/literature/ds/ds_nios_uart.pdf + */ + +#define ALTERA_UART_SIZE 32 + +#define ALTERA_UART_RXDATA_REG 0 +#define ALTERA_UART_TXDATA_REG 4 +#define ALTERA_UART_STATUS_REG 8 +#define ALTERA_UART_CONTROL_REG 12 +#define ALTERA_UART_DIVISOR_REG 16 +#define ALTERA_UART_EOP_REG 20 + +#define ALTERA_UART_STATUS_PE_MSK 0x0001 /* parity error */ +#define ALTERA_UART_STATUS_FE_MSK 0x0002 /* framing error */ +#define ALTERA_UART_STATUS_BRK_MSK 0x0004 /* break */ +#define ALTERA_UART_STATUS_ROE_MSK 0x0008 /* RX overrun error */ +#define ALTERA_UART_STATUS_TOE_MSK 0x0010 /* TX overrun error */ +#define ALTERA_UART_STATUS_TMT_MSK 0x0020 /* TX shift register state */ +#define ALTERA_UART_STATUS_TRDY_MSK 0x0040 /* TX ready */ +#define ALTERA_UART_STATUS_RRDY_MSK 0x0080 /* RX ready */ +#define ALTERA_UART_STATUS_E_MSK 0x0100 /* exception condition */ +#define ALTERA_UART_STATUS_DCTS_MSK 0x0400 /* CTS logic-level change */ +#define ALTERA_UART_STATUS_CTS_MSK 0x0800 /* CTS logic state */ +#define ALTERA_UART_STATUS_EOP_MSK 0x1000 /* EOP written/read */ + + /* Enable interrupt on... */ +#define ALTERA_UART_CONTROL_PE_MSK 0x0001 /* ...parity error */ +#define ALTERA_UART_CONTROL_FE_MSK 0x0002 /* ...framing error */ +#define ALTERA_UART_CONTROL_BRK_MSK 0x0004 /* ...break */ +#define ALTERA_UART_CONTROL_ROE_MSK 0x0008 /* ...RX overrun */ +#define ALTERA_UART_CONTROL_TOE_MSK 0x0010 /* ...TX overrun */ +#define ALTERA_UART_CONTROL_TMT_MSK 0x0020 /* ...TX shift register empty */ +#define ALTERA_UART_CONTROL_TRDY_MSK 0x0040 /* ...TX ready */ +#define ALTERA_UART_CONTROL_RRDY_MSK 0x0080 /* ...RX ready */ +#define ALTERA_UART_CONTROL_E_MSK 0x0100 /* ...exception*/ + +#define ALTERA_UART_CONTROL_TRBK_MSK 0x0200 /* TX break */ +#define ALTERA_UART_CONTROL_DCTS_MSK 0x0400 /* Interrupt on CTS change */ +#define ALTERA_UART_CONTROL_RTS_MSK 0x0800 /* RTS signal */ +#define ALTERA_UART_CONTROL_EOP_MSK 0x1000 /* Interrupt on EOP */ + +/* + * Local per-uart structure. + */ +struct altera_uart { + struct uart_port port; + unsigned int sigs; /* Local copy of line sigs */ + unsigned short imr; /* Local IMR mirror */ +}; + +static unsigned int altera_uart_tx_empty(struct uart_port *port) +{ + return (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TMT_MSK) ? TIOCSER_TEMT : 0; +} + +static unsigned int altera_uart_get_mctrl(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + unsigned int sigs; + + spin_lock_irqsave(&port->lock, flags); + sigs = + (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_CTS_MSK) ? TIOCM_CTS : 0; + sigs |= (pp->sigs & TIOCM_RTS); + spin_unlock_irqrestore(&port->lock, flags); + + return sigs; +} + +static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->sigs = sigs; + if (sigs & TIOCM_RTS) + pp->imr |= ALTERA_UART_CONTROL_RTS_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_start_tx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_stop_tx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_stop_rx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_break_ctl(struct uart_port *port, int break_state) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (break_state == -1) + pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_enable_ms(struct uart_port *port) +{ +} + +static void altera_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, baudclk; + + baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + baudclk = port->uartclk / baud; + + if (old) + tty_termios_copy_hw(termios, old); + tty_termios_encode_baud_rate(termios, baud, baud); + + spin_lock_irqsave(&port->lock, flags); + writel(baudclk, port->membase + ALTERA_UART_DIVISOR_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_rx_chars(struct altera_uart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + unsigned short status; + + while ((status = readl(port->membase + ALTERA_UART_STATUS_REG)) & + ALTERA_UART_STATUS_RRDY_MSK) { + ch = readl(port->membase + ALTERA_UART_RXDATA_REG); + flag = TTY_NORMAL; + port->icount.rx++; + + if (status & ALTERA_UART_STATUS_E_MSK) { + writel(status, port->membase + ALTERA_UART_STATUS_REG); + + if (status & ALTERA_UART_STATUS_BRK_MSK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (status & ALTERA_UART_STATUS_PE_MSK) { + port->icount.parity++; + } else if (status & ALTERA_UART_STATUS_ROE_MSK) { + port->icount.overrun++; + } else if (status & ALTERA_UART_STATUS_FE_MSK) { + port->icount.frame++; + } + + status &= port->read_status_mask; + + if (status & ALTERA_UART_STATUS_BRK_MSK) + flag = TTY_BREAK; + else if (status & ALTERA_UART_STATUS_PE_MSK) + flag = TTY_PARITY; + else if (status & ALTERA_UART_STATUS_FE_MSK) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, status, ALTERA_UART_STATUS_ROE_MSK, ch, + flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +static void altera_uart_tx_chars(struct altera_uart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + /* Send special char - probably flow control */ + writel(port->x_char, port->membase + ALTERA_UART_TXDATA_REG); + port->x_char = 0; + port->icount.tx++; + return; + } + + while (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) { + if (xmit->head == xmit->tail) + break; + writel(xmit->buf[xmit->tail], + port->membase + ALTERA_UART_TXDATA_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (xmit->head == xmit->tail) { + pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + } +} + +static irqreturn_t altera_uart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned int isr; + + isr = readl(port->membase + ALTERA_UART_STATUS_REG) & pp->imr; + if (isr & ALTERA_UART_STATUS_RRDY_MSK) + altera_uart_rx_chars(pp); + if (isr & ALTERA_UART_STATUS_TRDY_MSK) + altera_uart_tx_chars(pp); + return IRQ_RETVAL(isr); +} + +static void altera_uart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ALTERA_UART; + + /* Clear mask, so no surprise interrupts. */ + writel(0, port->membase + ALTERA_UART_CONTROL_REG); + /* Clear status register */ + writel(0, port->membase + ALTERA_UART_STATUS_REG); +} + +static int altera_uart_startup(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + int ret; + + ret = request_irq(port->irq, altera_uart_interrupt, IRQF_DISABLED, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_UART_CONTROL_RRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void altera_uart_shutdown(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +static const char *altera_uart_type(struct uart_port *port) +{ + return (port->type == PORT_ALTERA_UART) ? "Altera UART" : NULL; +} + +static int altera_uart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void altera_uart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int altera_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_ALTERA_UART)) + return -EINVAL; + return 0; +} + +/* + * Define the basic serial functions we support. + */ +static struct uart_ops altera_uart_ops = { + .tx_empty = altera_uart_tx_empty, + .get_mctrl = altera_uart_get_mctrl, + .set_mctrl = altera_uart_set_mctrl, + .start_tx = altera_uart_start_tx, + .stop_tx = altera_uart_stop_tx, + .stop_rx = altera_uart_stop_rx, + .enable_ms = altera_uart_enable_ms, + .break_ctl = altera_uart_break_ctl, + .startup = altera_uart_startup, + .shutdown = altera_uart_shutdown, + .set_termios = altera_uart_set_termios, + .type = altera_uart_type, + .request_port = altera_uart_request_port, + .release_port = altera_uart_release_port, + .config_port = altera_uart_config_port, + .verify_port = altera_uart_verify_port, +}; + +static struct altera_uart altera_uart_ports[CONFIG_SERIAL_ALTERA_UART_MAXPORTS]; + +#if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + +int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_uart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_UART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->uartclk = platp[i].uartclk; + port->flags = ASYNC_BOOT_AUTOCONF; + port->ops = &altera_uart_ops; + } + + return 0; +} + +static void altera_uart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_uart_ports + co->index)->port; + int i; + + for (i = 0; i < 0x10000; i++) { + if (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) + break; + } + writel(c, port->membase + ALTERA_UART_TXDATA_REG); + for (i = 0; i < 0x10000; i++) { + if (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) + break; + } +} + +static void altera_uart_console_write(struct console *co, const char *s, + unsigned int count) +{ + for (; count; count--, s++) { + altera_uart_console_putc(co, *s); + if (*s == '\n') + altera_uart_console_putc(co, '\r'); + } +} + +static int __init altera_uart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) + return -EINVAL; + port = &altera_uart_ports[co->index].port; + if (port->membase == 0) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver altera_uart_driver; + +static struct console altera_uart_console = { + .name = "ttyS", + .write = altera_uart_console_write, + .device = uart_console_device, + .setup = altera_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &altera_uart_driver, +}; + +static int __init altera_uart_console_init(void) +{ + register_console(&altera_uart_console); + return 0; +} + +console_initcall(altera_uart_console_init); + +#define ALTERA_UART_CONSOLE (&altera_uart_console) + +#else + +#define ALTERA_UART_CONSOLE NULL + +#endif /* CONFIG_ALTERA_UART_CONSOLE */ + +/* + * Define the altera_uart UART driver structure. + */ +static struct uart_driver altera_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = CONFIG_SERIAL_ALTERA_UART_MAXPORTS, + .cons = ALTERA_UART_CONSOLE, +}; + +static int __devinit altera_uart_probe(struct platform_device *pdev) +{ + struct altera_uart_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_uart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_UART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->uartclk = platp[i].uartclk; + port->ops = &altera_uart_ops; + port->flags = ASYNC_BOOT_AUTOCONF; + + uart_add_one_port(&altera_uart_driver, port); + } + + return 0; +} + +static int altera_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS; i++) { + port = &altera_uart_ports[i].port; + if (port) + uart_remove_one_port(&altera_uart_driver, port); + } + + return 0; +} + +static struct platform_driver altera_uart_platform_driver = { + .probe = altera_uart_probe, + .remove = __devexit_p(altera_uart_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = NULL, + }, +}; + +static int __init altera_uart_init(void) +{ + int rc; + + rc = uart_register_driver(&altera_uart_driver); + if (rc) + return rc; + rc = platform_driver_register(&altera_uart_platform_driver); + if (rc) { + uart_unregister_driver(&altera_uart_driver); + return rc; + } + return 0; +} + +static void __exit altera_uart_exit(void) +{ + platform_driver_unregister(&altera_uart_platform_driver); + uart_unregister_driver(&altera_uart_driver); +} + +module_init(altera_uart_init); +module_exit(altera_uart_exit); + +MODULE_DESCRIPTION("Altera UART driver"); +MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index 743ebf5f16d..eb4cb480b93 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -342,9 +342,9 @@ static int pl010_get_poll_char(struct uart_port *port) struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int status; - do { - status = readw(uap->port.membase + UART01x_FR); - } while (status & UART01x_FR_RXFE); + status = readw(uap->port.membase + UART01x_FR); + if (status & UART01x_FR_RXFE) + return NO_POLL_CHAR; return readw(uap->port.membase + UART01x_DR); } diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 2c9bf9b6832..eed3c2d8dd1 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -38,6 +38,7 @@ #include <linux/dma-mapping.h> #include <linux/atmel_pdc.h> #include <linux/atmel_serial.h> +#include <linux/uaccess.h> #include <asm/io.h> @@ -59,6 +60,9 @@ #include <linux/serial_core.h> +static void atmel_start_rx(struct uart_port *port); +static void atmel_stop_rx(struct uart_port *port); + #ifdef CONFIG_SERIAL_ATMEL_TTYAT /* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we @@ -93,6 +97,7 @@ #define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR) #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) +#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR) /* PDC registers */ #define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) @@ -147,6 +152,9 @@ struct atmel_uart_port { unsigned int irq_status_prev; struct circ_buf rx_ring; + + struct serial_rs485 rs485; /* rs485 settings */ + unsigned int tx_done_mask; }; static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; @@ -187,6 +195,46 @@ static bool atmel_use_dma_tx(struct uart_port *port) } #endif +/* Enable or disable the rs485 support */ +void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int mode; + + spin_lock(&port->lock); + + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + mode = UART_GET_MR(port); + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + atmel_port->rs485 = *rs485conf; + + if (rs485conf->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + UART_PUT_TTGR(port, rs485conf->delay_rts_before_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + if (atmel_use_dma_tx(port)) + atmel_port->tx_done_mask = ATMEL_US_ENDTX | + ATMEL_US_TXBUFE; + else + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } + UART_PUT_MR(port, mode); + + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + + spin_unlock(&port->lock); + +} + /* * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. */ @@ -202,6 +250,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) { unsigned int control = 0; unsigned int mode; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); #ifdef CONFIG_ARCH_AT91RM9200 if (cpu_is_at91rm9200()) { @@ -236,6 +285,17 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) mode |= ATMEL_US_CHMODE_LOC_LOOP; else mode |= ATMEL_US_CHMODE_NORMAL; + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } UART_PUT_MR(port, mode); } @@ -268,12 +328,17 @@ static u_int atmel_get_mctrl(struct uart_port *port) */ static void atmel_stop_tx(struct uart_port *port) { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + if (atmel_use_dma_tx(port)) { /* disable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); - } else - UART_PUT_IDR(port, ATMEL_US_TXRDY); + } + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_start_rx(port); } /* @@ -281,17 +346,39 @@ static void atmel_stop_tx(struct uart_port *port) */ static void atmel_start_tx(struct uart_port *port) { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + if (atmel_use_dma_tx(port)) { if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) /* The transmitter is already running. Yes, we really need this.*/ return; - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_stop_rx(port); + /* re-enable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - } else - UART_PUT_IER(port, ATMEL_US_TXRDY); + } + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); +} + +/* + * start receiving - port is in process of being opened. + */ +static void atmel_start_rx(struct uart_port *port) +{ + UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */ + + if (atmel_use_dma_rx(port)) { + /* enable PDC controller */ + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); + } else { + UART_PUT_IER(port, ATMEL_US_RXRDY); + } } /* @@ -302,9 +389,11 @@ static void atmel_stop_rx(struct uart_port *port) if (atmel_use_dma_rx(port)) { /* disable PDC receive */ UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); - UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); - } else + UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + } else { UART_PUT_IDR(port, ATMEL_US_RXRDY); + } } /* @@ -428,8 +517,9 @@ static void atmel_rx_chars(struct uart_port *port) static void atmel_tx_chars(struct uart_port *port) { struct circ_buf *xmit = &port->state->xmit; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) { + if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) { UART_PUT_CHAR(port, port->x_char); port->icount.tx++; port->x_char = 0; @@ -437,7 +527,7 @@ static void atmel_tx_chars(struct uart_port *port) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; - while (UART_GET_CSR(port) & ATMEL_US_TXRDY) { + while (UART_GET_CSR(port) & atmel_port->tx_done_mask) { UART_PUT_CHAR(port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; @@ -449,7 +539,8 @@ static void atmel_tx_chars(struct uart_port *port) uart_write_wakeup(port); if (!uart_circ_empty(xmit)) - UART_PUT_IER(port, ATMEL_US_TXRDY); + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); } /* @@ -501,18 +592,10 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_tx(port)) { - /* PDC transmit */ - if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) { - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); - tasklet_schedule(&atmel_port->tasklet); - } - } else { - /* Interrupt transmit */ - if (pending & ATMEL_US_TXRDY) { - UART_PUT_IDR(port, ATMEL_US_TXRDY); - tasklet_schedule(&atmel_port->tasklet); - } + if (pending & atmel_port->tx_done_mask) { + /* Either PDC or interrupt transmission */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + tasklet_schedule(&atmel_port->tasklet); } } @@ -590,9 +673,15 @@ static void atmel_tx_dma(struct uart_port *port) UART_PUT_TPR(port, pdc->dma_addr + xmit->tail); UART_PUT_TCR(port, count); - /* re-enable PDC transmit and interrupts */ + /* re-enable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + } else { + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + /* DMA done, stop TX, start RX for RS485 */ + atmel_start_rx(port); + } } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -1017,6 +1106,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, { unsigned long flags; unsigned int mode, imr, quot, baud; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); /* Get current mode register */ mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL @@ -1115,6 +1205,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, /* disable receiver and transmitter */ UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } + /* set the parity, stop bits and data size */ UART_PUT_MR(port, mode); @@ -1231,6 +1332,35 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) } #endif +static int +atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, + sizeof(rs485conf))) + return -EFAULT; + + atmel_config_rs485(port, &rs485conf); + break; + + case TIOCGRS485: + if (copy_to_user((struct serial_rs485 *) arg, + &(to_atmel_uart_port(port)->rs485), + sizeof(rs485conf))) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + + + static struct uart_ops atmel_pops = { .tx_empty = atmel_tx_empty, .set_mctrl = atmel_set_mctrl, @@ -1250,6 +1380,7 @@ static struct uart_ops atmel_pops = { .config_port = atmel_config_port, .verify_port = atmel_verify_port, .pm = atmel_serial_pm, + .ioctl = atmel_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = atmel_poll_get_char, .poll_put_char = atmel_poll_put_char, @@ -1265,13 +1396,12 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, struct uart_port *port = &atmel_port->uart; struct atmel_uart_data *data = pdev->dev.platform_data; - port->iotype = UPIO_MEM; - port->flags = UPF_BOOT_AUTOCONF; - port->ops = &atmel_pops; - port->fifosize = 1; - port->line = pdev->id; - port->dev = &pdev->dev; - + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &atmel_pops; + port->fifosize = 1; + port->line = pdev->id; + port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; @@ -1299,8 +1429,16 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, atmel_port->use_dma_rx = data->use_dma_rx; atmel_port->use_dma_tx = data->use_dma_tx; - if (atmel_use_dma_tx(port)) + atmel_port->rs485 = data->rs485; + /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + else if (atmel_use_dma_tx(port)) { port->fifosize = PDC_BUFFER_SIZE; + atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; + } else { + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } } /* @@ -1334,6 +1472,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch) static void atmel_console_write(struct console *co, const char *s, u_int count) { struct uart_port *port = &atmel_ports[co->index].uart; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned int status, imr; unsigned int pdc_tx; @@ -1341,7 +1480,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count) * First, save IMR and then disable interrupts */ imr = UART_GET_IMR(port); - UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY); + UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask); /* Store PDC transmit status and disable it */ pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN; diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index c88f8ad3ff8..e57fb3d228e 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -34,32 +34,12 @@ #include <linux/tty_flip.h> #include <linux/serial_core.h> +#include <asm/bfin_sport.h> #include <asm/delay.h> #include <asm/portmux.h> #include "bfin_sport_uart.h" -#ifdef CONFIG_SERIAL_BFIN_SPORT0_UART -unsigned short bfin_uart_pin_req_sport0[] = - {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ - P_SPORT0_DRPRI, P_SPORT0_RSCLK, P_SPORT0_DRSEC, P_SPORT0_DTSEC, 0}; -#endif -#ifdef CONFIG_SERIAL_BFIN_SPORT1_UART -unsigned short bfin_uart_pin_req_sport1[] = - {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ - P_SPORT1_DRPRI, P_SPORT1_RSCLK, P_SPORT1_DRSEC, P_SPORT1_DTSEC, 0}; -#endif -#ifdef CONFIG_SERIAL_BFIN_SPORT2_UART -unsigned short bfin_uart_pin_req_sport2[] = - {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, P_SPORT2_RFS, \ - P_SPORT2_DRPRI, P_SPORT2_RSCLK, P_SPORT2_DRSEC, P_SPORT2_DTSEC, 0}; -#endif -#ifdef CONFIG_SERIAL_BFIN_SPORT3_UART -unsigned short bfin_uart_pin_req_sport3[] = - {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, P_SPORT3_RFS, \ - P_SPORT3_DRPRI, P_SPORT3_RSCLK, P_SPORT3_DRSEC, P_SPORT3_DTSEC, 0}; -#endif - struct sport_uart_port { struct uart_port port; int err_irq; @@ -69,9 +49,13 @@ struct sport_uart_port { unsigned short txmask2; unsigned char stopb; /* unsigned char parib; */ +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + int cts_pin; + int rts_pin; +#endif }; -static void sport_uart_tx_chars(struct sport_uart_port *up); +static int sport_uart_tx_chars(struct sport_uart_port *up); static void sport_stop_tx(struct uart_port *port); static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value) @@ -219,6 +203,59 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + if (up->cts_pin < 0) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + + /* CTS PIN is negative assertive. */ + if (SPORT_UART_GET_CTS(up)) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + else + return TIOCM_DSR | TIOCM_CAR; +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + if (up->rts_pin < 0) + return; + + /* RTS PIN is negative assertive. */ + if (mctrl & TIOCM_RTS) + SPORT_UART_ENABLE_RTS(up); + else + SPORT_UART_DISABLE_RTS(up); +} + +/* + * Handle any change of modem status signal. + */ +static irqreturn_t sport_mctrl_cts_int(int irq, void *dev_id) +{ + struct sport_uart_port *up = (struct sport_uart_port *)dev_id; + unsigned int status; + + status = sport_get_mctrl(&up->port); + uart_handle_cts_change(&up->port, status & TIOCM_CTS); + + return IRQ_HANDLED; +} +#else +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + pr_debug("%s enter\n", __func__); + return TIOCM_CTS | TIOCM_CD | TIOCM_DSR; +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + pr_debug("%s enter\n", __func__); +} +#endif + /* Reqeust IRQ, Setup clock */ static int sport_startup(struct uart_port *port) { @@ -247,6 +284,21 @@ static int sport_startup(struct uart_port *port) goto fail2; } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + if (up->cts_pin >= 0) { + if (request_irq(gpio_to_irq(up->cts_pin), + sport_mctrl_cts_int, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_DISABLED, "BFIN_SPORT_UART_CTS", up)) { + up->cts_pin = -1; + dev_info(port->dev, "Unable to attach BlackFin UART \ + over SPORT CTS interrupt. So, disable it.\n"); + } + } + if (up->rts_pin >= 0) + gpio_direction_output(up->rts_pin, 0); +#endif + return 0; fail2: free_irq(up->port.irq+1, up); @@ -256,23 +308,35 @@ static int sport_startup(struct uart_port *port) return ret; } -static void sport_uart_tx_chars(struct sport_uart_port *up) +/* + * sport_uart_tx_chars + * + * ret 1 means need to enable sport. + * ret 0 means do nothing. + */ +static int sport_uart_tx_chars(struct sport_uart_port *up) { struct circ_buf *xmit = &up->port.state->xmit; if (SPORT_GET_STAT(up) & TXF) - return; + return 0; if (up->port.x_char) { tx_one_byte(up, up->port.x_char); up->port.icount.tx++; up->port.x_char = 0; - return; + return 1; } if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - sport_stop_tx(&up->port); - return; + /* The waiting loop to stop SPORT TX from TX interrupt is + * too long. This may block SPORT RX interrupts and cause + * RX FIFO overflow. So, do stop sport TX only after the last + * char in TX FIFO is moved into the shift register. + */ + if (SPORT_GET_STAT(up) & TXHRE) + sport_stop_tx(&up->port); + return 0; } while(!(SPORT_GET_STAT(up) & TXF) && !uart_circ_empty(xmit)) { @@ -283,6 +347,8 @@ static void sport_uart_tx_chars(struct sport_uart_port *up) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&up->port); + + return 1; } static unsigned int sport_tx_empty(struct uart_port *port) @@ -298,23 +364,15 @@ static unsigned int sport_tx_empty(struct uart_port *port) return 0; } -static unsigned int sport_get_mctrl(struct uart_port *port) -{ - pr_debug("%s enter\n", __func__); - return (TIOCM_CTS | TIOCM_CD | TIOCM_DSR); -} - -static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - pr_debug("%s enter\n", __func__); -} - static void sport_stop_tx(struct uart_port *port) { struct sport_uart_port *up = (struct sport_uart_port *)port; pr_debug("%s enter\n", __func__); + if (!(SPORT_GET_TCR1(up) & TSPEN)) + return; + /* Although the hold register is empty, last byte is still in shift * register and not sent out yet. So, put a dummy data into TX FIFO. * Then, sport tx stops when last byte is shift out and the dummy @@ -337,11 +395,12 @@ static void sport_start_tx(struct uart_port *port) pr_debug("%s enter\n", __func__); /* Write data into SPORT FIFO before enable SPROT to transmit */ - sport_uart_tx_chars(up); + if (sport_uart_tx_chars(up)) { + /* Enable transmit, then an interrupt will generated */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + } - /* Enable transmit, then an interrupt will generated */ - SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); - SSYNC(); pr_debug("%s exit\n", __func__); } @@ -379,6 +438,10 @@ static void sport_shutdown(struct uart_port *port) free_irq(up->port.irq, up); free_irq(up->port.irq+1, up); free_irq(up->err_irq, up); +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + if (up->cts_pin >= 0) + free_irq(gpio_to_irq(up->cts_pin), up); +#endif } static const char *sport_type(struct uart_port *port) @@ -448,27 +511,14 @@ static void sport_set_termios(struct uart_port *port, /* up->parib = 1; */ } - port->read_status_mask = OE; - if (termios->c_iflag & INPCK) - port->read_status_mask |= (FE | PE); - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= BI; + spin_lock_irqsave(&up->port.lock, flags); + + port->read_status_mask = 0; /* * Characters to ignore */ port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= FE | PE; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= OE; - } /* RX extract mask */ up->rxmask = 0x01 | (((up->csize + up->stopb) * 2 - 1) << 0x8); @@ -488,8 +538,6 @@ static void sport_set_termios(struct uart_port *port, /* uart baud rate */ port->uartclk = uart_get_baud_rate(port, termios, old, 0, get_sclk()/16); - spin_lock_irqsave(&up->port.lock, flags); - /* Disable UART */ SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); @@ -542,6 +590,8 @@ struct uart_ops sport_uart_ops = { static struct sport_uart_port *bfin_sport_uart_ports[BFIN_SPORT_UART_MAX_PORTS]; #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE +#define CLASS_BFIN_SPORT_CONSOLE "bfin-sport-console" + static int __init sport_uart_console_setup(struct console *co, char *options) { @@ -549,7 +599,11 @@ sport_uart_console_setup(struct console *co, char *options) int baud = 57600; int bits = 8; int parity = 'n'; +# ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + int flow = 'r'; +# else int flow = 'n'; +# endif /* Check whether an invalid uart number has been specified */ if (co->index < 0 || co->index >= BFIN_SPORT_UART_MAX_PORTS) @@ -690,11 +744,11 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) if (bfin_sport_uart_ports[pdev->id] == NULL) { bfin_sport_uart_ports[pdev->id] = - kmalloc(sizeof(struct sport_uart_port), GFP_KERNEL); + kzalloc(sizeof(struct sport_uart_port), GFP_KERNEL); sport = bfin_sport_uart_ports[pdev->id]; if (!sport) { dev_err(&pdev->dev, - "Fail to kmalloc sport_uart_port\n"); + "Fail to malloc sport_uart_port\n"); return -ENOMEM; } @@ -720,13 +774,13 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) goto out_error_free_peripherals; } - sport->port.membase = ioremap(res->start, - res->end - res->start); + sport->port.membase = ioremap(res->start, resource_size(res)); if (!sport->port.membase) { dev_err(&pdev->dev, "Cannot map sport IO\n"); ret = -ENXIO; goto out_error_free_peripherals; } + sport->port.mapbase = res->start; sport->port.irq = platform_get_irq(pdev, 0); if (sport->port.irq < 0) { @@ -741,6 +795,22 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) ret = -ENOENT; goto out_error_unmap; } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) + sport->cts_pin = -1; + else + sport->cts_pin = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (res == NULL) + sport->rts_pin = -1; + else + sport->rts_pin = res->start; + + if (sport->rts_pin >= 0) + gpio_request(sport->rts_pin, DRV_NAME); +#endif } #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE @@ -779,6 +849,10 @@ static int __devexit sport_uart_remove(struct platform_device *pdev) if (sport) { uart_remove_one_port(&sport_uart_reg, &sport->port); +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (sport->rts_pin >= 0) + gpio_free(sport->rts_pin); +#endif iounmap(sport->port.membase); peripheral_free_list( (unsigned short *)pdev->dev.platform_data); @@ -802,7 +876,7 @@ static struct platform_driver sport_uart_driver = { #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE static __initdata struct early_platform_driver early_sport_uart_driver = { - .class_str = DRV_NAME, + .class_str = CLASS_BFIN_SPORT_CONSOLE, .pdrv = &sport_uart_driver, .requested_id = EARLY_PLATFORM_ID_UNSET, }; @@ -811,7 +885,8 @@ static int __init sport_uart_rs_console_init(void) { early_platform_driver_register(&early_sport_uart_driver, DRV_NAME); - early_platform_driver_probe(DRV_NAME, BFIN_SPORT_UART_MAX_PORTS, 0); + early_platform_driver_probe(CLASS_BFIN_SPORT_CONSOLE, + BFIN_SPORT_UART_MAX_PORTS, 0); register_console(&sport_uart_console); @@ -824,7 +899,7 @@ static int __init sport_uart_init(void) { int ret; - pr_info("Serial: Blackfin uart over sport driver\n"); + pr_info("Blackfin uart over sport driver\n"); ret = uart_register_driver(&sport_uart_reg); if (ret) { diff --git a/drivers/serial/bfin_sport_uart.h b/drivers/serial/bfin_sport_uart.h index abe03614e4d..9ce253e381d 100644 --- a/drivers/serial/bfin_sport_uart.h +++ b/drivers/serial/bfin_sport_uart.h @@ -37,7 +37,21 @@ #define SPORT_GET_TFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TFSDIV)) #define SPORT_GET_TX(sport) bfin_read16(((sport)->port.membase + OFFSET_TX)) #define SPORT_GET_RX(sport) bfin_read16(((sport)->port.membase + OFFSET_RX)) -#define SPORT_GET_RX32(sport) bfin_read32(((sport)->port.membase + OFFSET_RX)) +/* + * If another interrupt fires while doing a 32-bit read from RX FIFO, + * a fake RX underflow error will be generated. So disable interrupts + * to prevent interruption while reading the FIFO. + */ +#define SPORT_GET_RX32(sport) \ +({ \ + unsigned int __ret; \ + if (ANOMALY_05000473) \ + local_irq_disable(); \ + __ret = bfin_read32((sport)->port.membase + OFFSET_RX); \ + if (ANOMALY_05000473) \ + local_irq_enable(); \ + __ret; \ +}) #define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1)) #define SPORT_GET_RCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR2)) #define SPORT_GET_RCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RCLKDIV)) @@ -58,4 +72,15 @@ #define SPORT_TX_FIFO_SIZE 8 +#define SPORT_UART_GET_CTS(x) gpio_get_value(x->cts_pin) +#define SPORT_UART_DISABLE_RTS(x) gpio_set_value(x->rts_pin, 1) +#define SPORT_UART_ENABLE_RTS(x) gpio_set_value(x->rts_pin, 0) + +#if defined(CONFIG_SERIAL_BFIN_SPORT0_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT1_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT2_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT3_UART_CTSRTS) +# define CONFIG_SERIAL_BFIN_SPORT_CTSRTS +#endif + #endif /* _BFIN_SPORT_UART_H */ diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index 4315b23590b..eacb588a934 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -120,7 +120,8 @@ #define MX2_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */ #define UCR3_INVT (1<<1) /* Inverted Infrared transmission */ #define UCR3_BPEN (1<<0) /* Preset registers enable */ -#define UCR4_CTSTL_32 (32<<10) /* CTS trigger level (32 chars) */ +#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */ +#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */ #define UCR4_INVR (1<<9) /* Inverted infrared reception */ #define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */ #define UCR4_WKEN (1<<7) /* Wake interrupt enable */ @@ -591,6 +592,9 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) return 0; } +/* half the RX buffer size */ +#define CTSTL 16 + static int imx_startup(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; @@ -607,6 +611,10 @@ static int imx_startup(struct uart_port *port) if (USE_IRDA(sport)) temp |= UCR4_IRSC; + /* set the trigger level for CTS */ + temp &= ~(UCR4_CTSTL_MASK<< UCR4_CTSTL_SHF); + temp |= CTSTL<< UCR4_CTSTL_SHF; + writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); if (USE_IRDA(sport)) { diff --git a/drivers/serial/kgdboc.c b/drivers/serial/kgdboc.c index eadc1ab6bbc..a9a94ae7234 100644 --- a/drivers/serial/kgdboc.c +++ b/drivers/serial/kgdboc.c @@ -14,7 +14,9 @@ #include <linux/kernel.h> #include <linux/ctype.h> #include <linux/kgdb.h> +#include <linux/kdb.h> #include <linux/tty.h> +#include <linux/console.h> #define MAX_CONFIG_LEN 40 @@ -32,6 +34,40 @@ static struct kparam_string kps = { static struct tty_driver *kgdb_tty_driver; static int kgdb_tty_line; +#ifdef CONFIG_KDB_KEYBOARD +static int kgdboc_register_kbd(char **cptr) +{ + if (strncmp(*cptr, "kbd", 3) == 0) { + if (kdb_poll_idx < KDB_POLL_FUNC_MAX) { + kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char; + kdb_poll_idx++; + if (cptr[0][3] == ',') + *cptr += 4; + else + return 1; + } + } + return 0; +} + +static void kgdboc_unregister_kbd(void) +{ + int i; + + for (i = 0; i < kdb_poll_idx; i++) { + if (kdb_poll_funcs[i] == kdb_get_kbd_char) { + kdb_poll_idx--; + kdb_poll_funcs[i] = kdb_poll_funcs[kdb_poll_idx]; + kdb_poll_funcs[kdb_poll_idx] = NULL; + i--; + } + } +} +#else /* ! CONFIG_KDB_KEYBOARD */ +#define kgdboc_register_kbd(x) 0 +#define kgdboc_unregister_kbd() +#endif /* ! CONFIG_KDB_KEYBOARD */ + static int kgdboc_option_setup(char *opt) { if (strlen(opt) > MAX_CONFIG_LEN) { @@ -45,25 +81,51 @@ static int kgdboc_option_setup(char *opt) __setup("kgdboc=", kgdboc_option_setup); +static void cleanup_kgdboc(void) +{ + kgdboc_unregister_kbd(); + if (configured == 1) + kgdb_unregister_io_module(&kgdboc_io_ops); +} + static int configure_kgdboc(void) { struct tty_driver *p; int tty_line = 0; int err; + char *cptr = config; + struct console *cons; err = kgdboc_option_setup(config); if (err || !strlen(config) || isspace(config[0])) goto noconfig; err = -ENODEV; + kgdboc_io_ops.is_console = 0; + kgdb_tty_driver = NULL; - p = tty_find_polling_driver(config, &tty_line); + if (kgdboc_register_kbd(&cptr)) + goto do_register; + + p = tty_find_polling_driver(cptr, &tty_line); if (!p) goto noconfig; + cons = console_drivers; + while (cons) { + int idx; + if (cons->device && cons->device(cons, &idx) == p && + idx == tty_line) { + kgdboc_io_ops.is_console = 1; + break; + } + cons = cons->next; + } + kgdb_tty_driver = p; kgdb_tty_line = tty_line; +do_register: err = kgdb_register_io_module(&kgdboc_io_ops); if (err) goto noconfig; @@ -75,6 +137,7 @@ static int configure_kgdboc(void) noconfig: config[0] = 0; configured = 0; + cleanup_kgdboc(); return err; } @@ -88,20 +151,18 @@ static int __init init_kgdboc(void) return configure_kgdboc(); } -static void cleanup_kgdboc(void) -{ - if (configured == 1) - kgdb_unregister_io_module(&kgdboc_io_ops); -} - static int kgdboc_get_char(void) { + if (!kgdb_tty_driver) + return -1; return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver, kgdb_tty_line); } static void kgdboc_put_char(u8 chr) { + if (!kgdb_tty_driver) + return; kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver, kgdb_tty_line, chr); } @@ -162,6 +223,25 @@ static struct kgdb_io kgdboc_io_ops = { .post_exception = kgdboc_post_exp_handler, }; +#ifdef CONFIG_KGDB_SERIAL_CONSOLE +/* This is only available if kgdboc is a built in for early debugging */ +int __init kgdboc_early_init(char *opt) +{ + /* save the first character of the config string because the + * init routine can destroy it. + */ + char save_ch; + + kgdboc_option_setup(opt); + save_ch = config[0]; + init_kgdboc(); + config[0] = save_ch; + return 0; +} + +early_param("ekgdboc", kgdboc_early_init); +#endif /* CONFIG_KGDB_SERIAL_CONSOLE */ + module_init(init_kgdboc); module_exit(cleanup_kgdboc); module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644); diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 3119fddaedb..02469c31bf0 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -29,39 +29,6 @@ * kind, whether express or implied. */ -/* Platform device Usage : - * - * Since PSCs can have multiple function, the correct driver for each one - * is selected by calling mpc52xx_match_psc_function(...). The function - * handled by this driver is "uart". - * - * The driver init all necessary registers to place the PSC in uart mode without - * DCD. However, the pin multiplexing aren't changed and should be set either - * by the bootloader or in the platform init code. - * - * The idx field must be equal to the PSC index (e.g. 0 for PSC1, 1 for PSC2, - * and so on). So the PSC1 is mapped to /dev/ttyPSC0, PSC2 to /dev/ttyPSC1 and - * so on. But be warned, it's an ABSOLUTE REQUIREMENT ! This is needed mainly - * fpr the console code : without this 1:1 mapping, at early boot time, when we - * are parsing the kernel args console=ttyPSC?, we wouldn't know which PSC it - * will be mapped to. - */ - -/* OF Platform device Usage : - * - * This driver is only used for PSCs configured in uart mode. The device - * tree will have a node for each PSC with "mpc52xx-psc-uart" in the compatible - * list. - * - * By default, PSC devices are enumerated in the order they are found. However - * a particular PSC number can be forces by adding 'device_no = <port#>' - * to the device node. - * - * The driver init all necessary registers to place the PSC in uart mode without - * DCD. However, the pin multiplexing aren't changed and should be set either - * by the bootloader or in the platform init code. - */ - #undef DEBUG #include <linux/device.h> @@ -1500,7 +1467,7 @@ mpc52xx_uart_init(void) /* * Map the PSC FIFO Controller and init if on MPC512x. */ - if (psc_ops->fifoc_init) { + if (psc_ops && psc_ops->fifoc_init) { ret = psc_ops->fifoc_init(); if (ret) return ret; diff --git a/drivers/serial/mpsc.c b/drivers/serial/mpsc.c index 55e113a0be0..6a9c6605666 100644 --- a/drivers/serial/mpsc.c +++ b/drivers/serial/mpsc.c @@ -2071,6 +2071,7 @@ static int mpsc_drv_probe(struct platform_device *dev) if (!(rc = mpsc_drv_map_regs(pi, dev))) { mpsc_drv_get_platform_data(pi, dev, dev->id); + pi->port.dev = &dev->dev; if (!(rc = mpsc_make_ready(pi))) { spin_lock_init(&pi->tx_lock); diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 4eaa043ca2a..700e10833bf 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -752,8 +752,10 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) uap->curregs[R5] = new_reg; /* NOTE: Not subject to 'transmitter active' rule. */ - if (ZS_IS_ASLEEP(uap)) + if (ZS_IS_ASLEEP(uap)) { + spin_unlock_irqrestore(&port->lock, flags); return; + } write_zsreg(uap, R5, uap->curregs[R5]); } diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index 8cfa5b12ea7..dadd686c980 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -89,7 +89,6 @@ struct serial_info { int manfid; int prodid; int c950ctrl; - dev_node_t node[4]; int line[4]; const struct serial_quirk *quirk; }; @@ -289,8 +288,6 @@ static void serial_remove(struct pcmcia_device *link) for (i = 0; i < info->ndev; i++) serial8250_unregister_port(info->line[i]); - info->p_dev->dev_node = NULL; - if (!info->slave) pcmcia_disable_device(link); } @@ -343,7 +340,6 @@ static int serial_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1 = 8; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->conf.Attributes = CONF_ENABLE_IRQ; if (do_sound) { link->conf.Attributes |= CONF_ENABLE_SPKR; @@ -411,11 +407,6 @@ static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, } info->line[info->ndev] = line; - sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); - info->node[info->ndev].major = TTY_MAJOR; - info->node[info->ndev].minor = 0x40 + line; - if (info->ndev > 0) - info->node[info->ndev - 1].next = &info->node[info->ndev]; info->ndev++; return 0; @@ -486,7 +477,7 @@ static int simple_config(struct pcmcia_device *link) } if (info->slave) { return setup_serial(link, info, port, - link->irq.AssignedIRQ); + link->irq); } } @@ -507,10 +498,6 @@ static int simple_config(struct pcmcia_device *link) return -1; found_port: - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) - link->irq.AssignedIRQ = 0; - if (info->multi && (info->manfid == MANFID_3COM)) link->conf.ConfigIndex &= ~(0x08); @@ -523,7 +510,7 @@ found_port: i = pcmcia_request_configuration(link, &link->conf); if (i != 0) return -1; - return setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); + return setup_serial(link, info, link->io.BasePort1, link->irq); } static int multi_config_check(struct pcmcia_device *p_dev, @@ -586,13 +573,9 @@ static int multi_config(struct pcmcia_device *link) } } - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) { - /* FIXME: comment does not fit, error handling does not fit */ - printk(KERN_NOTICE - "serial_cs: no usable port range found, giving up\n"); - link->irq.AssignedIRQ = 0; - } + if (!link->irq) + dev_warn(&link->dev, + "serial_cs: no usable IRQ found, continuing...\n"); /* * Apply any configuration quirks. @@ -615,11 +598,11 @@ static int multi_config(struct pcmcia_device *link) if (link->conf.ConfigIndex == 1 || link->conf.ConfigIndex == 3) { err = setup_serial(link, info, base2, - link->irq.AssignedIRQ); + link->irq); base2 = link->io.BasePort1; } else { err = setup_serial(link, info, link->io.BasePort1, - link->irq.AssignedIRQ); + link->irq); } info->c950ctrl = base2; @@ -633,10 +616,10 @@ static int multi_config(struct pcmcia_device *link) return 0; } - setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); + setup_serial(link, info, link->io.BasePort1, link->irq); for (i = 0; i < info->multi - 1; i++) setup_serial(link, info, base2 + (8 * i), - link->irq.AssignedIRQ); + link->irq); return 0; } @@ -720,7 +703,6 @@ static int serial_config(struct pcmcia_device * link) if (info->quirk->post(link)) goto failed; - link->dev_node = &info->node[0]; return 0; failed: diff --git a/drivers/serial/serial_ks8695.c b/drivers/serial/serial_ks8695.c index 2e71bbc04da..b1962025b1a 100644 --- a/drivers/serial/serial_ks8695.c +++ b/drivers/serial/serial_ks8695.c @@ -650,6 +650,7 @@ static struct console ks8695_console = { static int __init ks8695_console_init(void) { + add_preferred_console(SERIAL_KS8695_DEVNAME, 0, NULL); register_console(&ks8695_console); return 0; } diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 8eb094c1f61..f250a610a26 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -83,16 +83,16 @@ struct sci_port { /* Interface clock */ struct clk *iclk; - /* Data clock */ - struct clk *dclk; + /* Function clock */ + struct clk *fclk; struct list_head node; struct dma_chan *chan_tx; struct dma_chan *chan_rx; #ifdef CONFIG_SERIAL_SH_SCI_DMA struct device *dma_dev; - enum sh_dmae_slave_chan_id slave_tx; - enum sh_dmae_slave_chan_id slave_rx; + unsigned int slave_tx; + unsigned int slave_rx; struct dma_async_tx_descriptor *desc_tx; struct dma_async_tx_descriptor *desc_rx[2]; dma_cookie_t cookie_tx; @@ -107,6 +107,7 @@ struct sci_port { struct work_struct work_tx; struct work_struct work_rx; struct timer_list rx_timer; + unsigned int rx_timeout; #endif }; @@ -150,7 +151,11 @@ static int sci_poll_get_char(struct uart_port *port) handle_error(port); continue; } - } while (!(status & SCxSR_RDxF(port))); + break; + } while (1); + + if (!(status & SCxSR_RDxF(port))) + return NO_POLL_CHAR; c = sci_in(port, SCxRDR); @@ -674,22 +679,22 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) struct sci_port *s = to_sci_port(port); if (s->chan_rx) { - unsigned long tout; u16 scr = sci_in(port, SCSCR); u16 ssr = sci_in(port, SCxSR); /* Disable future Rx interrupts */ - sci_out(port, SCSCR, scr & ~SCI_CTRL_FLAGS_RIE); + if (port->type == PORT_SCIFA) { + disable_irq_nosync(irq); + scr |= 0x4000; + } else { + scr &= ~SCI_CTRL_FLAGS_RIE; + } + sci_out(port, SCSCR, scr); /* Clear current interrupt */ sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); - /* Calculate delay for 1.5 DMA buffers */ - tout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / - port->fifosize / 2; - dev_dbg(port->dev, "Rx IRQ: setup timeout in %lu ms\n", - tout * 1000 / HZ); - if (tout < 2) - tout = 2; - mod_timer(&s->rx_timer, jiffies + tout); + dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", + jiffies, s->rx_timeout); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); return IRQ_HANDLED; } @@ -799,7 +804,7 @@ static int sci_notifier(struct notifier_block *self, (phase == CPUFREQ_RESUMECHANGE)) { spin_lock_irqsave(&priv->lock, flags); list_for_each_entry(sci_port, &priv->ports, node) - sci_port->port.uartclk = clk_get_rate(sci_port->dclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); spin_unlock_irqrestore(&priv->lock, flags); } @@ -810,21 +815,17 @@ static void sci_clk_enable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - clk_enable(sci_port->dclk); - sci_port->port.uartclk = clk_get_rate(sci_port->dclk); - - if (sci_port->iclk) - clk_enable(sci_port->iclk); + clk_enable(sci_port->iclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); + clk_enable(sci_port->fclk); } static void sci_clk_disable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - if (sci_port->iclk) - clk_disable(sci_port->iclk); - - clk_disable(sci_port->dclk); + clk_disable(sci_port->fclk); + clk_disable(sci_port->iclk); } static int sci_request_irq(struct sci_port *port) @@ -913,22 +914,26 @@ static void sci_dma_tx_complete(void *arg) spin_lock_irqsave(&port->lock, flags); - xmit->tail += s->sg_tx.length; + xmit->tail += sg_dma_len(&s->sg_tx); xmit->tail &= UART_XMIT_SIZE - 1; - port->icount.tx += s->sg_tx.length; + port->icount.tx += sg_dma_len(&s->sg_tx); async_tx_ack(s->desc_tx); s->cookie_tx = -EINVAL; s->desc_tx = NULL; - spin_unlock_irqrestore(&port->lock, flags); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_chars_pending(xmit)) + if (!uart_circ_empty(xmit)) { schedule_work(&s->work_tx); + } else if (port->type == PORT_SCIFA) { + u16 ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE); + } + + spin_unlock_irqrestore(&port->lock, flags); } /* Locking: called with port lock held */ @@ -972,13 +977,13 @@ static void sci_dma_rx_complete(void *arg) unsigned long flags; int count; - dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx); spin_lock_irqsave(&port->lock, flags); count = sci_dma_rx_push(s, tty, s->buf_len_rx); - mod_timer(&s->rx_timer, jiffies + msecs_to_jiffies(5)); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); spin_unlock_irqrestore(&port->lock, flags); @@ -1050,6 +1055,8 @@ static void sci_submit_rx(struct sci_port *s) sci_rx_dma_release(s, true); return; } + dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, + s->cookie_rx[i], i); } s->active_rx = s->cookie_rx[0]; @@ -1107,10 +1114,10 @@ static void work_fn_rx(struct work_struct *work) return; } - dev_dbg(port->dev, "%s: cookie %d #%d\n", __func__, - s->cookie_rx[new], new); - s->active_rx = s->cookie_rx[!new]; + + dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, + s->cookie_rx[new], new, s->active_rx); } static void work_fn_tx(struct work_struct *work) @@ -1131,14 +1138,13 @@ static void work_fn_tx(struct work_struct *work) */ spin_lock_irq(&port->lock); sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); - sg->dma_address = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + + sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + sg->offset; - sg->length = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), + sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); - sg->dma_length = sg->length; spin_unlock_irq(&port->lock); - BUG_ON(!sg->length); + BUG_ON(!sg_dma_len(sg)); desc = chan->device->device_prep_slave_sg(chan, sg, s->sg_len_tx, DMA_TO_DEVICE, @@ -1173,23 +1179,28 @@ static void work_fn_tx(struct work_struct *work) static void sci_start_tx(struct uart_port *port) { + struct sci_port *s = to_sci_port(port); unsigned short ctrl; #ifdef CONFIG_SERIAL_SH_SCI_DMA - struct sci_port *s = to_sci_port(port); - - if (s->chan_tx) { - if (!uart_circ_empty(&s->port.state->xmit) && s->cookie_tx < 0) - schedule_work(&s->work_tx); - - return; + if (port->type == PORT_SCIFA) { + u16 new, scr = sci_in(port, SCSCR); + if (s->chan_tx) + new = scr | 0x8000; + else + new = scr & ~0x8000; + if (new != scr) + sci_out(port, SCSCR, new); } + if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + s->cookie_tx < 0) + schedule_work(&s->work_tx); #endif - - /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - ctrl |= SCI_CTRL_FLAGS_TIE; - sci_out(port, SCSCR, ctrl); + if (!s->chan_tx || port->type == PORT_SCIFA) { + /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE); + } } static void sci_stop_tx(struct uart_port *port) @@ -1198,6 +1209,8 @@ static void sci_stop_tx(struct uart_port *port) /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x8000; ctrl &= ~SCI_CTRL_FLAGS_TIE; sci_out(port, SCSCR, ctrl); } @@ -1208,6 +1221,8 @@ static void sci_start_rx(struct uart_port *port) /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl |= sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x4000; sci_out(port, SCSCR, ctrl); } @@ -1217,6 +1232,8 @@ static void sci_stop_rx(struct uart_port *port) /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x4000; ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); sci_out(port, SCSCR, ctrl); } @@ -1251,8 +1268,12 @@ static void rx_timer_fn(unsigned long arg) { struct sci_port *s = (struct sci_port *)arg; struct uart_port *port = &s->port; - u16 scr = sci_in(port, SCSCR); + + if (port->type == PORT_SCIFA) { + scr &= ~0x4000; + enable_irq(s->irqs[1]); + } sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE); dev_dbg(port->dev, "DMA Rx timed out\n"); schedule_work(&s->work_rx); @@ -1339,8 +1360,7 @@ static void sci_request_dma(struct uart_port *port) sg_init_table(sg, 1); sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, (int)buf[i] & ~PAGE_MASK); - sg->dma_address = dma[i]; - sg->dma_length = sg->length; + sg_dma_address(sg) = dma[i]; } INIT_WORK(&s->work_rx, work_fn_rx); @@ -1403,8 +1423,12 @@ static void sci_shutdown(struct uart_port *port) static void sci_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct sci_port *s = to_sci_port(port); +#endif unsigned int status, baud, smr_val, max_baud; int t = -1; + u16 scfcr = 0; /* * earlyprintk comes here early on with port->uartclk set to zero. @@ -1427,7 +1451,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ if (port->type != PORT_SCI) - sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); + sci_out(port, SCFCR, scfcr | SCFCR_RFRST | SCFCR_TFRST); smr_val = sci_in(port, SCSMR) & 3; if ((termios->c_cflag & CSIZE) == CS7) @@ -1458,10 +1482,32 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, } sci_init_pins(port, termios->c_cflag); - sci_out(port, SCFCR, (termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0); + sci_out(port, SCFCR, scfcr | ((termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0)); sci_out(port, SCSCR, SCSCR_INIT(port)); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + /* + * Calculate delay for 1.5 DMA buffers: see + * drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits + * (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function + * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." + * Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO + * sizes), but it has been found out experimentally, that this is not + * enough: the driver too often needlessly runs on a DMA timeout. 20ms + * as a minimum seem to work perfectly. + */ + if (s->chan_rx) { + s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / + port->fifosize / 2; + dev_dbg(port->dev, + "DMA Rx t-out %ums, tty t-out %u jiffies\n", + s->rx_timeout * 1000 / HZ, port->timeout); + if (s->rx_timeout < msecs_to_jiffies(20)) + s->rx_timeout = msecs_to_jiffies(20); + } +#endif + if ((termios->c_cflag & CREAD) != 0) sci_start_rx(port); } @@ -1553,10 +1599,10 @@ static struct uart_ops sci_uart_ops = { #endif }; -static void __devinit sci_init_single(struct platform_device *dev, - struct sci_port *sci_port, - unsigned int index, - struct plat_sci_port *p) +static int __devinit sci_init_single(struct platform_device *dev, + struct sci_port *sci_port, + unsigned int index, + struct plat_sci_port *p) { struct uart_port *port = &sci_port->port; @@ -1577,8 +1623,23 @@ static void __devinit sci_init_single(struct platform_device *dev, } if (dev) { - sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL; - sci_port->dclk = clk_get(&dev->dev, "peripheral_clk"); + sci_port->iclk = clk_get(&dev->dev, "sci_ick"); + if (IS_ERR(sci_port->iclk)) { + sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); + if (IS_ERR(sci_port->iclk)) { + dev_err(&dev->dev, "can't get iclk\n"); + return PTR_ERR(sci_port->iclk); + } + } + + /* + * The function clock is optional, ignore it if we can't + * find it. + */ + sci_port->fclk = clk_get(&dev->dev, "sci_fck"); + if (IS_ERR(sci_port->fclk)) + sci_port->fclk = NULL; + sci_port->enable = sci_clk_enable; sci_port->disable = sci_clk_disable; port->dev = &dev->dev; @@ -1605,6 +1666,7 @@ static void __devinit sci_init_single(struct platform_device *dev, #endif memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); + return 0; } #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE @@ -1754,8 +1816,11 @@ static int sci_remove(struct platform_device *dev) cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(p, &priv->ports, node) + list_for_each_entry(p, &priv->ports, node) { uart_remove_one_port(&sci_uart_driver, &p->port); + clk_put(p->iclk); + clk_put(p->fclk); + } spin_unlock_irqrestore(&priv->lock, flags); kfree(priv); @@ -1781,7 +1846,9 @@ static int __devinit sci_probe_single(struct platform_device *dev, return 0; } - sci_init_single(dev, sciport, index, p); + ret = sci_init_single(dev, sciport, index, p); + if (ret) + return ret; ret = uart_add_one_port(&sci_uart_driver, &sciport->port); if (ret) diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index 2c7a66af4f5..978b3cee02d 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -102,6 +102,8 @@ struct uart_sunzilog_port { #endif }; +static void sunzilog_putchar(struct uart_port *port, int ch); + #define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel __iomem *)((PORT)->membase)) #define UART_ZILOG(PORT) ((struct uart_sunzilog_port *)(PORT)) @@ -996,6 +998,50 @@ static int sunzilog_verify_port(struct uart_port *port, struct serial_struct *se return -EINVAL; } +#ifdef CONFIG_CONSOLE_POLL +static int sunzilog_get_poll_char(struct uart_port *port) +{ + unsigned char ch, r1; + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct zilog_channel __iomem *channel + = ZILOG_CHANNEL_FROM_PORT(&up->port); + + + r1 = read_zsreg(channel, R1); + if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { + writeb(ERR_RES, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + } + + ch = readb(&channel->control); + ZSDELAY(); + + /* This funny hack depends upon BRK_ABRT not interfering + * with the other bits we care about in R1. + */ + if (ch & BRK_ABRT) + r1 |= BRK_ABRT; + + if (!(ch & Rx_CH_AV)) + return NO_POLL_CHAR; + + ch = readb(&channel->data); + ZSDELAY(); + + ch &= up->parity_mask; + return ch; +} + +static void sunzilog_put_poll_char(struct uart_port *port, + unsigned char ch) +{ + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *)port; + + sunzilog_putchar(&up->port, ch); +} +#endif /* CONFIG_CONSOLE_POLL */ + static struct uart_ops sunzilog_pops = { .tx_empty = sunzilog_tx_empty, .set_mctrl = sunzilog_set_mctrl, @@ -1013,6 +1059,10 @@ static struct uart_ops sunzilog_pops = { .request_port = sunzilog_request_port, .config_port = sunzilog_config_port, .verify_port = sunzilog_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = sunzilog_get_poll_char, + .poll_put_char = sunzilog_put_poll_char, +#endif }; static int uart_chip_count; diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c index 786ba85c170..67ca642713b 100644 --- a/drivers/serial/timbuart.c +++ b/drivers/serial/timbuart.c @@ -68,12 +68,22 @@ static void timbuart_start_tx(struct uart_port *port) tasklet_schedule(&uart->tasklet); } +static unsigned int timbuart_tx_empty(struct uart_port *port) +{ + u32 isr = ioread32(port->membase + TIMBUART_ISR); + + return (isr & TXBE) ? TIOCSER_TEMT : 0; +} + static void timbuart_flush_buffer(struct uart_port *port) { - u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | TIMBUART_CTRL_FLSHTX; + if (!timbuart_tx_empty(port)) { + u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | + TIMBUART_CTRL_FLSHTX; - iowrite8(ctl, port->membase + TIMBUART_CTRL); - iowrite32(TXBF, port->membase + TIMBUART_ISR); + iowrite8(ctl, port->membase + TIMBUART_CTRL); + iowrite32(TXBF, port->membase + TIMBUART_ISR); + } } static void timbuart_rx_chars(struct uart_port *port) @@ -195,13 +205,6 @@ void timbuart_tasklet(unsigned long arg) dev_dbg(uart->port.dev, "%s leaving\n", __func__); } -static unsigned int timbuart_tx_empty(struct uart_port *port) -{ - u32 isr = ioread32(port->membase + TIMBUART_ISR); - - return (isr & TXBE) ? TIOCSER_TEMT : 0; -} - static unsigned int timbuart_get_mctrl(struct uart_port *port) { u8 cts = ioread8(port->membase + TIMBUART_CTRL); @@ -220,7 +223,7 @@ static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) if (mctrl & TIOCM_RTS) iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); else - iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); + iowrite8(0, port->membase + TIMBUART_CTRL); } static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier) diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index f0a6c61b17f..e6639a95d27 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -86,7 +86,7 @@ static int ulite_receive(struct uart_port *port, int stat) /* stats */ if (stat & ULITE_STATUS_RXVALID) { port->icount.rx++; - ch = readb(port->membase + ULITE_RX); + ch = ioread32be(port->membase + ULITE_RX); if (stat & ULITE_STATUS_PARITY) port->icount.parity++; @@ -131,7 +131,7 @@ static int ulite_transmit(struct uart_port *port, int stat) return 0; if (port->x_char) { - writeb(port->x_char, port->membase + ULITE_TX); + iowrite32be(port->x_char, port->membase + ULITE_TX); port->x_char = 0; port->icount.tx++; return 1; @@ -140,7 +140,7 @@ static int ulite_transmit(struct uart_port *port, int stat) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return 0; - writeb(xmit->buf[xmit->tail], port->membase + ULITE_TX); + iowrite32be(xmit->buf[xmit->tail], port->membase + ULITE_TX); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); port->icount.tx++; @@ -157,7 +157,7 @@ static irqreturn_t ulite_isr(int irq, void *dev_id) int busy, n = 0; do { - int stat = readb(port->membase + ULITE_STATUS); + int stat = ioread32be(port->membase + ULITE_STATUS); busy = ulite_receive(port, stat); busy |= ulite_transmit(port, stat); n++; @@ -178,7 +178,7 @@ static unsigned int ulite_tx_empty(struct uart_port *port) unsigned int ret; spin_lock_irqsave(&port->lock, flags); - ret = readb(port->membase + ULITE_STATUS); + ret = ioread32be(port->membase + ULITE_STATUS); spin_unlock_irqrestore(&port->lock, flags); return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; @@ -201,7 +201,7 @@ static void ulite_stop_tx(struct uart_port *port) static void ulite_start_tx(struct uart_port *port) { - ulite_transmit(port, readb(port->membase + ULITE_STATUS)); + ulite_transmit(port, ioread32be(port->membase + ULITE_STATUS)); } static void ulite_stop_rx(struct uart_port *port) @@ -230,17 +230,17 @@ static int ulite_startup(struct uart_port *port) if (ret) return ret; - writeb(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, + iowrite32be(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, port->membase + ULITE_CONTROL); - writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); return 0; } static void ulite_shutdown(struct uart_port *port) { - writeb(0, port->membase + ULITE_CONTROL); - readb(port->membase + ULITE_CONTROL); /* dummy */ + iowrite32be(0, port->membase + ULITE_CONTROL); + ioread32be(port->membase + ULITE_CONTROL); /* dummy */ free_irq(port->irq, port); } @@ -352,7 +352,7 @@ static void ulite_console_wait_tx(struct uart_port *port) /* Spin waiting for TX fifo to have space available */ for (i = 0; i < 100000; i++) { - val = readb(port->membase + ULITE_STATUS); + val = ioread32be(port->membase + ULITE_STATUS); if ((val & ULITE_STATUS_TXFULL) == 0) break; cpu_relax(); @@ -362,7 +362,7 @@ static void ulite_console_wait_tx(struct uart_port *port) static void ulite_console_putchar(struct uart_port *port, int ch) { ulite_console_wait_tx(port); - writeb(ch, port->membase + ULITE_TX); + iowrite32be(ch, port->membase + ULITE_TX); } static void ulite_console_write(struct console *co, const char *s, @@ -379,8 +379,8 @@ static void ulite_console_write(struct console *co, const char *s, spin_lock_irqsave(&port->lock, flags); /* save and disable interrupt */ - ier = readb(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; - writeb(0, port->membase + ULITE_CONTROL); + ier = ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; + iowrite32be(0, port->membase + ULITE_CONTROL); uart_console_write(port, s, count, ulite_console_putchar); @@ -388,7 +388,7 @@ static void ulite_console_write(struct console *co, const char *s, /* restore interrupt state */ if (ier) - writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); if (locked) spin_unlock_irqrestore(&port->lock, flags); @@ -601,7 +601,7 @@ ulite_of_probe(struct of_device *op, const struct of_device_id *match) id = of_get_property(op->node, "port-number", NULL); - return ulite_assign(&op->dev, id ? *id : -1, res.start+3, irq); + return ulite_assign(&op->dev, id ? *id : -1, res.start, irq); } static int __devexit ulite_of_remove(struct of_device *op) |