diff options
-rw-r--r-- | arch/x86/include/asm/mrst.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/early_printk.c | 2 | ||||
-rw-r--r-- | arch/x86/platform/mrst/early_printk_mrst.c | 16 | ||||
-rw-r--r-- | drivers/tty/pty.c | 26 | ||||
-rw-r--r-- | drivers/tty/serial/Kconfig | 32 | ||||
-rw-r--r-- | drivers/tty/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/tty/serial/atmel_serial.c | 7 | ||||
-rw-r--r-- | drivers/tty/serial/mfd.c | 18 | ||||
-rw-r--r-- | drivers/tty/serial/pch_uart.c | 160 | ||||
-rw-r--r-- | drivers/tty/serial/serial_core.c | 323 | ||||
-rw-r--r-- | drivers/tty/serial/sirfsoc_uart.c | 783 | ||||
-rw-r--r-- | drivers/tty/serial/sirfsoc_uart.h | 185 | ||||
-rw-r--r-- | drivers/tty/tty_io.c | 309 | ||||
-rw-r--r-- | drivers/tty/tty_ldisc.c | 22 | ||||
-rw-r--r-- | include/linux/serial_core.h | 99 |
15 files changed, 1580 insertions, 405 deletions
diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h index e6283129c821..7216907eb767 100644 --- a/arch/x86/include/asm/mrst.h +++ b/arch/x86/include/asm/mrst.h @@ -58,7 +58,7 @@ extern struct console early_mrst_console; extern void mrst_early_console_init(void); extern struct console early_hsu_console; -extern void hsu_early_console_init(void); +extern void hsu_early_console_init(const char *); extern void intel_scu_devices_create(void); extern void intel_scu_devices_destroy(void); diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index cd28a350f7f9..9d42a52d2331 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -247,7 +247,7 @@ static int __init setup_early_printk(char *buf) } if (!strncmp(buf, "hsu", 3)) { - hsu_early_console_init(); + hsu_early_console_init(buf + 3); early_console_register(&early_hsu_console, keep); } #endif diff --git a/arch/x86/platform/mrst/early_printk_mrst.c b/arch/x86/platform/mrst/early_printk_mrst.c index 25bfdbb5b130..3c6e328483c7 100644 --- a/arch/x86/platform/mrst/early_printk_mrst.c +++ b/arch/x86/platform/mrst/early_printk_mrst.c @@ -245,16 +245,24 @@ struct console early_mrst_console = { * Following is the early console based on Medfield HSU (High * Speed UART) device. */ -#define HSU_PORT2_PADDR 0xffa28180 +#define HSU_PORT_BASE 0xffa28080 static void __iomem *phsu; -void hsu_early_console_init(void) +void hsu_early_console_init(const char *s) { + unsigned long paddr, port = 0; u8 lcr; - phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, - HSU_PORT2_PADDR); + /* + * Select the early HSU console port if specified by user in the + * kernel command line. + */ + if (*s && !kstrtoul(s, 10, &port)) + port = clamp_val(port, 0, 2); + + paddr = HSU_PORT_BASE + port * 0x80; + phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr); /* Disable FIFO */ writeb(0x0, phsu + UART_FCR); diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index e18604b3fc7d..d8653ab6f498 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -446,19 +446,8 @@ static inline void legacy_pty_init(void) { } int pty_limit = NR_UNIX98_PTY_DEFAULT; static int pty_limit_min; static int pty_limit_max = NR_UNIX98_PTY_MAX; -static int tty_count; static int pty_count; -static inline void pty_inc_count(void) -{ - pty_count = (++tty_count) / 2; -} - -static inline void pty_dec_count(void) -{ - pty_count = (--tty_count) / 2; -} - static struct cdev ptmx_cdev; static struct ctl_table pty_table[] = { @@ -600,8 +589,7 @@ static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) */ tty_driver_kref_get(driver); tty->count++; - pty_inc_count(); /* tty */ - pty_inc_count(); /* tty->link */ + pty_count++; return 0; err_free_mem: deinitialize_tty_struct(o_tty); @@ -613,15 +601,19 @@ err_free_tty: return -ENOMEM; } -static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) +static void ptm_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) +{ + pty_count--; +} + +static void pts_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) { - pty_dec_count(); } static const struct tty_operations ptm_unix98_ops = { .lookup = ptm_unix98_lookup, .install = pty_unix98_install, - .remove = pty_unix98_remove, + .remove = ptm_unix98_remove, .open = pty_open, .close = pty_close, .write = pty_write, @@ -638,7 +630,7 @@ static const struct tty_operations ptm_unix98_ops = { static const struct tty_operations pty_unix98_ops = { .lookup = pts_unix98_lookup, .install = pty_unix98_install, - .remove = pty_unix98_remove, + .remove = pts_unix98_remove, .open = pty_open, .close = pty_close, .write = pty_write, diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 925a1e547a83..705d2dc39c9a 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -535,6 +535,27 @@ config SERIAL_S5PV210 help Serial port support for Samsung's S5P Family of SoC's +config SERIAL_SIRFSOC + tristate "SiRF SoC Platform Serial port support" + depends on ARM && ARCH_PRIMA2 + select SERIAL_CORE + help + Support for the on-chip UART on the CSR SiRFprimaII series, + providing /dev/ttySiRF0, 1 and 2 (note, some machines may not + provide all of these ports, depending on how the serial port + pins are configured). + +config SERIAL_SIRFSOC_CONSOLE + bool "Support for console on SiRF SoC serial port" + depends on SERIAL_SIRFSOC=y + select SERIAL_CORE_CONSOLE + help + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySiRFx". (Try "man bootparam" or see the documentation of + your boot loader about how to pass options to the kernel at + boot time.) config SERIAL_MAX3100 tristate "MAX3100 support" @@ -1324,7 +1345,7 @@ config SERIAL_OF_PLATFORM config SERIAL_OMAP tristate "OMAP serial port support" - depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4 + depends on ARCH_OMAP2PLUS select SERIAL_CORE help If you have a machine based on an Texas Instruments OMAP CPU you @@ -1575,6 +1596,15 @@ config SERIAL_PCH_UART ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series. ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH. +config SERIAL_PCH_UART_CONSOLE + bool "Support for console on Intel EG20T PCH UART/OKI SEMICONDUCTOR ML7213 IOH" + depends on SERIAL_PCH_UART=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you wish to use the PCH UART as the system console + (the system console is the device which receives all kernel messages and + warnings and which allows logins in single user mode). + config SERIAL_MSM_SMD bool "Enable tty device interface for some SMD ports" default n diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index e10cf5b54b6d..af57089ddb67 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -94,3 +94,4 @@ obj-$(CONFIG_SERIAL_MSM_SMD) += msm_smd_tty.o obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o +obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 4c823f341d98..bd85e32521d2 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1256,12 +1256,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, static void atmel_set_ldisc(struct uart_port *port, int new) { - int line = port->line; - - if (line >= port->state->port.tty->driver->num) - return; - - if (port->state->port.tty->ldisc->ops->num == N_PPS) { + if (new == N_PPS) { port->flags |= UPF_HARDPPS_CD; atmel_enable_ms(port); } else { diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index e272d3919c67..a9234ba8f8d5 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -1154,7 +1154,6 @@ serial_hsu_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; if (co->index == -1 || co->index >= serial_hsu_reg.nr) co->index = 0; @@ -1165,9 +1164,7 @@ serial_hsu_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - ret = uart_set_options(&up->port, co, baud, parity, bits, flow); - - return ret; + return uart_set_options(&up->port, co, baud, parity, bits, flow); } static struct console serial_hsu_console = { @@ -1176,9 +1173,13 @@ static struct console serial_hsu_console = { .device = uart_console_device, .setup = serial_hsu_console_setup, .flags = CON_PRINTBUFFER, - .index = 2, + .index = -1, .data = &serial_hsu_reg, }; + +#define SERIAL_HSU_CONSOLE (&serial_hsu_console) +#else +#define SERIAL_HSU_CONSOLE NULL #endif struct uart_ops serial_hsu_pops = { @@ -1208,6 +1209,7 @@ static struct uart_driver serial_hsu_reg = { .major = TTY_MAJOR, .minor = 128, .nr = 3, + .cons = SERIAL_HSU_CONSOLE, }; #ifdef CONFIG_PM @@ -1342,12 +1344,6 @@ static int serial_hsu_probe(struct pci_dev *pdev, } uart_add_one_port(&serial_hsu_reg, &uport->port); -#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE - if (index == 2) { - register_console(&serial_hsu_console); - uport->port.cons = &serial_hsu_console; - } -#endif pci_set_drvdata(pdev, uport); } diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index d6aba8c087e4..de0f613ed6f5 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -25,6 +25,9 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/dmi.h> +#include <linux/console.h> +#include <linux/nmi.h> +#include <linux/delay.h> #include <linux/dmaengine.h> #include <linux/pch_dma.h> @@ -198,6 +201,10 @@ enum { #define PCI_VENDOR_ID_ROHM 0x10DB +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +#define DEFAULT_BAUD_RATE 1843200 /* 1.8432MHz */ + struct pch_uart_buffer { unsigned char *buf; int size; @@ -276,6 +283,9 @@ static struct pch_uart_driver_data drv_dat[] = { [pch_ml7831_uart1] = {PCH_UART_2LINE, 1}, }; +#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE +static struct eg20t_port *pch_uart_ports[PCH_UART_NR]; +#endif static unsigned int default_baud = 9600; static const int trigger_level_256[4] = { 1, 64, 128, 224 }; static const int trigger_level_64[4] = { 1, 16, 32, 56 }; @@ -1385,6 +1395,143 @@ static struct uart_ops pch_uart_ops = { .verify_port = pch_uart_verify_port }; +#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE + +/* + * Wait for transmitter & holding register to empty + */ +static void wait_for_xmitr(struct eg20t_port *up, int bits) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + for (;;) { + status = ioread8(up->membase + UART_LSR); + + if ((status & bits) == bits) + break; + if (--tmout == 0) + break; + udelay(1); + } + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + unsigned int tmout; + for (tmout = 1000000; tmout; tmout--) { + unsigned int msr = ioread8(up->membase + UART_MSR); + if (msr & UART_MSR_CTS) + break; + udelay(1); + touch_nmi_watchdog(); + } + } +} + +static void pch_console_putchar(struct uart_port *port, int ch) +{ + struct eg20t_port *priv = + container_of(port, struct eg20t_port, port); + + wait_for_xmitr(priv, UART_LSR_THRE); + iowrite8(ch, priv->membase + PCH_UART_THR); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void +pch_console_write(struct console *co, const char *s, unsigned int count) +{ + struct eg20t_port *priv; + + unsigned long flags; + u8 ier; + int locked = 1; + + priv = pch_uart_ports[co->index]; + + touch_nmi_watchdog(); + + local_irq_save(flags); + if (priv->port.sysrq) { + /* serial8250_handle_port() already took the lock */ + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&priv->port.lock); + } else + spin_lock(&priv->port.lock); + + /* + * First save the IER then disable the interrupts + */ + ier = ioread8(priv->membase + UART_IER); + + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); + + uart_console_write(&priv->port, s, count, pch_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(priv, BOTH_EMPTY); + iowrite8(ier, priv->membase + UART_IER); + + if (locked) + spin_unlock(&priv->port.lock); + local_irq_restore(flags); +} + +static int __init pch_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= PCH_UART_NR) + co->index = 0; + port = &pch_uart_ports[co->index]->port; + + if (!port || (!port->iobase && !port->membase)) + return -ENODEV; + + /* setup uartclock */ + port->uartclk = DEFAULT_BAUD_RATE; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver pch_uart_driver; + +static struct console pch_console = { + .name = PCH_UART_DRIVER_DEVICE, + .write = pch_console_write, + .device = uart_console_device, + .setup = pch_console_setup, + .flags = CON_PRINTBUFFER | CON_ANYTIME, + .index = -1, + .data = &pch_uart_driver, +}; + +#define PCH_CONSOLE (&pch_console) +#else +#define PCH_CONSOLE NULL +#endif + static struct uart_driver pch_uart_driver = { .owner = THIS_MODULE, .driver_name = KBUILD_MODNAME, @@ -1392,6 +1539,7 @@ static struct uart_driver pch_uart_driver = { .major = 0, .minor = 0, .nr = PCH_UART_NR, + .cons = PCH_CONSOLE, }; static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, @@ -1418,7 +1566,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, if (!rxbuf) goto init_port_free_txbuf; - base_baud = 1843200; /* 1.8432MHz */ + base_baud = DEFAULT_BAUD_RATE; /* quirk for CM-iTC board */ board_name = dmi_get_system_info(DMI_BOARD_NAME); @@ -1468,6 +1616,9 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, pci_set_drvdata(pdev, priv); pch_uart_hal_request(pdev, fifosize, base_baud); +#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE + pch_uart_ports[board->line_no] = priv; +#endif ret = uart_add_one_port(&pch_uart_driver, &priv->port); if (ret < 0) goto init_port_hal_free; @@ -1475,6 +1626,9 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, return priv; init_port_hal_free: +#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE + pch_uart_ports[board->line_no] = NULL; +#endif free_page((unsigned long)rxbuf); init_port_free_txbuf: kfree(priv); @@ -1497,6 +1651,10 @@ static void pch_uart_pci_remove(struct pci_dev *pdev) priv = (struct eg20t_port *)pci_get_drvdata(pdev); pci_disable_msi(pdev); + +#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE + pch_uart_ports[priv->port.line] = NULL; +#endif pch_uart_exit_port(priv); pci_disable_device(pdev); kfree(priv); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0406d7ff505e..d2990f738606 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -22,6 +22,7 @@ */ #include <linux/module.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/console.h> @@ -60,6 +61,8 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, static void uart_wait_until_sent(struct tty_struct *tty, int timeout); static void uart_change_pm(struct uart_state *state, int pm_state); +static void uart_port_shutdown(struct tty_port *port); + /* * This routine is used by the interrupt handler to schedule processing in * the software interrupt portion of the driver. @@ -128,25 +131,16 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) * Startup the port. This will be called once per open. All calls * will be serialised by the per-port mutex. */ -static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw) +static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, + int init_hw) { struct uart_port *uport = state->uart_port; struct tty_port *port = &state->port; unsigned long page; int retval = 0; - if (port->flags & ASYNC_INITIALIZED) - return 0; - - /* - * Set the TTY IO error marker - we will only clear this - * once we have successfully opened the port. Also set - * up the tty->alt_speed kludge - */ - set_bit(TTY_IO_ERROR, &tty->flags); - if (uport->type == PORT_UNKNOWN) - return 0; + return 1; /* * Initialise and allocate the transmit and temporary @@ -188,10 +182,6 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, int in tty->hw_stopped = 1; spin_unlock_irq(&uport->lock); } - - set_bit(ASYNCB_INITIALIZED, &port->flags); - - clear_bit(TTY_IO_ERROR, &tty->flags); } /* @@ -200,6 +190,31 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, int in * now. */ if (retval && capable(CAP_SYS_ADMIN)) + return 1; + + return retval; +} + +static int uart_startup(struct tty_struct *tty, struct uart_state *state, + int init_hw) +{ + struct tty_port *port = &state->port; + int retval; + + if (port->flags & ASYNC_INITIALIZED) + return 0; + + /* + * Set the TTY IO error marker - we will only clear this + * once we have successfully opened the port. + */ + set_bit(TTY_IO_ERROR, &tty->flags); + + retval = uart_port_startup(tty, state, init_hw); + if (!retval) { + set_bit(ASYNCB_INITIALIZED, &port->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); + } else if (retval > 0) retval = 0; return retval; @@ -228,24 +243,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) if (!tty || (tty->termios->c_cflag & HUPCL)) uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); - /* - * clear delta_msr_wait queue to avoid mem leaks: we may free - * the irq here so the queue might never be woken up. Note - * that we won't end up waiting on delta_msr_wait again since - * any outstanding file descriptors should be pointing at - * hung_up_tty_fops now. - */ - wake_up_interruptible(&port->delta_msr_wait); - - /* - * Free the IRQ and disable the port. - */ - uport->ops->shutdown(uport); - - /* - * Ensure that the IRQ handler isn't running on another CPU. - */ - synchronize_irq(uport->irq); + uart_port_shutdown(port); } /* @@ -658,10 +656,10 @@ static int uart_get_info(struct uart_state *state, tmp.flags = uport->flags; tmp.xmit_fifo_size = uport->fifosize; tmp.baud_base = uport->uartclk / 16; - tmp.close_delay = port->close_delay / 10; + tmp.close_delay = jiffies_to_msecs(port->close_delay) / 10; tmp.closing_wait = port->closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : - port->closing_wait / 10; + jiffies_to_msecs(port->closing_wait) / 10; tmp.custom_divisor = uport->custom_divisor; tmp.hub6 = uport->hub6; tmp.io_type = uport->iotype; @@ -695,9 +693,10 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state, new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; new_serial.irq = irq_canonicalize(new_serial.irq); - close_delay = new_serial.close_delay * 10; + close_delay = msecs_to_jiffies(new_serial.close_delay * 10); closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; + ASYNC_CLOSING_WAIT_NONE : + msecs_to_jiffies(new_serial.closing_wait * 10); /* * This semaphore protects port->count. It is also @@ -1265,47 +1264,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp) pr_debug("uart_close(%d) called\n", uport->line); - spin_lock_irqsave(&port->lock, flags); - - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&port->lock, flags); + if (tty_port_close_start(port, tty, filp) == 0) return; - } - - if ((tty->count == 1) && (port->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. port->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk(KERN_ERR "uart_close: bad serial port count; tty->count is 1, " - "port->count is %d\n", port->count); - port->count = 1; - } - if (--port->count < 0) { - printk(KERN_ERR "uart_close: bad serial port count for %s: %d\n", - tty->name, port->count); - port->count = 0; - } - if (port->count) { - spin_unlock_irqrestore(&port->lock, flags); - return; - } - - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters by - * setting tty->closing. - */ - set_bit(ASYNCB_CLOSING, &port->flags); - tty->closing = 1; - spin_unlock_irqrestore(&port->lock, flags); - - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent_from_close(tty, - msecs_to_jiffies(port->closing_wait)); /* * At this point, we stop accepting input. To do this, we @@ -1337,7 +1297,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp) if (port->blocked_open) { spin_unlock_irqrestore(&port->lock, flags); if (port->close_delay) - msleep_interruptible(port->close_delay); + msleep_interruptible( + jiffies_to_msecs(port->close_delay)); spin_lock_irqsave(&port->lock, flags); } else if (!uart_console(uport)) { spin_unlock_irqrestore(&port->lock, flags); @@ -1441,6 +1402,36 @@ static void uart_hangup(struct tty_struct *tty) mutex_unlock(&port->mutex); } +static int uart_port_activate(struct tty_port *port, struct tty_struct *tty) +{ + return 0; +} + +static void uart_port_shutdown(struct tty_port *port) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free + * the irq here so the queue might never be woken up. Note + * that we won't end up waiting on delta_msr_wait again since + * any outstanding file descriptors should be pointing at + * hung_up_tty_fops now. + */ + wake_up_interruptible(&port->delta_msr_wait); + + /* + * Free the IRQ and disable the port. + */ + uport->ops->shutdown(uport); + + /* + * Ensure that the IRQ handler isn't running on another CPU. + */ + synchronize_irq(uport->irq); +} + static int uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); @@ -1466,33 +1457,6 @@ static void uart_dtr_rts(struct tty_port *port, int onoff) uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); } -static struct uart_state *uart_get(struct uart_driver *drv, int line) -{ - struct uart_state *state; - struct tty_port *port; - int ret = 0; - - state = drv->state + line; - port = &state->port; - if (mutex_lock_interruptible(&port->mutex)) { - ret = -ERESTARTSYS; - goto err; - } - - port->count++; - if (!state->uart_port || state->uart_port->flags & UPF_DEAD) { - ret = -ENXIO; - goto err_unlock; - } - return state; - - err_unlock: - port->count--; - mutex_unlock(&port->mutex); - err: - return ERR_PTR(ret); -} - /* * calls to uart_open are serialised by the BKL in * fs/char_dev.c:chrdev_open() @@ -1506,26 +1470,29 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) static int uart_open(struct tty_struct *tty, struct file *filp) { struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; - struct uart_state *state; - struct tty_port *port; int retval, line = tty->index; + struct uart_state *state = drv->state + line; + struct tty_port *port = &state->port; pr_debug("uart_open(%d) called\n", line); /* - * We take the semaphore inside uart_get to guarantee that we won't - * be re-entered while allocating the state structure, or while we - * request any IRQs that the driver may need. This also has the nice - * side-effect that it delays the action of uart_hangup, so we can - * guarantee that state->port.tty will always contain something - * reasonable. + * We take the semaphore here to guarantee that we won't be re-entered + * while allocating the state structure, or while we request any IRQs + * that the driver may need. This also has the nice side-effect that + * it delays the action of uart_hangup, so we can guarantee that + * state->port.tty will always contain something reasonable. */ - state = uart_get(drv, line); - if (IS_ERR(state)) { - retval = PTR_ERR(state); - goto fail; + if (mutex_lock_interruptible(&port->mutex)) { + retval = -ERESTARTSYS; + goto end; + } + + port->count++; + if (!state->uart_port || state->uart_port->flags & UPF_DEAD) { + retval = -ENXIO; + goto err_dec_count; } - port = &state->port; /* * Once we set tty->driver_data here, we are guaranteed that @@ -1535,7 +1502,6 @@ static int uart_open(struct tty_struct *tty, struct file *filp) tty->driver_data = state; state->uart_port->state = state; tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0; - tty->alt_speed = 0; tty_port_tty_set(port, tty); /* @@ -1543,9 +1509,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) */ if (tty_hung_up_p(filp)) { retval = -EAGAIN; - port->count--; - mutex_unlock(&port->mutex); - goto fail; + goto err_dec_count; } /* @@ -1566,8 +1530,12 @@ static int uart_open(struct tty_struct *tty, struct file *filp) if (retval == 0) retval = tty_port_block_til_ready(port, tty, filp); -fail: +end: return retval; +err_dec_count: + port->count--; + mutex_unlock(&port->mutex); + goto end; } static const char *uart_type(struct uart_port *port) @@ -1858,6 +1826,14 @@ uart_set_options(struct uart_port *port, struct console *co, EXPORT_SYMBOL_GPL(uart_set_options); #endif /* CONFIG_SERIAL_CORE_CONSOLE */ +/** + * uart_change_pm - set power state of the port + * + * @state: port descriptor + * @pm_state: new state + * + * Locking: port->mutex has to be held + */ static void uart_change_pm(struct uart_state *state, int pm_state) { struct uart_port *port = state->uart_port; @@ -2214,6 +2190,8 @@ static const struct tty_operations uart_ops = { }; static const struct tty_port_operations uart_port_ops = { + .activate = uart_port_activate, + .shutdown = uart_port_shutdown, .carrier_raised = uart_carrier_raised, .dtr_rts = uart_dtr_rts, }; @@ -2275,8 +2253,8 @@ int uart_register_driver(struct uart_driver *drv) tty_port_init(port); port->ops = &uart_port_ops; - port->close_delay = 500; /* .5 seconds */ - port->closing_wait = 30000; /* 30 seconds */ + port->close_delay = HZ / 2; /* .5 seconds */ + port->closing_wait = 30 * HZ;/* 30 seconds */ } retval = tty_register_driver(normal); @@ -2467,6 +2445,99 @@ int uart_match_port(struct uart_port *port1, struct uart_port *port2) } EXPORT_SYMBOL(uart_match_port); +/** + * uart_handle_dcd_change - handle a change of carrier detect state + * @uport: uart_port structure for the open port + * @status: new carrier detect status, nonzero if active + */ +void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) +{ + struct uart_state *state = uport->state; + struct tty_port *port = &state->port; + struct tty_ldisc *ld = tty_ldisc_ref(port->tty); + struct pps_event_time ts; + + if (ld && ld->ops->dcd_change) + pps_get_ts(&ts); + + uport->icount.dcd++; +#ifdef CONFIG_HARD_PPS + if ((uport->flags & UPF_HARDPPS_CD) && status) + hardpps(); +#endif + + if (port->flags & ASYNC_CHECK_CD) { + if (status) + wake_up_interruptible(&port->open_wait); + else if (port->tty) + tty_hangup(port->tty); + } + + if (ld && ld->ops->dcd_change) + ld->ops->dcd_change(port->tty, status, &ts); + if (ld) + tty_ldisc_deref(ld); +} +EXPORT_SYMBOL_GPL(uart_handle_dcd_change); + +/** + * uart_handle_cts_change - handle a change of clear-to-send state + * @uport: uart_port structure for the open port + * @status: new clear to send status, nonzero if active + */ +void uart_handle_cts_change(struct uart_port *uport, unsigned int status) +{ + struct tty_port *port = &uport->state->port; + struct tty_struct *tty = port->tty; + + uport->icount.cts++; + + if (port->flags & ASYNC_CTS_FLOW) { + if (tty->hw_stopped) { + if (status) { + tty->hw_stopped = 0; + uport->ops->start_tx(uport); + uart_write_wakeup(uport); + } + } else { + if (!status) { + tty->hw_stopped = 1; + uport->ops->stop_tx(uport); + } + } + } +} +EXPORT_SYMBOL_GPL(uart_handle_cts_change); + +/** + * uart_insert_char - push a char to the uart layer + * + * User is responsible to call tty_flip_buffer_push when they are done with + * insertion. + * + * @port: corresponding port + * @status: state of the serial port RX buffer (LSR for 8250) + * @overrun: mask of overrun bits in @status + * @ch: character to push + * @flag: flag for the character (see TTY_NORMAL and friends) + */ +void uart_insert_char(struct uart_port *port, unsigned int status, + unsigned int overrun, unsigned int ch, unsigned int flag) +{ + struct tty_struct *tty = port->state->port.tty; + + if ((status & port->ignore_status_mask & ~overrun) == 0) + tty_insert_flip_char(tty, ch, flag); + + /* + * Overrun is special. Since it's reported immediately, + * it doesn't affect the current character. + */ + if (status & ~port->ignore_status_mask & overrun) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); +} +EXPORT_SYMBOL_GPL(uart_insert_char); + EXPORT_SYMBOL(uart_write_wakeup); EXPORT_SYMBOL(uart_register_driver); EXPORT_SYMBOL(uart_unregister_driver); diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c new file mode 100644 index 000000000000..a60523fee11b --- /dev/null +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -0,0 +1,783 @@ +/* + * Driver for CSR SiRFprimaII onboard UARTs. + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/sysrq.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <asm/irq.h> +#include <asm/mach/irq.h> +#include <linux/pinctrl/pinmux.h> + +#include "sirfsoc_uart.h" + +static unsigned int +sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count); +static unsigned int +sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count); +static struct uart_driver sirfsoc_uart_drv; + +static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = { + {4000000, 2359296}, + {3500000, 1310721}, + {3000000, 1572865}, + {2500000, 1245186}, + {2000000, 1572866}, + {1500000, 1245188}, + {1152000, 1638404}, + {1000000, 1572869}, + {921600, 1114120}, + {576000, 1245196}, + {500000, 1245198}, + {460800, 1572876}, + {230400, 1310750}, + {115200, 1310781}, + {57600, 1310843}, + {38400, 1114328}, + {19200, 1114545}, + {9600, 1114979}, +}; + +static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = { + [0] = { + .port = { + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF, + .line = 0, + }, + }, + [1] = { + .port = { + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF, + .line = 1, + }, + }, + [2] = { + .port = { + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF, + .line = 2, + }, + }, +}; + +static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port) +{ + return container_of(port, struct sirfsoc_uart_port, port); +} + +static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port) +{ + unsigned long reg; + reg = rd_regl(port, SIRFUART_TX_FIFO_STATUS); + if (reg & SIRFUART_FIFOEMPTY_MASK(port)) + return TIOCSER_TEMT; + else + return 0; +} + +static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + if (!(sirfport->ms_enabled)) { + goto cts_asserted; + } else if (sirfport->hw_flow_ctrl) { + if (!(rd_regl(port, SIRFUART_AFC_CTRL) & + SIRFUART_CTS_IN_STATUS)) + goto cts_asserted; + else + goto cts_deasserted; + } +cts_deasserted: + return TIOCM_CAR | TIOCM_DSR; +cts_asserted: + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + unsigned int assert = mctrl & TIOCM_RTS; + unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0; + unsigned int current_val; + if (sirfport->hw_flow_ctrl) { + current_val = rd_regl(port, SIRFUART_AFC_CTRL) & ~0xFF; + val |= current_val; + wr_regl(port, SIRFUART_AFC_CTRL, val); + } +} + +static void sirfsoc_uart_stop_tx(struct uart_port *port) +{ + unsigned int regv; + regv = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_TX_INT_EN); +} + +void sirfsoc_uart_start_tx(struct uart_port *port) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + unsigned long regv; + sirfsoc_uart_pio_tx_chars(sirfport, 1); + wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_START); + regv = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_TX_INT_EN); +} + +static void sirfsoc_uart_stop_rx(struct uart_port *port) +{ + unsigned long regv; + wr_regl(port, SIRFUART_RX_FIFO_OP, 0); + regv = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_RX_IO_INT_EN); +} + +static void sirfsoc_uart_disable_ms(struct uart_port *port) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + unsigned long reg; + sirfport->ms_enabled = 0; + if (!sirfport->hw_flow_ctrl) + return; + reg = rd_regl(port, SIRFUART_AFC_CTRL); + wr_regl(port, SIRFUART_AFC_CTRL, reg & ~0x3FF); + reg = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, reg & ~SIRFUART_CTS_INT_EN); +} + +static void sirfsoc_uart_enable_ms(struct uart_port *port) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + unsigned long reg; + unsigned long flg; + if (!sirfport->hw_flow_ctrl) + return; + flg = SIRFUART_AFC_RX_EN | SIRFUART_AFC_TX_EN; + reg = rd_regl(port, SIRFUART_AFC_CTRL); + wr_regl(port, SIRFUART_AFC_CTRL, reg | flg); + reg = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, reg | SIRFUART_CTS_INT_EN); + uart_handle_cts_change(port, + !(rd_regl(port, SIRFUART_AFC_CTRL) & SIRFUART_CTS_IN_STATUS)); + sirfport->ms_enabled = 1; +} + +static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long ulcon = rd_regl(port, SIRFUART_LINE_CTRL); + if (break_state) + ulcon |= SIRFUART_SET_BREAK; + else + ulcon &= ~SIRFUART_SET_BREAK; + wr_regl(port, SIRFUART_LINE_CTRL, ulcon); +} + +static unsigned int +sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count) +{ + unsigned int ch, rx_count = 0; + struct tty_struct *tty; + + tty = tty_port_tty_get(&port->state->port); + if (!tty) + return -ENODEV; + + while (!(rd_regl(port, SIRFUART_RX_FIFO_STATUS) & + SIRFUART_FIFOEMPTY_MASK(port))) { + ch = rd_regl(port, SIRFUART_RX_FIFO_DATA) | SIRFUART_DUMMY_READ; + if (unlikely(uart_handle_sysrq_char(port, ch))) + continue; + uart_insert_char(port, 0, 0, ch, TTY_NORMAL); + rx_count++; + if (rx_count >= max_rx_count) + break; + } + + port->icount.rx += rx_count; + tty_flip_buffer_push(tty); + tty_kref_put(tty); + + return rx_count; +} + +static unsigned int +sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count) +{ + struct uart_port *port = &sirfport->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned int num_tx = 0; + while (!uart_circ_empty(xmit) && + !(rd_regl(port, SIRFUART_TX_FIFO_STATUS) & + SIRFUART_FIFOFULL_MASK(port)) && + count--) { + wr_regl(port, SIRFUART_TX_FIFO_DATA, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + num_tx++; + } + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + return num_tx; +} + +static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) +{ + unsigned long intr_status; + unsigned long cts_status; + unsigned long flag = TTY_NORMAL; + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id; + struct uart_port *port = &sirfport->port; + struct uart_state *state = port->state; + struct circ_buf *xmit = &port->state->xmit; + intr_status = rd_regl(port, SIRFUART_INT_STATUS); + wr_regl(port, SIRFUART_INT_STATUS, intr_status); + intr_status &= rd_regl(port, SIRFUART_INT_EN); + if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT))) { + if (intr_status & SIRFUART_RXD_BREAK) { + if (uart_handle_break(port)) + goto recv_char; + uart_insert_char(port, intr_status, + SIRFUART_RX_OFLOW, 0, TTY_BREAK); + return IRQ_HANDLED; + } + if (intr_status & SIRFUART_RX_OFLOW) + port->icount.overrun++; + if (intr_status & SIRFUART_FRM_ERR) { + port->icount.frame++; + flag = TTY_FRAME; + } + if (intr_status & SIRFUART_PARITY_ERR) + flag = TTY_PARITY; + wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); + wr_regl(port, SIRFUART_RX_FIFO_OP, 0); + wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START); + intr_status &= port->read_status_mask; + uart_insert_char(port, intr_status, + SIRFUART_RX_OFLOW_INT, 0, flag); + } +recv_char: + if (intr_status & SIRFUART_CTS_INT_EN) { + cts_status = !(rd_regl(port, SIRFUART_AFC_CTRL) & + SIRFUART_CTS_IN_STATUS); + if (cts_status != 0) { + uart_handle_cts_change(port, 1); + } else { + uart_handle_cts_change(port, 0); + wake_up_interruptible(&state->port.delta_msr_wait); + } + } + if (intr_status & SIRFUART_RX_IO_INT_EN) + sirfsoc_uart_pio_rx_chars(port, SIRFSOC_UART_IO_RX_MAX_CNT); + if (intr_status & SIRFUART_TX_INT_EN) { + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + return IRQ_HANDLED; + } else { + sirfsoc_uart_pio_tx_chars(sirfport, + SIRFSOC_UART_IO_TX_REASONABLE_CNT); + if ((uart_circ_empty(xmit)) && + (rd_regl(port, SIRFUART_TX_FIFO_STATUS) & + SIRFUART_FIFOEMPTY_MASK(port))) + sirfsoc_uart_stop_tx(port); + } + } + return IRQ_HANDLED; +} + +static void sirfsoc_uart_start_rx(struct uart_port *port) +{ + unsigned long regv; + regv = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_RX_IO_INT_EN); + wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); + wr_regl(port, SIRFUART_RX_FIFO_OP, 0); + wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START); +} + +static unsigned int +sirfsoc_calc_sample_div(unsigned long baud_rate, + unsigned long ioclk_rate, unsigned long *setted_baud) +{ + unsigned long min_delta = ~0UL; + unsigned short sample_div; + unsigned int regv = 0; + unsigned long ioclk_div; + unsigned long baud_tmp; + int temp_delta; + + for (sample_div = SIRF_MIN_SAMPLE_DIV; + sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) { + ioclk_div = (ioclk_rate / (baud_rate * (sample_div + 1))) - 1; + if (ioclk_div > SIRF_IOCLK_DIV_MAX) + continue; + baud_tmp = ioclk_rate / ((ioclk_div + 1) * (sample_div + 1)); + temp_delta = baud_tmp - baud_rate; + temp_delta = (temp_delta > 0) ? temp_delta : -temp_delta; + if (temp_delta < min_delta) { + regv = regv & (~SIRF_IOCLK_DIV_MASK); + regv = regv | ioclk_div; + regv = regv & (~SIRF_SAMPLE_DIV_MASK); + regv = regv | (sample_div << SIRF_SAMPLE_DIV_SHIFT); + min_delta = temp_delta; + *setted_baud = baud_tmp; + } + } + return regv; +} + +static void sirfsoc_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + unsigned long ioclk_rate; + unsigned long config_reg = 0; + unsigned long baud_rate; + unsigned long setted_baud; + unsigned long flags; + unsigned long ic; + unsigned int clk_div_reg = 0; + unsigned long temp_reg_val; + unsigned long rx_time_out; + int threshold_div; + int temp; + + ioclk_rate = 150000000; + switch (termios->c_cflag & CSIZE) { + default: + case CS8: + config_reg |= SIRFUART_DATA_BIT_LEN_8; + break; + case CS7: + config_reg |= SIRFUART_DATA_BIT_LEN_7; + break; + case CS6: + config_reg |= SIRFUART_DATA_BIT_LEN_6; + break; + case CS5: + config_reg |= SIRFUART_DATA_BIT_LEN_5; + break; + } + if (termios->c_cflag & CSTOPB) + config_reg |= SIRFUART_STOP_BIT_LEN_2; + baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000); + spin_lock_irqsave(&port->lock, flags); + port->read_status_mask = SIRFUART_RX_OFLOW_INT; + port->ignore_status_mask = 0; + /* read flags */ + if (termios->c_iflag & INPCK) + port->read_status_mask |= + SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SIRFUART_RXD_BREAK_INT; + /* ignore flags */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= + SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT; + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= SIRFUART_DUMMY_READ; + /* enable parity if PARENB is set*/ + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) + config_reg |= SIRFUART_STICK_BIT_MARK; + else + config_reg |= SIRFUART_STICK_BIT_SPACE; + } else if (termios->c_cflag & PARODD) { + config_reg |= SIRFUART_STICK_BIT_ODD; + } else { + config_reg |= SIRFUART_STICK_BIT_EVEN; + } + } + /* Hardware Flow Control Settings */ + if (UART_ENABLE_MS(port, termios->c_cflag)) { + if (!sirfport->ms_enabled) + sirfsoc_uart_enable_ms(port); + } else { + if (sirfport->ms_enabled) + sirfsoc_uart_disable_ms(port); + } + + /* common rate: fast calculation */ + for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++) + if (baud_rate == baudrate_to_regv[ic].baud_rate) + clk_div_reg = baudrate_to_regv[ic].reg_val; + setted_baud = baud_rate; + /* arbitary rate setting */ + if (unlikely(clk_div_reg == 0)) + clk_div_reg = sirfsoc_calc_sample_div(baud_rate, ioclk_rate, + &setted_baud); + wr_regl(port, SIRFUART_DIVISOR, clk_div_reg); + + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, setted_baud, setted_baud); + + /* set receive timeout */ + rx_time_out = SIRFSOC_UART_RX_TIMEOUT(baud_rate, 20000); + rx_time_out = (rx_time_out > 0xFFFF) ? 0xFFFF : rx_time_out; + config_reg |= SIRFUART_RECV_TIMEOUT(rx_time_out); + temp_reg_val = rd_regl(port, SIRFUART_TX_FIFO_OP); + wr_regl(port, SIRFUART_RX_FIFO_OP, 0); + wr_regl(port, SIRFUART_TX_FIFO_OP, + temp_reg_val & ~SIRFUART_TX_FIFO_START); + wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, SIRFUART_TX_MODE_IO); + wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, SIRFUART_RX_MODE_IO); + wr_regl(port, SIRFUART_LINE_CTRL, config_reg); + + /* Reset Rx/Tx FIFO Threshold level for proper baudrate */ + if (baud_rate < 1000000) + threshold_div = 1; + else + threshold_div = 2; + temp = port->line == 1 ? 16 : 64; + wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp / threshold_div); + wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp / threshold_div); + temp_reg_val |= SIRFUART_TX_FIFO_START; + wr_regl(port, SIRFUART_TX_FIFO_OP, temp_reg_val); + uart_update_timeout(port, termios->c_cflag, baud_rate); + sirfsoc_uart_start_rx(port); + wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_TX_EN | SIRFUART_RX_EN); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void startup_uart_controller(struct uart_port *port) +{ + unsigned long temp_regv; + int temp; + temp_regv = rd_regl(port, SIRFUART_TX_DMA_IO_CTRL); + wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, temp_regv | SIRFUART_TX_MODE_IO); + temp_regv = rd_regl(port, SIRFUART_RX_DMA_IO_CTRL); + wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, temp_regv | SIRFUART_RX_MODE_IO); + wr_regl(port, SIRFUART_TX_DMA_IO_LEN, 0); + wr_regl(port, SIRFUART_RX_DMA_IO_LEN, 0); + wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_RX_EN | SIRFUART_TX_EN); + wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_RESET); + wr_regl(port, SIRFUART_TX_FIFO_OP, 0); + wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); + wr_regl(port, SIRFUART_RX_FIFO_OP, 0); + temp = port->line == 1 ? 16 : 64; + wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp); + wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp); +} + +static int sirfsoc_uart_startup(struct uart_port *port) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + unsigned int index = port->line; + int ret; + set_irq_flags(port->irq, IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(port->irq, + sirfsoc_uart_isr, + 0, + SIRFUART_PORT_NAME, + sirfport); + if (ret != 0) { + dev_err(port->dev, "UART%d request IRQ line (%d) failed.\n", + index, port->irq); + goto irq_err; + } + startup_uart_controller(port); + enable_irq(port->irq); +irq_err: + return ret; +} + +static void sirfsoc_uart_shutdown(struct uart_port *port) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + wr_regl(port, SIRFUART_INT_EN, 0); + free_irq(port->irq, sirfport); + if (sirfport->ms_enabled) { + sirfsoc_uart_disable_ms(port); + sirfport->ms_enabled = 0; + } +} + +static const char *sirfsoc_uart_type(struct uart_port *port) +{ + return port->type == SIRFSOC_PORT_TYPE ? SIRFUART_PORT_NAME : NULL; +} + +static int sirfsoc_uart_request_port(struct uart_port *port) +{ + void *ret; + ret = request_mem_region(port->mapbase, + SIRFUART_MAP_SIZE, SIRFUART_PORT_NAME); + return ret ? 0 : -EBUSY; +} + +static void sirfsoc_uart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, SIRFUART_MAP_SIZE); +} + +static void sirfsoc_uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = SIRFSOC_PORT_TYPE; + sirfsoc_uart_request_port(port); + } +} + +static struct uart_ops sirfsoc_uart_ops = { + .tx_empty = sirfsoc_uart_tx_empty, + .get_mctrl = sirfsoc_uart_get_mctrl, + .set_mctrl = sirfsoc_uart_set_mctrl, + .stop_tx = sirfsoc_uart_stop_tx, + .start_tx = sirfsoc_uart_start_tx, + .stop_rx = sirfsoc_uart_stop_rx, + .enable_ms = sirfsoc_uart_enable_ms, + .break_ctl = sirfsoc_uart_break_ctl, + .startup = sirfsoc_uart_startup, + .shutdown = sirfsoc_uart_shutdown, + .set_termios = sirfsoc_uart_set_termios, + .type = sirfsoc_uart_type, + .release_port = sirfsoc_uart_release_port, + .request_port = sirfsoc_uart_request_port, + .config_port = sirfsoc_uart_config_port, +}; + +#ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE +static int __init sirfsoc_uart_console_setup(struct console *co, char *options) +{ + unsigned int baud = 115200; + unsigned int bits = 8; + unsigned int parity = 'n'; + unsigned int flow = 'n'; + struct uart_port *port = &sirfsoc_uart_ports[co->index].port; + + if (co->index < 0 || co->index >= SIRFSOC_UART_NR) + return -EINVAL; + + if (!port->mapbase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + port->cons = co; + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch) +{ + while (rd_regl(port, + SIRFUART_TX_FIFO_STATUS) & SIRFUART_FIFOFULL_MASK(port)) + cpu_relax(); + wr_regb(port, SIRFUART_TX_FIFO_DATA, ch); +} + +static void sirfsoc_uart_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &sirfsoc_uart_ports[co->index].port; + uart_console_write(port, s, count, sirfsoc_uart_console_putchar); +} + +static struct console sirfsoc_uart_console = { + .name = SIRFSOC_UART_NAME, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .write = sirfsoc_uart_console_write, + .setup = sirfsoc_uart_console_setup, + .data = &sirfsoc_uart_drv, +}; + +static int __init sirfsoc_uart_console_init(void) +{ + register_console(&sirfsoc_uart_console); + return 0; +} +console_initcall(sirfsoc_uart_console_init); +#endif + +static struct uart_driver sirfsoc_uart_drv = { + .owner = THIS_MODULE, + .driver_name = SIRFUART_PORT_NAME, + .nr = SIRFSOC_UART_NR, + .dev_name = SIRFSOC_UART_NAME, + .major = SIRFSOC_UART_MAJOR, + .minor = SIRFSOC_UART_MINOR, +#ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE + .cons = &sirfsoc_uart_console, +#else + .cons = NULL, +#endif +}; + +int sirfsoc_uart_probe(struct platform_device *pdev) +{ + struct sirfsoc_uart_port *sirfport; + struct uart_port *port; + struct resource *res; + int ret; + + if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) { + dev_err(&pdev->dev, + "Unable to find cell-index in uart node.\n"); + ret = -EFAULT; + goto err; + } + + sirfport = &sirfsoc_uart_ports[pdev->id]; + port = &sirfport->port; + port->dev = &pdev->dev; + port->private_data = sirfport; + + if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL)) + sirfport->hw_flow_ctrl = 1; + + if (of_property_read_u32(pdev->dev.of_node, + "fifosize", + &port->fifosize)) { + dev_err(&pdev->dev, + "Unable to find fifosize in uart node.\n"); + ret = -EFAULT; + goto err; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Insufficient resources.\n"); + ret = -EFAULT; + goto err; + } + port->mapbase = res->start; + port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!port->membase) { + dev_err(&pdev->dev, "Cannot remap resource.\n"); + ret = -ENOMEM; + goto err; + } + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Insufficient resources.\n"); + ret = -EFAULT; + goto irq_err; + } + port->irq = res->start; + + if (sirfport->hw_flow_ctrl) { + sirfport->pmx = pinmux_get(&pdev->dev, NULL); + ret = IS_ERR(sirfport->pmx); + if (ret) + goto pmx_err; + + pinmux_enable(sirfport->pmx); + } + + port->ops = &sirfsoc_uart_ops; + spin_lock_init(&port->lock); + + platform_set_drvdata(pdev, sirfport); + ret = uart_add_one_port(&sirfsoc_uart_drv, port); + if (ret != 0) { + dev_err(&pdev->dev, "Cannot add UART port(%d).\n", pdev->id); + goto port_err; + } + + return 0; + +port_err: + platform_set_drvdata(pdev, NULL); + if (sirfport->hw_flow_ctrl) { + pinmux_disable(sirfport->pmx); + pinmux_put(sirfport->pmx); + } +pmx_err: +irq_err: + devm_iounmap(&pdev->dev, port->membase); +err: + return ret; +} + +static int sirfsoc_uart_remove(struct platform_device *pdev) +{ + struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); + struct uart_port *port = &sirfport->port; + platform_set_drvdata(pdev, NULL); + if (sirfport->hw_flow_ctrl) { + pinmux_disable(sirfport->pmx); + pinmux_put(sirfport->pmx); + } + devm_iounmap(&pdev->dev, port->membase); + uart_remove_one_port(&sirfsoc_uart_drv, port); + return 0; +} + +static int +sirfsoc_uart_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); + struct uart_port *port = &sirfport->port; + uart_suspend_port(&sirfsoc_uart_drv, port); + return 0; +} + +static int sirfsoc_uart_resume(struct platform_device *pdev) +{ + struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); + struct uart_port *port = &sirfport->port; + uart_resume_port(&sirfsoc_uart_drv, port); + return 0; +} + +static struct of_device_id sirfsoc_uart_ids[] __devinitdata = { + { .compatible = "sirf,prima2-uart", }, + {} +}; +MODULE_DEVICE_TABLE(of, sirfsoc_serial_of_match); + +static struct platform_driver sirfsoc_uart_driver = { + .probe = sirfsoc_uart_probe, + .remove = __devexit_p(sirfsoc_uart_remove), + .suspend = sirfsoc_uart_suspend, + .resume = sirfsoc_uart_resume, + .driver = { + .name = SIRFUART_PORT_NAME, + .owner = THIS_MODULE, + .of_match_table = sirfsoc_uart_ids, + }, +}; + +static int __init sirfsoc_uart_init(void) +{ + int ret = 0; + + ret = uart_register_driver(&sirfsoc_uart_drv); + if (ret) + goto out; + + ret = platform_driver_register(&sirfsoc_uart_driver); + if (ret) + uart_unregister_driver(&sirfsoc_uart_drv); +out: + return ret; +} +module_init(sirfsoc_uart_init); + +static void __exit sirfsoc_uart_exit(void) +{ + platform_driver_unregister(&sirfsoc_uart_driver); + uart_unregister_driver(&sirfsoc_uart_drv); +} +module_exit(sirfsoc_uart_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Bin Shi <Bin.Shi@csr.com>, Rong Wang<Rong.Wang@csr.com>"); +MODULE_DESCRIPTION("CSR SiRFprimaII Uart Driver"); diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h new file mode 100644 index 000000000000..fc64260fa93c --- /dev/null +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -0,0 +1,185 @@ +/* + * Drivers for CSR SiRFprimaII onboard UARTs. + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ +#include <linux/bitops.h> + +/* UART Register Offset Define */ +#define SIRFUART_LINE_CTRL 0x0040 +#define SIRFUART_TX_RX_EN 0x004c +#define SIRFUART_DIVISOR 0x0050 +#define SIRFUART_INT_EN 0x0054 +#define SIRFUART_INT_STATUS 0x0058 +#define SIRFUART_TX_DMA_IO_CTRL 0x0100 +#define SIRFUART_TX_DMA_IO_LEN 0x0104 +#define SIRFUART_TX_FIFO_CTRL 0x0108 +#define SIRFUART_TX_FIFO_LEVEL_CHK 0x010C +#define SIRFUART_TX_FIFO_OP 0x0110 +#define SIRFUART_TX_FIFO_STATUS 0x0114 +#define SIRFUART_TX_FIFO_DATA 0x0118 +#define SIRFUART_RX_DMA_IO_CTRL 0x0120 +#define SIRFUART_RX_DMA_IO_LEN 0x0124 +#define SIRFUART_RX_FIFO_CTRL 0x0128 +#define SIRFUART_RX_FIFO_LEVEL_CHK 0x012C +#define SIRFUART_RX_FIFO_OP 0x0130 +#define SIRFUART_RX_FIFO_STATUS 0x0134 +#define SIRFUART_RX_FIFO_DATA 0x0138 +#define SIRFUART_AFC_CTRL 0x0140 +#define SIRFUART_SWH_DMA_IO 0x0148 + +/* UART Line Control Register */ +#define SIRFUART_DATA_BIT_LEN_MASK 0x3 +#define SIRFUART_DATA_BIT_LEN_5 BIT(0) +#define SIRFUART_DATA_BIT_LEN_6 1 +#define SIRFUART_DATA_BIT_LEN_7 2 +#define SIRFUART_DATA_BIT_LEN_8 3 +#define SIRFUART_STOP_BIT_LEN_1 0 +#define SIRFUART_STOP_BIT_LEN_2 BIT(2) +#define SIRFUART_PARITY_EN BIT(3) +#define SIRFUART_EVEN_BIT BIT(4) +#define SIRFUART_STICK_BIT_MASK (7 << 3) +#define SIRFUART_STICK_BIT_NONE (0 << 3) +#define SIRFUART_STICK_BIT_EVEN BIT(3) +#define SIRFUART_STICK_BIT_ODD (3 << 3) +#define SIRFUART_STICK_BIT_MARK (5 << 3) +#define SIRFUART_STICK_BIT_SPACE (7 << 3) +#define SIRFUART_SET_BREAK BIT(6) +#define SIRFUART_LOOP_BACK BIT(7) +#define SIRFUART_PARITY_MASK (7 << 3) +#define SIRFUART_DUMMY_READ BIT(16) + +#define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000) +#define SIRFUART_RECV_TIMEOUT_MASK (0xFFFF << 16) +#define SIRFUART_RECV_TIMEOUT(x) (((x) & 0xFFFF) << 16) + +/* UART Auto Flow Control */ +#define SIRFUART_AFC_RX_THD_MASK 0x000000FF +#define SIRFUART_AFC_RX_EN BIT(8) +#define SIRFUART_AFC_TX_EN BIT(9) +#define SIRFUART_CTS_CTRL BIT(10) +#define SIRFUART_RTS_CTRL BIT(11) +#define SIRFUART_CTS_IN_STATUS BIT(12) +#define SIRFUART_RTS_OUT_STATUS BIT(13) + +/* UART Interrupt Enable Register */ +#define SIRFUART_RX_DONE_INT BIT(0) +#define SIRFUART_TX_DONE_INT BIT(1) +#define SIRFUART_RX_OFLOW_INT BIT(2) +#define SIRFUART_TX_ALLOUT_INT BIT(3) +#define SIRFUART_RX_IO_DMA_INT BIT(4) +#define SIRFUART_TX_IO_DMA_INT BIT(5) +#define SIRFUART_RXFIFO_FULL_INT BIT(6) +#define SIRFUART_TXFIFO_EMPTY_INT BIT(7) +#define SIRFUART_RXFIFO_THD_INT BIT(8) +#define SIRFUART_TXFIFO_THD_INT BIT(9) +#define SIRFUART_FRM_ERR_INT BIT(10) +#define SIRFUART_RXD_BREAK_INT BIT(11) +#define SIRFUART_RX_TIMEOUT_INT BIT(12) +#define SIRFUART_PARITY_ERR_INT BIT(13) +#define SIRFUART_CTS_INT_EN BIT(14) +#define SIRFUART_RTS_INT_EN BIT(15) + +/* UART Interrupt Status Register */ +#define SIRFUART_RX_DONE BIT(0) +#define SIRFUART_TX_DONE BIT(1) +#define SIRFUART_RX_OFLOW BIT(2) +#define SIRFUART_TX_ALL_EMPTY BIT(3) +#define SIRFUART_DMA_IO_RX_DONE BIT(4) +#define SIRFUART_DMA_IO_TX_DONE BIT(5) +#define SIRFUART_RXFIFO_FULL BIT(6) +#define SIRFUART_TXFIFO_EMPTY BIT(7) +#define SIRFUART_RXFIFO_THD_REACH BIT(8) +#define SIRFUART_TXFIFO_THD_REACH BIT(9) +#define SIRFUART_FRM_ERR BIT(10) +#define SIRFUART_RXD_BREAK BIT(11) +#define SIRFUART_RX_TIMEOUT BIT(12) +#define SIRFUART_PARITY_ERR BIT(13) +#define SIRFUART_CTS_CHANGE BIT(14) +#define SIRFUART_RTS_CHANGE BIT(15) +#define SIRFUART_PLUG_IN BIT(16) + +#define SIRFUART_ERR_INT_STAT \ + (SIRFUART_RX_OFLOW | \ + SIRFUART_FRM_ERR | \ + SIRFUART_RXD_BREAK | \ + SIRFUART_PARITY_ERR) +#define SIRFUART_ERR_INT_EN \ + (SIRFUART_RX_OFLOW_INT | \ + SIRFUART_FRM_ERR_INT | \ + SIRFUART_RXD_BREAK_INT | \ + SIRFUART_PARITY_ERR_INT) +#define SIRFUART_TX_INT_EN SIRFUART_TXFIFO_EMPTY_INT +#define SIRFUART_RX_IO_INT_EN \ + (SIRFUART_RX_TIMEOUT_INT | \ + SIRFUART_RXFIFO_THD_INT | \ + SIRFUART_RXFIFO_FULL_INT | \ + SIRFUART_ERR_INT_EN) + +/* UART FIFO Register */ +#define SIRFUART_TX_FIFO_STOP 0x0 +#define SIRFUART_TX_FIFO_RESET 0x1 +#define SIRFUART_TX_FIFO_START 0x2 +#define SIRFUART_RX_FIFO_STOP 0x0 +#define SIRFUART_RX_FIFO_RESET 0x1 +#define SIRFUART_RX_FIFO_START 0x2 +#define SIRFUART_TX_MODE_DMA 0 +#define SIRFUART_TX_MODE_IO 1 +#define SIRFUART_RX_MODE_DMA 0 +#define SIRFUART_RX_MODE_IO 1 + +#define SIRFUART_RX_EN 0x1 +#define SIRFUART_TX_EN 0x2 + +/* Generic Definitions */ +#define SIRFSOC_UART_NAME "ttySiRF" +#define SIRFSOC_UART_MAJOR 0 +#define SIRFSOC_UART_MINOR 0 +#define SIRFUART_PORT_NAME "sirfsoc-uart" +#define SIRFUART_MAP_SIZE 0x200 +#define SIRFSOC_UART_NR 3 +#define SIRFSOC_PORT_TYPE 0xa5 + +/* Baud Rate Calculation */ +#define SIRF_MIN_SAMPLE_DIV 0xf +#define SIRF_MAX_SAMPLE_DIV 0x3f +#define SIRF_IOCLK_DIV_MAX 0xffff +#define SIRF_SAMPLE_DIV_SHIFT 16 +#define SIRF_IOCLK_DIV_MASK 0xffff +#define SIRF_SAMPLE_DIV_MASK 0x3f0000 +#define SIRF_BAUD_RATE_SUPPORT_NR 18 + +/* For Fast Baud Rate Calculation */ +struct sirfsoc_baudrate_to_regv { + unsigned int baud_rate; + unsigned int reg_val; +}; + +struct sirfsoc_uart_port { + unsigned char hw_flow_ctrl; + unsigned char ms_enabled; + + struct uart_port port; + struct pinmux *pmx; +}; + +/* Hardware Flow Control */ +#define SIRFUART_AFC_CTRL_RX_THD 0x70 + +/* Register Access Control */ +#define portaddr(port, reg) ((port)->membase + (reg)) +#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) +#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) +#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) +#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) + +/* UART Port Mask */ +#define SIRFUART_FIFOLEVEL_MASK(port) ((port->line == 1) ? (0x1f) : (0x7f)) +#define SIRFUART_FIFOFULL_MASK(port) ((port->line == 1) ? (0x20) : (0x80)) +#define SIRFUART_FIFOEMPTY_MASK(port) ((port->line == 1) ? (0x40) : (0x100)) + +/* I/O Mode */ +#define SIRFSOC_UART_IO_RX_MAX_CNT 256 +#define SIRFSOC_UART_IO_TX_REASONABLE_CNT 6 diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 05085beb83db..57c374399f59 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -790,19 +790,24 @@ static void session_clear_tty(struct pid *session) void disassociate_ctty(int on_exit) { struct tty_struct *tty; - struct pid *tty_pgrp = NULL; if (!current->signal->leader) return; tty = get_current_tty(); if (tty) { - tty_pgrp = get_pid(tty->pgrp); + struct pid *tty_pgrp = get_pid(tty->pgrp); if (on_exit) { if (tty->driver->type != TTY_DRIVER_TYPE_PTY) tty_vhangup(tty); } tty_kref_put(tty); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, SIGHUP, on_exit); + if (!on_exit) + kill_pgrp(tty_pgrp, SIGCONT, on_exit); + put_pid(tty_pgrp); + } } else if (on_exit) { struct pid *old_pgrp; spin_lock_irq(¤t->sighand->siglock); @@ -816,12 +821,6 @@ void disassociate_ctty(int on_exit) } return; } - if (tty_pgrp) { - kill_pgrp(tty_pgrp, SIGHUP, on_exit); - if (!on_exit) - kill_pgrp(tty_pgrp, SIGCONT, on_exit); - put_pid(tty_pgrp); - } spin_lock_irq(¤t->sighand->siglock); put_pid(current->signal->tty_old_pgrp); @@ -1558,6 +1557,59 @@ static void release_tty(struct tty_struct *tty, int idx) } /** + * tty_release_checks - check a tty before real release + * @tty: tty to check + * @o_tty: link of @tty (if any) + * @idx: index of the tty + * + * Performs some paranoid checking before true release of the @tty. + * This is a no-op unless TTY_PARANOIA_CHECK is defined. + */ +static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty, + int idx) +{ +#ifdef TTY_PARANOIA_CHECK + if (idx < 0 || idx >= tty->driver->num) { + printk(KERN_DEBUG "%s: bad idx when trying to free (%s)\n", + __func__, tty->name); + return -1; + } + + /* not much to check for devpts */ + if (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) + return 0; + + if (tty != tty->driver->ttys[idx]) { + printk(KERN_DEBUG "%s: driver.table[%d] not tty for (%s)\n", + __func__, idx, tty->name); + return -1; + } + if (tty->termios != tty->driver->termios[idx]) { + printk(KERN_DEBUG "%s: driver.termios[%d] not termios for (%s)\n", + __func__, idx, tty->name); + return -1; + } + if (tty->driver->other) { + if (o_tty != tty->driver->other->ttys[idx]) { + printk(KERN_DEBUG "%s: other->table[%d] not o_tty for (%s)\n", + __func__, idx, tty->name); + return -1; + } + if (o_tty->termios != tty->driver->other->termios[idx]) { + printk(KERN_DEBUG "%s: other->termios[%d] not o_termios for (%s)\n", + __func__, idx, tty->name); + return -1; + } + if (o_tty->link != tty) { + printk(KERN_DEBUG "%s: bad pty pointers\n", __func__); + return -1; + } + } +#endif + return 0; +} + +/** * tty_release - vfs callback for close * @inode: inode of tty * @filp: file pointer for handle to tty @@ -1585,11 +1637,11 @@ int tty_release(struct inode *inode, struct file *filp) int idx; char buf[64]; - if (tty_paranoia_check(tty, inode, "tty_release_dev")) + if (tty_paranoia_check(tty, inode, __func__)) return 0; tty_lock(); - check_tty_count(tty, "tty_release_dev"); + check_tty_count(tty, __func__); __tty_fasync(-1, filp, 0); @@ -1599,59 +1651,16 @@ int tty_release(struct inode *inode, struct file *filp) devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; o_tty = tty->link; -#ifdef TTY_PARANOIA_CHECK - if (idx < 0 || idx >= tty->driver->num) { - printk(KERN_DEBUG "tty_release_dev: bad idx when trying to " - "free (%s)\n", tty->name); + if (tty_release_checks(tty, o_tty, idx)) { tty_unlock(); return 0; } - if (!devpts) { - if (tty != tty->driver->ttys[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty " - "for (%s)\n", idx, tty->name); - return 0; - } - if (tty->termios != tty->driver->termios[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios " - "for (%s)\n", - idx, tty->name); - return 0; - } - } -#endif #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...", - tty_name(tty, buf), tty->count); + printk(KERN_DEBUG "%s: %s (tty count=%d)...\n", __func__, + tty_name(tty, buf), tty->count); #endif -#ifdef TTY_PARANOIA_CHECK - if (tty->driver->other && - !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { - if (o_tty != tty->driver->other->ttys[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: other->table[%d] " - "not o_tty for (%s)\n", - idx, tty->name); - return 0 ; - } - if (o_tty->termios != tty->driver->other->termios[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: other->termios[%d] " - "not o_termios for (%s)\n", - idx, tty->name); - return 0; - } - if (o_tty->link != tty) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n"); - return 0; - } - } -#endif if (tty->ops->close) tty->ops->close(tty, filp); @@ -1707,8 +1716,8 @@ int tty_release(struct inode *inode, struct file *filp) if (!do_sleep) break; - printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue " - "active!\n", tty_name(tty, buf)); + printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", + __func__, tty_name(tty, buf)); tty_unlock(); mutex_unlock(&tty_mutex); schedule(); @@ -1721,15 +1730,14 @@ int tty_release(struct inode *inode, struct file *filp) */ if (pty_master) { if (--o_tty->count < 0) { - printk(KERN_WARNING "tty_release_dev: bad pty slave count " - "(%d) for %s\n", - o_tty->count, tty_name(o_tty, buf)); + printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n", + __func__, o_tty->count, tty_name(o_tty, buf)); o_tty->count = 0; } } if (--tty->count < 0) { - printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n", - tty->count, tty_name(tty, buf)); + printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n", + __func__, tty->count, tty_name(tty, buf)); tty->count = 0; } @@ -1778,7 +1786,7 @@ int tty_release(struct inode *inode, struct file *filp) } #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "freeing tty structure..."); + printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__); #endif /* * Ask the line discipline code to release its structures @@ -1798,6 +1806,83 @@ int tty_release(struct inode *inode, struct file *filp) } /** + * tty_open_current_tty - get tty of current task for open + * @device: device number + * @filp: file pointer to tty + * @return: tty of the current task iff @device is /dev/tty + * + * We cannot return driver and index like for the other nodes because + * devpts will not work then. It expects inodes to be from devpts FS. + */ +static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) +{ + struct tty_struct *tty; + + if (device != MKDEV(TTYAUX_MAJOR, 0)) + return NULL; + + tty = get_current_tty(); + if (!tty) + return ERR_PTR(-ENXIO); + + filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ + /* noctty = 1; */ + tty_kref_put(tty); + /* FIXME: we put a reference and return a TTY! */ + return tty; +} + +/** + * tty_lookup_driver - lookup a tty driver for a given device file + * @device: device number + * @filp: file pointer to tty + * @noctty: set if the device should not become a controlling tty + * @index: index for the device in the @return driver + * @return: driver for this inode (with increased refcount) + * + * If @return is not erroneous, the caller is responsible to decrement the + * refcount by tty_driver_kref_put. + * + * Locking: tty_mutex protects get_tty_driver + */ +static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, + int *noctty, int *index) +{ + struct tty_driver *driver; + + switch (device) { +#ifdef CONFIG_VT + case MKDEV(TTY_MAJOR, 0): { + extern struct tty_driver *console_driver; + driver = tty_driver_kref_get(console_driver); + *index = fg_console; + *noctty = 1; + break; + } +#endif + case MKDEV(TTYAUX_MAJOR, 1): { + struct tty_driver *console_driver = console_device(index); + if (console_driver) { + driver = tty_driver_kref_get(console_driver); + if (driver) { + /* Don't let /dev/console block */ + filp->f_flags |= O_NONBLOCK; + *noctty = 1; + break; + } + } + return ERR_PTR(-ENODEV); + } + default: + driver = get_tty_driver(device, index); + if (!driver) + return ERR_PTR(-ENODEV); + break; + } + return driver; +} + +/** * tty_open - open a tty device * @inode: inode of device file * @filp: file pointer to tty @@ -1813,16 +1898,16 @@ int tty_release(struct inode *inode, struct file *filp) * The termios state of a pty is reset on first open so that * settings don't persist across reuse. * - * Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work. + * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand */ static int tty_open(struct inode *inode, struct file *filp) { - struct tty_struct *tty = NULL; + struct tty_struct *tty; int noctty, retval; - struct tty_driver *driver; + struct tty_driver *driver = NULL; int index; dev_t device = inode->i_rdev; unsigned saved_flags = filp->f_flags; @@ -1841,66 +1926,22 @@ retry_open: mutex_lock(&tty_mutex); tty_lock(); - if (device == MKDEV(TTYAUX_MAJOR, 0)) { - tty = get_current_tty(); - if (!tty) { - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_free_file(filp); - return -ENXIO; - } - driver = tty_driver_kref_get(tty->driver); - index = tty->index; - filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ - /* noctty = 1; */ - /* FIXME: Should we take a driver reference ? */ - tty_kref_put(tty); - goto got_driver; - } -#ifdef CONFIG_VT - if (device == MKDEV(TTY_MAJOR, 0)) { - extern struct tty_driver *console_driver; - driver = tty_driver_kref_get(console_driver); - index = fg_console; - noctty = 1; - goto got_driver; - } -#endif - if (device == MKDEV(TTYAUX_MAJOR, 1)) { - struct tty_driver *console_driver = console_device(&index); - if (console_driver) { - driver = tty_driver_kref_get(console_driver); - if (driver) { - /* Don't let /dev/console block */ - filp->f_flags |= O_NONBLOCK; - noctty = 1; - goto got_driver; - } + tty = tty_open_current_tty(device, filp); + if (IS_ERR(tty)) { + retval = PTR_ERR(tty); + goto err_unlock; + } else if (!tty) { + driver = tty_lookup_driver(device, filp, &noctty, &index); + if (IS_ERR(driver)) { + retval = PTR_ERR(driver); + goto err_unlock; } - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_free_file(filp); - return -ENODEV; - } - driver = get_tty_driver(device, &index); - if (!driver) { - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_free_file(filp); - return -ENODEV; - } -got_driver: - if (!tty) { /* check whether we're reopening an existing tty */ tty = tty_driver_lookup_tty(driver, inode, index); - if (IS_ERR(tty)) { - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_driver_kref_put(driver); - tty_free_file(filp); - return PTR_ERR(tty); + retval = PTR_ERR(tty); + goto err_unlock; } } @@ -1912,21 +1953,22 @@ got_driver: tty = tty_init_dev(driver, index, 0); mutex_unlock(&tty_mutex); - tty_driver_kref_put(driver); + if (driver) + tty_driver_kref_put(driver); if (IS_ERR(tty)) { tty_unlock(); - tty_free_file(filp); - return PTR_ERR(tty); + retval = PTR_ERR(tty); + goto err_file; } tty_add_file(tty, filp); - check_tty_count(tty, "tty_open"); + check_tty_count(tty, __func__); if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) noctty = 1; #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "opening %s...", tty->name); + printk(KERN_DEBUG "%s: opening %s...\n", __func__, tty->name); #endif if (tty->ops->open) retval = tty->ops->open(tty, filp); @@ -1940,8 +1982,8 @@ got_driver: if (retval) { #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "error %d in opening %s...", retval, - tty->name); + printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, + retval, tty->name); #endif tty_unlock(); /* need to call tty_release without BTM */ tty_release(inode, filp); @@ -1976,6 +2018,15 @@ got_driver: tty_unlock(); mutex_unlock(&tty_mutex); return 0; +err_unlock: + tty_unlock(); + mutex_unlock(&tty_mutex); + /* after locks to avoid deadlock */ + if (!IS_ERR_OR_NULL(driver)) + tty_driver_kref_put(driver); +err_file: + tty_free_file(filp); + return retval; } diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 8e0924f55446..24b95db75d84 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -1,19 +1,11 @@ #include <linux/types.h> -#include <linux/major.h> #include <linux/errno.h> -#include <linux/signal.h> -#include <linux/fcntl.h> +#include <linux/kmod.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/tty_driver.h> -#include <linux/tty_flip.h> -#include <linux/devpts_fs.h> #include <linux/file.h> -#include <linux/console.h> -#include <linux/timer.h> -#include <linux/ctype.h> -#include <linux/kd.h> #include <linux/mm.h> #include <linux/string.h> #include <linux/slab.h> @@ -24,18 +16,8 @@ #include <linux/device.h> #include <linux/wait.h> #include <linux/bitops.h> -#include <linux/delay.h> #include <linux/seq_file.h> - #include <linux/uaccess.h> -#include <asm/system.h> - -#include <linux/kbd_kern.h> -#include <linux/vt_kern.h> -#include <linux/selection.h> - -#include <linux/kmod.h> -#include <linux/nsproxy.h> #include <linux/ratelimit.h> /* @@ -558,8 +540,6 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) long ret; ret = wait_event_timeout(tty_ldisc_idle, atomic_read(&tty->ldisc->users) == 1, timeout); - if (ret < 0) - return ret; return ret > 0 ? 0 : -EBUSY; } diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index eadf33d0abba..945e02cae614 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -483,10 +483,19 @@ static inline int uart_tx_stopped(struct uart_port *port) /* * The following are helper functions for the low level drivers. */ + +extern void uart_handle_dcd_change(struct uart_port *uport, + unsigned int status); +extern void uart_handle_cts_change(struct uart_port *uport, + unsigned int status); + +extern void uart_insert_char(struct uart_port *port, unsigned int status, + unsigned int overrun, unsigned int ch, unsigned int flag); + +#ifdef SUPPORT_SYSRQ static inline int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) { -#ifdef SUPPORT_SYSRQ if (port->sysrq) { if (ch && time_before(jiffies, port->sysrq)) { handle_sysrq(ch); @@ -495,11 +504,10 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) } port->sysrq = 0; } -#endif return 0; } -#ifndef SUPPORT_SYSRQ -#define uart_handle_sysrq_char(port,ch) uart_handle_sysrq_char(port, 0) +#else +#define uart_handle_sysrq_char(port,ch) ({ (void)port; 0; }) #endif /* @@ -522,89 +530,6 @@ static inline int uart_handle_break(struct uart_port *port) return 0; } -/** - * uart_handle_dcd_change - handle a change of carrier detect state - * @uport: uart_port structure for the open port - * @status: new carrier detect status, nonzero if active - */ -static inline void -uart_handle_dcd_change(struct uart_port *uport, unsigned int status) -{ - struct uart_state *state = uport->state; - struct tty_port *port = &state->port; - struct tty_ldisc *ld = tty_ldisc_ref(port->tty); - struct pps_event_time ts; - - if (ld && ld->ops->dcd_change) - pps_get_ts(&ts); - - uport->icount.dcd++; -#ifdef CONFIG_HARD_PPS - if ((uport->flags & UPF_HARDPPS_CD) && status) - hardpps(); -#endif - - if (port->flags & ASYNC_CHECK_CD) { - if (status) - wake_up_interruptible(&port->open_wait); - else if (port->tty) - tty_hangup(port->tty); - } - - if (ld && ld->ops->dcd_change) - ld->ops->dcd_change(port->tty, status, &ts); - if (ld) - tty_ldisc_deref(ld); -} - -/** - * uart_handle_cts_change - handle a change of clear-to-send state - * @uport: uart_port structure for the open port - * @status: new clear to send status, nonzero if active - */ -static inline void -uart_handle_cts_change(struct uart_port *uport, unsigned int status) -{ - struct tty_port *port = &uport->state->port; - struct tty_struct *tty = port->tty; - - uport->icount.cts++; - - if (port->flags & ASYNC_CTS_FLOW) { - if (tty->hw_stopped) { - if (status) { - tty->hw_stopped = 0; - uport->ops->start_tx(uport); - uart_write_wakeup(uport); - } - } else { - if (!status) { - tty->hw_stopped = 1; - uport->ops->stop_tx(uport); - } - } - } -} - -#include <linux/tty_flip.h> - -static inline void -uart_insert_char(struct uart_port *port, unsigned int status, - unsigned int overrun, unsigned int ch, unsigned int flag) -{ - struct tty_struct *tty = port->state->port.tty; - - if ((status & port->ignore_status_mask & ~overrun) == 0) - tty_insert_flip_char(tty, ch, flag); - - /* - * Overrun is special. Since it's reported immediately, - * it doesn't affect the current character. - */ - if (status & ~port->ignore_status_mask & overrun) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); -} - /* * UART_ENABLE_MS - determine if port should enable modem status irqs */ |