From 66ef27c3fd0e91038029054c862c9439be5137c1 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Tue, 8 Nov 2011 13:44:59 +0800 Subject: tty_ldisc: remove unnecessary negative return check for wait_event_timeout wait_event_timeout always return value >= 0 remove the unnecessary ret < 0 check Signed-off-by: Dave Young Acked-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 512c49f98e85..5201f78d39a6 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -556,8 +556,6 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty) int ret; ret = wait_event_timeout(tty_ldisc_idle, atomic_read(&tty->ldisc->users) == 1, 5 * HZ); - if (ret < 0) - return ret; return ret > 0 ? 0 : -EBUSY; } -- cgit v1.2.3 From b82e324b3c46a554595c12b45465d1943a57326c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 10 Nov 2011 13:18:09 +0000 Subject: serial, mfd: don't hardcode the console Add support to specify which HSU port to use as an early console. This can be selected by passing "earlyprintk=hsu" on the kernel command line. By default port 0 is still used. Signed-off-by: Mika Westerberg Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mrst.h | 2 +- arch/x86/kernel/early_printk.c | 2 +- arch/x86/platform/mrst/early_printk_mrst.c | 16 ++++++++++++---- drivers/tty/serial/mfd.c | 18 +++++++----------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h index 719f00b28ff5..470776039af9 100644 --- a/arch/x86/include/asm/mrst.h +++ b/arch/x86/include/asm/mrst.h @@ -51,7 +51,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/serial/mfd.c b/drivers/tty/serial/mfd.c index 286c386d9c46..565f3fe3dd77 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -1156,7 +1156,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; @@ -1167,9 +1166,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 = { @@ -1178,9 +1175,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 = { @@ -1210,6 +1211,7 @@ static struct uart_driver serial_hsu_reg = { .major = TTY_MAJOR, .minor = 128, .nr = 3, + .cons = SERIAL_HSU_CONSOLE, }; #ifdef CONFIG_PM @@ -1344,12 +1346,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); } -- cgit v1.2.3 From e30f867d402d6dcc2d03d8dd5da3863f7c83572a Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 15 Nov 2011 15:04:07 -0800 Subject: drivers/tty/serial/pch_uart.c: add console support Add console support to pch_uart. To enable append e.g. console=ttyPCH0,115200 to your kernel command line. This is not expected work on CM-iTC boards due to their having a different clock. Signed-off-by: Alexander Stein Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 9 +++ drivers/tty/serial/pch_uart.c | 160 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 168 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 5f479dada6f2..956f2f02947b 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1575,6 +1575,15 @@ config SERIAL_PCH_UART ML7213/ML7223 is companion chip for Intel Atom E6xx series. ML7213/ML7223 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/pch_uart.c b/drivers/tty/serial/pch_uart.c index 21febef926aa..b950d059a781 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include #include @@ -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; @@ -272,6 +279,9 @@ static struct pch_uart_driver_data drv_dat[] = { [pch_ml7223_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 }; @@ -1380,6 +1390,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, @@ -1387,6 +1534,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, @@ -1413,7 +1561,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); @@ -1463,6 +1611,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; @@ -1470,6 +1621,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); @@ -1492,6 +1646,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); -- cgit v1.2.3 From 1411dc4aa21d364f40ed363c8e715939c15f57c2 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:18 +0100 Subject: TTY: move pgrp killing Move it to the only branch where tty_pgrp may be set. This is only a cleanup which allows having tty_pgrp defined at that place. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 05085beb83db..391cec3ce638 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); -- cgit v1.2.3 From b82154ac37a7d12335c7c3d3c34c549171ec0cb4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:19 +0100 Subject: TTY: extract /dev/tty handling from tty_open This one is special to others (done in the next patch). We have the tty directly, not its driver and index. So this will reside in a separation function. In the next patch, the rest will be moved to another function. So now we set neither driver nor index. Hence we need to init driver and check whether we are supposed to put a ref of that. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 57 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 391cec3ce638..1b90c986b1bf 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1796,6 +1796,33 @@ int tty_release(struct inode *inode, struct file *filp) return 0; } +/** + * 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_open - open a tty device * @inode: inode of device file @@ -1819,9 +1846,9 @@ int tty_release(struct inode *inode, struct file *filp) 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; @@ -1840,22 +1867,15 @@ 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); + tty = tty_open_current_tty(device, filp); + if (IS_ERR(tty)) { + tty_unlock(); + mutex_unlock(&tty_mutex); + tty_free_file(filp); + return PTR_ERR(tty); + } else if (tty) goto got_driver; - } + #ifdef CONFIG_VT if (device == MKDEV(TTY_MAJOR, 0)) { extern struct tty_driver *console_driver; @@ -1911,7 +1931,8 @@ 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); -- cgit v1.2.3 From 5b5e70408f1e8a48deebedc26ba982bbc7db343e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:20 +0100 Subject: TTY: extract driver lookup from tty_open The error handling in tty_open became unbearable. There were many errors fixed recently. Extract the tty driver lookup from tty_open to a separate function. This reduces the fail paths significantly and makes tty_open more readable. In the next patch we will move the fail path handling to the end of the function. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 93 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 38 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 1b90c986b1bf..00b84984308d 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1823,6 +1823,53 @@ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) 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; + +#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; + return 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; + return driver; + } + } + return ERR_PTR(-ENODEV); + } + + driver = get_tty_driver(device, index); + if (!driver) + return ERR_PTR(-ENODEV); + return driver; +} + /** * tty_open - open a tty device * @inode: inode of device file @@ -1839,7 +1886,7 @@ static struct tty_struct *tty_open_current_tty(dev_t device, 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 */ @@ -1873,47 +1920,17 @@ retry_open: mutex_unlock(&tty_mutex); tty_free_file(filp); return PTR_ERR(tty); - } else if (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; - } + } else if (!tty) { + driver = tty_lookup_driver(device, filp, &noctty, &index); + if (IS_ERR(driver)) { + tty_unlock(); + mutex_unlock(&tty_mutex); + tty_free_file(filp); + return PTR_ERR(driver); } - 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); -- cgit v1.2.3 From ba5db44895ec3abc5317a9af86001e688a72185c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:21 +0100 Subject: TTY: coalesce fail paths in tty_open Move them to the end of the function and use gotos as usual. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 00b84984308d..ba9194e7b9c8 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1916,27 +1916,20 @@ retry_open: tty = tty_open_current_tty(device, filp); if (IS_ERR(tty)) { - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_free_file(filp); - return PTR_ERR(tty); + retval = PTR_ERR(tty); + goto err_unlock; } else if (!tty) { driver = tty_lookup_driver(device, filp, &noctty, &index); if (IS_ERR(driver)) { - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_free_file(filp); - return PTR_ERR(driver); + retval = PTR_ERR(driver); + goto err_unlock; } /* 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; } } @@ -1952,8 +1945,8 @@ retry_open: 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); @@ -2013,6 +2006,15 @@ retry_open: 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; } -- cgit v1.2.3 From 2cd0050cf3ec4da847c3a2f7d95cffd548aef39d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:22 +0100 Subject: TTY: move tty_lookup_driver to switch-cases The labels express more the nature of the decision tree. We returned from each if with a driver. Now we do this at the end of the function and the code flow is clear. While at it, remove an obsolete comment (we already take the reference). Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index ba9194e7b9c8..76e66ff5e65d 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1841,16 +1841,17 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, { struct tty_driver *driver; + switch (device) { #ifdef CONFIG_VT - if (device == MKDEV(TTY_MAJOR, 0)) { + case MKDEV(TTY_MAJOR, 0): { extern struct tty_driver *console_driver; driver = tty_driver_kref_get(console_driver); *index = fg_console; *noctty = 1; - return driver; + break; } #endif - if (device == MKDEV(TTYAUX_MAJOR, 1)) { + case MKDEV(TTYAUX_MAJOR, 1): { struct tty_driver *console_driver = console_device(index); if (console_driver) { driver = tty_driver_kref_get(console_driver); @@ -1858,15 +1859,17 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, /* Don't let /dev/console block */ filp->f_flags |= O_NONBLOCK; *noctty = 1; - return driver; + break; } } return ERR_PTR(-ENODEV); } - - driver = get_tty_driver(device, index); - if (!driver) - return ERR_PTR(-ENODEV); + default: + driver = get_tty_driver(device, index); + if (!driver) + return ERR_PTR(-ENODEV); + break; + } return driver; } -- cgit v1.2.3 From 955787ca94a17bdfd00e369a21ceb97aa21792fc Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 11 Nov 2011 10:47:23 +0100 Subject: TTY: move debug checking out of tty_release There is no need to taint the tty_release code with paranoia checking. So move it out of line to a separate function. Making thus tty_release more readable. [v2] don't introduce a hard to reproduce use after free (scheduled work would need to preempt the current thread) Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 101 +++++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 76e66ff5e65d..b874b6d1b0be 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1556,6 +1556,62 @@ static void release_tty(struct tty_struct *tty, int idx) tty_kref_put(tty); } +/** + * 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 "tty_release_dev: bad idx when trying to " + "free (%s)\n", 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 "tty_release_dev: driver.table[%d] not tty " + "for (%s)\n", idx, tty->name); + return -1; + } + if (tty->termios != tty->driver->termios[idx]) { + printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios " + "for (%s)\n", + idx, tty->name); + return -1; + } + if (tty->driver->other) { + if (o_tty != tty->driver->other->ttys[idx]) { + printk(KERN_DEBUG "tty_release_dev: other->table[%d] " + "not o_tty for (%s)\n", + idx, tty->name); + return -1; + } + if (o_tty->termios != tty->driver->other->termios[idx]) { + printk(KERN_DEBUG "tty_release_dev: other->termios[%d] " + "not o_termios for (%s)\n", + idx, tty->name); + return -1; + } + if (o_tty->link != tty) { + printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n"); + return -1; + } + } +#endif + return 0; +} + /** * tty_release - vfs callback for close * @inode: inode of tty @@ -1598,59 +1654,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); #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); -- cgit v1.2.3 From 9de44bd604ccdd25f8ffb04d828080f210679266 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:24 +0100 Subject: TTY: open/release, cleanup printks * use __func__ instead of hardcoded names (tty_release_dev is a non-existant function) * add missing \n's * unwrap for better grepping Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 56 ++++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index b874b6d1b0be..57c374399f59 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1570,8 +1570,8 @@ static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty, { #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); + printk(KERN_DEBUG "%s: bad idx when trying to free (%s)\n", + __func__, tty->name); return -1; } @@ -1580,31 +1580,28 @@ static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty, return 0; if (tty != tty->driver->ttys[idx]) { - printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty " - "for (%s)\n", idx, tty->name); + 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 "tty_release_dev: driver.termios[%d] not termios " - "for (%s)\n", - idx, tty->name); + 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 "tty_release_dev: other->table[%d] " - "not o_tty for (%s)\n", - idx, tty->name); + 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 "tty_release_dev: other->termios[%d] " - "not o_termios for (%s)\n", - idx, tty->name); + 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 "tty_release_dev: bad pty pointers\n"); + printk(KERN_DEBUG "%s: bad pty pointers\n", __func__); return -1; } } @@ -1640,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); @@ -1660,8 +1657,8 @@ int tty_release(struct inode *inode, struct file *filp) } #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 if (tty->ops->close) @@ -1719,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(); @@ -1733,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; } @@ -1790,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 @@ -1967,12 +1963,12 @@ retry_open: 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); @@ -1986,8 +1982,8 @@ retry_open: 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); -- cgit v1.2.3 From f3706266198d3efb0fd589f34fa8471478ea364d Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Tue, 15 Nov 2011 11:57:40 +0530 Subject: serial: OMAP2+: UART: Make the SERIAL_OMAP depend on ARCH_OMAP2PLUS Making SERIAL_OMAP depend on ARCH_OMAP2PLUS instead of oring with ARCH2/3/4. Acked-by: Felipe Balbi Suggested-by: Sricharan R Signed-off-by: Shubhrajyoti D Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 956f2f02947b..3d86bdaee047 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1324,7 +1324,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 -- cgit v1.2.3 From 027d7dacf73273dbe07a75b2ef5579616f17272c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:43 +0100 Subject: TTY: serial, cleanup include file There are some functions (uart_handle_dcd_change, _handle_cts_change, _insert_char) which are big enough to not be inlined. So move them from .h to .c. We need to export them so that modules can actually use them. They will be even bigger when we introduce tty refcounting to them. While at it, cleanup the "Proud member of Uglyhacks'R'US". It means, define uart_handle_sysrq_char only when SUPPORT_SYSRQ is set. Otherwise define it as a macro. This is needed for some arm driver where the second parameter is undefined if expanded. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 82 +++++++++++++++++++++++++++++++++ include/linux/serial_core.h | 99 +++++----------------------------------- 2 files changed, 94 insertions(+), 87 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0406d7ff505e..8d825a36c842 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -22,6 +22,7 @@ */ #include #include +#include #include #include #include @@ -2467,6 +2468,87 @@ 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); + +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/include/linux/serial_core.h b/include/linux/serial_core.h index eadf33d0abba..945e02cae614 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -483,10 +483,19 @@ static inline int uart_tx_stopped(struct uart_port *port) /* * The following are helper functions for the low level drivers. */ + +extern void uart_handle_dcd_change(struct uart_port *uport, + unsigned int status); +extern void uart_handle_cts_change(struct uart_port *uport, + unsigned int status); + +extern void uart_insert_char(struct uart_port *port, unsigned int status, + unsigned int overrun, unsigned int ch, unsigned int flag); + +#ifdef SUPPORT_SYSRQ static inline int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) { -#ifdef SUPPORT_SYSRQ if (port->sysrq) { if (ch && time_before(jiffies, port->sysrq)) { handle_sysrq(ch); @@ -495,11 +504,10 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) } port->sysrq = 0; } -#endif return 0; } -#ifndef SUPPORT_SYSRQ -#define uart_handle_sysrq_char(port,ch) uart_handle_sysrq_char(port, 0) +#else +#define uart_handle_sysrq_char(port,ch) ({ (void)port; 0; }) #endif /* @@ -522,89 +530,6 @@ static inline int uart_handle_break(struct uart_port *port) return 0; } -/** - * uart_handle_dcd_change - handle a change of carrier detect state - * @uport: uart_port structure for the open port - * @status: new carrier detect status, nonzero if active - */ -static inline void -uart_handle_dcd_change(struct uart_port *uport, unsigned int status) -{ - struct uart_state *state = uport->state; - struct tty_port *port = &state->port; - struct tty_ldisc *ld = tty_ldisc_ref(port->tty); - struct pps_event_time ts; - - if (ld && ld->ops->dcd_change) - pps_get_ts(&ts); - - uport->icount.dcd++; -#ifdef CONFIG_HARD_PPS - if ((uport->flags & UPF_HARDPPS_CD) && status) - hardpps(); -#endif - - if (port->flags & ASYNC_CHECK_CD) { - if (status) - wake_up_interruptible(&port->open_wait); - else if (port->tty) - tty_hangup(port->tty); - } - - if (ld && ld->ops->dcd_change) - ld->ops->dcd_change(port->tty, status, &ts); - if (ld) - tty_ldisc_deref(ld); -} - -/** - * uart_handle_cts_change - handle a change of clear-to-send state - * @uport: uart_port structure for the open port - * @status: new clear to send status, nonzero if active - */ -static inline void -uart_handle_cts_change(struct uart_port *uport, unsigned int status) -{ - struct tty_port *port = &uport->state->port; - struct tty_struct *tty = port->tty; - - uport->icount.cts++; - - if (port->flags & ASYNC_CTS_FLOW) { - if (tty->hw_stopped) { - if (status) { - tty->hw_stopped = 0; - uport->ops->start_tx(uport); - uart_write_wakeup(uport); - } - } else { - if (!status) { - tty->hw_stopped = 1; - uport->ops->stop_tx(uport); - } - } - } -} - -#include - -static inline void -uart_insert_char(struct uart_port *port, unsigned int status, - unsigned int overrun, unsigned int ch, unsigned int flag) -{ - struct tty_struct *tty = port->state->port.tty; - - if ((status & port->ignore_status_mask & ~overrun) == 0) - tty_insert_flip_char(tty, ch, flag); - - /* - * Overrun is special. Since it's reported immediately, - * it doesn't affect the current character. - */ - if (status & ~port->ignore_status_mask & overrun) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); -} - /* * UART_ENABLE_MS - determine if port should enable modem status irqs */ -- cgit v1.2.3 From b54bf3b24908cd4f6a1d24276f795cfbccba5a3f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:44 +0100 Subject: TTY: serial, cleanup atmel_set_ldisc Current ldisc number is passed as a paramneter -- no need to dig it out of the tty or ldisc. So switch PPS check to that. No tty callback can be called with port->line higher than TTY driver num. So remove the check. This removes some port.tty users. Signed-off-by: Jiri Slaby Cc: Alan Cox Cc: Viktar Palstsiuk Cc: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 4a0f86fa1e90..6b844d422578 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 { -- cgit v1.2.3 From 4cb0fbfdc81f29a414583bd05a9e324f8f19984d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:45 +0100 Subject: TTY: serial, switch closing_wait and close_delay to jiffies As the tty_port helpers think closing_wait and close_delay are in jiffies and we want to use the helpers (next patches), we have to switch the closing_wait and close_delay from ms to jiffies now. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 8d825a36c842..2b1ee7cc4f8a 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -659,10 +659,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; @@ -696,9 +696,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 @@ -1305,8 +1306,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) 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)); + tty_wait_until_sent_from_close(tty, port->closing_wait); /* * At this point, we stop accepting input. To do this, we @@ -1338,7 +1338,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); @@ -2276,8 +2277,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); -- cgit v1.2.3 From d30ccf08e8e01060288587f52a78c3ca0fcfc1fc Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:46 +0100 Subject: TTY: serial, use tty_port_close_start helper After the previous patches, the code is almost identical. There are few differences in the helper code: 1) flush_buffer when flow_stopped * when a user doesn't care about the data, delete it anyways 2) ASYNCB_INITIALIZED test before wait_until_sent_from * obviously, there is nothing to wait for if the port is dead 3) drain_delay wait * we don't set drain_delay So we can use the helper now. It indeed removes a bunch of duplicated code. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 40 +--------------------------------------- 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 2b1ee7cc4f8a..fcb534250c85 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1267,46 +1267,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, port->closing_wait); /* * At this point, we stop accepting input. To do this, we -- cgit v1.2.3 From cf75525f374dd5c49d705d9c8c3757f9aa3e9fd2 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:47 +0100 Subject: TTY: serial, document few functions Just put a kernel-doc comment to uart_change_pm and uart_insert_char. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index fcb534250c85..036e0ccbd25a 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1822,6 +1822,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; @@ -2495,6 +2503,18 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) } 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) { -- cgit v1.2.3 From b39c49a05ec7322c96ec917922eae8b908d2c76f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:48 +0100 Subject: TTY: serial, do not touch tty->alt_speed It is not used at all, so no need to play any games with that. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 036e0ccbd25a..4b1dcd3f2a40 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -141,8 +141,7 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, int in /* * 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 + * once we have successfully opened the port. */ set_bit(TTY_IO_ERROR, &tty->flags); @@ -1499,7 +1498,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); /* -- cgit v1.2.3 From 1c7b13c4886f5cfaf02fb1052f65ef1a2fe88b9a Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:49 +0100 Subject: TTY: serial, inline uart_get We need to expand uart_get into uart_open. We need it to move on with conversion to use tty_port_open helper. After we do this, the code will be much more similar to what tty_port_open does. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 66 ++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 4b1dcd3f2a40..2ca4df4a4836 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1429,33 +1429,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() @@ -1469,26 +1442,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 @@ -1505,9 +1481,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; } /* @@ -1528,8 +1502,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) -- cgit v1.2.3 From 0b1db83081599615cf7b254aebc14a2d8f6ca056 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:50 +0100 Subject: TTY: serial, define uart_port_activate/shutdown This is a preparation for the next patches which will move the stuff from uart_open and uart_close/hangup here. Then we will use tty_port_* helpers. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 2ca4df4a4836..d7e8a5ef1879 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1404,6 +1404,15 @@ 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) +{ +} + static int uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); @@ -2162,6 +2171,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, }; -- cgit v1.2.3 From b922e19d03a680d732b61dc8e82d9948f6f8b6c7 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:33:51 +0100 Subject: TTY: serial, fill uart_port_shutdown Let's fill the port_ops->shutdown. We will need this for hangup and close port helpers. We don't need to touch DTR/RTS registers in uart_port_shutdown. They are set to off from port_close_start properly already. Also we don't need to pin the TTY_IO_ERROR bit. This will be done in close/hangup paths. We leave uart_shutdown as is, because it is used (and will be) from several paths now. Like from suspend. The point is to not touch ASYNC_INITIALIZED bit. It will be set (and checked) properly by the tty port helpers. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 42 +++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index d7e8a5ef1879..68763c0fa82c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -61,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. @@ -228,24 +230,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); } /* @@ -1411,6 +1396,27 @@ static int uart_port_activate(struct tty_port *port, struct tty_struct *tty) 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) -- cgit v1.2.3 From c0d92be6bc4fbbf6402fde2c0bc08fc4d2b58430 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Nov 2011 21:34:14 +0100 Subject: TTY: serial, extract uart_port_startup Extract ASYNC_INITIALIZED/TTY_IO_ERROR handling from uart_startup. This will be useful for tty port helpers. These flags are handled by the helpers instead. So we create a new function uart_port_startup without touching these flags there. And we keep uart_startup with the exact behavior as before. We need that one because we start/stop the device from other paths than open/close/hangup. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 43 ++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 68763c0fa82c..d2990f738606 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -131,24 +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. - */ - set_bit(TTY_IO_ERROR, &tty->flags); - if (uport->type == PORT_UNKNOWN) - return 0; + return 1; /* * Initialise and allocate the transmit and temporary @@ -190,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); } /* @@ -202,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; -- cgit v1.2.3 From 484af54d432c39891ff27ad0e5194d28513063cc Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 16 Nov 2011 16:27:11 +0100 Subject: TTY: pty, cleanup the pty counting Instead of the hackish way of counting ptys, let's define a specific ->remove hook both from slave and master. And decrease the count only for master. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) 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, -- cgit v1.2.3 From 8b3ffa173ffa13ac47c1d7524af92d4b2c95abfc Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 16 Nov 2011 16:27:10 +0100 Subject: TTY: ldisc, remove some unneeded includes They were cut&pasted from tty_io. Many of them are not needed in tty_ldisc. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 5201f78d39a6..174db3b0c09e 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -1,19 +1,11 @@ #include -#include #include -#include -#include +#include #include #include #include #include -#include -#include #include -#include -#include -#include -#include #include #include #include @@ -24,18 +16,8 @@ #include #include #include -#include #include - #include -#include - -#include -#include -#include - -#include -#include /* * This guards the refcounted line discipline lists. The lock -- cgit v1.2.3 From 161e773cbd0c3d1b5b8cc00602e1f72de61ed4f7 Mon Sep 17 00:00:00 2001 From: Rong Wang Date: Thu, 17 Nov 2011 23:17:04 +0800 Subject: UART: add CSR SiRFprimaII SoC on-chip uart drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SiRFprimaII is the latest generation application processor from CSR’s multi-function SoC product family. The SoC support codes are in arch/arm/mach-prima2 from Linux mainline 3.0. There are three dedicated UARTs in system. This patch adds basic driver support for them. It has used the newest pinmux subsystem from Linus Walleij. Cc: Linus Walleij Signed-off-by: Rong Wang Signed-off-by: Bin Shi Signed-off-by: Barry Song Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 21 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sirfsoc_uart.c | 783 ++++++++++++++++++++++++++++++++++++++ drivers/tty/serial/sirfsoc_uart.h | 185 +++++++++ 4 files changed, 990 insertions(+) create mode 100644 drivers/tty/serial/sirfsoc_uart.c create mode 100644 drivers/tty/serial/sirfsoc_uart.h diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 3d86bdaee047..45c5758f7690 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" 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/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 , Rong Wang"); +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 + +/* 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 -- cgit v1.2.3 From 514f31d19ed6d001cac37af0b4a3f61b07969d59 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Mon, 21 Nov 2011 15:43:28 +0530 Subject: OMAP: UART: fix the return type of check_modem_status The function check_modem_status returns an int currently it is stored in a char. Signed-off-by: Shubhrajyoti D Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 5e713d3ef1f4..f2a1380ed678 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -399,7 +399,7 @@ static unsigned int serial_omap_tx_empty(struct uart_port *port) static unsigned int serial_omap_get_mctrl(struct uart_port *port) { struct uart_omap_port *up = (struct uart_omap_port *)port; - unsigned char status; + unsigned int status; unsigned int ret = 0; status = check_modem_status(up); -- cgit v1.2.3 From f227824e84c8622e68d9fdc29e78af88a285dfdc Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 22 Nov 2011 14:22:55 +0100 Subject: serial/imx: propagate error of platform_driver_register in init routine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 163fc9021f5a..962cafa175f7 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1476,7 +1476,7 @@ static int __init imx_serial_init(void) if (ret != 0) uart_unregister_driver(&imx_reg); - return 0; + return ret; } static void __exit imx_serial_exit(void) -- cgit v1.2.3 From d0758a285caaf86192cdb22136a5eb84ed72f276 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 22 Nov 2011 14:22:56 +0100 Subject: serial/mxs-auart: only wake up tty layer once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to call uart_write_wakeup after each character send. Once at the end of the write sequence is enough. Signed-off-by: Uwe Kleine-König Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 7e02c9c344fe..076169f50b01 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -145,11 +145,12 @@ static inline void mxs_auart_tx_chars(struct mxs_auart_port *s) writel(xmit->buf[xmit->tail], s->port.membase + AUART_DATA); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&s->port); } else break; } + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + if (uart_circ_empty(&(s->port.state->xmit))) writel(AUART_INTR_TXIEN, s->port.membase + AUART_INTR_CLR); -- cgit v1.2.3 From 448ac154c957c4580531fa0c8f2045816fe2f0e7 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 22 Nov 2011 13:41:24 -0800 Subject: serial/8250_pci: setup-quirk workaround for the kt serial controller Workaround dropped notifications in the iir register. Prevent reads coincident with new interrupt notifications by reading the iir at most once per interrupt. Reported-by: Nhan H Mai Signed-off-by: Dan Williams Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 4 +++- drivers/tty/serial/8250_pci.c | 17 ++++++++++++++++- include/linux/serial_core.h | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index eeadf1b8e093..3a8e5bfe17e2 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1619,11 +1619,13 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) do { struct uart_8250_port *up; struct uart_port *port; + bool skip; up = list_entry(l, struct uart_8250_port, list); port = &up->port; + skip = pass_counter && up->port.flags & UPF_IIR_ONCE; - if (port->handle_irq(port)) { + if (!skip && port->handle_irq(port)) { handled = 1; end = NULL; } else if (end == NULL) diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 825937a5f210..8742ef5be6ba 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -1092,6 +1092,14 @@ static int skip_tx_en_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +static int kt_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + port->flags |= UPF_IIR_ONCE; + return skip_tx_en_setup(priv, board, port, idx); +} + static int pci_eg20t_init(struct pci_dev *dev) { #if defined(CONFIG_SERIAL_PCH_UART) || defined(CONFIG_SERIAL_PCH_UART_MODULE) @@ -1110,7 +1118,6 @@ pci_xr17c154_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } -/* This should be in linux/pci_ids.h */ #define PCI_VENDOR_ID_SBSMODULARIO 0x124B #define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B #define PCI_DEVICE_ID_OCTPRO 0x0001 @@ -1136,6 +1143,7 @@ pci_xr17c154_setup(struct serial_private *priv, #define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 #define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6 #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 +#define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -1220,6 +1228,13 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = ce4100_serial_setup, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_PATSBURG_KT, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = kt_serial_setup, + }, /* * ITE */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 945e02cae614..b67305e3ad57 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -351,6 +351,7 @@ struct uart_port { #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) #define UPF_EXAR_EFR ((__force upf_t) (1 << 25)) +#define UPF_IIR_ONCE ((__force upf_t) (1 << 26)) /* The exact UART type is known and should not be probed. */ #define UPF_FIXED_TYPE ((__force upf_t) (1 << 27)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) -- cgit v1.2.3 From e86ff4a63c9fdd875ba8492577cd1ad2252f525c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 22 Nov 2011 13:41:29 -0800 Subject: serial/8250_pci: init-quirk msi support for kt serial controller The semantics of UPF_IIR_ONCE (once per serial irq) are only guaranteed if the kt irq is not shared (once per serial isr in the shared case == potentially unwanted reads of the IIR). Signed-off-by: Dan Williams Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250_pci.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 8742ef5be6ba..2cbf78f77c4a 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -1118,6 +1118,18 @@ pci_xr17c154_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +static int try_enable_msi(struct pci_dev *dev) +{ + /* use msi if available, but fallback to legacy otherwise */ + pci_enable_msi(dev); + return 0; +} + +static void disable_msi(struct pci_dev *dev) +{ + pci_disable_msi(dev); +} + #define PCI_VENDOR_ID_SBSMODULARIO 0x124B #define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B #define PCI_DEVICE_ID_OCTPRO 0x0001 @@ -1233,7 +1245,9 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .device = PCI_DEVICE_ID_INTEL_PATSBURG_KT, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, + .init = try_enable_msi, .setup = kt_serial_setup, + .exit = disable_msi, }, /* * ITE -- cgit v1.2.3 From bb74041b1367b4935b8284a2b541f5e4365d3f50 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 24 Nov 2011 16:29:12 +0100 Subject: TTY: Remove redundant spi driver bus initialization In ancient times it was necessary to manually initialize the bus field of an spi_driver to spi_bus_type. These days this is done in spi_driver_register(), so we can drop the manual assignment. The patch was generated using the following coccinelle semantic patch: // @@ identifier _driver; @@ struct spi_driver _driver = { .driver = { - .bus = &spi_bus_type, }, }; // Signed-off-by: Lars-Peter Clausen Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 1 - drivers/tty/serial/max3100.c | 1 - drivers/tty/serial/max3107-aava.c | 1 - drivers/tty/serial/max3107.c | 1 - drivers/tty/serial/mrst_max3110.c | 1 - 5 files changed, 5 deletions(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 426434e5eb7c..7e925e20cbaa 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -1334,7 +1334,6 @@ MODULE_DEVICE_TABLE(spi, ifx_id_table); static const struct spi_driver ifx_spi_driver = { .driver = { .name = DRVNAME, - .bus = &spi_bus_type, .pm = &ifx_spi_pm, .owner = THIS_MODULE}, .probe = ifx_spi_spi_probe, diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 8a6cc8c30b5a..b4902b99cfd2 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -901,7 +901,6 @@ static int max3100_resume(struct spi_device *spi) static struct spi_driver max3100_driver = { .driver = { .name = "max3100", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, diff --git a/drivers/tty/serial/max3107-aava.c b/drivers/tty/serial/max3107-aava.c index 90c40f22ec70..aae772a71de6 100644 --- a/drivers/tty/serial/max3107-aava.c +++ b/drivers/tty/serial/max3107-aava.c @@ -315,7 +315,6 @@ static int __devinit max3107_probe_aava(struct spi_device *spi) static struct spi_driver max3107_driver = { .driver = { .name = "aava-max3107", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = max3107_probe_aava, diff --git a/drivers/tty/serial/max3107.c b/drivers/tty/serial/max3107.c index 7827000db4f5..17c7ba805d98 100644 --- a/drivers/tty/serial/max3107.c +++ b/drivers/tty/serial/max3107.c @@ -1181,7 +1181,6 @@ static int max3107_probe_generic(struct spi_device *spi) static struct spi_driver max3107_driver = { .driver = { .name = "max3107", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = max3107_probe_generic, diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 4c309e869903..df2a2240a3ae 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -876,7 +876,6 @@ static int __devexit serial_m3110_remove(struct spi_device *dev) static struct spi_driver uart_max3110_driver = { .driver = { .name = "spi_max3111", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = serial_m3110_probe, -- cgit v1.2.3 From 782ee87702fbd0a175da64a8e71e029b19ef97bf Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Nov 2011 12:43:49 +0800 Subject: parport: convert drivers/parport/* to use module_platform_driver() This patch converts the drivers in drivers/parport/* to use the module_platform_driver() macro which makes the code smaller and a bit simpler. Cc: Ben Dooks Cc: Derrick J. Brashear Signed-off-by: Axel Lin Signed-off-by: Greg Kroah-Hartman --- drivers/parport/parport_ax88796.c | 13 +------------ drivers/parport/parport_sunbpp.c | 13 +------------ 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/drivers/parport/parport_ax88796.c b/drivers/parport/parport_ax88796.c index 844f6137970a..7c5d86696eed 100644 --- a/drivers/parport/parport_ax88796.c +++ b/drivers/parport/parport_ax88796.c @@ -420,18 +420,7 @@ static struct platform_driver axdrv = { .resume = parport_ax88796_resume, }; -static int __init parport_ax88796_init(void) -{ - return platform_driver_register(&axdrv); -} - -static void __exit parport_ax88796_exit(void) -{ - platform_driver_unregister(&axdrv); -} - -module_init(parport_ax88796_init) -module_exit(parport_ax88796_exit) +module_platform_driver(axdrv); MODULE_AUTHOR("Ben Dooks "); MODULE_DESCRIPTION("AX88796 Parport parallel port driver"); diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c index 910c5a26e347..9390a534a2b2 100644 --- a/drivers/parport/parport_sunbpp.c +++ b/drivers/parport/parport_sunbpp.c @@ -391,21 +391,10 @@ static struct platform_driver bpp_sbus_driver = { .remove = __devexit_p(bpp_remove), }; -static int __init parport_sunbpp_init(void) -{ - return platform_driver_register(&bpp_sbus_driver); -} - -static void __exit parport_sunbpp_exit(void) -{ - platform_driver_unregister(&bpp_sbus_driver); -} +module_platform_driver(bpp_sbus_driver); MODULE_AUTHOR("Derrick J Brashear"); MODULE_DESCRIPTION("Parport Driver for Sparc bidirectional Port"); MODULE_SUPPORTED_DEVICE("Sparc Bidirectional Parallel Port"); MODULE_VERSION("2.0"); MODULE_LICENSE("GPL"); - -module_init(parport_sunbpp_init) -module_exit(parport_sunbpp_exit) -- cgit v1.2.3 From c8381c15b14b7c2d212c182d3b9b3fa7217994da Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 28 Nov 2011 19:22:15 +0800 Subject: TTY: serial: convert drivers/tty/serial/* to use module_platform_driver() This patch converts the drivers in drivers/tty/serial/* to use the module_platform_driver() macro which makes the code smaller and a bit simpler. Cc: Arnd Bergmann Cc: Jamie Iles Cc: Yoichi Yuasa Signed-off-by: Axel Lin Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250_dw.c | 12 +----------- drivers/tty/serial/sc26xx.c | 14 +------------- drivers/tty/serial/timbuart.c | 15 +-------------- drivers/tty/serial/vr41xx_siu.c | 13 +------------ 4 files changed, 4 insertions(+), 50 deletions(-) diff --git a/drivers/tty/serial/8250_dw.c b/drivers/tty/serial/8250_dw.c index bf1fba640c2d..f574eef3075f 100644 --- a/drivers/tty/serial/8250_dw.c +++ b/drivers/tty/serial/8250_dw.c @@ -177,17 +177,7 @@ static struct platform_driver dw8250_platform_driver = { .remove = __devexit_p(dw8250_remove), }; -static int __init dw8250_init(void) -{ - return platform_driver_register(&dw8250_platform_driver); -} -module_init(dw8250_init); - -static void __exit dw8250_exit(void) -{ - platform_driver_unregister(&dw8250_platform_driver); -} -module_exit(dw8250_exit); +module_platform_driver(dw8250_platform_driver); MODULE_AUTHOR("Jamie Iles"); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c index 75038ad2b242..e0b4b0a30a5a 100644 --- a/drivers/tty/serial/sc26xx.c +++ b/drivers/tty/serial/sc26xx.c @@ -736,19 +736,7 @@ static struct platform_driver sc26xx_driver = { }, }; -static int __init sc26xx_init(void) -{ - return platform_driver_register(&sc26xx_driver); -} - -static void __exit sc26xx_exit(void) -{ - platform_driver_unregister(&sc26xx_driver); -} - -module_init(sc26xx_init); -module_exit(sc26xx_exit); - +module_platform_driver(sc26xx_driver); MODULE_AUTHOR("Thomas Bogendörfer"); MODULE_DESCRIPTION("SC681/SC2692 serial driver"); diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index e76c8b747fb8..70f97491d8f2 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -513,20 +513,7 @@ static struct platform_driver timbuart_platform_driver = { .remove = __devexit_p(timbuart_remove), }; -/*--------------------------------------------------------------------------*/ - -static int __init timbuart_init(void) -{ - return platform_driver_register(&timbuart_platform_driver); -} - -static void __exit timbuart_exit(void) -{ - platform_driver_unregister(&timbuart_platform_driver); -} - -module_init(timbuart_init); -module_exit(timbuart_exit); +module_platform_driver(timbuart_platform_driver); MODULE_DESCRIPTION("Timberdale UART driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c index 3beb6ab4fa68..83148e79ca13 100644 --- a/drivers/tty/serial/vr41xx_siu.c +++ b/drivers/tty/serial/vr41xx_siu.c @@ -961,18 +961,7 @@ static struct platform_driver siu_device_driver = { }, }; -static int __init vr41xx_siu_init(void) -{ - return platform_driver_register(&siu_device_driver); -} - -static void __exit vr41xx_siu_exit(void) -{ - platform_driver_unregister(&siu_device_driver); -} - -module_init(vr41xx_siu_init); -module_exit(vr41xx_siu_exit); +module_platform_driver(siu_device_driver); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:SIU"); -- cgit v1.2.3 From 7962fce9a052f34390b6dab9f11df1d2e48c138d Mon Sep 17 00:00:00 2001 From: Ilya Zykov Date: Mon, 7 Nov 2011 11:32:46 +0400 Subject: tty: n_hdlc not atomic use tty->flags. N_HDLC can spoil tty->flags because use not atomic operations on tty->flags. I use n_hdlc line discipline and it happens. Signed-off-by: Ilya Zykov Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_hdlc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index cea56033b34c..a09ce3ef5d74 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -417,7 +417,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) __FILE__,__LINE__,tbuf,tbuf->count); /* Send the next block of data to device */ - tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); actual = tty->ops->write(tty, tbuf->buf, tbuf->count); /* rollback was possible and has been done */ @@ -459,7 +459,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) } if (!tbuf) - tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); /* Clear the re-entry flag */ spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); @@ -491,7 +491,7 @@ static void n_hdlc_tty_wakeup(struct tty_struct *tty) return; if (tty != n_hdlc->tty) { - tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); return; } -- cgit v1.2.3 From 32b4456877319fed8d5630f3db011097fd3cf7a8 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 5 Dec 2011 15:12:50 +0800 Subject: serial: bfin-uart: Request CTS GPIO PIN when the serial device starts up. Serial device may be probed earlier before GPIOLIB is initialized. Requesting and configuring CTS GPIO PIN fails in that early stage. Do it when the serial device really starts up. Signed-off-by: Sonic Zhang Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_uart.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 66afb98b77b5..ff5a1dc43e08 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -739,8 +739,13 @@ static int bfin_serial_startup(struct uart_port *port) pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n"); } } - if (uart->rts_pin >= 0) - gpio_direction_output(uart->rts_pin, 0); + if (uart->rts_pin >= 0) { + if (gpio_request(uart->rts_pin, DRIVER_NAME)) { + pr_info("fail to request RTS PIN at GPIO_%d\n", uart->rts_pin); + uart->rts_pin = -1; + } else + gpio_direction_output(uart->rts_pin, 0); + } #endif #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS if (uart->cts_pin >= 0 && request_irq(uart->status_irq, @@ -792,6 +797,8 @@ static void bfin_serial_shutdown(struct uart_port *port) #ifdef CONFIG_SERIAL_BFIN_CTSRTS if (uart->cts_pin >= 0) free_irq(gpio_to_irq(uart->cts_pin), uart); + if (uart->rts_pin >= 0) + gpio_free(uart->rts_pin); #endif #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS if (uart->cts_pin >= 0) @@ -1378,10 +1385,6 @@ static int bfin_serial_probe(struct platform_device *pdev) uart->rts_pin = -1; else uart->rts_pin = res->start; -# if defined(CONFIG_SERIAL_BFIN_CTSRTS) - if (uart->rts_pin >= 0) - gpio_request(uart->rts_pin, DRIVER_NAME); -# endif #endif } @@ -1421,10 +1424,6 @@ static int __devexit bfin_serial_remove(struct platform_device *pdev) if (uart) { uart_remove_one_port(&bfin_serial_reg, &uart->port); -#ifdef CONFIG_SERIAL_BFIN_CTSRTS - if (uart->rts_pin >= 0) - gpio_free(uart->rts_pin); -#endif iounmap(uart->port.membase); peripheral_free_list( (unsigned short *)pdev->dev.platform_data); -- cgit v1.2.3 From ee948e379ede642b7499e78e8e30c10f90c0a1c5 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 5 Dec 2011 18:13:10 +0800 Subject: serial: bfin-uart: Add tty ASYNC_CTS_FLOW flag to do CTS flow control. Signed-off-by: Sonic Zhang Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_uart.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index ff5a1dc43e08..f24f8a2073d9 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -1377,8 +1377,10 @@ static int bfin_serial_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (res == NULL) uart->cts_pin = -1; - else + else { uart->cts_pin = res->start; + uart->port.flags |= ASYNC_CTS_FLOW; + } res = platform_get_resource(pdev, IORESOURCE_IO, 1); if (res == NULL) -- cgit v1.2.3 From f5b6940cbc6fb955a0c2be7ed324efc00e61f481 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 5 Dec 2011 18:13:11 +0800 Subject: serial: bfin-uart: remove redundant CTS check for hardware CTS control. Blackfin hardware CTS control generate interrupt for both CTS on and off. Signed-off-by: Sonic Zhang Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_uart.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index f24f8a2073d9..abac23741057 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -119,12 +119,10 @@ static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id) unsigned int status; status = bfin_serial_get_mctrl(&uart->port); - uart_handle_cts_change(&uart->port, status & TIOCM_CTS); #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - uart->scts = 1; UART_CLEAR_SCTS(uart); - UART_CLEAR_IER(uart, EDSSI); #endif + uart_handle_cts_change(&uart->port, status & TIOCM_CTS); return IRQ_HANDLED; } @@ -175,13 +173,6 @@ static void bfin_serial_start_tx(struct uart_port *port) struct bfin_serial_port *uart = (struct bfin_serial_port *)port; struct tty_struct *tty = uart->port.state->port.tty; -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { - uart->scts = 0; - uart_handle_cts_change(&uart->port, uart->scts); - } -#endif - /* * To avoid losting RX interrupt, we reset IR function * before sending data. @@ -380,12 +371,6 @@ static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id) { struct bfin_serial_port *uart = dev_id; -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { - uart->scts = 0; - uart_handle_cts_change(&uart->port, uart->scts); - } -#endif spin_lock(&uart->port.lock); if (UART_GET_LSR(uart) & THRE) bfin_serial_tx_chars(uart); @@ -531,13 +516,6 @@ static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id) struct bfin_serial_port *uart = dev_id; struct circ_buf *xmit = &uart->port.state->xmit; -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->scts && !(bfin_serial_get_mctrl(&uart->port)&TIOCM_CTS)) { - uart->scts = 0; - uart_handle_cts_change(&uart->port, uart->scts); - } -#endif - spin_lock(&uart->port.lock); if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) { disable_dma(uart->tx_dma_channel); -- cgit v1.2.3 From 07143eaefd025098089edee7047714f6604a4a21 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 6 Dec 2011 15:16:33 +0800 Subject: tty: bfin-sport-uart: Rx interrupt is not called always with irq disabled. Replace local_irq_disable by local_irq_save. Signed-off-by: Sonic Zhang Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_sport_uart.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/bfin_sport_uart.h b/drivers/tty/serial/bfin_sport_uart.h index 6d06ce1d5675..e4510ea135ce 100644 --- a/drivers/tty/serial/bfin_sport_uart.h +++ b/drivers/tty/serial/bfin_sport_uart.h @@ -45,11 +45,12 @@ #define SPORT_GET_RX32(sport) \ ({ \ unsigned int __ret; \ + unsigned long flags; \ if (ANOMALY_05000473) \ - local_irq_disable(); \ + local_irq_save(flags); \ __ret = bfin_read32((sport)->port.membase + OFFSET_RX); \ if (ANOMALY_05000473) \ - local_irq_enable(); \ + local_irq_restore(flags); \ __ret; \ }) #define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1)) -- cgit v1.2.3 From dc8f3703e733f4f48553076c8615aeb004351662 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 6 Dec 2011 15:16:34 +0800 Subject: serial: bfin-sport-uart: Request CTS GPIO PIN when the sport emulated serial device starts up. This patch is similar to that for bfin-uart hardware flow control. Sport emulated serial device may be probed earlier before GPIOLIB is initialized. Requesting and configuring CTS GPIO PIN fails in that early stage. Signed-off-by: Sonic Zhang Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_sport_uart.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index ee101c0d358f..f6505264ad45 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -299,8 +299,13 @@ static int sport_startup(struct uart_port *port) 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); + if (up->rts_pin >= 0) { + if (gpio_request(up->rts_pin, DRV_NAME)) { + dev_info(port->dev, "fail to request RTS PIN at GPIO_%d\n", up->rts_pin); + up->rts_pin = -1; + } else + gpio_direction_output(up->rts_pin, 0); + } #endif return 0; @@ -445,6 +450,8 @@ static void sport_shutdown(struct uart_port *port) #ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS if (up->cts_pin >= 0) free_irq(gpio_to_irq(up->cts_pin), up); + if (up->rts_pin >= 0) + gpio_free(up->rts_pin); #endif } @@ -811,9 +818,6 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) sport->rts_pin = -1; else sport->rts_pin = res->start; - - if (sport->rts_pin >= 0) - gpio_request(sport->rts_pin, DRV_NAME); #endif } @@ -853,10 +857,6 @@ 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); -- cgit v1.2.3 From cee3948d126d30341e08e935d3cc3e380088a2e8 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 6 Dec 2011 16:22:34 +0800 Subject: serial: bfin-sport-uart: Add tty ASYNC_CTS_FLOW flag to do CTS flow control. Signed-off-by: Sonic Zhang Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_sport_uart.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index f6505264ad45..7fbc3a08f10d 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -810,8 +810,10 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (res == NULL) sport->cts_pin = -1; - else + else { sport->cts_pin = res->start; + sport->port.flags |= ASYNC_CTS_FLOW; + } res = platform_get_resource(pdev, IORESOURCE_IO, 1); if (res == NULL) -- cgit v1.2.3 From 7d73aaf1d45d9bd95638680361db4d019ebb75bb Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 9 Dec 2011 18:07:13 +0100 Subject: serial: 8250: replace hardcoded 0xbf with #define Makes it easier to find all occurences requesting CONF_MODE_B. Signed-off-by: Wolfram Sang Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 3a8e5bfe17e2..580d12c898cf 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -2002,7 +2002,7 @@ static int serial8250_startup(struct uart_port *port) serial_outp(up, UART_IER, 0); serial_outp(up, UART_LCR, 0); serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ - serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_outp(up, UART_EFR, UART_EFR_ECB); serial_outp(up, UART_LCR, 0); } -- cgit v1.2.3 From 7f97c000e87751fdca69d14cba2bbba795c1a972 Mon Sep 17 00:00:00 2001 From: Maciej Szmigiero Date: Thu, 1 Dec 2011 22:39:22 +0100 Subject: serial: fix serial_cs I/O windows for Argosy RS-COM 2P Current serial_cs driver has a problem when trying to detect whether a card has multiple ports: serial_config() calls pcmcia_loop_config() which iterates over card CIS configurations by calling serial_check_for_multi() for each of them. This function wants to check (and select) a configuration that has either one long I/O window spanning multiple ports or two 8-port windows for two serial ports. Problem is, that every pcmcia_loop_config() iteration only updates the windows (via pcmcia_do_loop_config() in resource[0] and resource[1]) when CONF_AUTO_SET_IO flag is set on the device, which is set only later in the code. Fix it by setting this flag earlier. In addition to this, when multi-port card is detected and it does not have an one, long I/O window multi_config_check_notpicky() tries to locate two I/O windows and assumes they are continuous without checking. On an Argosy RS-COM 2P this selects first configuration, which unfortunately has two non-continuous I/O windows. The net effect is that the second serial port on the card does not work. Fix it by checking whether the windows are really continuous. Signed-off-by: Maciej Szmigiero Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_cs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/serial_cs.c b/drivers/tty/serial/serial_cs.c index eef736ff810a..86090605a84e 100644 --- a/drivers/tty/serial/serial_cs.c +++ b/drivers/tty/serial/serial_cs.c @@ -317,7 +317,7 @@ static int serial_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = info; - link->config_flags |= CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; if (do_sound) link->config_flags |= CONF_ENABLE_SPKR; @@ -445,7 +445,7 @@ static int simple_config(struct pcmcia_device *link) /* First pass: look for a config entry that looks normal. * Two tries: without IO aliases, then with aliases */ - link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_SET_IO; + link->config_flags |= CONF_AUTO_SET_VPP; for (try = 0; try < 4; try++) if (!pcmcia_loop_config(link, simple_config_check, &try)) goto found_port; @@ -501,7 +501,8 @@ static int multi_config_check_notpicky(struct pcmcia_device *p_dev, { int *base2 = priv_data; - if (!p_dev->resource[0]->end || !p_dev->resource[1]->end) + if (!p_dev->resource[0]->end || !p_dev->resource[1]->end || + p_dev->resource[0]->start + 8 != p_dev->resource[1]->start) return -ENODEV; p_dev->resource[0]->end = p_dev->resource[1]->end = 8; @@ -520,7 +521,6 @@ static int multi_config(struct pcmcia_device *link) struct serial_info *info = link->priv; int i, base2 = 0; - link->config_flags |= CONF_AUTO_SET_IO; /* First, look for a generic full-sized window */ if (!pcmcia_loop_config(link, multi_config_check, &info->multi)) base2 = link->resource[0]->start + 8; -- cgit v1.2.3 From 8431de80dad20979cc8354a90f70e2faac017932 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 8 Dec 2011 09:06:08 +0530 Subject: msm_serial_hs: Fix type inconsistency for tx and rx command_ptr_ptr Both tx and rx command_ptr_ptr are of type u32*. While allocating memory for it, sizeof(u32 *) is used as part of kmalloc API instead of sizeof(u32). ADM Hardare requires size of command_ptr_ptr as 1 Word. Both sizeof(u32 *) and sizeof(u32) are same on 32-bit architecture whereas sizeof(u32 *) would be different in size compare to sizeof(u32) on anyother architecture. Hence correct usage of sizeof(command_ptr_ptr) for Tx and Rx with kmalloc and dma_(map/unmap)_single APIs. Signed-off-by: Mayank Rana Reported-by: Ilia Mirkin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial_hs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 60c6eb850265..9f67b62cb77d 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -422,9 +422,9 @@ static int __devexit msm_hs_remove(struct platform_device *pdev) msm_uport->rx.rbuffer); dma_pool_destroy(msm_uport->rx.pool); - dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32 *), + dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32), DMA_TO_DEVICE); - dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32 *), + dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32), DMA_TO_DEVICE); dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box), DMA_TO_DEVICE); @@ -812,7 +812,7 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) *tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr); dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr, - sizeof(u32 *), DMA_TO_DEVICE); + sizeof(u32), DMA_TO_DEVICE); /* Save tx_count to use in Callback */ tx->tx_count = tx_count; @@ -1537,7 +1537,7 @@ static int __devinit uartdm_init_port(struct uart_port *uport) if (!tx->command_ptr) return -ENOMEM; - tx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA); + tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA); if (!tx->command_ptr_ptr) { ret = -ENOMEM; goto err_tx_command_ptr_ptr; @@ -1547,7 +1547,7 @@ static int __devinit uartdm_init_port(struct uart_port *uport) sizeof(dmov_box), DMA_TO_DEVICE); tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev, tx->command_ptr_ptr, - sizeof(u32 *), DMA_TO_DEVICE); + sizeof(u32), DMA_TO_DEVICE); tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr); init_waitqueue_head(&rx->wait); @@ -1575,7 +1575,7 @@ static int __devinit uartdm_init_port(struct uart_port *uport) goto err_rx_command_ptr; } - rx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA); + rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA); if (!rx->command_ptr_ptr) { pr_err("%s(): cannot allocate rx->command_ptr_ptr", __func__); ret = -ENOMEM; @@ -1593,7 +1593,7 @@ static int __devinit uartdm_init_port(struct uart_port *uport) *rx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(rx->mapped_cmd_ptr); rx->cmdptr_dmaaddr = dma_map_single(uport->dev, rx->command_ptr_ptr, - sizeof(u32 *), DMA_TO_DEVICE); + sizeof(u32), DMA_TO_DEVICE); rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr); INIT_WORK(&rx->tty_work, msm_hs_tty_flip_buffer_work); @@ -1609,7 +1609,7 @@ err_dma_pool_alloc: dma_pool_destroy(msm_uport->rx.pool); err_dma_pool_create: dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr, - sizeof(u32 *), DMA_TO_DEVICE); + sizeof(u32), DMA_TO_DEVICE); dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box), DMA_TO_DEVICE); kfree(msm_uport->tx.command_ptr_ptr); -- cgit v1.2.3 From ee815f3a76a0d874bd29635366af572fb6ebe535 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 8 Dec 2011 09:06:09 +0530 Subject: msm_serial_hs: Fix spinlock recursion in handling CTS msm_hs_handle_delta_cts tries to acquire port->lock already acquired by the callee function msm_hs_isr. Change function name to follow "_locked" convention. Signed-off-by: Mayank Rana Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial_hs.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 9f67b62cb77d..5e85e1e14c44 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1087,12 +1087,10 @@ static void msm_hs_config_port(struct uart_port *uport, int cfg_flags) } /* Handle CTS changes (Called from interrupt handler) */ -static void msm_hs_handle_delta_cts(struct uart_port *uport) +static void msm_hs_handle_delta_cts_locked(struct uart_port *uport) { - unsigned long flags; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - spin_lock_irqsave(&uport->lock, flags); clk_enable(msm_uport->clk); /* clear interrupt */ @@ -1100,7 +1098,6 @@ static void msm_hs_handle_delta_cts(struct uart_port *uport) uport->icount.cts++; clk_disable(msm_uport->clk); - spin_unlock_irqrestore(&uport->lock, flags); /* clear the IOCTL TIOCMIWAIT if called */ wake_up_interruptible(&uport->state->port.delta_msr_wait); @@ -1248,7 +1245,7 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) /* Change in CTS interrupt */ if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK) - msm_hs_handle_delta_cts(uport); + msm_hs_handle_delta_cts_locked(uport); spin_unlock_irqrestore(&uport->lock, flags); -- cgit v1.2.3 From bbd20759d157e6ecbe2e64e2d9d2505b504bb6b9 Mon Sep 17 00:00:00 2001 From: Thorsten Wißmann Date: Thu, 8 Dec 2011 17:47:33 +0100 Subject: drivers/tty: Remove unneeded spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit coding style fixes in n_tty.c Signed-off-by: Maximilian Krüger Signed-off-by: Thorsten Wißmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 39d6ab6551e0..d2256d08ee7e 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -61,7 +61,7 @@ * controlling the space in the read buffer. */ #define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ -#define TTY_THRESHOLD_UNTHROTTLE 128 +#define TTY_THRESHOLD_UNTHROTTLE 128 /* * Special byte codes used in the echo buffer to represent operations @@ -405,7 +405,7 @@ static ssize_t process_output_block(struct tty_struct *tty, const unsigned char *buf, unsigned int nr) { int space; - int i; + int i; const unsigned char *cp; mutex_lock(&tty->output_lock); @@ -1607,7 +1607,7 @@ static inline int input_available_p(struct tty_struct *tty, int amt) } /** - * copy_from_read_buf - copy read data directly + * copy_from_read_buf - copy read data directly * @tty: terminal device * @b: user data * @nr: size of data @@ -1909,7 +1909,7 @@ do_it_again: if (nr) clear_bit(TTY_PUSH, &tty->flags); } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) - goto do_it_again; + goto do_it_again; n_tty_set_room(tty); return retval; -- cgit v1.2.3 From 850624c15da4651f4c6b821723419d8777659577 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 4 Dec 2011 18:42:18 -0500 Subject: serial: move struct uart_8250_port from 8250.c to 8250.h Since we want to promote sharing and move away from one single uart driver with a bunch of platform specific bugfixes all munged into one, relocate some header like material from the C file to the header. Signed-off-by: Paul Gortmaker Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 26 -------------------------- drivers/tty/serial/8250.h | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 580d12c898cf..c58e9e286bff 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -129,32 +129,6 @@ static unsigned long probe_rsa[PORT_RSA_MAX]; static unsigned int probe_rsa_count; #endif /* CONFIG_SERIAL_8250_RSA */ -struct uart_8250_port { - struct uart_port port; - struct timer_list timer; /* "no irq" timer */ - struct list_head list; /* ports on this IRQ */ - unsigned short capabilities; /* port capabilities */ - unsigned short bugs; /* port bugs */ - unsigned int tx_loadsz; /* transmit fifo load size */ - unsigned char acr; - unsigned char ier; - unsigned char lcr; - unsigned char mcr; - unsigned char mcr_mask; /* mask of user bits */ - unsigned char mcr_force; /* mask of forced bits */ - unsigned char cur_iotype; /* Running I/O type */ - - /* - * Some bits in registers are cleared on a read, so they must - * be saved whenever the register is read but the bits will not - * be immediately processed. - */ -#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS - unsigned char lsr_saved_flags; -#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA - unsigned char msr_saved_flags; -}; - struct irq_info { struct hlist_node node; int irq; diff --git a/drivers/tty/serial/8250.h b/drivers/tty/serial/8250.h index 6edf4a6a22d4..ae027be57e25 100644 --- a/drivers/tty/serial/8250.h +++ b/drivers/tty/serial/8250.h @@ -13,6 +13,32 @@ #include +struct uart_8250_port { + struct uart_port port; + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned short capabilities; /* port capabilities */ + unsigned short bugs; /* port bugs */ + unsigned int tx_loadsz; /* transmit fifo load size */ + unsigned char acr; + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned char mcr_mask; /* mask of user bits */ + unsigned char mcr_force; /* mask of forced bits */ + unsigned char cur_iotype; /* Running I/O type */ + + /* + * Some bits in registers are cleared on a read, so they must + * be saved whenever the register is read but the bits will not + * be immediately processed. + */ +#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS + unsigned char lsr_saved_flags; +#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA + unsigned char msr_saved_flags; +}; + struct old_serial_port { unsigned int uart; unsigned int baud_base; -- cgit v1.2.3 From 0690f41fddd285c3473e4af2a42d15bce7ff3e68 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 4 Dec 2011 18:42:19 -0500 Subject: serial: clean up parameter passing for 8250 Rx IRQ handling The receive_chars() was taking a pointer to a passed in LSR value in status and knocking off bits as it processed them. But since receive_chars isn't returning a value, we can instead pass in a normal non-pointer value for LSR, and simply return the residual (unprocessed) LSR once it is done. The value in this cleanup, is that it clarifies the API of the receive_chars prior to exporting it to other 8250-like drivers for shared usage. Signed-off-by: Paul Gortmaker Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index c58e9e286bff..5274228fa03c 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1375,11 +1375,16 @@ static void clear_rx_fifo(struct uart_8250_port *up) } while (1); } -static void -receive_chars(struct uart_8250_port *up, unsigned int *status) +/* + * receive_chars: processes according to the passed in LSR + * value, and returns the remaining LSR bits not handled + * by this Rx routine. + */ +static unsigned char +receive_chars(struct uart_8250_port *up, unsigned char lsr) { struct tty_struct *tty = up->port.state->port.tty; - unsigned char ch, lsr = *status; + unsigned char ch; int max_count = 256; char flag; @@ -1455,7 +1460,7 @@ ignore_char: spin_unlock(&up->port.lock); tty_flip_buffer_push(tty); spin_lock(&up->port.lock); - *status = lsr; + return lsr; } static void transmit_chars(struct uart_8250_port *up) @@ -1524,7 +1529,7 @@ static unsigned int check_modem_status(struct uart_8250_port *up) */ static void serial8250_handle_port(struct uart_8250_port *up) { - unsigned int status; + unsigned char status; unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); @@ -1534,7 +1539,7 @@ static void serial8250_handle_port(struct uart_8250_port *up) DEBUG_INTR("status = %x...", status); if (status & (UART_LSR_DR | UART_LSR_BI)) - receive_chars(up, &status); + status = receive_chars(up, status); check_modem_status(up); if (status & UART_LSR_THRE) transmit_chars(up); -- cgit v1.2.3 From 3986fb2ba67bb30cac18b0cff48c88d69ad37681 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 4 Dec 2011 18:42:20 -0500 Subject: serial: export the key functions for an 8250 IRQ handler For drivers that need to construct their own IRQ handler, the three components are seen in the current handle_port -- i.e. Rx, Tx and modem_status. Make these exported symbols so that "almost" 8250 UARTs can construct their own IRQ handler with these shared components, while working around their own unique errata issues. The function names are given a serial8250 prefix, since they are now entering the global namespace. Signed-off-by: Paul Gortmaker Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 29 +++++++++++++++-------------- include/linux/serial_8250.h | 4 ++++ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 5274228fa03c..9c0396fa3915 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1300,8 +1300,6 @@ static void serial8250_stop_tx(struct uart_port *port) } } -static void transmit_chars(struct uart_8250_port *up); - static void serial8250_start_tx(struct uart_port *port) { struct uart_8250_port *up = @@ -1318,7 +1316,7 @@ static void serial8250_start_tx(struct uart_port *port) if ((up->port.type == PORT_RM9000) ? (lsr & UART_LSR_THRE) : (lsr & UART_LSR_TEMT)) - transmit_chars(up); + serial8250_tx_chars(up); } } @@ -1376,12 +1374,12 @@ static void clear_rx_fifo(struct uart_8250_port *up) } /* - * receive_chars: processes according to the passed in LSR + * serial8250_rx_chars: processes according to the passed in LSR * value, and returns the remaining LSR bits not handled * by this Rx routine. */ -static unsigned char -receive_chars(struct uart_8250_port *up, unsigned char lsr) +unsigned char +serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) { struct tty_struct *tty = up->port.state->port.tty; unsigned char ch; @@ -1462,8 +1460,9 @@ ignore_char: spin_lock(&up->port.lock); return lsr; } +EXPORT_SYMBOL_GPL(serial8250_rx_chars); -static void transmit_chars(struct uart_8250_port *up) +void serial8250_tx_chars(struct uart_8250_port *up) { struct circ_buf *xmit = &up->port.state->xmit; int count; @@ -1500,8 +1499,9 @@ static void transmit_chars(struct uart_8250_port *up) if (uart_circ_empty(xmit)) __stop_tx(up); } +EXPORT_SYMBOL_GPL(serial8250_tx_chars); -static unsigned int check_modem_status(struct uart_8250_port *up) +unsigned int serial8250_modem_status(struct uart_8250_port *up) { unsigned int status = serial_in(up, UART_MSR); @@ -1523,6 +1523,7 @@ static unsigned int check_modem_status(struct uart_8250_port *up) return status; } +EXPORT_SYMBOL_GPL(serial8250_modem_status); /* * This handles the interrupt from one port. @@ -1539,10 +1540,10 @@ static void serial8250_handle_port(struct uart_8250_port *up) DEBUG_INTR("status = %x...", status); if (status & (UART_LSR_DR | UART_LSR_BI)) - status = receive_chars(up, status); - check_modem_status(up); + status = serial8250_rx_chars(up, status); + serial8250_modem_status(up); if (status & UART_LSR_THRE) - transmit_chars(up); + serial8250_tx_chars(up); spin_unlock_irqrestore(&up->port.lock, flags); } @@ -1782,7 +1783,7 @@ static void serial8250_backup_timeout(unsigned long data) } if (!(iir & UART_IIR_NO_INT)) - transmit_chars(up); + serial8250_tx_chars(up); if (is_real_interrupt(up->port.irq)) serial_out(up, UART_IER, ier); @@ -1816,7 +1817,7 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) unsigned int status; unsigned int ret; - status = check_modem_status(up); + status = serial8250_modem_status(up); ret = 0; if (status & UART_MSR_DCD) @@ -2863,7 +2864,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) * while processing with interrupts off. */ if (up->msr_saved_flags) - check_modem_status(up); + serial8250_modem_status(up); if (locked) spin_unlock(&up->port.lock); diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 1f05bbeac01e..b44034eca123 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -66,6 +66,7 @@ enum { * dependent on the 8250 driver. */ struct uart_port; +struct uart_8250_port; int serial8250_register_port(struct uart_port *); void serial8250_unregister_port(int line); @@ -82,6 +83,9 @@ extern void serial8250_do_set_termios(struct uart_port *port, extern void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); int serial8250_handle_irq(struct uart_port *port, unsigned int iir); +unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr); +void serial8250_tx_chars(struct uart_8250_port *up); +unsigned int serial8250_modem_status(struct uart_8250_port *up); extern void serial8250_set_isa_configurator(void (*v) (int port, struct uart_port *up, -- cgit v1.2.3 From a0431476e95d18bb65349e7bcf98eb10b5ad00b9 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 4 Dec 2011 18:42:21 -0500 Subject: serial: make 8250 timeout use the specified IRQ handler The current 8250 timeout code duplicates the code path in serial8250_default_handle_irq and then serial8250_handle_irq i.e. reading iir, check for IIR_NO_INT, and then calling serial8250_handle_port. So the immediate thought is to replace the duplicated code with a call to serial8250_default_handle_irq. But this highlights a problem. We let 8250 driver variants use their own IRQ handler via specifying their own custom ->handle_irq, but in the event of a timeout, we ignore their handler and implicitly run serial8250_default_handle_irq instead. So, go through the struct to get ->handle_irq and call that, which for most will still be serial8250_default_handle_irq. Signed-off-by: Paul Gortmaker Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 9c0396fa3915..f1c4f4575426 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1740,11 +1740,8 @@ static void serial_unlink_irq_chain(struct uart_8250_port *up) static void serial8250_timeout(unsigned long data) { struct uart_8250_port *up = (struct uart_8250_port *)data; - unsigned int iir; - iir = serial_in(up, UART_IIR); - if (!(iir & UART_IIR_NO_INT)) - serial8250_handle_port(up); + up->port.handle_irq(&up->port); mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port)); } -- cgit v1.2.3 From 86b21199fc45e0052e181fefc07c747e9dc903b3 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 4 Dec 2011 18:42:22 -0500 Subject: serial: manually inline serial8250_handle_port Currently serial8250_handle_irq is a trivial wrapper around serial8250_handle_port, which actually does all the work. Since there are no other callers of serial8250_handle_port, we can just move it inline into serial8250_handle_irq. This also makes it more clear what functionality any custom IRQ handlers need to provide if not using serial8250_default_handle_irq. Signed-off-by: Paul Gortmaker Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index f1c4f4575426..9f50c4e3c2be 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1528,10 +1528,15 @@ EXPORT_SYMBOL_GPL(serial8250_modem_status); /* * This handles the interrupt from one port. */ -static void serial8250_handle_port(struct uart_8250_port *up) +int serial8250_handle_irq(struct uart_port *port, unsigned int iir) { unsigned char status; unsigned long flags; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + if (iir & UART_IIR_NO_INT) + return 0; spin_lock_irqsave(&up->port.lock, flags); @@ -1546,19 +1551,7 @@ static void serial8250_handle_port(struct uart_8250_port *up) serial8250_tx_chars(up); spin_unlock_irqrestore(&up->port.lock, flags); -} - -int serial8250_handle_irq(struct uart_port *port, unsigned int iir) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - if (!(iir & UART_IIR_NO_INT)) { - serial8250_handle_port(up); - return 1; - } - - return 0; + return 1; } EXPORT_SYMBOL_GPL(serial8250_handle_irq); @@ -2827,7 +2820,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) local_irq_save(flags); if (up->port.sysrq) { - /* serial8250_handle_port() already took the lock */ + /* serial8250_handle_irq() already took the lock */ locked = 0; } else if (oops_in_progress) { locked = spin_trylock(&up->port.lock); -- cgit v1.2.3 From 9deaa53ac7fa373623123aa4f18828dd62292b1a Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 4 Dec 2011 18:42:23 -0500 Subject: serial: add irq handler for Freescale 16550 errata. Sending a break on the SOC UARTs found in some MPC83xx/85xx/86xx chips seems to cause a short lived IRQ storm (/proc/interrupts typically shows somewhere between 300 and 1500 events). Unfortunately this renders SysRQ over the serial console completely inoperable. The suggested workaround in the errata is to read the Rx register, wait one character period, and then read the Rx register again. We achieve this by tracking the old LSR value, and on the subsequent interrupt event after a break, we don't read LSR, instead we just read the RBR again and return immediately. The "fsl,ns16550" is used in the compatible field of the serial device to mark UARTs known to have this issue. Thanks to Scott Wood for providing the errata data which led to a much cleaner fix. Signed-off-by: Paul Gortmaker Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/legacy_serial.c | 3 ++ drivers/tty/serial/8250_fsl.c | 63 +++++++++++++++++++++++++++++++++++++ drivers/tty/serial/Kconfig | 5 +++ drivers/tty/serial/Makefile | 1 + include/linux/serial_8250.h | 1 + 5 files changed, 73 insertions(+) create mode 100644 drivers/tty/serial/8250_fsl.c diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index c7b5afeecaf2..3fea3689527e 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -441,6 +441,9 @@ static void __init fixup_port_irq(int index, return; port->irq = virq; + + if (of_device_is_compatible(np, "fsl,ns16550")) + port->handle_irq = fsl8250_handle_irq; } static void __init fixup_port_pio(int index, diff --git a/drivers/tty/serial/8250_fsl.c b/drivers/tty/serial/8250_fsl.c new file mode 100644 index 000000000000..f4d3c47b88e8 --- /dev/null +++ b/drivers/tty/serial/8250_fsl.c @@ -0,0 +1,63 @@ +#include +#include + +#include "8250.h" + +/* + * Freescale 16550 UART "driver", Copyright (C) 2011 Paul Gortmaker. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This isn't a full driver; it just provides an alternate IRQ + * handler to deal with an errata. Everything else is just + * using the bog standard 8250 support. + * + * We follow code flow of serial8250_default_handle_irq() but add + * a check for a break and insert a dummy read on the Rx for the + * immediately following IRQ event. + * + * We re-use the already existing "bug handling" lsr_saved_flags + * field to carry the "what we just did" information from the one + * IRQ event to the next one. + */ + +int fsl8250_handle_irq(struct uart_port *port) +{ + unsigned char lsr, orig_lsr; + unsigned long flags; + unsigned int iir; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + spin_lock_irqsave(&up->port.lock, flags); + + iir = port->serial_in(port, UART_IIR); + if (iir & UART_IIR_NO_INT) { + spin_unlock_irqrestore(&up->port.lock, flags); + return 0; + } + + /* This is the WAR; if last event was BRK, then read and return */ + if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { + up->lsr_saved_flags &= ~UART_LSR_BI; + port->serial_in(port, UART_RX); + spin_unlock_irqrestore(&up->port.lock, flags); + return 1; + } + + lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); + + if (lsr & (UART_LSR_DR | UART_LSR_BI)) + lsr = serial8250_rx_chars(up, lsr); + + serial8250_modem_status(up); + + if (lsr & UART_LSR_THRE) + serial8250_tx_chars(up); + + up->lsr_saved_flags = orig_lsr; + spin_unlock_irqrestore(&up->port.lock, flags); + return 1; +} diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 705d2dc39c9a..fee9e04f42e7 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -97,6 +97,11 @@ config SERIAL_8250_PNP This builds standard PNP serial support. You may be able to disable this feature if you only need legacy serial support. +config SERIAL_8250_FSL + bool + depends on SERIAL_8250 && PPC + default PPC + config SERIAL_8250_HP300 tristate depends on SERIAL_8250 && HP300 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index af57089ddb67..75eadb8d7178 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o +obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index b44034eca123..8f012f8ac8e9 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -82,6 +82,7 @@ extern void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old); extern void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); +extern int fsl8250_handle_irq(struct uart_port *port); int serial8250_handle_irq(struct uart_port *port, unsigned int iir); unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr); void serial8250_tx_chars(struct uart_8250_port *up); -- cgit v1.2.3 From 5c2f37dddd963df61aed14283adb98067fb6afe5 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 12 Dec 2011 21:47:38 -0500 Subject: serial: make FSL errata depend on 8250_CONSOLE, not just 8250 The recent commit "serial: add irq handler for Freescale 16550 errata" would allow Kconfig choices that had 8250 support as a module and yet still try and build in the errata fix non-modular, resulting in build failures for some non-embedded PPC targets. Since we hook in the errata fix from legacy_serial.c, which is built only for PPC_UDBG_16550, and since the errata is only really relevant for SysRQ on serial console, tighten up the dependencies to be exactly that. We'll get coverage on the relevant Freescale boards because the Kconfig for their CPU types all select the PPC_UDBG_16550 option, and the defconfigs also all select the 8250_CONSOLE option. Also, the 8250_CONSOLE option has a strict dependency on "SERIAL_8250=y" which resolves the reported problem for non Freescale targets. Reported-by: Michael Neuling Signed-off-by: Paul Gortmaker Tested-by: Michael Neuling Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index fee9e04f42e7..d1e4f203ae61 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -99,7 +99,7 @@ config SERIAL_8250_PNP config SERIAL_8250_FSL bool - depends on SERIAL_8250 && PPC + depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550 default PPC config SERIAL_8250_HP300 -- cgit v1.2.3 From b48dc711c33034958768fadf15f75abff95fb499 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 13 Dec 2011 12:22:01 +0800 Subject: serial: bfin-uart: Enable hardware automatic CTS only when CTS pin is available. Signed-off-by: Sonic Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_uart.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index abac23741057..dedd9ab06d82 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -726,16 +726,17 @@ static int bfin_serial_startup(struct uart_port *port) } #endif #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->cts_pin >= 0 && request_irq(uart->status_irq, - bfin_serial_mctrl_cts_int, - 0, "BFIN_UART_MODEM_STATUS", uart)) { - uart->cts_pin = -1; - pr_info("Unable to attach BlackFin UART Modem Status interrupt.\n"); - } + if (uart->cts_pin >= 0) { + if (request_irq(uart->status_irq, bfin_serial_mctrl_cts_int, + IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) { + uart->cts_pin = -1; + dev_info(port->dev, "Unable to attach BlackFin UART Modem Status interrupt.\n"); + } - /* CTS RTS PINs are negative assertive. */ - UART_PUT_MCR(uart, ACTS); - UART_SET_IER(uart, EDSSI); + /* CTS RTS PINs are negative assertive. */ + UART_PUT_MCR(uart, ACTS); + UART_SET_IER(uart, EDSSI); + } #endif UART_SET_IER(uart, ERBFI); -- cgit v1.2.3 From 64851636d568ae9f167cd5d1dcdbfe17e6eef73c Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 13 Dec 2011 12:22:02 +0800 Subject: serial: bfin-uart: Remove ASYNC_CTS_FLOW flag for hardware automatic CTS. Blackfin uart supports automatic CTS trigger when hardware flow control is enabled. No need to start and top tx in CTS interrupt. So, remote ASYNC_CTS_FLOW flag. Signed-off-by: Sonic Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_uart.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index dedd9ab06d82..26953bfa6922 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -116,11 +116,20 @@ static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id) { struct bfin_serial_port *uart = dev_id; - unsigned int status; - - status = bfin_serial_get_mctrl(&uart->port); + unsigned int status = bfin_serial_get_mctrl(&uart->port); #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + struct tty_struct *tty = uart->port.state->port.tty; + UART_CLEAR_SCTS(uart); + if (tty->hw_stopped) { + if (status) { + tty->hw_stopped = 0; + uart_write_wakeup(&uart->port); + } + } else { + if (!status) + tty->hw_stopped = 1; + } #endif uart_handle_cts_change(&uart->port, status & TIOCM_CTS); @@ -1358,7 +1367,9 @@ static int bfin_serial_probe(struct platform_device *pdev) uart->cts_pin = -1; else { uart->cts_pin = res->start; +#ifdef CONFIG_SERIAL_BFIN_CTSRTS uart->port.flags |= ASYNC_CTS_FLOW; +#endif } res = platform_get_resource(pdev, IORESOURCE_IO, 1); -- cgit v1.2.3 From 1e9deb118ed76b9df89d189f27a06522a03cf743 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Tue, 27 Dec 2011 15:47:37 +0100 Subject: serial: add support for 400 and 800 v3 series Titan cards add support for 400Hv3, 410Hv3 and 800Hv3 Signed-off-by: Yegor Yefremov Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250_pci.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 2cbf78f77c4a..da2b0b0a183f 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -1152,6 +1152,10 @@ static void disable_msi(struct pci_dev *dev) #define PCI_DEVICE_ID_TITAN_800E 0xA014 #define PCI_DEVICE_ID_TITAN_200EI 0xA016 #define PCI_DEVICE_ID_TITAN_200EISI 0xA017 +#define PCI_DEVICE_ID_TITAN_400V3 0xA310 +#define PCI_DEVICE_ID_TITAN_410V3 0xA312 +#define PCI_DEVICE_ID_TITAN_800V3 0xA314 +#define PCI_DEVICE_ID_TITAN_800V3B 0xA315 #define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 #define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6 #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 @@ -3443,6 +3447,18 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_410V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, -- cgit v1.2.3 From 97d24634daff8b83dae21cfde68553ff4997e558 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 20 Dec 2011 11:47:44 +0100 Subject: serial: use DIV_ROUND_CLOSEST instead of open coding it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index d2990f738606..c7bf31a6a7e7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -421,7 +421,7 @@ uart_get_divisor(struct uart_port *port, unsigned int baud) if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) quot = port->custom_divisor; else - quot = (port->uartclk + (8 * baud)) / (16 * baud); + quot = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud); return quot; } -- cgit v1.2.3 From 106b5fa700bd1ff422382dc907d1ca97f0c3972d Mon Sep 17 00:00:00 2001 From: Zeng Zhaoming Date: Tue, 20 Dec 2011 19:30:05 +0800 Subject: tty: Fix memory leak in virtual console when enable unicode translation Virtual console unicode translation map leaks with following message when enable kmemleak: unreferenced object 0xeb5ec450 (size 192): comm "setfont", pid 665, jiffies 4294899028 (age 3696.220s) hex dump (first 32 bytes): e0 5b 9d eb 00 00 00 00 00 00 00 00 80 b9 ea eb .[.............. b0 5a 9d eb 00 00 00 00 00 00 00 00 00 00 00 00 .Z.............. backtrace: [] kmemleak_alloc+0x3c/0xa0 [] kmem_cache_alloc_trace+0xe2/0x250 [] con_clear_unimap+0x78/0xd0 [] vt_ioctl+0x1562/0x1d00 [] tty_ioctl+0x230/0x7c0 [] do_vfs_ioctl+0x79/0x2d0 [] sys_ioctl+0x6f/0x80 [] sysenter_do_call+0x12/0x38 [] 0xffffffff unreferenced object 0xeb9d5be0 (size 128): comm "setfont", pid 660, jiffies 4294899030 (age 3696.212s) hex dump (first 32 bytes): 60 c2 a6 eb 50 c8 a6 eb c0 54 9d eb 80 59 9d eb `...P....T...Y.. 90 53 9d eb 60 52 9d eb 60 92 9b eb 00 00 00 00 .S..`R..`....... backtrace: [] kmemleak_alloc+0x3c/0xa0 [] kmem_cache_alloc_trace+0xe2/0x250 [] con_insert_unipair+0x7c/0x150 [] con_set_unimap+0x15c/0x1f0 [] vt_ioctl+0x170b/0x1d00 [] tty_ioctl+0x230/0x7c0 [] do_vfs_ioctl+0x79/0x2d0 [] sys_ioctl+0x6f/0x80 [] sysenter_do_call+0x12/0x38 [] 0xffffffff The leak caused by con_set_default_unimap() not correct free the old map. Signed-off-by: Zeng Zhaoming Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/consolemap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 45d3e80156d4..a0f3d6c4d39d 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -584,7 +584,7 @@ int con_set_default_unimap(struct vc_data *vc) return 0; dflt->refcount++; *vc->vc_uni_pagedir_loc = (unsigned long)dflt; - if (p && --p->refcount) { + if (p && !--p->refcount) { con_release_unimap(p); kfree(p); } -- cgit v1.2.3 From dbf1115d3f8c7052788aa4e6e46abd27f3b3eeba Mon Sep 17 00:00:00 2001 From: Claudio Scordino Date: Fri, 16 Dec 2011 15:08:49 +0100 Subject: atmel_serial: fix spinlock lockup in RS485 code Patch to fix a spinlock lockup in the driver that sometimes happens when the tasklet starts. Signed-off-by: Claudio Scordino Signed-off-by: Dave Bender Tested-by: Dave Bender Acked-by: Nicolas Ferre Acked-by: Alan Cox Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index bd85e32521d2..10605ecc99ab 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -212,8 +212,9 @@ 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; + unsigned long flags; - spin_lock(&port->lock); + spin_lock_irqsave(&port->lock, flags); /* Disable interrupts */ UART_PUT_IDR(port, atmel_port->tx_done_mask); @@ -244,7 +245,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) /* Enable interrupts */ UART_PUT_IER(port, atmel_port->tx_done_mask); - spin_unlock(&port->lock); + spin_unlock_irqrestore(&port->lock, flags); } -- cgit v1.2.3 From 343fe4074bf8a1551831f6505e738e99777f739d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 4 Jan 2012 15:27:32 -0800 Subject: serial/documentation: fix documented name of DCD cpp symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Acked-by: Alan Cox Acked-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman --- Documentation/serial/driver | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/serial/driver b/Documentation/serial/driver index 77ba0afbe4db..0a25a9191864 100644 --- a/Documentation/serial/driver +++ b/Documentation/serial/driver @@ -101,7 +101,7 @@ hardware. Returns the current state of modem control inputs. The state of the outputs should not be returned, since the core keeps track of their state. The state information should include: - - TIOCM_DCD state of DCD signal + - TIOCM_CAR state of DCD signal - TIOCM_CTS state of CTS signal - TIOCM_DSR state of DSR signal - TIOCM_RI state of RI signal -- cgit v1.2.3 From 59087384e41e33ed070575aa4a834a7ff7d67e77 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 4 Jan 2012 15:16:24 -0500 Subject: serial: driver for m32 arch should not have DEC alpha errata This driver was copied from the original 8250 driver and hence got the DEC alpha errata workaround. But the workaround is ugly and we don't really want it in any more places than it absolutely needs to be. Obviously ARCH=m32r means ARCH != alpha, so just remove the references to the ALPHA_KLUDGE_MCR define. Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/m32r_sio.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c index 08018934e013..94a6792bf97b 100644 --- a/drivers/tty/serial/m32r_sio.c +++ b/drivers/tty/serial/m32r_sio.c @@ -1000,11 +1000,8 @@ static void __init m32r_sio_register_ports(struct uart_driver *drv) init_timer(&up->timer); up->timer.function = m32r_sio_timeout; - /* - * ALPHA_KLUDGE_MCR needs to be killed. - */ - up->mcr_mask = ~ALPHA_KLUDGE_MCR; - up->mcr_force = ALPHA_KLUDGE_MCR; + up->mcr_mask = ~0; + up->mcr_force = 0; uart_add_one_port(drv, &up->port); } -- cgit v1.2.3 From db1a9b55004c83ded54c1f869f81a8a59c6dde87 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 13 Dec 2011 01:23:48 -0200 Subject: tty: serial: imx: Allow UART to be a source for wakeup Allow UART to be a source for wakeup from low power mode. Tested on a MX27PDK by doing: echo enabled > /sys/devices/platform/imx21-uart.0/tty/ttymxc0/power/wakeup echo mem > /sys/power/state and then pressing a key in the console will wakeup the sytem. Suggested-by: Shawn Guo Signed-off-by: Fabio Estevam Tested-by: Richard Zhao Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 962cafa175f7..ed6e1448e7eb 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -566,6 +566,9 @@ static irqreturn_t imx_int(int irq, void *dev_id) if (sts & USR1_RTSD) imx_rtsint(irq, dev_id); + if (sts & USR1_AWAKE) + writel(USR1_AWAKE, sport->port.membase + USR1); + return IRQ_HANDLED; } @@ -1269,6 +1272,12 @@ static struct uart_driver imx_reg = { static int serial_imx_suspend(struct platform_device *dev, pm_message_t state) { struct imx_port *sport = platform_get_drvdata(dev); + unsigned int val; + + /* enable wakeup from i.MX UART */ + val = readl(sport->port.membase + UCR3); + val |= UCR3_AWAKEN; + writel(val, sport->port.membase + UCR3); if (sport) uart_suspend_port(&imx_reg, &sport->port); @@ -1279,6 +1288,12 @@ static int serial_imx_suspend(struct platform_device *dev, pm_message_t state) static int serial_imx_resume(struct platform_device *dev) { struct imx_port *sport = platform_get_drvdata(dev); + unsigned int val; + + /* disable wakeup from i.MX UART */ + val = readl(sport->port.membase + UCR3); + val &= ~UCR3_AWAKEN; + writel(val, sport->port.membase + UCR3); if (sport) uart_resume_port(&imx_reg, &sport->port); -- cgit v1.2.3 From a197a191f73a75d80d5b67e09e0b89c214dc3690 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 14 Dec 2011 21:26:51 +0100 Subject: serial/imx: propagate error from of_alias_get_id instead of using -ENODEV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A quick look at of_alias_get_id shows that in the error case it returns -ENODEV, too, but still it's better style to propagate the value as is. Signed-off-by: Uwe Kleine-König Cc: Shawn Guo Cc: Alan Cox Cc: Grant Likely Cc: Jeremy Kerr Cc: Jason Liu Cc: Sascha Hauer Acked-by: Jason Liu Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index ed6e1448e7eb..2813f0210377 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1316,7 +1316,7 @@ static int serial_imx_probe_dt(struct imx_port *sport, ret = of_alias_get_id(np, "serial"); if (ret < 0) { dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); - return -ENODEV; + return ret; } sport->port.line = ret; -- cgit v1.2.3 From 20bb8095a467dde88bd09a55ed62c60fada2e5c9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 15 Dec 2011 09:16:34 +0100 Subject: serial/imx: let probing fail for the dt case without a valid alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the uart device is instantiated by dt but dt doesn't provide an alias then better let probing fail instead of falling back to an unrelated device id used for the line number and no platform data. Signed-off-by: Uwe Kleine-König Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 2813f0210377..92a5987924cf 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1302,6 +1302,10 @@ static int serial_imx_resume(struct platform_device *dev) } #ifdef CONFIG_OF +/* + * This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it + * could successfully get all information from dt or a negative errno. + */ static int serial_imx_probe_dt(struct imx_port *sport, struct platform_device *pdev) { @@ -1311,7 +1315,8 @@ static int serial_imx_probe_dt(struct imx_port *sport, int ret; if (!np) - return -ENODEV; + /* no device tree device */ + return 1; ret = of_alias_get_id(np, "serial"); if (ret < 0) { @@ -1334,7 +1339,7 @@ static int serial_imx_probe_dt(struct imx_port *sport, static inline int serial_imx_probe_dt(struct imx_port *sport, struct platform_device *pdev) { - return -ENODEV; + return 1; } #endif @@ -1369,8 +1374,10 @@ static int serial_imx_probe(struct platform_device *pdev) return -ENOMEM; ret = serial_imx_probe_dt(sport, pdev); - if (ret == -ENODEV) + if (ret > 0) serial_imx_probe_pdata(sport, pdev); + else if (ret < 0) + goto free; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { -- cgit v1.2.3 From 0ad5a81472a9d6a0e826e0c6ebe66d3792932a93 Mon Sep 17 00:00:00 2001 From: Dirk Behme Date: Thu, 22 Dec 2011 09:57:52 +0100 Subject: imx: Add save/restore functions for UART control regs Factor out the uart save/restore functionality instead of having the same code several times in the driver. Signed-off-by: Dirk Behme CC: Saleem Abdulrasool CC: Sascha Hauer CC: Fabio Estevam CC: Uwe Kleine-Koenig CC: linux-serial@vger.kernel.org CC: Alan Cox Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 92a5987924cf..225922c7ce99 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -207,6 +207,12 @@ struct imx_port { struct imx_uart_data *devdata; }; +struct imx_port_ucrs { + unsigned int ucr1; + unsigned int ucr2; + unsigned int ucr3; +}; + #ifdef CONFIG_IRDA #define USE_IRDA(sport) ((sport)->use_irda) #else @@ -259,6 +265,27 @@ static inline int is_imx21_uart(struct imx_port *sport) return sport->devdata->devtype == IMX21_UART; } +/* + * Save and restore functions for UCR1, UCR2 and UCR3 registers + */ +static void imx_port_ucrs_save(struct uart_port *port, + struct imx_port_ucrs *ucr) +{ + /* save control registers */ + ucr->ucr1 = readl(port->membase + UCR1); + ucr->ucr2 = readl(port->membase + UCR2); + ucr->ucr3 = readl(port->membase + UCR3); +} + +static void imx_port_ucrs_restore(struct uart_port *port, + struct imx_port_ucrs *ucr) +{ + /* restore control registers */ + writel(ucr->ucr1, port->membase + UCR1); + writel(ucr->ucr2, port->membase + UCR2); + writel(ucr->ucr3, port->membase + UCR3); +} + /* * Handle any change of modem status signal since we were last called. */ @@ -1121,13 +1148,14 @@ static void imx_console_write(struct console *co, const char *s, unsigned int count) { struct imx_port *sport = imx_ports[co->index]; - unsigned int old_ucr1, old_ucr2, ucr1; + struct imx_port_ucrs old_ucr; + unsigned int ucr1; /* - * First, save UCR1/2 and then disable interrupts + * First, save UCR1/2/3 and then disable interrupts */ - ucr1 = old_ucr1 = readl(sport->port.membase + UCR1); - old_ucr2 = readl(sport->port.membase + UCR2); + imx_port_ucrs_save(&sport->port, &old_ucr); + ucr1 = old_ucr.ucr1; if (is_imx1_uart(sport)) ucr1 |= IMX1_UCR1_UARTCLKEN; @@ -1136,18 +1164,17 @@ imx_console_write(struct console *co, const char *s, unsigned int count) writel(ucr1, sport->port.membase + UCR1); - writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2); + writel(old_ucr.ucr2 | UCR2_TXEN, sport->port.membase + UCR2); uart_console_write(&sport->port, s, count, imx_console_putchar); /* * Finally, wait for transmitter to become empty - * and restore UCR1/2 + * and restore UCR1/2/3 */ while (!(readl(sport->port.membase + USR2) & USR2_TXDC)); - writel(old_ucr1, sport->port.membase + UCR1); - writel(old_ucr2, sport->port.membase + UCR2); + imx_port_ucrs_restore(&sport->port, &old_ucr); } /* -- cgit v1.2.3 From 01f56abd089dd216e537817b61497c58bb66aab3 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 22 Dec 2011 09:57:53 +0100 Subject: imx: add polled io uart methods These methods are invoked if the iMX uart is used in conjuction with kgdb during early boot. In order to access the UART without the interrupts, the kernel uses the basic polling methods for IO with the device. With these methods implemented, it is now possible to enable kgdb during early boot over serial. Signed-off-by: Saleem Abdulrasool Signed-off-by: Dirk Behme CC: Sascha Hauer CC: Fabio Estevam CC: Uwe Kleine-Koenig CC: linux-serial@vger.kernel.org CC: Alan Cox Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 225922c7ce99..6b98a524eca2 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -102,6 +102,7 @@ #define UCR2_STPB (1<<6) /* Stop */ #define UCR2_WS (1<<5) /* Word size */ #define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */ +#define UCR2_ATEN (1<<3) /* Aging Timer Enable */ #define UCR2_TXEN (1<<2) /* Transmitter enabled */ #define UCR2_RXEN (1<<1) /* Receiver enabled */ #define UCR2_SRST (1<<0) /* SW reset */ @@ -1109,6 +1110,70 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser) return ret; } +#if defined(CONFIG_CONSOLE_POLL) +static int imx_poll_get_char(struct uart_port *port) +{ + struct imx_port_ucrs old_ucr; + unsigned int status; + unsigned char c; + + /* save control registers */ + imx_port_ucrs_save(port, &old_ucr); + + /* disable interrupts */ + writel(UCR1_UARTEN, port->membase + UCR1); + writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI), + port->membase + UCR2); + writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN), + port->membase + UCR3); + + /* poll */ + do { + status = readl(port->membase + USR2); + } while (~status & USR2_RDR); + + /* read */ + c = readl(port->membase + URXD0); + + /* restore control registers */ + imx_port_ucrs_restore(port, &old_ucr); + + return c; +} + +static void imx_poll_put_char(struct uart_port *port, unsigned char c) +{ + struct imx_port_ucrs old_ucr; + unsigned int status; + + /* save control registers */ + imx_port_ucrs_save(port, &old_ucr); + + /* disable interrupts */ + writel(UCR1_UARTEN, port->membase + UCR1); + writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI), + port->membase + UCR2); + writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN), + port->membase + UCR3); + + /* drain */ + do { + status = readl(port->membase + USR1); + } while (~status & USR1_TRDY); + + /* write */ + writel(c, port->membase + URTX0); + + /* flush */ + do { + status = readl(port->membase + USR2); + } while (~status & USR2_TXDC); + + /* restore control registers */ + imx_port_ucrs_restore(port, &old_ucr); +} +#endif + static struct uart_ops imx_pops = { .tx_empty = imx_tx_empty, .set_mctrl = imx_set_mctrl, @@ -1126,6 +1191,10 @@ static struct uart_ops imx_pops = { .request_port = imx_request_port, .config_port = imx_config_port, .verify_port = imx_verify_port, +#if defined(CONFIG_CONSOLE_POLL) + .poll_get_char = imx_poll_get_char, + .poll_put_char = imx_poll_put_char, +#endif }; static struct imx_port *imx_ports[UART_NR]; -- cgit v1.2.3 From 995234da19b927f42722d796e8270384f33be11c Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 23 Dec 2011 05:39:27 +0800 Subject: tty: serial: imx: move del_timer_sync() to avoid potential deadlock del_timer_sync() acquires its own lock and doesn't have to be nested within the spinlock of sport->port.lock in imx_set_termios(), which will cause potential deadlock. Fix this by moving it outside. Cc: Fabio Estevam Cc: Shawn Guo Cc: Sascha Hauer Cc: Greg Kroah-Hartman Signed-off-by: Eric Miao Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 6b98a524eca2..0b7fed746b27 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -932,6 +932,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, ucr2 |= UCR2_PROE; } + del_timer_sync(&sport->timer); + /* * Ask the core to calculate the divisor for us. */ @@ -962,8 +964,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, sport->port.ignore_status_mask |= URXD_OVRRUN; } - del_timer_sync(&sport->timer); - /* * Update the per-port timeout. */ -- cgit v1.2.3