From a4dc923640418120fe7bbdfac149397bc761a249 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 28 Sep 2014 20:10:17 +0200 Subject: goldfish: fix sparse warnings drivers/tty/goldfish.c:160:46: warning: Using plain integer as NULL pointer drivers/tty/goldfish.c:320:22: warning: Using plain integer as NULL pointer Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 09495f5..c24b963 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -157,7 +157,7 @@ static int goldfish_tty_console_setup(struct console *co, char *options) { if ((unsigned)co->index > goldfish_tty_line_count) return -ENODEV; - if (goldfish_ttys[co->index].base == 0) + if (!goldfish_ttys[co->index].base) return -ENODEV; return 0; } @@ -317,7 +317,7 @@ static int goldfish_tty_remove(struct platform_device *pdev) unregister_console(&qtty->console); tty_unregister_device(goldfish_tty_driver, pdev->id); iounmap(qtty->base); - qtty->base = 0; + qtty->base = NULL; free_irq(qtty->irq, pdev); goldfish_tty_current_line_count--; if (goldfish_tty_current_line_count == 0) -- cgit v0.10.2 From fda2b418a1945bebd5c8f670768fe75c515816ec Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 29 Oct 2014 11:43:25 +0300 Subject: goldfish: off by one in goldfish_tty_console_setup() The goldfish_ttys[] array has "goldfish_tty_line_count" number of elements. It's allocated in goldfish_tty_create_driver(). This test should be >= instead of >. Signed-off-by: Dan Carpenter Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index c24b963..967b2c2 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -155,7 +155,7 @@ static struct tty_driver *goldfish_tty_console_device(struct console *c, static int goldfish_tty_console_setup(struct console *co, char *options) { - if ((unsigned)co->index > goldfish_tty_line_count) + if ((unsigned)co->index >= goldfish_tty_line_count) return -ENODEV; if (!goldfish_ttys[co->index].base) return -ENODEV; -- cgit v0.10.2 From d4260b51699082c7dea257bea002d79394e876e0 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:19:47 -0400 Subject: serial: Fix upstat_t sparse warnings Commit 299245a145b2ad4cfb4c5432eb1264299f55e7e0, serial: core: Privatize modem status enable flags, introduced the upstat_t type and matching bit definitions. The purpose is to produce sparse warnings if the wrong bit definitions are used (by warning of implicit integer conversions). Fix implicit conversion to integer return type from uart_cts_enabled() and uart_dcd_enabled(). Fixes the following sparse warnings: drivers/tty/serial/serial_core.c:63:30: warning: incorrect type in return expression (different base types) drivers/tty/serial/serial_core.c:63:30: expected int drivers/tty/serial/serial_core.c:63:30: got restricted upstat_t include/linux/serial_core.h:364:30: warning: incorrect type in return expression (different base types) include/linux/serial_core.h:364:30: expected bool include/linux/serial_core.h:364:30: got restricted upstat_t include/linux/serial_core.h:364:30: warning: incorrect type in return expression (different base types) include/linux/serial_core.h:364:30: expected bool include/linux/serial_core.h:364:30: got restricted upstat_t Reported-by: Fengguang Wu Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index df3a8c7..9711037 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -61,7 +61,7 @@ static void uart_port_shutdown(struct tty_port *port); static int uart_dcd_enabled(struct uart_port *uport) { - return uport->status & UPSTAT_DCD_ENABLE; + return !!(uport->status & UPSTAT_DCD_ENABLE); } /* diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 21c2e05..bccf4ba 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -367,7 +367,7 @@ static inline int uart_tx_stopped(struct uart_port *port) static inline bool uart_cts_enabled(struct uart_port *uport) { - return uport->status & UPSTAT_CTS_ENABLE; + return !!(uport->status & UPSTAT_CTS_ENABLE); } /* -- cgit v0.10.2 From 91f189de4640a6531a00fbc6a6c6adc7804af1e9 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:19:48 -0400 Subject: serial: Fix sparse warnings in uart_throttle()/uart_unthrottle() The struct uart_port.flags field is type upf_t, as are the matching bit definitions. Change local mask variable to type upf_t. Fixes sparse warnings: drivers/tty/serial/serial_core.c:620:22: warning: invalid assignment: |= drivers/tty/serial/serial_core.c:620:22: left side has type unsigned int drivers/tty/serial/serial_core.c:620:22: right side has type restricted upf_t drivers/tty/serial/serial_core.c:622:22: warning: invalid assignment: |= drivers/tty/serial/serial_core.c:622:22: left side has type unsigned int drivers/tty/serial/serial_core.c:622:22: right side has type restricted upf_t drivers/tty/serial/serial_core.c:624:17: warning: restricted upf_t degrades to integer drivers/tty/serial/serial_core.c:626:22: warning: invalid assignment: &= drivers/tty/serial/serial_core.c:626:22: left side has type unsigned int drivers/tty/serial/serial_core.c:626:22: right side has type restricted upf_t drivers/tty/serial/serial_core.c:629:20: warning: restricted upf_t degrades to integer drivers/tty/serial/serial_core.c:632:20: warning: restricted upf_t degrades to integer drivers/tty/serial/serial_core.c:643:22: warning: invalid assignment: |= drivers/tty/serial/serial_core.c:643:22: left side has type unsigned int drivers/tty/serial/serial_core.c:643:22: right side has type restricted upf_t drivers/tty/serial/serial_core.c:645:22: warning: invalid assignment: |= drivers/tty/serial/serial_core.c:645:22: left side has type unsigned int drivers/tty/serial/serial_core.c:645:22: right side has type restricted upf_t drivers/tty/serial/serial_core.c:647:17: warning: restricted upf_t degrades to integer drivers/tty/serial/serial_core.c:649:22: warning: invalid assignment: &= drivers/tty/serial/serial_core.c:649:22: left side has type unsigned int drivers/tty/serial/serial_core.c:649:22: right side has type restricted upf_t drivers/tty/serial/serial_core.c:652:20: warning: restricted upf_t degrades to integer drivers/tty/serial/serial_core.c:655:20: warning: restricted upf_t degrades to integer Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 9711037..1166c52 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -618,7 +618,7 @@ static void uart_throttle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; - uint32_t mask = 0; + upf_t mask = 0; if (I_IXOFF(tty)) mask |= UPF_SOFT_FLOW; @@ -641,7 +641,7 @@ static void uart_unthrottle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; - uint32_t mask = 0; + upf_t mask = 0; if (I_IXOFF(tty)) mask |= UPF_SOFT_FLOW; -- cgit v0.10.2 From 369e2b84e4eed08e5368abc3bc4277d500a186ea Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:19:49 -0400 Subject: tty: Remove sparse lock annotations from tty_write_lock()/_unlock() sparse lock annotations cannot represent conditional acquire, such as mutex_lock_interruptible() or mutex_trylock(), and produce sparse warnings at _every_ correct call site. Remove lock annotations from tty_write_lock() and tty_write_unlock(). Fixes sparse warnings: drivers/tty/tty_io.c:1083:13: warning: context imbalance in 'tty_write_unlock' - wrong count at exit drivers/tty/tty_io.c:1090:12: warning: context imbalance in 'tty_write_lock' - wrong count at exit drivers/tty/tty_io.c:1211:17: warning: context imbalance in 'tty_write_message' - unexpected unlock drivers/tty/tty_io.c:1233:16: warning: context imbalance in 'tty_write' - different lock contexts for basic block drivers/tty/tty_io.c:1285:5: warning: context imbalance in 'tty_send_xchar' - different lock contexts for basic block drivers/tty/tty_io.c:2653:12: warning: context imbalance in 'send_break' - different lock contexts for basic block Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 16a2c02..c322e7a 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1024,14 +1024,12 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, } static void tty_write_unlock(struct tty_struct *tty) - __releases(&tty->atomic_write_lock) { mutex_unlock(&tty->atomic_write_lock); wake_up_interruptible_poll(&tty->write_wait, POLLOUT); } static int tty_write_lock(struct tty_struct *tty, int ndelay) - __acquires(&tty->atomic_write_lock) { if (!mutex_trylock(&tty->atomic_write_lock)) { if (ndelay) -- cgit v0.10.2 From 513e438581020334e0345561adeeeaefa36701be Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Wed, 15 Oct 2014 14:19:27 +0800 Subject: serial: of-serial: fix up PM ops on no_console_suspend and port type This patch fixes commit 2dea53bf57783f243c892e99c10c6921e956aa7e, "serial: of-serial: add PM suspend/resume support", which disables the uart clock on suspend, but also causes a hardware hang on register access if no_console_suspend command line option is used. Also, not every of_serial device is an 8250 port, so the serial8250 suspend/resume functions should only be applied to a real 8250 port. Signed-off-by: Jingchang Lu Tested-by: Joseph Lo Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 8bc2563..9c64ad2 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -9,6 +9,7 @@ * 2 of the License, or (at your option) any later version. * */ +#include #include #include #include @@ -241,13 +242,48 @@ static int of_platform_serial_remove(struct platform_device *ofdev) } #ifdef CONFIG_PM_SLEEP -static int of_serial_suspend(struct device *dev) +#ifdef CONFIG_SERIAL_8250 +static void of_serial_suspend_8250(struct of_serial_info *info) { - struct of_serial_info *info = dev_get_drvdata(dev); + struct uart_8250_port *port8250 = serial8250_get_port(info->line); + struct uart_port *port = &port8250->port; serial8250_suspend_port(info->line); - if (info->clk) + if (info->clk && (!uart_console(port) || console_suspend_enabled)) clk_disable_unprepare(info->clk); +} + +static void of_serial_resume_8250(struct of_serial_info *info) +{ + struct uart_8250_port *port8250 = serial8250_get_port(info->line); + struct uart_port *port = &port8250->port; + + if (info->clk && (!uart_console(port) || console_suspend_enabled)) + clk_prepare_enable(info->clk); + + serial8250_resume_port(info->line); +} +#else +static inline void of_serial_suspend_8250(struct of_serial_info *info) +{ +} + +static inline void of_serial_resume_8250(struct of_serial_info *info) +{ +} +#endif + +static int of_serial_suspend(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + switch(info->type) { + case PORT_8250 ... PORT_MAX_8250: + of_serial_suspend_8250(info); + break; + default: + break; + } return 0; } @@ -256,10 +292,13 @@ static int of_serial_resume(struct device *dev) { struct of_serial_info *info = dev_get_drvdata(dev); - if (info->clk) - clk_prepare_enable(info->clk); - - serial8250_resume_port(info->line); + switch(info->type) { + case PORT_8250 ... PORT_MAX_8250: + of_serial_resume_8250(info); + break; + default: + break; + } return 0; } -- cgit v0.10.2 From edf4edacb331caf326a2e0f15d81c549bd9bede7 Mon Sep 17 00:00:00 2001 From: Sudhir Sreedharan Date: Fri, 17 Oct 2014 18:09:18 +0530 Subject: tty: serial: 8250_core: restore the LCR register in set_sleep In ST16650V2 based serial uarts, while initalizing the PM state, LCR registers are being initialized to 0 in serial8250_set_sleep(). If console port is already initialized and being used, this will throws garbage in the console. Signed-off-by: Sudhir Sreedharan Reviewed-by: Peter Hurley Tested-by: Kevin Hilman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index ca5cfdc..b170487 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -595,6 +595,7 @@ static void serial8250_rpm_put_tx(struct uart_8250_port *p) */ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) { + unsigned char lcr = 0, efr = 0; /* * Exar UARTs have a SLEEP register that enables or disables * each UART to enter sleep mode separately. On the XR17V35x the @@ -611,6 +612,8 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) if (p->capabilities & UART_CAP_SLEEP) { if (p->capabilities & UART_CAP_EFR) { + lcr = serial_in(p, UART_LCR); + efr = serial_in(p, UART_EFR); serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(p, UART_EFR, UART_EFR_ECB); serial_out(p, UART_LCR, 0); @@ -618,8 +621,8 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) serial_out(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); if (p->capabilities & UART_CAP_EFR) { serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(p, UART_EFR, 0); - serial_out(p, UART_LCR, 0); + serial_out(p, UART_EFR, efr); + serial_out(p, UART_LCR, lcr); } } out: -- cgit v0.10.2 From 8f166e00196fcb16364c9c39dab6fe7b72e63f18 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:59:41 -0400 Subject: tty: Remove tty_pair_get_tty()/tty_pair_get_pty() api tty_pair_get_pty() has no in-tree users and tty_pair_get_tty() has only one file-local user. Remove the external declarations, the export declarations, and declare tty_pair_get_tty() static. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index c322e7a..d3e7133 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2703,23 +2703,17 @@ static int tty_tiocgicount(struct tty_struct *tty, void __user *arg) return 0; } -struct tty_struct *tty_pair_get_tty(struct tty_struct *tty) +/* + * if pty, return the slave side (real_tty) + * otherwise, return self + */ +static struct tty_struct *tty_pair_get_tty(struct tty_struct *tty) { if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) tty = tty->link; return tty; } -EXPORT_SYMBOL(tty_pair_get_tty); - -struct tty_struct *tty_pair_get_pty(struct tty_struct *tty) -{ - if (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER) - return tty; - return tty->link; -} -EXPORT_SYMBOL(tty_pair_get_pty); /* * Split this up, as gcc can choke on it otherwise.. diff --git a/include/linux/tty.h b/include/linux/tty.h index 5171ef8..b36b0b4 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -498,9 +498,6 @@ extern int tty_init_termios(struct tty_struct *tty); extern int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty); -extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty); -extern struct tty_struct *tty_pair_get_pty(struct tty_struct *tty); - extern struct mutex tty_mutex; extern spinlock_t tty_files_lock; -- cgit v0.10.2 From 11d9befda294d8f40fd6a3d967722cf0ff0a0f5a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:59:42 -0400 Subject: tty: Reorder proc_set_tty() and related fns Move the controlling tty-related functions and remove forward declarations for __proc_set_tty() and proc_set_tty(). Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d3e7133..4966575 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -153,8 +153,6 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, static int __tty_fasync(int fd, struct file *filp, int on); static int tty_fasync(int fd, struct file *filp, int on); static void release_tty(struct tty_struct *tty, int idx); -static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); -static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); /** * free_tty_struct - free a disused tty @@ -492,6 +490,68 @@ static const struct file_operations hung_up_tty_fops = { static DEFINE_SPINLOCK(redirect_lock); static struct file *redirect; + +void proc_clear_tty(struct task_struct *p) +{ + unsigned long flags; + struct tty_struct *tty; + spin_lock_irqsave(&p->sighand->siglock, flags); + tty = p->signal->tty; + p->signal->tty = NULL; + spin_unlock_irqrestore(&p->sighand->siglock, flags); + tty_kref_put(tty); +} + +/* Called under the sighand lock */ + +static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) +{ + if (tty) { + unsigned long flags; + /* We should not have a session or pgrp to put here but.... */ + spin_lock_irqsave(&tty->ctrl_lock, flags); + put_pid(tty->session); + put_pid(tty->pgrp); + tty->pgrp = get_pid(task_pgrp(tsk)); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + tty->session = get_pid(task_session(tsk)); + if (tsk->signal->tty) { + printk(KERN_DEBUG "tty not NULL!!\n"); + tty_kref_put(tsk->signal->tty); + } + } + put_pid(tsk->signal->tty_old_pgrp); + tsk->signal->tty = tty_kref_get(tty); + tsk->signal->tty_old_pgrp = NULL; +} + +static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) +{ + spin_lock_irq(&tsk->sighand->siglock); + __proc_set_tty(tsk, tty); + spin_unlock_irq(&tsk->sighand->siglock); +} + +struct tty_struct *get_current_tty(void) +{ + struct tty_struct *tty; + unsigned long flags; + + spin_lock_irqsave(¤t->sighand->siglock, flags); + tty = tty_kref_get(current->signal->tty); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); + return tty; +} +EXPORT_SYMBOL_GPL(get_current_tty); + +static void session_clear_tty(struct pid *session) +{ + struct task_struct *p; + do_each_pid_task(session, PIDTYPE_SID, p) { + proc_clear_tty(p); + } while_each_pid_task(session, PIDTYPE_SID, p); +} + /** * tty_wakeup - request more data * @tty: terminal @@ -792,14 +852,6 @@ int tty_hung_up_p(struct file *filp) EXPORT_SYMBOL(tty_hung_up_p); -static void session_clear_tty(struct pid *session) -{ - struct task_struct *p; - do_each_pid_task(session, PIDTYPE_SID, p) { - proc_clear_tty(p); - } while_each_pid_task(session, PIDTYPE_SID, p); -} - /** * disassociate_ctty - disconnect controlling tty * @on_exit: true if exiting so need to "hang up" the session @@ -3426,59 +3478,6 @@ dev_t tty_devnum(struct tty_struct *tty) } EXPORT_SYMBOL(tty_devnum); -void proc_clear_tty(struct task_struct *p) -{ - unsigned long flags; - struct tty_struct *tty; - spin_lock_irqsave(&p->sighand->siglock, flags); - tty = p->signal->tty; - p->signal->tty = NULL; - spin_unlock_irqrestore(&p->sighand->siglock, flags); - tty_kref_put(tty); -} - -/* Called under the sighand lock */ - -static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) -{ - if (tty) { - unsigned long flags; - /* We should not have a session or pgrp to put here but.... */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - put_pid(tty->session); - put_pid(tty->pgrp); - tty->pgrp = get_pid(task_pgrp(tsk)); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - tty->session = get_pid(task_session(tsk)); - if (tsk->signal->tty) { - printk(KERN_DEBUG "tty not NULL!!\n"); - tty_kref_put(tsk->signal->tty); - } - } - put_pid(tsk->signal->tty_old_pgrp); - tsk->signal->tty = tty_kref_get(tty); - tsk->signal->tty_old_pgrp = NULL; -} - -static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) -{ - spin_lock_irq(&tsk->sighand->siglock); - __proc_set_tty(tsk, tty); - spin_unlock_irq(&tsk->sighand->siglock); -} - -struct tty_struct *get_current_tty(void) -{ - struct tty_struct *tty; - unsigned long flags; - - spin_lock_irqsave(¤t->sighand->siglock, flags); - tty = tty_kref_get(current->signal->tty); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - return tty; -} -EXPORT_SYMBOL_GPL(get_current_tty); - void tty_default_fops(struct file_operations *fops) { *fops = tty_fops; -- cgit v0.10.2 From bce65f18316cc6dbf858b1577a36956d30caa315 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:59:43 -0400 Subject: tty: Remove tsk parameter from proc_set_tty() Only the current task itself can set its controlling tty (other than before the task has been forked). Equivalent to existing usage. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 4966575..3d71642 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -504,7 +504,7 @@ void proc_clear_tty(struct task_struct *p) /* Called under the sighand lock */ -static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) +static void __proc_set_tty(struct tty_struct *tty) { if (tty) { unsigned long flags; @@ -512,24 +512,24 @@ static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) spin_lock_irqsave(&tty->ctrl_lock, flags); put_pid(tty->session); put_pid(tty->pgrp); - tty->pgrp = get_pid(task_pgrp(tsk)); + tty->pgrp = get_pid(task_pgrp(current)); spin_unlock_irqrestore(&tty->ctrl_lock, flags); - tty->session = get_pid(task_session(tsk)); - if (tsk->signal->tty) { + tty->session = get_pid(task_session(current)); + if (current->signal->tty) { printk(KERN_DEBUG "tty not NULL!!\n"); - tty_kref_put(tsk->signal->tty); + tty_kref_put(current->signal->tty); } } - put_pid(tsk->signal->tty_old_pgrp); - tsk->signal->tty = tty_kref_get(tty); - tsk->signal->tty_old_pgrp = NULL; + put_pid(current->signal->tty_old_pgrp); + current->signal->tty = tty_kref_get(tty); + current->signal->tty_old_pgrp = NULL; } -static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) +static void proc_set_tty(struct tty_struct *tty) { - spin_lock_irq(&tsk->sighand->siglock); - __proc_set_tty(tsk, tty); - spin_unlock_irq(&tsk->sighand->siglock); + spin_lock_irq(¤t->sighand->siglock); + __proc_set_tty(tty); + spin_unlock_irq(¤t->sighand->siglock); } struct tty_struct *get_current_tty(void) @@ -2156,7 +2156,7 @@ retry_open: current->signal->leader && !current->signal->tty && tty->session == NULL) - __proc_set_tty(current, tty); + __proc_set_tty(tty); spin_unlock_irq(¤t->sighand->siglock); tty_unlock(tty); mutex_unlock(&tty_mutex); @@ -2482,7 +2482,7 @@ static int tiocsctty(struct tty_struct *tty, int arg) goto unlock; } } - proc_set_tty(current, tty); + proc_set_tty(tty); unlock: mutex_unlock(&tty_mutex); return ret; -- cgit v0.10.2 From 8a8a55105dd2857743dc4f7097f28a6cb2e696ee Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:59:44 -0400 Subject: uml: Fix unsafe pid reference to foreground process group Although the tty core maintains a pid reference for the foreground process group, if the foreground process group is changed that pid reference is dropped. Thus, the pid reference used for signalling could become stale. Safely obtain a pid reference to the foreground process group and release the reference after signalling is complete. cc: Jeff Dike Acked-by: Richard Weinberger Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index 8035145..6208702 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -632,6 +632,7 @@ static irqreturn_t winch_interrupt(int irq, void *data) int fd = winch->fd; int err; char c; + struct pid *pgrp; if (fd != -1) { err = generic_read(fd, &c, NULL); @@ -657,7 +658,10 @@ static irqreturn_t winch_interrupt(int irq, void *data) if (line != NULL) { chan_window_size(line, &tty->winsize.ws_row, &tty->winsize.ws_col); - kill_pgrp(tty->pgrp, SIGWINCH, 1); + pgrp = tty_get_pgrp(tty); + if (pgrp) + kill_pgrp(pgrp, SIGWINCH, 1); + put_pid(pgrp); } tty_kref_put(tty); } -- cgit v0.10.2 From 5b239542207d83687489a22e6dd93ffef1ad9eb9 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:59:45 -0400 Subject: tty: Replace open-coded tty_get_pgrp() Replace open-coded instances of tty_get_pgrp(). Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 7c4447a..4c0218d 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -207,15 +207,12 @@ static int pty_get_pktmode(struct tty_struct *tty, int __user *arg) /* Send a signal to the slave */ static int pty_signal(struct tty_struct *tty, int sig) { - unsigned long flags; struct pid *pgrp; if (tty->link) { - spin_lock_irqsave(&tty->link->ctrl_lock, flags); - pgrp = get_pid(tty->link->pgrp); - spin_unlock_irqrestore(&tty->link->ctrl_lock, flags); - - kill_pgrp(pgrp, sig, 1); + pgrp = tty_get_pgrp(tty->link); + if (pgrp) + kill_pgrp(pgrp, sig, 1); put_pid(pgrp); } return 0; @@ -278,7 +275,6 @@ static void pty_set_termios(struct tty_struct *tty, static int pty_resize(struct tty_struct *tty, struct winsize *ws) { struct pid *pgrp, *rpgrp; - unsigned long flags; struct tty_struct *pty = tty->link; /* For a PTY we need to lock the tty side */ @@ -286,17 +282,9 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws) if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; - /* Get the PID values and reference them so we can - avoid holding the tty ctrl lock while sending signals. - We need to lock these individually however. */ - - spin_lock_irqsave(&tty->ctrl_lock, flags); - pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - - spin_lock_irqsave(&pty->ctrl_lock, flags); - rpgrp = get_pid(pty->pgrp); - spin_unlock_irqrestore(&pty->ctrl_lock, flags); + /* Signal the foreground process group of both ptys */ + pgrp = tty_get_pgrp(tty); + rpgrp = tty_get_pgrp(pty); if (pgrp) kill_pgrp(pgrp, SIGWINCH, 1); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 3d71642..6bf5f44 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2324,18 +2324,14 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) int tty_do_resize(struct tty_struct *tty, struct winsize *ws) { struct pid *pgrp; - unsigned long flags; /* Lock the tty */ mutex_lock(&tty->winsize_mutex); if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; - /* Get the PID values and reference them so we can - avoid holding the tty ctrl lock while sending signals */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + /* Signal the foreground process group */ + pgrp = tty_get_pgrp(tty); if (pgrp) kill_pgrp(pgrp, SIGWINCH, 1); put_pid(pgrp); -- cgit v0.10.2 From ae28fa721683f29105d6894c6c1482bf5a470d3b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:59:46 -0400 Subject: tty: Remove !tty condition from __proc_set_tty() The tty parameter to __proc_set_tty() cannot be NULL; all call sites have already dereferenced tty. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 6bf5f44..b645519 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -506,19 +506,18 @@ void proc_clear_tty(struct task_struct *p) static void __proc_set_tty(struct tty_struct *tty) { - if (tty) { - unsigned long flags; - /* We should not have a session or pgrp to put here but.... */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - put_pid(tty->session); - put_pid(tty->pgrp); - tty->pgrp = get_pid(task_pgrp(current)); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - tty->session = get_pid(task_session(current)); - if (current->signal->tty) { - printk(KERN_DEBUG "tty not NULL!!\n"); - tty_kref_put(current->signal->tty); - } + unsigned long flags; + + /* We should not have a session or pgrp to put here but.... */ + spin_lock_irqsave(&tty->ctrl_lock, flags); + put_pid(tty->session); + put_pid(tty->pgrp); + tty->pgrp = get_pid(task_pgrp(current)); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + tty->session = get_pid(task_session(current)); + if (current->signal->tty) { + printk(KERN_DEBUG "tty not NULL!!\n"); + tty_kref_put(current->signal->tty); } put_pid(current->signal->tty_old_pgrp); current->signal->tty = tty_kref_get(tty); -- cgit v0.10.2 From 2c411c11020ff356748268ca9cae4c1b4c410f00 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:59:47 -0400 Subject: tty: Fix multiple races when setting the controlling terminal Claim a read lock on the tasklist_lock while setting the controlling terminal for the session leader. This fixes multiple races: 1. task_pgrp() and task_session() cannot be safely dereferenced, such as passing to get_pid(), without holding either rcu_read_lock() or tasklist_lock 2. setsid() unwisely allows any thread in the thread group to make the thread group leader the session leader; this makes the unlocked reads of ->signal->leader and signal->tty potentially unordered, stale or even have spurious values. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index b645519..114854c 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -502,8 +502,15 @@ void proc_clear_tty(struct task_struct *p) tty_kref_put(tty); } -/* Called under the sighand lock */ - +/** + * proc_set_tty - set the controlling terminal + * + * Only callable by the session leader and only if it does not already have + * a controlling terminal. + * + * Caller must hold: a readlock on tasklist_lock + * sighand lock + */ static void __proc_set_tty(struct tty_struct *tty) { unsigned long flags; @@ -2150,6 +2157,7 @@ retry_open: mutex_lock(&tty_mutex); tty_lock(tty); + read_lock(&tasklist_lock); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && @@ -2157,6 +2165,7 @@ retry_open: tty->session == NULL) __proc_set_tty(tty); spin_unlock_irq(¤t->sighand->siglock); + read_unlock(&tasklist_lock); tty_unlock(tty); mutex_unlock(&tty_mutex); return 0; @@ -2447,10 +2456,13 @@ static int fionbio(struct file *file, int __user *p) static int tiocsctty(struct tty_struct *tty, int arg) { int ret = 0; - if (current->signal->leader && (task_session(current) == tty->session)) - return ret; mutex_lock(&tty_mutex); + read_lock(&tasklist_lock); + + if (current->signal->leader && (task_session(current) == tty->session)) + goto unlock; + /* * The process must be a session leader and * not have a controlling tty already. @@ -2469,9 +2481,7 @@ static int tiocsctty(struct tty_struct *tty, int arg) /* * Steal it away */ - read_lock(&tasklist_lock); session_clear_tty(tty->session); - read_unlock(&tasklist_lock); } else { ret = -EPERM; goto unlock; @@ -2479,6 +2489,7 @@ static int tiocsctty(struct tty_struct *tty, int arg) } proc_set_tty(tty); unlock: + read_unlock(&tasklist_lock); mutex_unlock(&tty_mutex); return ret; } -- cgit v0.10.2 From e1c2296c3485158304bfad5a80e89078463d70c8 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:59:48 -0400 Subject: tty: Move session_of_pgrp() and make static tiocspgrp() is the lone caller of session_of_pgrp(); relocate and limit to file scope. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 114854c..ae8f53c 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2515,6 +2515,27 @@ struct pid *tty_get_pgrp(struct tty_struct *tty) } EXPORT_SYMBOL_GPL(tty_get_pgrp); +/* + * This checks not only the pgrp, but falls back on the pid if no + * satisfactory pgrp is found. I dunno - gdb doesn't work correctly + * without this... + * + * The caller must hold rcu lock or the tasklist lock. + */ +static struct pid *session_of_pgrp(struct pid *pgrp) +{ + struct task_struct *p; + struct pid *sid = NULL; + + p = pid_task(pgrp, PIDTYPE_PGID); + if (p == NULL) + p = pid_task(pgrp, PIDTYPE_PID); + if (p != NULL) + sid = task_session(p); + + return sid; +} + /** * tiocgpgrp - get process group * @tty: tty passed by user diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 3d770f55..01bc530 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -411,9 +411,6 @@ extern int __kernel_text_address(unsigned long addr); extern int kernel_text_address(unsigned long addr); extern int func_ptr_is_kernel_text(void *ptr); -struct pid; -extern struct pid *session_of_pgrp(struct pid *pgrp); - unsigned long int_sqrt(unsigned long); extern void bust_spinlocks(int yes); diff --git a/kernel/exit.c b/kernel/exit.c index 5d30019..6a3e2e5 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -215,27 +215,6 @@ repeat: } /* - * This checks not only the pgrp, but falls back on the pid if no - * satisfactory pgrp is found. I dunno - gdb doesn't work correctly - * without this... - * - * The caller must hold rcu lock or the tasklist lock. - */ -struct pid *session_of_pgrp(struct pid *pgrp) -{ - struct task_struct *p; - struct pid *sid = NULL; - - p = pid_task(pgrp, PIDTYPE_PGID); - if (p == NULL) - p = pid_task(pgrp, PIDTYPE_PID); - if (p != NULL) - sid = task_session(p); - - return sid; -} - -/* * Determine if a process group is "orphaned", according to the POSIX * definition in 2.2.2.52. Orphaned process groups are not to be affected * by terminal-generated stop signals. Newly orphaned process groups are -- cgit v0.10.2 From e218eb32f508c828dc87d0d724c70e2cf9b7866e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:59:49 -0400 Subject: tty: Serialize proc_set_tty() with tty_lock Setting the controlling terminal for a session occurs with either the first open of a non-pty master tty or with ioctl(TIOCSCTTY). Since only the session leader can set the controlling terminal for a session (and the session leader cannot change), it is not necessary to prevent a process from attempting to set different ttys as the controlling terminal concurrently. So it's only necessary to prevent the same tty from becoming the controlling terminal for different session leaders. The tty_lock() is sufficient to prevent concurrent proc_set_tty() for the same tty. Remove the tty_mutex lock region; add tty_lock() to tiocsctty(). While this may appear to allow a race condition between opening the controlling tty via tty_open_current_tty() and stealing the controlling tty via ioctl(TIOCSCTTY, 1), that race condition already existed. Even if the tty_mutex prevented stealing the controlling tty while tty_open_current_tty() returned the original controlling tty, it cannot prevent stealing the controlling tty before tty_open() returns. Thus, tty_open() could already return a no-longer-controlling tty when opening /dev/tty. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index ae8f53c..07275d0 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -508,7 +508,8 @@ void proc_clear_tty(struct task_struct *p) * Only callable by the session leader and only if it does not already have * a controlling terminal. * - * Caller must hold: a readlock on tasklist_lock + * Caller must hold: tty_lock() + * a readlock on tasklist_lock * sighand lock */ static void __proc_set_tty(struct tty_struct *tty) @@ -2152,11 +2153,8 @@ retry_open: goto retry_open; } clear_bit(TTY_HUPPED, &tty->flags); - tty_unlock(tty); - mutex_lock(&tty_mutex); - tty_lock(tty); read_lock(&tasklist_lock); spin_lock_irq(¤t->sighand->siglock); if (!noctty && @@ -2167,7 +2165,6 @@ retry_open: spin_unlock_irq(¤t->sighand->siglock); read_unlock(&tasklist_lock); tty_unlock(tty); - mutex_unlock(&tty_mutex); return 0; err_unlock: mutex_unlock(&tty_mutex); @@ -2448,7 +2445,7 @@ static int fionbio(struct file *file, int __user *p) * leader to set this tty as the controlling tty for the session. * * Locking: - * Takes tty_mutex() to protect tty instance + * Takes tty_lock() to serialize proc_set_tty() for this tty * Takes tasklist_lock internally to walk sessions * Takes ->siglock() when updating signal->tty */ @@ -2457,7 +2454,7 @@ static int tiocsctty(struct tty_struct *tty, int arg) { int ret = 0; - mutex_lock(&tty_mutex); + tty_lock(tty); read_lock(&tasklist_lock); if (current->signal->leader && (task_session(current) == tty->session)) @@ -2490,7 +2487,7 @@ static int tiocsctty(struct tty_struct *tty, int arg) proc_set_tty(tty); unlock: read_unlock(&tasklist_lock); - mutex_unlock(&tty_mutex); + tty_unlock(tty); return ret; } -- cgit v0.10.2 From a361858da327263e04dc521ca39091d3119ccff8 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 14:59:50 -0400 Subject: tty: Update code comment in __proc_set_tty() The session and foreground process group pid references will be non-NULL if tiocsctty() is stealing the controlling tty from another session (ie., arg == 1 in tiocsctty()). Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 07275d0..9d1e247 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -516,8 +516,11 @@ static void __proc_set_tty(struct tty_struct *tty) { unsigned long flags; - /* We should not have a session or pgrp to put here but.... */ spin_lock_irqsave(&tty->ctrl_lock, flags); + /* + * The session and fg pgrp references will be non-NULL if + * tiocsctty() is stealing the controlling tty + */ put_pid(tty->session); put_pid(tty->pgrp); tty->pgrp = get_pid(task_pgrp(current)); -- cgit v0.10.2 From 6460fbbf476ad8d4f4196534c00019975b33e1db Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 15:33:22 -0400 Subject: tty: WARN for attempted set_termios() of pty master The pty master's termios should never be set; currently, all code paths which call the driver's set_termios() method ensure that the pty slave's termios is being set. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 4c0218d..6c76025 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -477,7 +477,6 @@ static const struct tty_operations master_pty_ops_bsd = { .flush_buffer = pty_flush_buffer, .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, - .set_termios = pty_set_termios, .ioctl = pty_bsd_ioctl, .cleanup = pty_cleanup, .resize = pty_resize, @@ -654,7 +653,6 @@ static const struct tty_operations ptm_unix98_ops = { .flush_buffer = pty_flush_buffer, .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, - .set_termios = pty_set_termios, .ioctl = pty_unix98_ioctl, .resize = pty_resize, .shutdown = pty_unix98_shutdown, diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 62380cc..ab4562c 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -528,6 +528,8 @@ EXPORT_SYMBOL(tty_termios_hw_change); * is a bit of layering violation here with n_tty in terms of the * internal knowledge of this function. * + * A master pty's termios should never be set. + * * Locking: termios_rwsem */ @@ -537,6 +539,8 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) struct tty_ldisc *ld; unsigned long flags; + WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY && + tty->driver->subtype == PTY_TYPE_MASTER); /* * Perform the actual termios internal changes under lock. */ -- cgit v0.10.2 From dbfcd851a9ee0bf6952e538be75230b7c071dafb Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 15:33:23 -0400 Subject: tty: Move pty-specific set_termios() handling to pty driver Packet mode is unique to the pty driver; move the packet mode state change code from the generic tty ioctl handler to the pty driver. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 6c76025..7f612c5 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -259,6 +259,34 @@ out: static void pty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { + unsigned long flags; + + /* See if packet mode change of state. */ + if (tty->link && tty->link->packet) { + int extproc = (old_termios->c_lflag & EXTPROC) | + (tty->termios.c_lflag & EXTPROC); + int old_flow = ((old_termios->c_iflag & IXON) && + (old_termios->c_cc[VSTOP] == '\023') && + (old_termios->c_cc[VSTART] == '\021')); + int new_flow = (I_IXON(tty) && + STOP_CHAR(tty) == '\023' && + START_CHAR(tty) == '\021'); + if ((old_flow != new_flow) || extproc) { + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (old_flow != new_flow) { + tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); + if (new_flow) + tty->ctrl_status |= TIOCPKT_DOSTOP; + else + tty->ctrl_status |= TIOCPKT_NOSTOP; + } + if (extproc) + tty->ctrl_status |= TIOCPKT_IOCTL; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + wake_up_interruptible(&tty->link->read_wait); + } + } + tty->termios.c_cflag &= ~(CSIZE | PARENB); tty->termios.c_cflag |= (CS8 | CREAD); } diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index ab4562c..24a1360 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -524,10 +524,7 @@ EXPORT_SYMBOL(tty_termios_hw_change); * @tty: tty to update * @new_termios: desired new value * - * Perform updates to the termios values set on this terminal. There - * is a bit of layering violation here with n_tty in terms of the - * internal knowledge of this function. - * + * Perform updates to the termios values set on this terminal. * A master pty's termios should never be set. * * Locking: termios_rwsem @@ -537,7 +534,6 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) { struct ktermios old_termios; struct tty_ldisc *ld; - unsigned long flags; WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); @@ -553,32 +549,6 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) tty->termios = *new_termios; unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked); - /* See if packet mode change of state. */ - if (tty->link && tty->link->packet) { - int extproc = (old_termios.c_lflag & EXTPROC) | - (tty->termios.c_lflag & EXTPROC); - int old_flow = ((old_termios.c_iflag & IXON) && - (old_termios.c_cc[VSTOP] == '\023') && - (old_termios.c_cc[VSTART] == '\021')); - int new_flow = (I_IXON(tty) && - STOP_CHAR(tty) == '\023' && - START_CHAR(tty) == '\021'); - if ((old_flow != new_flow) || extproc) { - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (old_flow != new_flow) { - tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); - if (new_flow) - tty->ctrl_status |= TIOCPKT_DOSTOP; - else - tty->ctrl_status |= TIOCPKT_NOSTOP; - } - if (extproc) - tty->ctrl_status |= TIOCPKT_IOCTL; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - wake_up_interruptible(&tty->link->read_wait); - } - } - if (tty->ops->set_termios) (*tty->ops->set_termios)(tty, &old_termios); else -- cgit v0.10.2 From 4d8c1dff6a8666a2e18a7d5954c2156be0d5b152 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 15:33:24 -0400 Subject: pty: Use spin_lock_irq() for pty_set_termios() The tty driver's set_termios() method is called with interrupts enabled; there is no need to save and restore the local interrupt state. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 7f612c5..3943747 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -259,8 +259,6 @@ out: static void pty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { - unsigned long flags; - /* See if packet mode change of state. */ if (tty->link && tty->link->packet) { int extproc = (old_termios->c_lflag & EXTPROC) | @@ -272,7 +270,7 @@ static void pty_set_termios(struct tty_struct *tty, STOP_CHAR(tty) == '\023' && START_CHAR(tty) == '\021'); if ((old_flow != new_flow) || extproc) { - spin_lock_irqsave(&tty->ctrl_lock, flags); + spin_lock_irq(&tty->ctrl_lock); if (old_flow != new_flow) { tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); if (new_flow) @@ -282,7 +280,7 @@ static void pty_set_termios(struct tty_struct *tty, } if (extproc) tty->ctrl_status |= TIOCPKT_IOCTL; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + spin_unlock_irq(&tty->ctrl_lock); wake_up_interruptible(&tty->link->read_wait); } } -- cgit v0.10.2 From 6054c16e80318d32ee084a0f5620a863ae8cea33 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 15:33:25 -0400 Subject: tty: Use spin_lock_irq() for ctrl_lock when interrupts enabled Interrupts are enabled in the n_tty_read() loop, ioctl(TIOCPKT) and pty driver flush_buffer() routine; no need to save and restore local interrupt state. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 89c4cee..d521058 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2128,7 +2128,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, int minimum, time; ssize_t retval = 0; long timeout; - unsigned long flags; int packet; c = job_control(tty, file); @@ -2174,10 +2173,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char cs; if (b != buf) break; - spin_lock_irqsave(&tty->link->ctrl_lock, flags); + spin_lock_irq(&tty->link->ctrl_lock); cs = tty->link->ctrl_status; tty->link->ctrl_status = 0; - spin_unlock_irqrestore(&tty->link->ctrl_lock, flags); + spin_unlock_irq(&tty->link->ctrl_lock); if (tty_put_user(tty, cs, b++)) { retval = -EFAULT; b--; diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 3943747..29b5eeb 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -178,13 +178,12 @@ static int pty_get_lock(struct tty_struct *tty, int __user *arg) /* Set the packet mode on a pty */ static int pty_set_pktmode(struct tty_struct *tty, int __user *arg) { - unsigned long flags; int pktmode; if (get_user(pktmode, arg)) return -EFAULT; - spin_lock_irqsave(&tty->ctrl_lock, flags); + spin_lock_irq(&tty->ctrl_lock); if (pktmode) { if (!tty->packet) { tty->packet = 1; @@ -192,7 +191,7 @@ static int pty_set_pktmode(struct tty_struct *tty, int __user *arg) } } else tty->packet = 0; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + spin_unlock_irq(&tty->ctrl_lock); return 0; } @@ -221,16 +220,15 @@ static int pty_signal(struct tty_struct *tty, int sig) static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; - unsigned long flags; if (!to) return; /* tty_buffer_flush(to); FIXME */ if (to->packet) { - spin_lock_irqsave(&tty->ctrl_lock, flags); + spin_lock_irq(&tty->ctrl_lock); tty->ctrl_status |= TIOCPKT_FLUSHWRITE; wake_up_interruptible(&to->read_wait); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + spin_unlock_irq(&tty->ctrl_lock); } } -- cgit v0.10.2 From 54e8e5fcaae109f0303f52efb24f29bfac79ca86 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 15:33:26 -0400 Subject: pty: Don't claim slave's ctrl_lock for master's packet mode The slave's ctrl_lock serializes updates to the ctrl_status field only, whereas the master's ctrl_lock serializes updates to the packet mode enable (ie., the master does not have ctrl_status and the slave does not have packet mode). Thus, claiming the slave's ctrl_lock to access ->packet is useless. Unlocked reads of ->packet are already smp-safe. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index d521058..cd725cc 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -351,13 +351,13 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty) { unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->link->packet) { + spin_lock_irqsave(&tty->ctrl_lock, flags); tty->ctrl_status |= TIOCPKT_FLUSHREAD; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (waitqueue_active(&tty->link->read_wait)) wake_up_interruptible(&tty->link->read_wait); } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); } /** diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 29b5eeb..e554393 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -339,26 +339,26 @@ static void pty_start(struct tty_struct *tty) { unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->link && tty->link->packet) { + spin_lock_irqsave(&tty->ctrl_lock, flags); tty->ctrl_status &= ~TIOCPKT_STOP; tty->ctrl_status |= TIOCPKT_START; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); } static void pty_stop(struct tty_struct *tty) { unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->link && tty->link->packet) { + spin_lock_irqsave(&tty->ctrl_lock, flags); tty->ctrl_status &= ~TIOCPKT_START; tty->ctrl_status |= TIOCPKT_STOP; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); } /** -- cgit v0.10.2 From 2622d73e51acfe1dbeb21bf2299066bdead85163 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 15:33:27 -0400 Subject: pty: Fix packet mode setting race Because pty_set_pktmode() does not claim the slave's ctrl_lock to clear ->ctrl_status (to avoid unnecessary lock nesting), pty_set_pktmode() may accidentally erase new ->ctrl_status updates. For example, CPU 0 | CPU 1 pty_set_pktmode() | pty_start() spin_lock(master's ctrl_lock) | tty->packet = 1 | | if (tty->link->packet) | spin_lock(slave's ctrl_lock) | tty->ctrl_status = TIOCPKT_START tty->link->ctrl_status = 0 | Ensure the clear of ->ctrl_status occurs before packet mode is set (and observable on another cpu). Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index e554393..bcec4c7 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -186,8 +186,9 @@ static int pty_set_pktmode(struct tty_struct *tty, int __user *arg) spin_lock_irq(&tty->ctrl_lock); if (pktmode) { if (!tty->packet) { - tty->packet = 1; tty->link->ctrl_status = 0; + smp_mb(); + tty->packet = 1; } } else tty->packet = 0; -- cgit v0.10.2 From 4ed60fc257185dbb0daea7cd3e4289d1658b11f6 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 15:33:28 -0400 Subject: pty: Hold ctrl_lock for packet mode updates Updates to the packet mode enable require holding the ctrl_lock; the serialization prevents corruption of adjacent fields. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index bcec4c7..7a1a538 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -47,7 +47,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp) set_bit(TTY_IO_ERROR, &tty->flags); wake_up_interruptible(&tty->read_wait); wake_up_interruptible(&tty->write_wait); + spin_lock_irq(&tty->ctrl_lock); tty->packet = 0; + spin_unlock_irq(&tty->ctrl_lock); /* Review - krefs on tty_link ?? */ if (!tty->link) return; -- cgit v0.10.2 From 1aa1bf1115272d22fc4c758ee8769b73d8079a79 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 15:33:29 -0400 Subject: tty: Fix missed wakeup from packet mode status update The pty master read() can miss the wake up for a packet mode status change. For example, CPU 0 | CPU 1 n_tty_read() | n_tty_packet_mode_flush() ... | . if (packet & link->ctrl_status) { | . /* no new ctrl_status ATM */ | . | spin_lock | ctrl_status |= TIOCPKT_FLUSHREAD | spin_unlock | wake_up(link->read_wait) } | set_current_state(TASK_INTERRUPTIBLE) | ... | The pty master read() will now sleep (assuming there is no input) having missed the read_wait wakeup. Set the task state before the condition test. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index cd725cc..7f134b9 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2168,6 +2168,11 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, add_wait_queue(&tty->read_wait, &wait); while (nr) { + /* This statement must be first before checking for input + so that any interrupt will set the state back to + TASK_RUNNING. */ + set_current_state(TASK_INTERRUPTIBLE); + /* First test for status change. */ if (packet && tty->link->ctrl_status) { unsigned char cs; @@ -2185,10 +2190,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, nr--; break; } - /* This statement must be first before checking for input - so that any interrupt will set the state back to - TASK_RUNNING. */ - set_current_state(TASK_INTERRUPTIBLE); if (((minimum - (b - buf)) < ldata->minimum_to_wake) && ((minimum - (b - buf)) >= 1)) -- cgit v0.10.2 From 95ea90db019561450826ac32d8341f5735c78eee Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 15:33:30 -0400 Subject: n_tty: Only process packet mode data in raw mode Packet mode can only be set for a pty master, and a pty master is always in raw mode since its termios cannot be changed. Signed-off-by: Peter Hurley Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 7f134b9..6dcaa52 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2228,16 +2228,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, } __set_current_state(TASK_RUNNING); - /* Deal with packet mode. */ - if (packet && b == buf) { - if (tty_put_user(tty, TIOCPKT_DATA, b++)) { - retval = -EFAULT; - b--; - break; - } - nr--; - } - if (ldata->icanon && !L_EXTPROC(tty)) { retval = canon_copy_from_read_buf(tty, &b, &nr); if (retval == -EAGAIN) { @@ -2247,6 +2237,17 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, break; } else { int uncopied; + + /* Deal with packet mode. */ + if (packet && b == buf) { + if (tty_put_user(tty, TIOCPKT_DATA, b++)) { + retval = -EFAULT; + b--; + break; + } + nr--; + } + /* The copy function takes the read lock and handles locking internally for this case */ uncopied = copy_from_read_buf(tty, &b, &nr); -- cgit v0.10.2 From fa59e25664fc2a9e0fec494e921480e37b9e3c1c Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 15:36:38 -0400 Subject: n_tty: Remove stale read lock comment The stale comment refers to lock behavior which was eliminated in commit 6d76bd2618535c581f1673047b8341fd291abc67, n_tty: Make N_TTY ldisc receive path lockless. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 6dcaa52..0cb0e12 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2248,8 +2248,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, nr--; } - /* The copy function takes the read lock and handles - locking internally for this case */ uncopied = copy_from_read_buf(tty, &b, &nr); uncopied += copy_from_read_buf(tty, &b, &nr); if (uncopied) { -- cgit v0.10.2 From 1bc8cde46a159c928f62ac48981cf230582e4b78 Mon Sep 17 00:00:00 2001 From: Mike Skoog Date: Thu, 16 Oct 2014 13:10:01 -0700 Subject: 8250_pci: Added driver for Endrun Technologies PTP PCIe card. Added recognition of EndRun Technologies PCIe PTP slave card and setup two ttySx ports for communication with the card for retrieval of PTP based time and to communicate with the card's Linux OS. Signed-off-by: Mike Skoog Signed-off-by: Mike Korreng Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index beb9d71..a9b935a 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1003,6 +1003,40 @@ static void pci_ite887x_exit(struct pci_dev *dev) } /* + * EndRun Technologies. + * Determine the number of ports available on the device. + */ +#define PCI_VENDOR_ID_ENDRUN 0x7401 +#define PCI_DEVICE_ID_ENDRUN_1588 0xe100 + +static int pci_endrun_init(struct pci_dev *dev) +{ + u8 __iomem *p; + unsigned long deviceID; + unsigned int number_uarts = 0; + + /* EndRun device is all 0xexxx */ + if (dev->vendor == PCI_VENDOR_ID_ENDRUN && + (dev->device & 0xf000) != 0xe000) + return 0; + + p = pci_iomap(dev, 0, 5); + if (p == NULL) + return -ENOMEM; + + deviceID = ioread32(p); + /* EndRun device */ + if (deviceID == 0x07000200) { + number_uarts = ioread8(p + 4); + dev_dbg(&dev->dev, + "%d ports detected on EndRun PCI Express device\n", + number_uarts); + } + pci_iounmap(dev, p); + return number_uarts; +} + +/* * Oxford Semiconductor Inc. * Check that device is part of the Tornado range of devices, then determine * the number of ports available on the device. @@ -2346,6 +2380,17 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .setup = pci_netmos_9900_setup, }, /* + * EndRun Technologies + */ + { + .vendor = PCI_VENDOR_ID_ENDRUN, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_endrun_init, + .setup = pci_default_setup, + }, + /* * For Oxford Semiconductor Tornado based devices */ { @@ -2754,6 +2799,7 @@ enum pci_board_num_t { pbn_panacom2, pbn_panacom4, pbn_plx_romulus, + pbn_endrun_2_4000000, pbn_oxsemi, pbn_oxsemi_1_4000000, pbn_oxsemi_2_4000000, @@ -3299,6 +3345,20 @@ static struct pciserial_board pci_boards[] = { }, /* + * EndRun Technologies + * Uses the size of PCI Base region 0 to + * signal now many ports are available + * 2 port 952 Uart support + */ + [pbn_endrun_2_4000000] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + + /* * This board uses the size of PCI Base region 0 to * signal now many ports are available */ @@ -4171,6 +4231,13 @@ static struct pci_device_id serial_pci_tbl[] = { 0x10b5, 0x106a, 0, 0, pbn_plx_romulus }, /* + * EndRun Technologies. PCI express device range. + * EndRun PTP/1588 has 2 Native UARTs. + */ + { PCI_VENDOR_ID_ENDRUN, PCI_DEVICE_ID_ENDRUN_1588, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_endrun_2_4000000 }, + /* * Quatech cards. These actually have configurable clocks but for * now we just use the default. * -- cgit v0.10.2 From 3ee175d9d9f9be0e345f7237ee921e58f68afdd7 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:44 -0500 Subject: tty: Don't hold tty_lock for ldisc release The tty->ldisc_sem write lock is sufficient for serializing changes to tty->ldisc; holding the tty lock is not required. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 2d822aa..332a622 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -764,6 +764,8 @@ static void tty_ldisc_kill(struct tty_struct *tty) * Called during the final close of a tty/pty pair in order to shut down * the line discpline layer. On exit the ldisc assigned is N_TTY and the * ldisc has not been opened. + * + * Holding ldisc_sem write lock serializes tty->ldisc changes. */ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) @@ -776,13 +778,9 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); tty_ldisc_lock_pair(tty, o_tty); - tty_lock_pair(tty, o_tty); - tty_ldisc_kill(tty); if (o_tty) tty_ldisc_kill(o_tty); - - tty_unlock_pair(tty, o_tty); tty_ldisc_unlock_pair(tty, o_tty); /* And the memory resources remaining (buffers, termios) will be -- cgit v0.10.2 From c8483bc9deff9bf9118aaab2e840b973b75cac3e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:45 -0500 Subject: tty: Invert tty_lock/ldisc_sem lock order Dropping the tty lock to acquire the tty->ldisc_sem allows several race conditions (such as hangup while changing the ldisc) which requires extra states and testing. The ldisc_sem->tty_lock lock order has not been required since tty buffer ownership was moved to tty_port. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 332a622..28858eb 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -523,9 +523,11 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); + tty_lock(tty); retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ); if (retval) { tty_ldisc_put(new_ldisc); + tty_unlock(tty); return retval; } @@ -536,11 +538,11 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (tty->ldisc->ops->num == ldisc) { tty_ldisc_enable_pair(tty, o_tty); tty_ldisc_put(new_ldisc); + tty_unlock(tty); return 0; } old_ldisc = tty->ldisc; - tty_lock(tty); if (test_bit(TTY_HUPPING, &tty->flags) || test_bit(TTY_HUPPED, &tty->flags)) { @@ -675,8 +677,6 @@ void tty_ldisc_hangup(struct tty_struct *tty) wake_up_interruptible_poll(&tty->write_wait, POLLOUT); wake_up_interruptible_poll(&tty->read_wait, POLLIN); - tty_unlock(tty); - /* * Shutdown the current line discipline, and reset it to * N_TTY if need be. @@ -684,7 +684,6 @@ void tty_ldisc_hangup(struct tty_struct *tty) * Avoid racing set_ldisc or tty_ldisc_release */ tty_ldisc_lock_pair(tty, tty->link); - tty_lock(tty); if (tty->ldisc) { -- cgit v0.10.2 From 3ff51a199f9e85aed843471bc10dae9e94dbb0fc Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:46 -0500 Subject: tty: Remove TTY_HUPPING Now that tty_ldisc_hangup() does not drop the tty lock, it is no longer possible to observe TTY_HUPPING while holding the tty lock on another cpu. Remove TTY_HUPPING bit definition. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 9d1e247..873793c 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -690,9 +690,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) return; } - /* some functions below drop BTM, so we need this bit */ - set_bit(TTY_HUPPING, &tty->flags); - /* inuse_filps is protected by the single tty lock, this really needs to change if we want to flush the workqueue with the lock held */ @@ -717,10 +714,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) while (refs--) tty_kref_put(tty); - /* - * it drops BTM and thus races with reopen - * we protect the race by TTY_HUPPING - */ tty_ldisc_hangup(tty); spin_lock_irq(&tty->ctrl_lock); @@ -752,8 +745,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) * can't yet guarantee all that. */ set_bit(TTY_HUPPED, &tty->flags); - clear_bit(TTY_HUPPING, &tty->flags); - tty_unlock(tty); if (f) @@ -1461,8 +1452,7 @@ static int tty_reopen(struct tty_struct *tty) { struct tty_driver *driver = tty->driver; - if (test_bit(TTY_CLOSING, &tty->flags) || - test_bit(TTY_HUPPING, &tty->flags)) + if (test_bit(TTY_CLOSING, &tty->flags)) return -EIO; if (driver->type == TTY_DRIVER_TYPE_PTY && diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 28858eb..49001fa 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -544,8 +544,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) old_ldisc = tty->ldisc; - if (test_bit(TTY_HUPPING, &tty->flags) || - test_bit(TTY_HUPPED, &tty->flags)) { + if (test_bit(TTY_HUPPED, &tty->flags)) { /* We were raced by the hangup method. It will have stomped the ldisc data and closed the ldisc down */ tty_ldisc_enable_pair(tty, o_tty); diff --git a/include/linux/tty.h b/include/linux/tty.h index b36b0b4..ff0dd7a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -321,7 +321,6 @@ struct tty_file_private { #define TTY_PTY_LOCK 16 /* pty private */ #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ #define TTY_HUPPED 18 /* Post driver->hangup() */ -#define TTY_HUPPING 21 /* ->hangup() in progress */ #define TTY_LDISC_HALTED 22 /* Line discipline is halted */ #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty)) -- cgit v0.10.2 From 5d93e748957336de1f28e7356bb43d6da01718bc Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:47 -0500 Subject: tty: Clarify re-open behavior of master ptys Re-opening master ptys is not allowed. Once opened and for the remaining lifetime of the master pty, its tty count is 1. If its tty count has dropped to 0, then the master pty was closed and TTY_CLOSING was set, and destruction may begin imminently. Besides the normal case of a legacy BSD pty master being re-opened (which always returns -EIO), this code is only reachable in 2 degenerate cases: 1. The pty master is the controlling terminal (this is possible through the TIOCSCTTY ioctl). pty masters are not designed to be controlling terminals and it's an oversight that tiocsctty() ever let that happen. The attempted open of /dev/tty will always fail. No known program does this. 2. The legacy BSD pty slave was opened first. The slave open will fail in pty_open() and tty_release() will commence. But before tty_release() claims the tty_mutex, there is a very small window where a parallel master open might succeed. In a test of racing legacy BSD slave and master parallel opens, where: slave open attempts: 10000 success:4527 failure:5473 master open attempts: 11728 success:5789 failure:5939 only 8 master open attempts would have succeeded reaching this code and successfully opened the master pty. This case is not possible with SysV ptys. Always return -EIO if a master pty is re-opened or the slave is opened first and the master opened in parallel (for legacy BSD ptys). Furthermore, now that changing the slave's count is not required, the tty_lock is sufficient for preventing concurrent changes to the tty being re-opened (or failing re-opening). Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 873793c..168382b 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1444,9 +1444,9 @@ void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty) * @tty - the tty to open * * Return 0 on success, -errno on error. + * Re-opens on master ptys are not allowed and return -EIO. * - * Locking: tty_mutex must be held from the time the tty was found - * till this open completes. + * Locking: Caller must hold tty_lock */ static int tty_reopen(struct tty_struct *tty) { @@ -1456,16 +1456,9 @@ static int tty_reopen(struct tty_struct *tty) return -EIO; if (driver->type == TTY_DRIVER_TYPE_PTY && - driver->subtype == PTY_TYPE_MASTER) { - /* - * special case for PTY masters: only one open permitted, - * and the slave side open count is incremented as well. - */ - if (tty->count) - return -EIO; + driver->subtype == PTY_TYPE_MASTER) + return -EIO; - tty->link->count++; - } tty->count++; WARN_ON(!tty->ldisc); -- cgit v0.10.2 From 216030ec55e08646be629ee32725c0189ad74c9a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:48 -0500 Subject: tty: Check tty->count instead of TTY_CLOSING in tty_reopen() Although perhaps not obvious, the TTY_CLOSING bit is set when the tty count has been decremented to 0 (which occurs while holding tty_lock). The only other case when tty count is 0 during a re-open is when a legacy BSD pty master has been opened in parallel but after the pty slave, which is unsupported and returns an error. Thus !tty->count contains the complete set of degenerate conditions under which a tty open fails. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 168382b..0eaf632 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1452,7 +1452,7 @@ static int tty_reopen(struct tty_struct *tty) { struct tty_driver *driver = tty->driver; - if (test_bit(TTY_CLOSING, &tty->flags)) + if (!tty->count) return -EIO; if (driver->type == TTY_DRIVER_TYPE_PTY && -- cgit v0.10.2 From 55199ea3bd2e53007715d544fb9094cbbdda1597 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:49 -0500 Subject: pty: Always return -EIO if slave BSD pty opened first Opening the slave BSD pty first already returns -EIO from the slave pty_open(), which in turn causes the newly installed tty pair to be released before returning from tty_open(). However, this can also cause a parallel master BSD pty open to fail because the pty pair destruction may already been taking place in tty_release(). Failing at driver->install() if the slave pty is opened first ensures that a pty master open cannot fail, because the driver tables will not have been updated so tty_driver_lookup_tty() won't find the master pty (and attempt to "re-open" it). In turn, this guarantees that any tty with a tty->count == 0 is in final close (rather than never opened). Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 7a1a538..bdb8fd1 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -383,6 +383,10 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, int idx = tty->index; int retval = -ENOMEM; + /* Opening the slave first has always returned -EIO */ + if (driver->subtype != PTY_TYPE_MASTER) + return -EIO; + ports[0] = kmalloc(sizeof **ports, GFP_KERNEL); ports[1] = kmalloc(sizeof **ports, GFP_KERNEL); if (!ports[0] || !ports[1]) @@ -419,8 +423,6 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, * Everything allocated ... set up the o_tty structure. */ tty_driver_kref_get(driver->other); - if (driver->subtype == PTY_TYPE_MASTER) - o_tty->count++; /* Establish the links in both directions */ tty->link = o_tty; o_tty->link = tty; @@ -432,6 +434,7 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, tty_driver_kref_get(driver); tty->count++; + o_tty->count++; return 0; err_free_termios: if (legacy) -- cgit v0.10.2 From 52494eeb993ef6865f7817c8b473f798771371fc Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:50 -0500 Subject: tty: Re-open /dev/tty without tty_mutex Opening /dev/tty (ie., the controlling tty for the current task) is always a re-open of the underlying tty. Because holding the tty_lock is sufficient for safely re-opening a tty, and because having a tty kref is sufficient for safely acquiring the tty_lock [1], tty_open_current_tty() does not require holding tty_mutex. Repurpose tty_open_current_tty() to perform the re-open itself and refactor tty_open(). [1] Analysis of safely re-opening the current tty w/o tty_mutex get_current_tty() gets a tty kref from the already kref'ed tty value of current->signal->tty while holding the sighand lock for the current task. This guarantees that the tty pointer returned from get_current_tty() points to a tty which remains referenceable while holding the kref. Although release_tty() may run concurrently, and thus the driver reference may be removed, release_one_tty() cannot have run, and won't while holding the tty kref. This, in turn, guarantees the tty_lock() can safely be acquired (since tty->magic and tty->legacy_mutex are still a valid dereferences). The tty_lock() also gets a tty kref to prevent the tty_unlock() from dereferencing a released tty. Thus, the kref returned from get_current_tty() can be released. Lastly, the first operation of tty_reopen() is to check the tty count. If non-zero, this ensures release_tty() is not running concurrently, and the driver references have not been removed. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 0eaf632..2e166a1 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1935,20 +1935,20 @@ int tty_release(struct inode *inode, struct file *filp) } /** - * tty_open_current_tty - get tty of current task for open + * tty_open_current_tty - get locked tty of current task * @device: device number * @filp: file pointer to tty - * @return: tty of the current task iff @device is /dev/tty + * @return: locked tty of the current task iff @device is /dev/tty + * + * Performs a re-open of the current task's controlling 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. - * - * We need to move to returning a refcounted object from all the lookup - * paths including this one. */ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) { struct tty_struct *tty; + int retval; if (device != MKDEV(TTYAUX_MAJOR, 0)) return NULL; @@ -1959,9 +1959,14 @@ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) 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! */ - /* This is only safe because the caller holds tty_mutex */ + tty_lock(tty); + tty_kref_put(tty); /* safe to drop the kref now */ + + retval = tty_reopen(tty); + if (retval < 0) { + tty_unlock(tty); + tty = ERR_PTR(retval); + } return tty; } @@ -2059,13 +2064,9 @@ retry_open: index = -1; retval = 0; - mutex_lock(&tty_mutex); - /* This is protected by the tty_mutex */ tty = tty_open_current_tty(device, filp); - if (IS_ERR(tty)) { - retval = PTR_ERR(tty); - goto err_unlock; - } else if (!tty) { + if (!tty) { + mutex_lock(&tty_mutex); driver = tty_lookup_driver(device, filp, &noctty, &index); if (IS_ERR(driver)) { retval = PTR_ERR(driver); @@ -2078,21 +2079,21 @@ retry_open: retval = PTR_ERR(tty); goto err_unlock; } - } - if (tty) { - tty_lock(tty); - retval = tty_reopen(tty); - if (retval < 0) { - tty_unlock(tty); - tty = ERR_PTR(retval); - } - } else /* Returns with the tty_lock held for now */ - tty = tty_init_dev(driver, index); + if (tty) { + tty_lock(tty); + retval = tty_reopen(tty); + if (retval < 0) { + tty_unlock(tty); + tty = ERR_PTR(retval); + } + } else /* Returns with the tty_lock held for now */ + tty = tty_init_dev(driver, index); - mutex_unlock(&tty_mutex); - if (driver) + mutex_unlock(&tty_mutex); tty_driver_kref_put(driver); + } + if (IS_ERR(tty)) { retval = PTR_ERR(tty); goto err_file; -- cgit v0.10.2 From aa3cb814a8efae1b7c81516b4bee41f831fe2e7a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:51 -0500 Subject: tty: Drop tty_mutex before tty reopen Holding tty_mutex for a tty re-open is no longer necessary since "tty: Clarify re-open behavior of master ptys". Because the slave tty count is no longer accessed by tty_reopen(), holding tty_mutex to prevent concurrent final tty_release() of the slave pty is not required. As with "tty: Re-open /dev/tty without tty_mutex", holding a tty kref until the tty_lock is acquired is sufficient to ensure the tty has not been freed, which, in turn, is sufficient to ensure the tty_lock can be safely acquired and the tty count can be safely retrieved. A non-zero tty count with the tty lock held guarantees that release_tty() has not run and cannot run concurrently with tty_reopen(). Change tty_driver_lookup_tty() to acquire the tty kref, which allows the tty_mutex to be dropped before acquiring the tty lock. Dropping the tty_mutex before attempting the tty_lock allows other ttys to be opened and released, without needing this tty_reopen() to complete. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 2e166a1..66d6bcc 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1344,19 +1344,24 @@ static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p) * @driver: the driver for the tty * @idx: the minor number * - * Return the tty, if found or ERR_PTR() otherwise. + * Return the tty, if found. If not found, return NULL or ERR_PTR() if the + * driver lookup() method returns an error. * - * Locking: tty_mutex must be held. If tty is found, the mutex must - * be held until the 'fast-open' is also done. Will change once we - * have refcounting in the driver and per driver locking + * Locking: tty_mutex must be held. If the tty is found, bump the tty kref. */ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, struct inode *inode, int idx) { + struct tty_struct *tty; + if (driver->ops->lookup) - return driver->ops->lookup(driver, inode, idx); + tty = driver->ops->lookup(driver, inode, idx); + else + tty = driver->ttys[idx]; - return driver->ttys[idx]; + if (!IS_ERR(tty)) + tty_kref_get(tty); + return tty; } /** @@ -2081,16 +2086,20 @@ retry_open: } if (tty) { + mutex_unlock(&tty_mutex); tty_lock(tty); + /* safe to drop the kref from tty_driver_lookup_tty() */ + tty_kref_put(tty); retval = tty_reopen(tty); if (retval < 0) { tty_unlock(tty); tty = ERR_PTR(retval); } - } else /* Returns with the tty_lock held for now */ + } else { /* Returns with the tty_lock held for now */ tty = tty_init_dev(driver, index); + mutex_unlock(&tty_mutex); + } - mutex_unlock(&tty_mutex); tty_driver_kref_put(driver); } -- cgit v0.10.2 From 04980706c8febe41ec598116b174bd3a2dc82355 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:52 -0500 Subject: tty: Remove TTY_CLOSING Now that re-open is not permitted for a legacy BSD pty master, using TTY_CLOSING to indicate when a tty can be torn-down is no longer necessary. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 66d6bcc..ea8c6ca 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1197,7 +1197,7 @@ void tty_write_message(struct tty_struct *tty, char *msg) if (tty) { mutex_lock(&tty->atomic_write_lock); tty_lock(tty); - if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { + if (tty->ops->write && tty->count > 0) { tty_unlock(tty); tty->ops->write(tty, msg, strlen(msg)); } else @@ -1879,16 +1879,6 @@ int tty_release(struct inode *inode, struct file *filp) /* * Perform some housekeeping before deciding whether to return. * - * Set the TTY_CLOSING flag if this was the last open. In the - * case of a pty we may have to wait around for the other side - * to close, and TTY_CLOSING makes sure we can't be reopened. - */ - if (tty_closing) - set_bit(TTY_CLOSING, &tty->flags); - if (o_tty_closing) - set_bit(TTY_CLOSING, &o_tty->flags); - - /* * If _either_ side is closing, make sure there aren't any * processes that still think tty or o_tty is their controlling * tty. @@ -1903,7 +1893,7 @@ int tty_release(struct inode *inode, struct file *filp) mutex_unlock(&tty_mutex); tty_unlock_pair(tty, o_tty); - /* At this point the TTY_CLOSING flag should ensure a dead tty + /* At this point, the tty->count == 0 should ensure a dead tty cannot be re-opened by a racing opener */ /* check whether both sides are closing ... */ diff --git a/include/linux/tty.h b/include/linux/tty.h index ff0dd7a..35b29f0 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -316,7 +316,6 @@ struct tty_file_private { #define TTY_EXCLUSIVE 3 /* Exclusive open mode */ #define TTY_DEBUG 4 /* Debugging */ #define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */ -#define TTY_CLOSING 7 /* ->close() in progress */ #define TTY_LDISC_OPEN 11 /* Line discipline is open */ #define TTY_PTY_LOCK 16 /* pty private */ #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ -- cgit v0.10.2 From 0911261d4cb614ef6900cd2906be2c61a87f43ff Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:53 -0500 Subject: tty: Don't take tty_mutex for tty count changes Holding tty_mutex is no longer required to serialize changes to the tty_count or to prevent concurrent opens of closing ttys; tty_lock() is sufficient. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index ea8c6ca..e59de81 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1804,10 +1804,6 @@ int tty_release(struct inode *inode, struct file *filp) * each iteration we avoid any problems. */ while (1) { - /* Guard against races with tty->count changes elsewhere and - opens on /dev/tty */ - - mutex_lock(&tty_mutex); tty_lock_pair(tty, o_tty); tty_closing = tty->count <= 1; o_tty_closing = o_tty && @@ -1840,7 +1836,6 @@ int tty_release(struct inode *inode, struct file *filp) printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", __func__, tty_name(tty, buf)); tty_unlock_pair(tty, o_tty); - mutex_unlock(&tty_mutex); schedule(); } @@ -1891,7 +1886,6 @@ int tty_release(struct inode *inode, struct file *filp) read_unlock(&tasklist_lock); } - mutex_unlock(&tty_mutex); tty_unlock_pair(tty, o_tty); /* At this point, the tty->count == 0 should ensure a dead tty cannot be re-opened by a racing opener */ -- cgit v0.10.2 From d5e370a4eeb701201bd441b4ec089091dd6f2ce0 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:54 -0500 Subject: tty: Don't release tty locks for wait queue sanity check Releasing the tty locks while waiting for the tty wait queues to be empty is no longer necessary nor desirable. Prior to "tty: Don't take tty_mutex for tty count changes", dropping the tty locks was necessary to reestablish the correct lock order between tty_mutex and the tty locks. Dropping the global tty_mutex was necessary; otherwise new ttys could not have been opened while waiting. However, without needing the global tty_mutex held, the tty locks for the releasing tty can now be held through the sleep. The sanity check is for abnormal conditions caused by kernel bugs, not for recoverable errors caused by misbehaving userspace; dropping the tty locks only allows the tty state to get more sideways. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e59de81..b008e2b 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1798,13 +1798,10 @@ int tty_release(struct inode *inode, struct file *filp) * first, its count will be one, since the master side holds an open. * Thus this test wouldn't be triggered at the time the slave closes, * so we do it now. - * - * Note that it's possible for the tty to be opened again while we're - * flushing out waiters. By recalculating the closing flags before - * each iteration we avoid any problems. */ + tty_lock_pair(tty, o_tty); + while (1) { - tty_lock_pair(tty, o_tty); tty_closing = tty->count <= 1; o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); @@ -1835,7 +1832,6 @@ int tty_release(struct inode *inode, struct file *filp) printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", __func__, tty_name(tty, buf)); - tty_unlock_pair(tty, o_tty); schedule(); } -- cgit v0.10.2 From deb287e7401bbbf7803731805acbda1d983d1999 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:55 -0500 Subject: tty: Document check_tty_count() requires tty_lock held Holding the tty_lock() is necessary to prevent concurrent changes to the tty count that may cause it to differ from the open file list count. The tty_lock() is already held at all call sites. NB: Note that the check for the pty master tty count is safe because the slave's tty_lock() is held while decrementing the pty master tty count. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index b008e2b..adbd9ac 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -275,6 +275,7 @@ int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, return 0; } +/* Caller must hold tty_lock */ static int check_tty_count(struct tty_struct *tty, const char *routine) { #ifdef CHECK_TTY_COUNT -- cgit v0.10.2 From 324c1650ca2d6c1325afbb53d1187e63bbeaad0b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:56 -0500 Subject: tty: Simplify pty pair teardown logic When the slave side closes and its tty count is 0, the pty pair can be destroyed; the master side must have already closed for the slave side tty count to be 0. Thus, only the pty master close must check if the slave side has closed by checking the slave tty count. Remove the pre-computed closing flags and check the actual count(s). Regular ttys are unaffected by this change. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index adbd9ac..ffa31c6 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1755,7 +1755,7 @@ int tty_release(struct inode *inode, struct file *filp) { struct tty_struct *tty = file_tty(filp); struct tty_struct *o_tty; - int pty_master, tty_closing, o_tty_closing, do_sleep; + int pty_master, do_sleep, final; int idx; char buf[64]; @@ -1797,18 +1797,15 @@ int tty_release(struct inode *inode, struct file *filp) * The test for the o_tty closing is necessary, since the master and * slave sides may close in any order. If the slave side closes out * first, its count will be one, since the master side holds an open. - * Thus this test wouldn't be triggered at the time the slave closes, + * Thus this test wouldn't be triggered at the time the slave closed, * so we do it now. */ tty_lock_pair(tty, o_tty); while (1) { - tty_closing = tty->count <= 1; - o_tty_closing = o_tty && - (o_tty->count <= (pty_master ? 1 : 0)); do_sleep = 0; - if (tty_closing) { + if (tty->count <= 1) { if (waitqueue_active(&tty->read_wait)) { wake_up_poll(&tty->read_wait, POLLIN); do_sleep++; @@ -1818,7 +1815,7 @@ int tty_release(struct inode *inode, struct file *filp) do_sleep++; } } - if (o_tty_closing) { + if (pty_master && o_tty->count <= 1) { if (waitqueue_active(&o_tty->read_wait)) { wake_up_poll(&o_tty->read_wait, POLLIN); do_sleep++; @@ -1836,14 +1833,6 @@ int tty_release(struct inode *inode, struct file *filp) schedule(); } - /* - * The closing flags are now consistent with the open counts on - * both sides, and we've completed the last operation that could - * block, so it's safe to proceed with closing. - * - * We must *not* drop the tty_mutex until we ensure that a further - * entry into tty_open can not pick up this tty. - */ if (pty_master) { if (--o_tty->count < 0) { printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n", @@ -1875,20 +1864,22 @@ int tty_release(struct inode *inode, struct file *filp) * processes that still think tty or o_tty is their controlling * tty. */ - if (tty_closing || o_tty_closing) { + if (!tty->count) { read_lock(&tasklist_lock); session_clear_tty(tty->session); - if (o_tty) + if (pty_master) session_clear_tty(o_tty->session); read_unlock(&tasklist_lock); } + /* check whether both sides are closing ... */ + final = !tty->count && !(pty_master && o_tty->count); + tty_unlock_pair(tty, o_tty); /* At this point, the tty->count == 0 should ensure a dead tty cannot be re-opened by a racing opener */ - /* check whether both sides are closing ... */ - if (!tty_closing || (o_tty && !o_tty_closing)) + if (!final) return 0; #ifdef TTY_DEBUG_HANGUP -- cgit v0.10.2 From 949aa64ff90d9a9b3aae13bb5a8247614adf4600 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:57 -0500 Subject: tty: Fold pty pair handling into tty_flush_works() Perform work flush for both ends of a pty pair within tty_flush_works(), rather than calling twice. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index ffa31c6..f5b62b9 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1584,15 +1584,19 @@ void tty_free_termios(struct tty_struct *tty) EXPORT_SYMBOL(tty_free_termios); /** - * tty_flush_works - flush all works of a tty - * @tty: tty device to flush works for + * tty_flush_works - flush all works of a tty/pty pair + * @tty: tty device to flush works for (or either end of a pty pair) * - * Sync flush all works belonging to @tty. + * Sync flush all works belonging to @tty (and the 'other' tty). */ static void tty_flush_works(struct tty_struct *tty) { flush_work(&tty->SAK_work); flush_work(&tty->hangup_work); + if (tty->link) { + flush_work(&tty->link->SAK_work); + flush_work(&tty->link->hangup_work); + } } /** @@ -1892,8 +1896,6 @@ int tty_release(struct inode *inode, struct file *filp) /* Wait for pending work before tty destruction commmences */ tty_flush_works(tty); - if (o_tty) - tty_flush_works(o_tty); #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, tty_name(tty, buf)); -- cgit v0.10.2 From 62462aefeb5aff092fc97037d9c12a4afe95a3ff Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:58 -0500 Subject: tty: Simplify tty_ldisc_release() interface Passing the 'other' tty to tty_ldisc_release() only makes sense for a pty pair; make o_tty function local instead. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index f5b62b9..cd95508 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1892,7 +1892,7 @@ int tty_release(struct inode *inode, struct file *filp) /* * Ask the line discipline code to release its structures */ - tty_ldisc_release(tty, o_tty); + tty_ldisc_release(tty); /* Wait for pending work before tty destruction commmences */ tty_flush_works(tty); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 49001fa..1c4d7b6 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -756,18 +756,17 @@ static void tty_ldisc_kill(struct tty_struct *tty) /** * tty_ldisc_release - release line discipline - * @tty: tty being shut down - * @o_tty: pair tty for pty/tty pairs - * - * Called during the final close of a tty/pty pair in order to shut down - * the line discpline layer. On exit the ldisc assigned is N_TTY and the - * ldisc has not been opened. + * @tty: tty being shut down (or one end of pty pair) * - * Holding ldisc_sem write lock serializes tty->ldisc changes. + * Called during the final close of a tty or a pty pair in order to shut + * down the line discpline layer. On exit, each ldisc assigned is N_TTY and + * each ldisc has not been opened. */ -void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) +void tty_ldisc_release(struct tty_struct *tty) { + struct tty_struct *o_tty = tty->link; + /* * Shutdown this line discipline. As this is the final close, * it does not race with the set_ldisc code path. diff --git a/include/linux/tty.h b/include/linux/tty.h index 35b29f0..af1a7f3 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -557,7 +557,7 @@ extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); extern int tty_set_ldisc(struct tty_struct *tty, int ldisc); extern int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty); -extern void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty); +extern void tty_ldisc_release(struct tty_struct *tty); extern void tty_ldisc_init(struct tty_struct *tty); extern void tty_ldisc_deinit(struct tty_struct *tty); extern void tty_ldisc_begin(void); -- cgit v0.10.2 From 359b9fb5c4f0d49f7062356f55073a169f36e29d Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:12:59 -0500 Subject: tty: Simplify tty_release_checks() interface Passing the 'other' tty to tty_release_checks() only makes sense for a pty pair; make o_tty scope local instead. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index cd95508..e927650 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1702,8 +1702,7 @@ static void release_tty(struct tty_struct *tty, int idx) * 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) +static int tty_release_checks(struct tty_struct *tty, int idx) { #ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver->num) { @@ -1722,6 +1721,8 @@ static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty, return -1; } if (tty->driver->other) { + struct tty_struct *o_tty = tty->link; + if (o_tty != tty->driver->other->ttys[idx]) { printk(KERN_DEBUG "%s: other->table[%d] not o_tty for (%s)\n", __func__, idx, tty->name); @@ -1777,7 +1778,7 @@ int tty_release(struct inode *inode, struct file *filp) /* Review: parallel close */ o_tty = tty->link; - if (tty_release_checks(tty, o_tty, idx)) { + if (tty_release_checks(tty, idx)) { tty_unlock(tty); return 0; } -- cgit v0.10.2 From 7ffb6da93c383a71b7f2d29d70cd420faa1ddeee Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:13:00 -0500 Subject: tty: Simplify tty_release() state checks The local o_tty variable in tty_release() is now accessed only when closing the pty master. Set o_tty to slave pty when closing pty master, otherwise NULL; use o_tty != NULL as replacement for pty_master. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e927650..bd7cde3 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1759,8 +1759,8 @@ static int tty_release_checks(struct tty_struct *tty, int idx) int tty_release(struct inode *inode, struct file *filp) { struct tty_struct *tty = file_tty(filp); - struct tty_struct *o_tty; - int pty_master, do_sleep, final; + struct tty_struct *o_tty = NULL; + int do_sleep, final; int idx; char buf[64]; @@ -1773,10 +1773,9 @@ int tty_release(struct inode *inode, struct file *filp) __tty_fasync(-1, filp, 0); idx = tty->index; - pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER); - /* Review: parallel close */ - o_tty = tty->link; + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && + tty->driver->subtype == PTY_TYPE_MASTER) + o_tty = tty->link; if (tty_release_checks(tty, idx)) { tty_unlock(tty); @@ -1820,7 +1819,7 @@ int tty_release(struct inode *inode, struct file *filp) do_sleep++; } } - if (pty_master && o_tty->count <= 1) { + if (o_tty && o_tty->count <= 1) { if (waitqueue_active(&o_tty->read_wait)) { wake_up_poll(&o_tty->read_wait, POLLIN); do_sleep++; @@ -1838,7 +1837,7 @@ int tty_release(struct inode *inode, struct file *filp) schedule(); } - if (pty_master) { + if (o_tty) { if (--o_tty->count < 0) { printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n", __func__, o_tty->count, tty_name(o_tty, buf)); @@ -1872,13 +1871,13 @@ int tty_release(struct inode *inode, struct file *filp) if (!tty->count) { read_lock(&tasklist_lock); session_clear_tty(tty->session); - if (pty_master) + if (o_tty) session_clear_tty(o_tty->session); read_unlock(&tasklist_lock); } /* check whether both sides are closing ... */ - final = !tty->count && !(pty_master && o_tty->count); + final = !tty->count && !(o_tty && o_tty->count); tty_unlock_pair(tty, o_tty); /* At this point, the tty->count == 0 should ensure a dead tty -- cgit v0.10.2 From 2aff5e2bc62db43e05c814461a08aff0fc2b7fe5 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:13:01 -0500 Subject: tty: Change tty lock order to master->slave When releasing the master pty, the slave pty also needs to be locked to prevent concurrent tty count changes for the slave pty and to ensure that only one parallel master and slave release observe the final close, and proceed to destruct the pty pair. Conversely, when releasing the slave pty, locking the master pty is not necessary (since the master's state can be inferred by the slave tty count). Introduce tty_lock_slave()/tty_unlock_slave() which acquires/releases the tty lock of the slave pty. Remove tty_lock_pair()/tty_unlock_pair(). Dropping the tty_lock is no longer required to re-establish a stable lock order. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index bd7cde3..4ecee28 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1790,7 +1790,9 @@ int tty_release(struct inode *inode, struct file *filp) if (tty->ops->close) tty->ops->close(tty, filp); - tty_unlock(tty); + /* If tty is pty master, lock the slave pty (stable lock order) */ + tty_lock_slave(o_tty); + /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1804,8 +1806,6 @@ int tty_release(struct inode *inode, struct file *filp) * Thus this test wouldn't be triggered at the time the slave closed, * so we do it now. */ - tty_lock_pair(tty, o_tty); - while (1) { do_sleep = 0; @@ -1879,7 +1879,9 @@ int tty_release(struct inode *inode, struct file *filp) /* check whether both sides are closing ... */ final = !tty->count && !(o_tty && o_tty->count); - tty_unlock_pair(tty, o_tty); + tty_unlock_slave(o_tty); + tty_unlock(tty); + /* At this point, the tty->count == 0 should ensure a dead tty cannot be re-opened by a racing opener */ diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 2e41abe..f43e995 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -4,6 +4,11 @@ #include #include +/* + * Nested tty locks are necessary for releasing pty pairs. + * The stable lock order is master pty first, then slave pty. + */ + /* Legacy tty mutex glue */ enum { @@ -45,29 +50,18 @@ void __lockfunc tty_unlock(struct tty_struct *tty) } EXPORT_SYMBOL(tty_unlock); -/* - * Getting the big tty mutex for a pair of ttys with lock ordering - * On a non pty/tty pair tty2 can be NULL which is just fine. - */ -void __lockfunc tty_lock_pair(struct tty_struct *tty, - struct tty_struct *tty2) +void __lockfunc tty_lock_slave(struct tty_struct *tty) { - if (tty < tty2) { - tty_lock(tty); - tty_lock_nested(tty2, TTY_MUTEX_NESTED); - } else { - if (tty2 && tty2 != tty) - tty_lock(tty2); + if (tty && tty != tty->link) { + WARN_ON(!mutex_is_locked(&tty->link->legacy_mutex) || + !tty->driver->type == TTY_DRIVER_TYPE_PTY || + !tty->driver->type == PTY_TYPE_SLAVE); tty_lock_nested(tty, TTY_MUTEX_NESTED); } } -EXPORT_SYMBOL(tty_lock_pair); -void __lockfunc tty_unlock_pair(struct tty_struct *tty, - struct tty_struct *tty2) +void __lockfunc tty_unlock_slave(struct tty_struct *tty) { - tty_unlock(tty); - if (tty2 && tty2 != tty) - tty_unlock(tty2); + if (tty && tty != tty->link) + tty_unlock(tty); } -EXPORT_SYMBOL(tty_unlock_pair); diff --git a/include/linux/tty.h b/include/linux/tty.h index af1a7f3..a07b4b4 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -638,11 +638,8 @@ extern long vt_compat_ioctl(struct tty_struct *tty, /* functions for preparation of BKL removal */ extern void __lockfunc tty_lock(struct tty_struct *tty); extern void __lockfunc tty_unlock(struct tty_struct *tty); -extern void __lockfunc tty_lock_pair(struct tty_struct *tty, - struct tty_struct *tty2); -extern void __lockfunc tty_unlock_pair(struct tty_struct *tty, - struct tty_struct *tty2); - +extern void __lockfunc tty_lock_slave(struct tty_struct *tty); +extern void __lockfunc tty_unlock_slave(struct tty_struct *tty); /* * this shall be called only from where BTM is held (like close) * -- cgit v0.10.2 From 2febdb632bb96235b94b8fccaf882a78f8f4b2bb Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:13:02 -0500 Subject: tty: Preset lock subclass for nested tty locks Eliminate the requirement of specifying the tty lock nesting at lock time; instead, set the lock subclass for slave ptys at pty install (normal ttys and master ptys use subclass 0). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index bdb8fd1..11db7dc 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -399,6 +399,8 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, if (!o_tty) goto err_put_module; + tty_set_lock_subclass(o_tty); + if (legacy) { /* We always use new tty termios data so we can do this the easy way .. */ diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index f43e995..4486741 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -13,15 +13,14 @@ enum { TTY_MUTEX_NORMAL, - TTY_MUTEX_NESTED, + TTY_MUTEX_SLAVE, }; /* * Getting the big tty mutex. */ -static void __lockfunc tty_lock_nested(struct tty_struct *tty, - unsigned int subclass) +void __lockfunc tty_lock(struct tty_struct *tty) { if (tty->magic != TTY_MAGIC) { pr_err("L Bad %p\n", tty); @@ -29,12 +28,7 @@ static void __lockfunc tty_lock_nested(struct tty_struct *tty, return; } tty_kref_get(tty); - mutex_lock_nested(&tty->legacy_mutex, subclass); -} - -void __lockfunc tty_lock(struct tty_struct *tty) -{ - return tty_lock_nested(tty, TTY_MUTEX_NORMAL); + mutex_lock(&tty->legacy_mutex); } EXPORT_SYMBOL(tty_lock); @@ -56,7 +50,7 @@ void __lockfunc tty_lock_slave(struct tty_struct *tty) WARN_ON(!mutex_is_locked(&tty->link->legacy_mutex) || !tty->driver->type == TTY_DRIVER_TYPE_PTY || !tty->driver->type == PTY_TYPE_SLAVE); - tty_lock_nested(tty, TTY_MUTEX_NESTED); + tty_lock(tty); } } @@ -65,3 +59,8 @@ void __lockfunc tty_unlock_slave(struct tty_struct *tty) if (tty && tty != tty->link) tty_unlock(tty); } + +void tty_set_lock_subclass(struct tty_struct *tty) +{ + lockdep_set_subclass(&tty->legacy_mutex, TTY_MUTEX_SLAVE); +} diff --git a/include/linux/tty.h b/include/linux/tty.h index a07b4b4..196c352 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -640,6 +640,7 @@ extern void __lockfunc tty_lock(struct tty_struct *tty); extern void __lockfunc tty_unlock(struct tty_struct *tty); extern void __lockfunc tty_lock_slave(struct tty_struct *tty); extern void __lockfunc tty_unlock_slave(struct tty_struct *tty); +extern void tty_set_lock_subclass(struct tty_struct *tty); /* * this shall be called only from where BTM is held (like close) * -- cgit v0.10.2 From 35af935ee47eb4548e0052733e9d75328e260fa1 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:13:03 -0500 Subject: tty: Remove tty_unhangup() declaration The tty_unhangup() function is not defined. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/tty.h b/include/linux/tty.h index 196c352..e33ea1a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -435,7 +435,6 @@ extern int is_ignored(int sig); extern int tty_signal(int sig, struct tty_struct *tty); extern void tty_hangup(struct tty_struct *tty); extern void tty_vhangup(struct tty_struct *tty); -extern void tty_unhangup(struct file *filp); extern int tty_hung_up_p(struct file *filp); extern void do_SAK(struct tty_struct *tty); extern void __do_SAK(struct tty_struct *tty); -- cgit v0.10.2 From 1bb954153aa97702db4363c6746794d097d9c707 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:13:04 -0500 Subject: pty: Don't drop pty master tty lock to hangup slave With the revised tty lock order and lockdep annotation, claiming the pty slave lock is now safe while still holding the pty master lock. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 11db7dc..bee9776 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -66,9 +66,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&devpts_mutex); } #endif - tty_unlock(tty); tty_vhangup(tty->link); - tty_lock(tty); } } -- cgit v0.10.2 From 52bce7f8d4fc633c9a9d0646eef58ba6ae9a3b73 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:13:05 -0500 Subject: pty, n_tty: Simplify input processing on final close When releasing one end of a pty pair, that end may just have written to the other, which the input processing worker, flush_to_ldisc(), is still working on but has not completed the copy to the other end's read buffer. So input may not appear to be available to a waiting reader but yet TTY_OTHER_CLOSED is now observed. The n_tty line discipline has worked around this by waiting for input processing to complete and then re-checking if input is available before exiting with -EIO. Since the tty/ldisc lock reordering, the wait for input processing to complete can now occur during final close before setting TTY_OTHER_CLOSED. In this way, a waiting reader is guaranteed to see input available (if any) before observing TTY_OTHER_CLOSED. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 0cb0e12..112eda7 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2197,34 +2197,28 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, if (!input_available_p(tty, 0)) { if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { - up_read(&tty->termios_rwsem); - tty_flush_to_ldisc(tty); - down_read(&tty->termios_rwsem); - if (!input_available_p(tty, 0)) { - retval = -EIO; - break; - } - } else { - if (tty_hung_up_p(file)) - break; - if (!timeout) - break; - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - n_tty_set_room(tty); - up_read(&tty->termios_rwsem); + retval = -EIO; + break; + } + if (tty_hung_up_p(file)) + break; + if (!timeout) + break; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + n_tty_set_room(tty); + up_read(&tty->termios_rwsem); - timeout = schedule_timeout(timeout); + timeout = schedule_timeout(timeout); - down_read(&tty->termios_rwsem); - continue; - } + down_read(&tty->termios_rwsem); + continue; } __set_current_state(TASK_RUNNING); diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index bee9776..a9d256d 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -53,6 +53,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) /* Review - krefs on tty_link ?? */ if (!tty->link) return; + tty_flush_to_ldisc(tty->link); set_bit(TTY_OTHER_CLOSED, &tty->link->flags); wake_up_interruptible(&tty->link->read_wait); wake_up_interruptible(&tty->link->write_wait); -- cgit v0.10.2 From e80a10ee4de8f654cf34170da00f989470331da8 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:13:06 -0500 Subject: tty: Prefix tty_ldisc_{lock,lock_nested,unlock} functions tty_ldisc_lock(), tty_ldisc_unlock(), and tty_ldisc_lock_nested() are low-level aliases for the underlying lock mechanism. Rename with double underscore to allow for new, higher level functions with those names. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 1c4d7b6..5bdc241 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -308,19 +308,19 @@ EXPORT_SYMBOL_GPL(tty_ldisc_deref); static inline int __lockfunc -tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) +__tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) { return ldsem_down_write(&tty->ldisc_sem, timeout); } static inline int __lockfunc -tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout) +__tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout) { return ldsem_down_write_nested(&tty->ldisc_sem, LDISC_SEM_OTHER, timeout); } -static inline void tty_ldisc_unlock(struct tty_struct *tty) +static inline void __tty_ldisc_unlock(struct tty_struct *tty) { return ldsem_up_write(&tty->ldisc_sem); } @@ -332,24 +332,24 @@ tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, int ret; if (tty < tty2) { - ret = tty_ldisc_lock(tty, timeout); + ret = __tty_ldisc_lock(tty, timeout); if (ret) { - ret = tty_ldisc_lock_nested(tty2, timeout); + ret = __tty_ldisc_lock_nested(tty2, timeout); if (!ret) - tty_ldisc_unlock(tty); + __tty_ldisc_unlock(tty); } } else { /* if this is possible, it has lots of implications */ WARN_ON_ONCE(tty == tty2); if (tty2 && tty != tty2) { - ret = tty_ldisc_lock(tty2, timeout); + ret = __tty_ldisc_lock(tty2, timeout); if (ret) { - ret = tty_ldisc_lock_nested(tty, timeout); + ret = __tty_ldisc_lock_nested(tty, timeout); if (!ret) - tty_ldisc_unlock(tty2); + __tty_ldisc_unlock(tty2); } } else - ret = tty_ldisc_lock(tty, timeout); + ret = __tty_ldisc_lock(tty, timeout); } if (!ret) @@ -370,9 +370,9 @@ tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2) static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty, struct tty_struct *tty2) { - tty_ldisc_unlock(tty); + __tty_ldisc_unlock(tty); if (tty2) - tty_ldisc_unlock(tty2); + __tty_ldisc_unlock(tty2); } static void __lockfunc tty_ldisc_enable_pair(struct tty_struct *tty, -- cgit v0.10.2 From fae76e9adfa450f4c2dd5773265eb3c811a9c484 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:13:07 -0500 Subject: tty: Fix hung task on pty hangup When hanging up one end of a pty pair, there may be waiting readers/writers on the other end which may not exit, preventing tty_ldisc_lock_pair() from acquiring the other side's ldisc lock. Only acquire this side's ldisc lock; although this will no longer prevent the other side from writing new input, that input will not be processing until after the ldisc hangup is complete. Reported-by: Sasha Levin Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 5bdc241..1dbe278 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -326,6 +326,24 @@ static inline void __tty_ldisc_unlock(struct tty_struct *tty) } static int __lockfunc +tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) +{ + int ret; + + ret = __tty_ldisc_lock(tty, timeout); + if (!ret) + return -EBUSY; + set_bit(TTY_LDISC_HALTED, &tty->flags); + return 0; +} + +static void tty_ldisc_unlock(struct tty_struct *tty) +{ + clear_bit(TTY_LDISC_HALTED, &tty->flags); + __tty_ldisc_unlock(tty); +} + +static int __lockfunc tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, unsigned long timeout) { @@ -682,7 +700,7 @@ void tty_ldisc_hangup(struct tty_struct *tty) * * Avoid racing set_ldisc or tty_ldisc_release */ - tty_ldisc_lock_pair(tty, tty->link); + tty_ldisc_lock(tty, MAX_SCHEDULE_TIMEOUT); if (tty->ldisc) { @@ -704,7 +722,7 @@ void tty_ldisc_hangup(struct tty_struct *tty) WARN_ON(tty_ldisc_open(tty, tty->ldisc)); } } - tty_ldisc_enable_pair(tty, tty->link); + tty_ldisc_unlock(tty); if (reset) tty_reset_termios(tty); -- cgit v0.10.2 From 276a661a4d75258b3aa28701b0748f99b773563b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:13:08 -0500 Subject: tty: Fix timeout on pty set ldisc When changing the ldisc on one end of a pty pair, there may be waiting readers/writers on the other end which may not exit from the ldisc i/o loop, preventing tty_ldisc_lock_pair_timeout() from acquiring the other side's ldisc lock. Only acquire this side's ldisc lock; although this will no longer prevent the other side from writing new input, that input will not be processed until after the ldisc change completes. This has no effect on normal ttys; new input from the driver was never disabled. Remove tty_ldisc_enable_pair(). Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 1dbe278..6368dd9 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -393,16 +393,6 @@ static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty, __tty_ldisc_unlock(tty2); } -static void __lockfunc tty_ldisc_enable_pair(struct tty_struct *tty, - struct tty_struct *tty2) -{ - clear_bit(TTY_LDISC_HALTED, &tty->flags); - if (tty2) - clear_bit(TTY_LDISC_HALTED, &tty2->flags); - - tty_ldisc_unlock_pair(tty, tty2); -} - /** * tty_ldisc_flush - flush line discipline queue * @tty: tty @@ -535,14 +525,13 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) { int retval; struct tty_ldisc *old_ldisc, *new_ldisc; - struct tty_struct *o_tty = tty->link; new_ldisc = tty_ldisc_get(tty, ldisc); if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); tty_lock(tty); - retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ); + retval = tty_ldisc_lock(tty, 5 * HZ); if (retval) { tty_ldisc_put(new_ldisc); tty_unlock(tty); @@ -554,7 +543,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) */ if (tty->ldisc->ops->num == ldisc) { - tty_ldisc_enable_pair(tty, o_tty); + tty_ldisc_unlock(tty); tty_ldisc_put(new_ldisc); tty_unlock(tty); return 0; @@ -565,7 +554,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (test_bit(TTY_HUPPED, &tty->flags)) { /* We were raced by the hangup method. It will have stomped the ldisc data and closed the ldisc down */ - tty_ldisc_enable_pair(tty, o_tty); + tty_ldisc_unlock(tty); tty_ldisc_put(new_ldisc); tty_unlock(tty); return -EIO; @@ -599,13 +588,11 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) /* * Allow ldisc referencing to occur again */ - tty_ldisc_enable_pair(tty, o_tty); + tty_ldisc_unlock(tty); /* Restart the work queue in case no characters kick it off. Safe if already running */ schedule_work(&tty->port->buf.work); - if (o_tty) - schedule_work(&o_tty->port->buf.work); tty_unlock(tty); return retval; -- cgit v0.10.2 From 86c80a8e2ab443e9c4261b3499de4ce808399104 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:13:09 -0500 Subject: tty: Flush ldisc buffer atomically with tty flip buffers tty_ldisc_flush() first clears the line discipline input buffer, then clears the tty flip buffers. However, this allows for existing data in the tty flip buffers to be added after the ldisc input buffer has been cleared, but before the flip buffers have been cleared. Add an optional ldisc parameter to tty_buffer_flush() to allow tty_ldisc_flush() to pass the ldisc to clear. NB: Initially, the plan was to do this automatically in tty_buffer_flush(). However, an audit of the behavior of existing line disciplines showed that performing a ldisc buffer flush on ioctl(TCFLSH) was not always the outcome. For example, some line disciplines have flush_buffer() methods but not ioctl() methods, so a ->flush_buffer() command would be unexpected. Reviewed-by: Alan Cox Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 143deb6..3605103 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -202,14 +202,16 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) /** * tty_buffer_flush - flush full tty buffers * @tty: tty to flush + * @ld: optional ldisc ptr (must be referenced) * - * flush all the buffers containing receive data. + * flush all the buffers containing receive data. If ld != NULL, + * flush the ldisc input buffer. * * Locking: takes buffer lock to ensure single-threaded flip buffer * 'consumer' */ -void tty_buffer_flush(struct tty_struct *tty) +void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld) { struct tty_port *port = tty->port; struct tty_bufhead *buf = &port->buf; @@ -223,6 +225,10 @@ void tty_buffer_flush(struct tty_struct *tty) buf->head = next; } buf->head->read = buf->head->commit; + + if (ld && ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); + atomic_dec(&buf->priority); mutex_unlock(&buf->lock); } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 4ecee28..aa83cd1 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2890,7 +2890,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TCIFLUSH: case TCIOFLUSH: /* flush tty buffer and allow ldisc to process ioctl */ - tty_buffer_flush(tty); + tty_buffer_flush(tty, NULL); break; } break; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 6368dd9..b66a81d 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -397,19 +397,17 @@ static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty, * tty_ldisc_flush - flush line discipline queue * @tty: tty * - * Flush the line discipline queue (if any) for this tty. If there - * is no line discipline active this is a no-op. + * Flush the line discipline queue (if any) and the tty flip buffers + * for this tty. */ void tty_ldisc_flush(struct tty_struct *tty) { struct tty_ldisc *ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->ops->flush_buffer) - ld->ops->flush_buffer(tty); + + tty_buffer_flush(tty, ld); + if (ld) tty_ldisc_deref(ld); - } - tty_buffer_flush(tty); } EXPORT_SYMBOL_GPL(tty_ldisc_flush); diff --git a/include/linux/tty.h b/include/linux/tty.h index e33ea1a..f6835ea 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -441,7 +441,7 @@ extern void __do_SAK(struct tty_struct *tty); extern void no_tty(void); extern void tty_flush_to_ldisc(struct tty_struct *tty); extern void tty_buffer_free_all(struct tty_port *port); -extern void tty_buffer_flush(struct tty_struct *tty); +extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld); extern void tty_buffer_init(struct tty_port *port); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); -- cgit v0.10.2 From 479e9b94fdce7bc46f669831012fc12f56696fd7 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 16:54:18 -0400 Subject: serial: Refactor uart_flush_buffer() from uart_close() In the context of the final tty & port close, flushing the tx ring buffer after the hardware has already been shutdown and the ring buffer freed is neither required nor desirable. uart_flush_buffer() performs 3 operations: 1. Resets tx ring buffer indices, but the tx ring buffer has already been freed and the indices are reset if the port is re-opened. 2. Calls uart driver's flush_buffer() method 5 in-tree uart drivers define flush_buffer() methods: amba-pl011, atmel-serial, imx, serial-tegra, timbuart These have been refactored into the shutdown() method, if required. 3. Kicks the ldisc for more writing, but this is undesirable. The file handle is being released; any waiting writer will will be kicked out by tty_release() with a warning. Further, the N_TTY ldisc may generate SIGIO for a file handle which is no longer valid. Cc: Nicolas Ferre Cc: Russell King Cc: Laxman Dewangan Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 02016fc..8469b66 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1689,6 +1689,7 @@ static void pl011_shutdown(struct uart_port *port) plat->exit(); } + pl011_dma_flush_buffer(port); } static void diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index edde3ec..8a84034 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1802,6 +1802,20 @@ free_irq: } /* + * Flush any TX data submitted for DMA. Called when the TX circular + * buffer is reset. + */ +static void atmel_flush_buffer(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_pdc_tx(port)) { + UART_PUT_TCR(port, 0); + atmel_port->pdc_tx.ofs = 0; + } +} + +/* * Disable the port */ static void atmel_shutdown(struct uart_port *port) @@ -1852,20 +1866,8 @@ static void atmel_shutdown(struct uart_port *port) atmel_free_gpio_irq(port); atmel_port->ms_irq_enabled = false; -} -/* - * Flush any TX data submitted for DMA. Called when the TX circular - * buffer is reset. - */ -static void atmel_flush_buffer(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - if (atmel_use_pdc_tx(port)) { - UART_PUT_TCR(port, 0); - atmel_port->pdc_tx.ofs = 0; - } + atmel_flush_buffer(port); } /* diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 53d7c31..78a5cf6 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -1034,6 +1034,20 @@ fail_rx_dma: return ret; } +/* + * Flush any TX data submitted for DMA and PIO. Called when the + * TX circular buffer is reset. + */ +static void tegra_uart_flush_buffer(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + + tup->tx_bytes = 0; + if (tup->tx_dma_chan) + dmaengine_terminate_all(tup->tx_dma_chan); + return; +} + static void tegra_uart_shutdown(struct uart_port *u) { struct tegra_uart_port *tup = to_tegra_uport(u); @@ -1046,6 +1060,8 @@ static void tegra_uart_shutdown(struct uart_port *u) tegra_uart_dma_channel_free(tup, true); tegra_uart_dma_channel_free(tup, false); free_irq(u->irq, tup); + + tegra_uart_flush_buffer(u); } static void tegra_uart_enable_ms(struct uart_port *u) @@ -1174,20 +1190,6 @@ static void tegra_uart_set_termios(struct uart_port *u, return; } -/* - * Flush any TX data submitted for DMA and PIO. Called when the - * TX circular buffer is reset. - */ -static void tegra_uart_flush_buffer(struct uart_port *u) -{ - struct tegra_uart_port *tup = to_tegra_uport(u); - - tup->tx_bytes = 0; - if (tup->tx_dma_chan) - dmaengine_terminate_all(tup->tx_dma_chan); - return; -} - static const char *tegra_uart_type(struct uart_port *u) { return TEGRA_UART_TYPE; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 1166c52..787d67f 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1361,7 +1361,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) mutex_lock(&port->mutex); uart_shutdown(tty, state); - uart_flush_buffer(tty); tty_ldisc_flush(tty); diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 0d11d50..e9e2523 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -273,6 +273,8 @@ static void timbuart_shutdown(struct uart_port *port) dev_dbg(port->dev, "%s\n", __func__); free_irq(port->irq, uart); iowrite32(0, port->membase + TIMBUART_IER); + + timbuart_flush_buffer(port); } static int get_bindex(int baud) -- cgit v0.10.2 From 2e758910832dce99761a29688b33fd77dcbf6f6c Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 16:54:19 -0400 Subject: serial: core: Flush ldisc after dropping port mutex in uart_close() The tty buffers (and any line discipline buffers) must be flushed after the UART hardware has shutdown; otherwise, a racing open on the same tty may receive data from the previous session, which is a security hazard. However, holding the port mutex while flushing the line discipline buffers creates a lock inversion if the set_termios() handler takes the port mutex (as it does in the followup patch, 'serial: Fix locking for uart driver set_termios method'. Flush the ldisc buffers after dropping the port mutex; the tty lock is still held which prevents a concurrent open() from advancing while flushing. Since no new rx data is possible after uart_shutdown() until a new open reinitializes the port, the later flush has no impact on what data is being discarded. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 787d67f..9d14297 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1361,9 +1361,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) mutex_lock(&port->mutex); uart_shutdown(tty, state); - - tty_ldisc_flush(tty); - tty_port_tty_set(port, NULL); tty->closing = 0; spin_lock_irqsave(&port->lock, flags); @@ -1390,6 +1387,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&port->close_wait); mutex_unlock(&port->mutex); + + tty_ldisc_flush(tty); } static void uart_wait_until_sent(struct tty_struct *tty, int timeout) -- cgit v0.10.2 From 7c8ab967e3cd1513cd79fd5edc404fb43c7f3a96 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 16:54:20 -0400 Subject: serial: Fix locking for uart driver set_termios() method The low-level uart driver may modify termios settings to override settings that are not compatible with the uart, such as CRTSCTS. Thus, callers of the low-level uart driver's set_termios() method must hold termios_rwsem write lock to prevent concurrent access to termios, in case such override occurs. The termios_rwsem lock requirement does not extend to console setup (ie., uart_set_options), as console setup cannot race with tty operations. Nor does this lock requirement extend to functions which cannot be concurrent with tty ioctls (ie., uart_port_startup() and uart_resume_port()). Further, always claim the port mutex to protect hardware re-reprogramming in the set_termios() uart driver method. Note this is unnecessary for console initialization in uart_set_options() which cannot be concurrent with other uart operations. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/serial/driver b/Documentation/serial/driver index ba64e4b..c415b0e 100644 --- a/Documentation/serial/driver +++ b/Documentation/serial/driver @@ -59,7 +59,9 @@ The core driver uses the info->tmpbuf_sem lock to prevent multi-threaded access to the info->tmpbuf bouncebuffer used for port writes. The port_sem semaphore is used to protect against ports being added/ -removed or reconfigured at inappropriate times. +removed or reconfigured at inappropriate times. Since v2.6.27, this +semaphore has been the 'mutex' member of the tty_port struct, and +commonly referred to as the port mutex (or port->mutex). uart_ops @@ -248,7 +250,7 @@ hardware. Other flags may be used (eg, xon/xoff characters) if your hardware supports hardware "soft" flow control. - Locking: none. + Locking: caller holds port->mutex Interrupts: caller dependent. This call must not sleep diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 9d14297..e31d561 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -436,7 +436,7 @@ uart_get_divisor(struct uart_port *port, unsigned int baud) EXPORT_SYMBOL(uart_get_divisor); -/* FIXME: Consistent locking policy */ +/* Caller holds port mutex */ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios) { @@ -1173,11 +1173,15 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, break; case TIOCSSERIAL: + down_write(&tty->termios_rwsem); ret = uart_set_info_user(tty, state, uarg); + up_write(&tty->termios_rwsem); break; case TIOCSERCONFIG: + down_write(&tty->termios_rwsem); ret = uart_do_autoconfig(tty, state); + up_write(&tty->termios_rwsem); break; case TIOCSERGWILD: /* obsolete */ @@ -1278,7 +1282,9 @@ static void uart_set_termios(struct tty_struct *tty, return; } + mutex_lock(&state->port.mutex); uart_change_speed(tty, state, old_termios); + mutex_unlock(&state->port.mutex); /* reload cflag from termios; port driver may have overriden flags */ cflag = tty->termios.c_cflag; -- cgit v0.10.2 From 904326ecac022ebaeb39cdfb206fd3b6551cdfca Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 16:54:21 -0400 Subject: tty,serial: Unify UPF_* and ASYNC_* flag definitions The userspace-defined ASYNC_* flags in include/uapi/linux/tty_flags.h are the authoritative bit definitions for the serial_struct flags, and thus for any derivative values or fields. Although the serial core provides the TIOCSSERIAL and TIOCGSERIAL ioctls to set and retrieve these flags from userspace, it defines these bits independently, as UPF_* macros. Define the UPF_* macros which are userspace-modifiable directly from the ASYNC_* symbolic constants. Add compile-time test to ensure the bits changeable by TIOCSSERIAL match the defined range in the uapi header. Add ASYNCB_MAGIC_MULTIPLIER to the uapi header since this bit is programmable by userspace. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index bccf4ba..ad93296 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -160,21 +160,33 @@ struct uart_port { /* flags must be updated while holding port mutex */ upf_t flags; -#define UPF_FOURPORT ((__force upf_t) (1 << 1)) -#define UPF_SAK ((__force upf_t) (1 << 2)) -#define UPF_SPD_MASK ((__force upf_t) (0x1030)) -#define UPF_SPD_HI ((__force upf_t) (0x0010)) -#define UPF_SPD_VHI ((__force upf_t) (0x0020)) -#define UPF_SPD_CUST ((__force upf_t) (0x0030)) -#define UPF_SPD_SHI ((__force upf_t) (0x1000)) -#define UPF_SPD_WARP ((__force upf_t) (0x1010)) -#define UPF_SKIP_TEST ((__force upf_t) (1 << 6)) -#define UPF_AUTO_IRQ ((__force upf_t) (1 << 7)) -#define UPF_HARDPPS_CD ((__force upf_t) (1 << 11)) -#define UPF_LOW_LATENCY ((__force upf_t) (1 << 13)) -#define UPF_BUGGY_UART ((__force upf_t) (1 << 14)) + /* + * These flags must be equivalent to the flags defined in + * include/uapi/linux/tty_flags.h which are the userspace definitions + * assigned from the serial_struct flags in uart_set_info() + * [for bit definitions in the UPF_CHANGE_MASK] + * + * Bits [0..UPF_LAST_USER] are userspace defined/visible/changeable + * except bit 15 (UPF_NO_TXEN_TEST) which is masked off. + * The remaining bits are serial-core specific and not modifiable by + * userspace. + */ +#define UPF_FOURPORT ((__force upf_t) ASYNC_FOURPORT /* 1 */ ) +#define UPF_SAK ((__force upf_t) ASYNC_SAK /* 2 */ ) +#define UPF_SPD_HI ((__force upf_t) ASYNC_SPD_HI /* 4 */ ) +#define UPF_SPD_VHI ((__force upf_t) ASYNC_SPD_VHI /* 5 */ ) +#define UPF_SPD_CUST ((__force upf_t) ASYNC_SPD_CUST /* 0x0030 */ ) +#define UPF_SPD_WARP ((__force upf_t) ASYNC_SPD_WARP /* 0x1010 */ ) +#define UPF_SPD_MASK ((__force upf_t) ASYNC_SPD_MASK /* 0x1030 */ ) +#define UPF_SKIP_TEST ((__force upf_t) ASYNC_SKIP_TEST /* 6 */ ) +#define UPF_AUTO_IRQ ((__force upf_t) ASYNC_AUTO_IRQ /* 7 */ ) +#define UPF_HARDPPS_CD ((__force upf_t) ASYNC_HARDPPS_CD /* 11 */ ) +#define UPF_SPD_SHI ((__force upf_t) ASYNC_SPD_SHI /* 12 */ ) +#define UPF_LOW_LATENCY ((__force upf_t) ASYNC_LOW_LATENCY /* 13 */ ) +#define UPF_BUGGY_UART ((__force upf_t) ASYNC_BUGGY_UART /* 14 */ ) #define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15)) -#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16)) +#define UPF_MAGIC_MULTIPLIER ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ ) + /* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */ #define UPF_HARD_FLOW ((__force upf_t) (1 << 21)) /* Port has hardware-assisted s/w flow control */ @@ -190,9 +202,14 @@ struct uart_port { #define UPF_DEAD ((__force upf_t) (1 << 30)) #define UPF_IOREMAP ((__force upf_t) (1 << 31)) -#define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) +#define __UPF_CHANGE_MASK 0x17fff +#define UPF_CHANGE_MASK ((__force upf_t) __UPF_CHANGE_MASK) #define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) +#if __UPF_CHANGE_MASK > ASYNC_FLAGS +#error Change mask not equivalent to userspace-visible bit defines +#endif + /* status must be updated while holding port lock */ upstat_t status; diff --git a/include/uapi/linux/tty_flags.h b/include/uapi/linux/tty_flags.h index eefcb48..7b516f7 100644 --- a/include/uapi/linux/tty_flags.h +++ b/include/uapi/linux/tty_flags.h @@ -6,6 +6,8 @@ * shared by the tty_port flags structures. * * Define ASYNCB_* for convenient use with {test,set,clear}_bit. + * + * Bits [0..ASYNCB_LAST_USER] are userspace defined/visible/changeable */ #define ASYNCB_HUP_NOTIFY 0 /* Notify getty on hangups and closes * on the callout port */ @@ -26,7 +28,8 @@ #define ASYNCB_BUGGY_UART 14 /* This is a buggy UART, skip some safety * checks. Note: can be dangerous! */ #define ASYNCB_AUTOPROBE 15 /* Port was autoprobed by PCI or PNP code */ -#define ASYNCB_LAST_USER 15 +#define ASYNCB_MAGIC_MULTIPLIER 16 /* Use special CLK or divisor */ +#define ASYNCB_LAST_USER 16 /* Internal flags used only by kernel */ #define ASYNCB_INITIALIZED 31 /* Serial port was initialized */ @@ -57,6 +60,7 @@ #define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY) #define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART) #define ASYNC_AUTOPROBE (1U << ASYNCB_AUTOPROBE) +#define ASYNC_MAGIC_MULTIPLIER (1U << ASYNCB_MAGIC_MULTIPLIER) #define ASYNC_FLAGS ((1U << (ASYNCB_LAST_USER + 1)) - 1) #define ASYNC_USR_MASK (ASYNC_SPD_MASK|ASYNC_CALLOUT_NOHUP| \ -- cgit v0.10.2 From 352f86187e1d3e9e27d3be237103dc83a48f882c Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 16:54:22 -0400 Subject: tty: Document defunct ASYNC_* bits in uapi header Note the serial_struct flags for which the kernel ignores and performs no action. The flags cannot be removed since they form part of the userspace interface via the TIOCSSERIAL/TIOCGSERIAL ioctls. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/uapi/linux/tty_flags.h b/include/uapi/linux/tty_flags.h index 7b516f7..4e021e3 100644 --- a/include/uapi/linux/tty_flags.h +++ b/include/uapi/linux/tty_flags.h @@ -8,6 +8,7 @@ * Define ASYNCB_* for convenient use with {test,set,clear}_bit. * * Bits [0..ASYNCB_LAST_USER] are userspace defined/visible/changeable + * [x] in the bit comments indicates the flag is defunct and no longer used. */ #define ASYNCB_HUP_NOTIFY 0 /* Notify getty on hangups and closes * on the callout port */ @@ -19,15 +20,15 @@ #define ASYNCB_SKIP_TEST 6 /* Skip UART test during autoconfiguration */ #define ASYNCB_AUTO_IRQ 7 /* Do automatic IRQ during * autoconfiguration */ -#define ASYNCB_SESSION_LOCKOUT 8 /* Lock out cua opens based on session */ -#define ASYNCB_PGRP_LOCKOUT 9 /* Lock out cua opens based on pgrp */ -#define ASYNCB_CALLOUT_NOHUP 10 /* Don't do hangups for cua device */ +#define ASYNCB_SESSION_LOCKOUT 8 /* [x] Lock out cua opens based on session */ +#define ASYNCB_PGRP_LOCKOUT 9 /* [x] Lock out cua opens based on pgrp */ +#define ASYNCB_CALLOUT_NOHUP 10 /* [x] Don't do hangups for cua device */ #define ASYNCB_HARDPPS_CD 11 /* Call hardpps when CD goes high */ #define ASYNCB_SPD_SHI 12 /* Use 230400 instead of 38400 bps */ #define ASYNCB_LOW_LATENCY 13 /* Request low latency behaviour */ #define ASYNCB_BUGGY_UART 14 /* This is a buggy UART, skip some safety * checks. Note: can be dangerous! */ -#define ASYNCB_AUTOPROBE 15 /* Port was autoprobed by PCI or PNP code */ +#define ASYNCB_AUTOPROBE 15 /* [x] Port was autoprobed by PCI/PNP code */ #define ASYNCB_MAGIC_MULTIPLIER 16 /* Use special CLK or divisor */ #define ASYNCB_LAST_USER 16 -- cgit v0.10.2 From 74866e7593d9824d527e73f548c4fb8e412588a1 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 16:54:23 -0400 Subject: serial: core: Unwrap >80 char line in uart_close() The wrapped line looks wrong and out-of-place; leave it as >80 char line. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index e31d561..67b2c35 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1374,8 +1374,7 @@ 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( - jiffies_to_msecs(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); -- cgit v0.10.2 From 1f0afd1607cde1a58068937f8c247fd3c58a866d Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 16:54:24 -0400 Subject: serial: core: Remove redundant timeout assignments tty_port_init() initializes close_delay and closing_wait to these same values; remove. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 67b2c35..af1d466 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2342,8 +2342,6 @@ int uart_register_driver(struct uart_driver *drv) tty_port_init(port); port->ops = &uart_port_ops; - port->close_delay = HZ / 2; /* .5 seconds */ - port->closing_wait = 30 * HZ;/* 30 seconds */ } retval = tty_register_driver(normal); -- cgit v0.10.2 From 2b702b9b680cd17dbdd93461dd3f66297e313809 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 16:54:25 -0400 Subject: serial: core: Colocate crucial structure linkage The key function of uart_add_one_port() is to cross-reference the UART driver's port structure with the serial core's state table; keep the assignments together and document this crucial association. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index af1d466..e0082b7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2590,11 +2590,12 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) goto out; } + /* Link the port to the driver state table and vice versa */ state->uart_port = uport; - state->pm_state = UART_PM_STATE_UNDEFINED; + uport->state = state; + state->pm_state = UART_PM_STATE_UNDEFINED; uport->cons = drv->cons; - uport->state = state; /* * If this port is a console, then the spinlock is already -- cgit v0.10.2 From 64dbee31050d5b93b5fcda25508e4d3c69f6589c Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 16:54:26 -0400 Subject: serial: core: Remove extra locking in uart_write() uart_start() only claims the port->lock to call __uart_start(), which does the actual processing. Eliminate the extra acquire/release in uart_write(); call __uart_start() directly with port->lock already held. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index e0082b7..0282eaa 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -537,9 +537,10 @@ static int uart_write(struct tty_struct *tty, count -= c; ret += c; } + + __uart_start(tty); spin_unlock_irqrestore(&port->lock, flags); - uart_start(tty); return ret; } -- cgit v0.10.2 From 91b32f5413c17e80ac6616dd9c62e826fa438e2d Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 16:54:27 -0400 Subject: serial: core: Fix port count when uart_open() errors A port count mismatch occurs if mutex_lock_interruptible() exits uart_open() and the port has already been opened. This may prematurely close a port on an open tty. Since uart_close() is _always_ called if uart_open() fails, the port count must be corrected if errors occur. Always increment the port count in uart_open(), regardless of errors; always decrement the port count in uart_close(). Note that tty_port_close_start() decrements the port count when uart_open() was successful. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0282eaa..3e36ecf 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1338,8 +1338,16 @@ static void uart_close(struct tty_struct *tty, struct file *filp) struct uart_port *uport; unsigned long flags; - if (!state) + if (!state) { + struct uart_driver *drv = tty->driver->driver_state; + + state = drv->state + tty->index; + port = &state->port; + spin_lock_irq(&port->lock); + --port->count; + spin_unlock_irq(&port->lock); return; + } uport = state->uart_port; port = &state->port; @@ -1556,6 +1564,10 @@ static int uart_open(struct tty_struct *tty, struct file *filp) pr_debug("uart_open(%d) called\n", line); + spin_lock_irq(&port->lock); + ++port->count; + spin_unlock_irq(&port->lock); + /* * 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 @@ -1568,17 +1580,11 @@ static int uart_open(struct tty_struct *tty, struct file *filp) goto end; } - port->count++; if (!state->uart_port || state->uart_port->flags & UPF_DEAD) { retval = -ENXIO; - goto err_dec_count; + goto err_unlock; } - /* - * Once we set tty->driver_data here, we are guaranteed that - * uart_close() will decrement the driver module use count. - * Any failures from here onwards should not touch the count. - */ tty->driver_data = state; state->uart_port->state = state; state->port.low_latency = @@ -1599,8 +1605,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) end: return retval; -err_dec_count: - port->count--; +err_unlock: mutex_unlock(&port->mutex); goto end; } -- cgit v0.10.2 From d4ac0633ad28580df12ed050993fa426d60c6429 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 5 Nov 2014 13:20:50 +0100 Subject: tty: serial: omap: Remove probe error message This error message is not necessary. The driver core code will print all probe error messages. It also resolves some error codes to proper error messages. For example -EPROBE_DEFER will only be printed as an info message. This patch removes the error message as the core prints the same information. Signed-off-by: Markus Pargmann Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 18c30ca..6cda5cd 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1747,8 +1747,6 @@ err_add_port: pm_runtime_disable(&pdev->dev); err_rs485: err_port_line: - dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", - pdev->id, __func__, ret); return ret; } -- cgit v0.10.2 From 914d3b17e90d9f75f764ffbdbdae354056b9fb79 Mon Sep 17 00:00:00 2001 From: Janusz Uzycki Date: Fri, 10 Oct 2014 13:13:28 +0200 Subject: serial: mxs-auart: add sysrq support When using mxs-auart based console, sometime we need the sysrq function to help debugging kernel. The sysrq code is basically there, this patch just simply enable it. Signed-off-by: Janusz Uzycki Reviewed-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 10c2933..0296c1c 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -14,6 +14,10 @@ * http://www.gnu.org/copyleft/gpl.html */ +#if defined(CONFIG_SERIAL_MXS_AUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include #include #include -- cgit v0.10.2 From 6a529abae8ef51e3cac8b09e3a03b054d5099808 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 20:06:37 +0200 Subject: tty: serial: 8250: Fix wording in runtime-PM comments Frans reworded the two comments with better English for better understanding. His review hit the mailing list after the patch got applied so here is an incremental update. Reported-by: Frans Klaver Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index b170487..3b66668 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -557,7 +557,7 @@ static void serial8250_rpm_put(struct uart_8250_port *p) } /* - * This two wrapper ensure, that enable_runtime_pm_tx() can be called more than + * These two wrappers ensure that enable_runtime_pm_tx() can be called more than * once and disable_runtime_pm_tx() will still disable RPM because the fifo is * empty and the HW can idle again. */ @@ -1535,7 +1535,7 @@ void serial8250_tx_chars(struct uart_8250_port *up) DEBUG_INTR("THRE..."); /* - * With RPM enabled, we have to wait once the FIFO is empty before the + * With RPM enabled, we have to wait until the FIFO is empty before the * HW can go idle. So we get here once again with empty FIFO and disable * the interrupt and RPM in __stop_tx() */ -- cgit v0.10.2 From f31b5d27be486b62901f78c693fc0d7a52afc1cd Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 20:06:38 +0200 Subject: tty: serial: 8250: make serial8250_console_setup() non _init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit if I boot with console=ttyS0 and the omap driver is module I end up with | console [ttyS0] disabled | omap8250 44e09000.serial: ttyS0 at MMIO 0x44e09000 (irq = 88, base_baud = 3000000) is a 8250 | Unable to handle kernel paging request at virtual address c07a9de0 | Modules linked in: 8250_omap(+) | CPU: 0 PID: 908 Comm: modprobe Not tainted 3.17.0-rc5+ #1593 | PC is at serial8250_console_setup+0x0/0xc8 | LR is at register_console+0x13c/0x3a4 | [] (register_console) from [] (uart_add_one_port+0x3cc/0x420) | [] (uart_add_one_port) from [] (serial8250_register_8250_port+0x298/0x39c) | [] (serial8250_register_8250_port) from [] (omap8250_probe+0x218/0x3dc [8250_omap]) | [] (omap8250_probe [8250_omap]) from [] (platform_drv_probe+0x2c/0x5c) | [] (platform_drv_probe) from [] (driver_probe_device+0x104/0x228) … | [] (SyS_init_module) from [] (ret_fast_syscall+0x0/0x30) | Code: 7823603b f8314620 051b3013 491ed416 (44792204) because serial8250_console_setup() is already gone. Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 3b66668..2713664 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3240,7 +3240,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) serial8250_rpm_put(up); } -static int __init serial8250_console_setup(struct console *co, char *options) +static int serial8250_console_setup(struct console *co, char *options) { struct uart_port *port; int baud = 9600; -- cgit v0.10.2 From 61929cf0169d91366fd3f30d6ee60681b037bc19 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 20:06:39 +0200 Subject: tty: serial: Add 8250-core based omap driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch provides a 8250-core based UART driver for the internal OMAP UART. The long term goal is to provide the same functionality as the current OMAP uart driver and DMA support. I tried to merge omap-serial code together with the 8250-core code. There should should be hardly a noticable difference. The trigger levels are different compared to omap-serial: - omap serial TX: Interrupt comes after TX FIFO has room for 16 bytes. TX of 4096 bytes in one go results in 256 interrupts RX: Interrupt comes after there is on byte in the FIFO. RX of 4096 bytes results in 4096 interrupts. - this driver TX: Interrupt comes once the TX FIFO is empty. TX of 4096 bytes results in 65 interrupts. That means there will be gaps on the line while the driver reloads the FIFO. RX: Interrupt comes once there are 48 bytes in the FIFO or less over "longer" time frame. We have 1 / 11520 * 10^3 * 16 => 1.38… ms 1.38ms to react and purge the FIFO on 115200,8N1. Since the other driver fired after each byte it had ~5.47ms time to react. This _may_ cause problems if one relies on no missing bytes and has no flow control. On the other hand we get only 85 interrupts for the same amount of data. It has been only tested as console UART on am335x-evm, dra7-evm and beagle bone. I also did some longer raw-transfers to meassure the load. The device name is ttyS based instead of ttyO. If a ttyO based node name is required please ask udev for it. If both driver are activated (this and omap-serial) then this serial driver will take control over the device due to the link order v9…v10: - Tony noticed that omap3 won't show anything after waking up from core off. In v9 I reworked the register restore and set IER to 0 by accident. This went unnoticed because start_tx usually sets ier (either due to DMA bug or due to TX-complete IRQ). - dropped EFR and SLEEP from capabilities. We do have both but nobody should touch it. We already handle SLEEP ourself. - make the private copy of the registers (like EFR) u8 instead u32 - drop MDR1 & DL[ML] reset in restore registers. Does not look required it is set to the required value later. - update MDR1 & SCR only if changed. - set MDR1 as the last thing. The errata says that we should setup everything before MDR1 set. - avoid div by 0 in omap_8250_get_divisor() if baud rate gets very large (Frans Klaver fixed the same thing omap-serial) - drop "is in early stage" from Kconfig. v8…v9: - less on a file seems to hang the am335x after a while. I believe I introduce this bug a while ago since I can reproduce this prior to v8. Fixed by redoing the omap8250_restore_regs() v7…v8: - redo the register write. There is now one function for that which is used from set_termios() and runtime-resume. - drop PORT_OMAP_16750 and move the setup to the omap file. We have our own set termios function anyway (Heikki Krogerus) - use MEM instead of MEM32. TRM of AM/DM37x says that 32bit access on THR might result in data abort. We only need 32bit access in the errata function which is before we use 8250's read function so it doesn't matter. v4…v7: - change trigger levels after some tests with raw transfers. v3…v4: - drop RS485 support - wire up ->throttle / ->unthrottle v2…v3: - wire up startup & shutdown for wakeup-irq handling. - RS485 handling (well the core does). v1…v2: - added runtime PM. Could somebody could please double check this? - added omap_8250_set_termios() Reviewed-by: Tony Lindgren Tested-by: Tony Lindgren Tested-by: Frans Klaver Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c new file mode 100644 index 0000000..2f653c4 --- /dev/null +++ b/drivers/tty/serial/8250/8250_omap.c @@ -0,0 +1,914 @@ +/* + * 8250-core based driver for the OMAP internal UART + * + * based on omap-serial.c, Copyright (C) 2010 Texas Instruments. + * + * Copyright (C) 2014 Sebastian Andrzej Siewior + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "8250.h" + +#define DEFAULT_CLK_SPEED 48000000 + +#define UART_ERRATA_i202_MDR1_ACCESS (1 << 0) +#define OMAP_UART_WER_HAS_TX_WAKEUP (1 << 1) + +#define OMAP_UART_FCR_RX_TRIG 6 +#define OMAP_UART_FCR_TX_TRIG 4 + +/* SCR register bitmasks */ +#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) +#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK (1 << 6) +#define OMAP_UART_SCR_TX_EMPTY (1 << 3) +#define OMAP_UART_SCR_DMAMODE_MASK (3 << 1) +#define OMAP_UART_SCR_DMAMODE_1 (1 << 1) +#define OMAP_UART_SCR_DMAMODE_CTL (1 << 0) + +/* MVR register bitmasks */ +#define OMAP_UART_MVR_SCHEME_SHIFT 30 +#define OMAP_UART_LEGACY_MVR_MAJ_MASK 0xf0 +#define OMAP_UART_LEGACY_MVR_MAJ_SHIFT 4 +#define OMAP_UART_LEGACY_MVR_MIN_MASK 0x0f +#define OMAP_UART_MVR_MAJ_MASK 0x700 +#define OMAP_UART_MVR_MAJ_SHIFT 8 +#define OMAP_UART_MVR_MIN_MASK 0x3f + +#define UART_TI752_TLR_TX 0 +#define UART_TI752_TLR_RX 4 + +#define TRIGGER_TLR_MASK(x) ((x & 0x3c) >> 2) +#define TRIGGER_FCR_MASK(x) (x & 3) + +/* Enable XON/XOFF flow control on output */ +#define OMAP_UART_SW_TX 0x08 +/* Enable XON/XOFF flow control on input */ +#define OMAP_UART_SW_RX 0x02 + +#define OMAP_UART_WER_MOD_WKUP 0x7f +#define OMAP_UART_TX_WAKEUP_EN (1 << 7) + +#define TX_TRIGGER 1 +#define RX_TRIGGER 48 + +#define OMAP_UART_TCR_RESTORE(x) ((x / 4) << 4) +#define OMAP_UART_TCR_HALT(x) ((x / 4) << 0) + +#define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) + +#define OMAP_UART_REV_46 0x0406 +#define OMAP_UART_REV_52 0x0502 +#define OMAP_UART_REV_63 0x0603 + +struct omap8250_priv { + int line; + u8 habit; + u8 mdr1; + u8 efr; + u8 scr; + u8 wer; + u8 xon; + u8 xoff; + u16 quot; + + bool is_suspending; + int wakeirq; + int wakeups_enabled; + u32 latency; + u32 calc_latency; + struct pm_qos_request pm_qos_request; + struct work_struct qos_work; + struct uart_8250_dma omap8250_dma; +}; + +static u32 uart_read(struct uart_8250_port *up, u32 reg) +{ + return readl(up->port.membase + (reg << up->port.regshift)); +} + +/* + * Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460) + * The access to uart register after MDR1 Access + * causes UART to corrupt data. + * + * Need a delay = + * 5 L4 clock cycles + 5 UART functional clock cycle (@48MHz = ~0.2uS) + * give 10 times as much + */ +static void omap_8250_mdr1_errataset(struct uart_8250_port *up, + struct omap8250_priv *priv) +{ + u8 timeout = 255; + u8 old_mdr1; + + old_mdr1 = serial_in(up, UART_OMAP_MDR1); + if (old_mdr1 == priv->mdr1) + return; + + serial_out(up, UART_OMAP_MDR1, priv->mdr1); + udelay(2); + serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_XMIT | + UART_FCR_CLEAR_RCVR); + /* + * Wait for FIFO to empty: when empty, RX_FIFO_E bit is 0 and + * TX_FIFO_E bit is 1. + */ + while (UART_LSR_THRE != (serial_in(up, UART_LSR) & + (UART_LSR_THRE | UART_LSR_DR))) { + timeout--; + if (!timeout) { + /* Should *never* happen. we warn and carry on */ + dev_crit(up->port.dev, "Errata i202: timedout %x\n", + serial_in(up, UART_LSR)); + break; + } + udelay(1); + } +} + +static void omap_8250_get_divisor(struct uart_port *port, unsigned int baud, + struct omap8250_priv *priv) +{ + unsigned int uartclk = port->uartclk; + unsigned int div_13, div_16; + unsigned int abs_d13, abs_d16; + + /* + * Old custom speed handling. + */ + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) { + priv->quot = port->custom_divisor & 0xffff; + /* + * I assume that nobody is using this. But hey, if somebody + * would like to specify the divisor _and_ the mode then the + * driver is ready and waiting for it. + */ + if (port->custom_divisor & (1 << 16)) + priv->mdr1 = UART_OMAP_MDR1_13X_MODE; + else + priv->mdr1 = UART_OMAP_MDR1_16X_MODE; + return; + } + div_13 = DIV_ROUND_CLOSEST(uartclk, 13 * baud); + div_16 = DIV_ROUND_CLOSEST(uartclk, 16 * baud); + + if (!div_13) + div_13 = 1; + if (!div_16) + div_16 = 1; + + abs_d13 = abs(baud - uartclk / 13 / div_13); + abs_d16 = abs(baud - uartclk / 16 / div_16); + + if (abs_d13 >= abs_d16) { + priv->mdr1 = UART_OMAP_MDR1_16X_MODE; + priv->quot = div_16; + } else { + priv->mdr1 = UART_OMAP_MDR1_13X_MODE; + priv->quot = div_13; + } +} + +static void omap8250_update_scr(struct uart_8250_port *up, + struct omap8250_priv *priv) +{ + u8 old_scr; + + old_scr = serial_in(up, UART_OMAP_SCR); + if (old_scr == priv->scr) + return; + + /* + * The manual recommends not to enable the DMA mode selector in the SCR + * (instead of the FCR) register _and_ selecting the DMA mode as one + * register write because this may lead to malfunction. + */ + if (priv->scr & OMAP_UART_SCR_DMAMODE_MASK) + serial_out(up, UART_OMAP_SCR, + priv->scr & ~OMAP_UART_SCR_DMAMODE_MASK); + serial_out(up, UART_OMAP_SCR, priv->scr); +} + +static void omap8250_restore_regs(struct uart_8250_port *up) +{ + struct omap8250_priv *priv = up->port.private_data; + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, UART_EFR_ECB); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_MCR, UART_MCR_TCRTLR); + serial_out(up, UART_FCR, up->fcr); + + omap8250_update_scr(up, priv); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_RESTORE(16) | + OMAP_UART_TCR_HALT(52)); + serial_out(up, UART_TI752_TLR, + TRIGGER_TLR_MASK(TX_TRIGGER) << UART_TI752_TLR_TX | + TRIGGER_TLR_MASK(RX_TRIGGER) << UART_TI752_TLR_RX); + + serial_out(up, UART_LCR, 0); + + /* drop TCR + TLR access, we setup XON/XOFF later */ + serial_out(up, UART_MCR, up->mcr); + serial_out(up, UART_IER, up->ier); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_dl_write(up, priv->quot); + + serial_out(up, UART_EFR, priv->efr); + + /* Configure flow control */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_XON1, priv->xon); + serial_out(up, UART_XOFF1, priv->xoff); + + serial_out(up, UART_LCR, up->lcr); + /* need mode A for FCR */ + if (priv->habit & UART_ERRATA_i202_MDR1_ACCESS) + omap_8250_mdr1_errataset(up, priv); + else + serial_out(up, UART_OMAP_MDR1, priv->mdr1); + up->port.ops->set_mctrl(&up->port, up->port.mctrl); +} + +/* + * OMAP can use "CLK / (16 or 13) / div" for baud rate. And then we have have + * some differences in how we want to handle flow control. + */ +static void omap_8250_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + struct omap8250_priv *priv = up->port.private_data; + unsigned char cval = 0; + unsigned int baud; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + if (termios->c_cflag & CMSPAR) + cval |= UART_LCR_SPAR; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 13); + omap_8250_get_divisor(port, baud, priv); + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + pm_runtime_get_sync(port->dev); + spin_lock_irq(&port->lock); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (IGNBRK | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * Modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + + up->lcr = cval; + /* Up to here it was mostly serial8250_do_set_termios() */ + + /* + * We enable TRIG_GRANU for RX and TX and additionaly we set + * SCR_TX_EMPTY bit. The result is the following: + * - RX_TRIGGER amount of bytes in the FIFO will cause an interrupt. + * - less than RX_TRIGGER number of bytes will also cause an interrupt + * once the UART decides that there no new bytes arriving. + * - Once THRE is enabled, the interrupt will be fired once the FIFO is + * empty - the trigger level is ignored here. + * + * Once DMA is enabled: + * - UART will assert the TX DMA line once there is room for TX_TRIGGER + * bytes in the TX FIFO. On each assert the DMA engine will move + * TX_TRIGGER bytes into the FIFO. + * - UART will assert the RX DMA line once there are RX_TRIGGER bytes in + * the FIFO and move RX_TRIGGER bytes. + * This is because threshold and trigger values are the same. + */ + up->fcr = UART_FCR_ENABLE_FIFO; + up->fcr |= TRIGGER_FCR_MASK(TX_TRIGGER) << OMAP_UART_FCR_TX_TRIG; + up->fcr |= TRIGGER_FCR_MASK(RX_TRIGGER) << OMAP_UART_FCR_RX_TRIG; + + priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY | + OMAP_UART_SCR_TX_TRIG_GRANU1_MASK; + + priv->xon = termios->c_cc[VSTART]; + priv->xoff = termios->c_cc[VSTOP]; + + priv->efr = 0; + up->mcr &= ~(UART_MCR_RTS | UART_MCR_XONANY); + if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { + /* Enable AUTORTS and AUTOCTS */ + priv->efr |= UART_EFR_CTS | UART_EFR_RTS; + + /* Ensure MCR RTS is asserted */ + up->mcr |= UART_MCR_RTS; + } else if (up->port.flags & UPF_SOFT_FLOW) { + /* + * IXON Flag: + * Enable XON/XOFF flow control on input. + * Receiver compares XON1, XOFF1. + */ + if (termios->c_iflag & IXON) + priv->efr |= OMAP_UART_SW_RX; + + /* + * IXOFF Flag: + * Enable XON/XOFF flow control on output. + * Transmit XON1, XOFF1 + */ + if (termios->c_iflag & IXOFF) + priv->efr |= OMAP_UART_SW_TX; + + /* + * IXANY Flag: + * Enable any character to restart output. + * Operation resumes after receiving any + * character after recognition of the XOFF character + */ + if (termios->c_iflag & IXANY) + up->mcr |= UART_MCR_XONANY; + } + omap8250_restore_regs(up); + + spin_unlock_irq(&up->port.lock); + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); + + /* calculate wakeup latency constraint */ + priv->calc_latency = USEC_PER_SEC * 64 * 8 / baud; + priv->latency = priv->calc_latency; + + schedule_work(&priv->qos_work); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +/* same as 8250 except that we may have extra flow bits set in EFR */ +static void omap_8250_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + struct omap8250_priv *priv = up->port.private_data; + + pm_runtime_get_sync(port->dev); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, priv->efr | UART_EFR_ECB); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, priv->efr); + serial_out(up, UART_LCR, 0); + + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); +} + +static void omap_serial_fill_features_erratas(struct uart_8250_port *up, + struct omap8250_priv *priv) +{ + u32 mvr, scheme; + u16 revision, major, minor; + + mvr = uart_read(up, UART_OMAP_MVER); + + /* Check revision register scheme */ + scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT; + + switch (scheme) { + case 0: /* Legacy Scheme: OMAP2/3 */ + /* MINOR_REV[0:4], MAJOR_REV[4:7] */ + major = (mvr & OMAP_UART_LEGACY_MVR_MAJ_MASK) >> + OMAP_UART_LEGACY_MVR_MAJ_SHIFT; + minor = (mvr & OMAP_UART_LEGACY_MVR_MIN_MASK); + break; + case 1: + /* New Scheme: OMAP4+ */ + /* MINOR_REV[0:5], MAJOR_REV[8:10] */ + major = (mvr & OMAP_UART_MVR_MAJ_MASK) >> + OMAP_UART_MVR_MAJ_SHIFT; + minor = (mvr & OMAP_UART_MVR_MIN_MASK); + break; + default: + dev_warn(up->port.dev, + "Unknown revision, defaulting to highest\n"); + /* highest possible revision */ + major = 0xff; + minor = 0xff; + } + /* normalize revision for the driver */ + revision = UART_BUILD_REVISION(major, minor); + + switch (revision) { + case OMAP_UART_REV_46: + priv->habit = UART_ERRATA_i202_MDR1_ACCESS; + break; + case OMAP_UART_REV_52: + priv->habit = UART_ERRATA_i202_MDR1_ACCESS | + OMAP_UART_WER_HAS_TX_WAKEUP; + break; + case OMAP_UART_REV_63: + priv->habit = UART_ERRATA_i202_MDR1_ACCESS | + OMAP_UART_WER_HAS_TX_WAKEUP; + break; + default: + break; + } +} + +static void omap8250_uart_qos_work(struct work_struct *work) +{ + struct omap8250_priv *priv; + + priv = container_of(work, struct omap8250_priv, qos_work); + pm_qos_update_request(&priv->pm_qos_request, priv->latency); +} + +static irqreturn_t omap_wake_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + int ret; + + ret = port->handle_irq(port); + if (ret) + return IRQ_HANDLED; + return IRQ_NONE; +} + +static int omap_8250_startup(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + struct omap8250_priv *priv = port->private_data; + + int ret; + + if (priv->wakeirq) { + ret = request_irq(priv->wakeirq, omap_wake_irq, + port->irqflags, "uart wakeup irq", port); + if (ret) + return ret; + disable_irq(priv->wakeirq); + } + + pm_runtime_get_sync(port->dev); + + ret = serial8250_do_startup(port); + if (ret) + goto err; + +#ifdef CONFIG_PM_RUNTIME + up->capabilities |= UART_CAP_RPM; +#endif + + /* Enable module level wake up */ + priv->wer = OMAP_UART_WER_MOD_WKUP; + if (priv->habit & OMAP_UART_WER_HAS_TX_WAKEUP) + priv->wer |= OMAP_UART_TX_WAKEUP_EN; + serial_out(up, UART_OMAP_WER, priv->wer); + + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); + return 0; +err: + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); + if (priv->wakeirq) + free_irq(priv->wakeirq, port); + return ret; +} + +static void omap_8250_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + struct omap8250_priv *priv = port->private_data; + + flush_work(&priv->qos_work); + + pm_runtime_get_sync(port->dev); + + serial_out(up, UART_OMAP_WER, 0); + serial8250_do_shutdown(port); + + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); + + if (priv->wakeirq) + free_irq(priv->wakeirq, port); +} + +static void omap_8250_throttle(struct uart_port *port) +{ + unsigned long flags; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + pm_runtime_get_sync(port->dev); + + spin_lock_irqsave(&port->lock, flags); + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); + serial_out(up, UART_IER, up->ier); + spin_unlock_irqrestore(&port->lock, flags); + + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); +} + +static void omap_8250_unthrottle(struct uart_port *port) +{ + unsigned long flags; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + pm_runtime_get_sync(port->dev); + + spin_lock_irqsave(&port->lock, flags); + up->ier |= UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + spin_unlock_irqrestore(&port->lock, flags); + + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); +} + +static int omap8250_probe(struct platform_device *pdev) +{ + struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + struct omap8250_priv *priv; + struct uart_8250_port up; + int ret; + void __iomem *membase; + + if (!regs || !irq) { + dev_err(&pdev->dev, "missing registers or irq\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + membase = devm_ioremap_nocache(&pdev->dev, regs->start, + resource_size(regs)); + if (!membase) + return -ENODEV; + + memset(&up, 0, sizeof(up)); + up.port.dev = &pdev->dev; + up.port.mapbase = regs->start; + up.port.membase = membase; + up.port.irq = irq->start; + /* + * It claims to be 16C750 compatible however it is a little different. + * It has EFR and has no FCR7_64byte bit. The AFE (which it claims to + * have) is enabled via EFR instead of MCR. The type is set here 8250 + * just to get things going. UNKNOWN does not work for a few reasons and + * we don't need our own type since we don't use 8250's set_termios() + * or pm callback. + */ + up.port.type = PORT_8250; + up.port.iotype = UPIO_MEM; + up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SOFT_FLOW | + UPF_HARD_FLOW; + up.port.private_data = priv; + + up.port.regshift = 2; + up.port.fifosize = 64; + up.tx_loadsz = 64; + up.capabilities = UART_CAP_FIFO; +#ifdef CONFIG_PM_RUNTIME + /* + * PM_RUNTIME is mostly transparent. However to do it right we need to a + * TX empty interrupt before we can put the device to auto idle. So if + * PM_RUNTIME is not enabled we don't add that flag and can spare that + * one extra interrupt in the TX path. + */ + up.capabilities |= UART_CAP_RPM; +#endif + up.port.set_termios = omap_8250_set_termios; + up.port.pm = omap_8250_pm; + up.port.startup = omap_8250_startup; + up.port.shutdown = omap_8250_shutdown; + up.port.throttle = omap_8250_throttle; + up.port.unthrottle = omap_8250_unthrottle; + + if (pdev->dev.of_node) { + up.port.line = of_alias_get_id(pdev->dev.of_node, "serial"); + of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &up.port.uartclk); + priv->wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1); + } else { + up.port.line = pdev->id; + } + + if (up.port.line < 0) { + dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", + up.port.line); + return -ENODEV; + } + if (!up.port.uartclk) { + up.port.uartclk = DEFAULT_CLK_SPEED; + dev_warn(&pdev->dev, + "No clock speed specified: using default: %d\n", + DEFAULT_CLK_SPEED); + } + + priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + priv->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + pm_qos_add_request(&priv->pm_qos_request, PM_QOS_CPU_DMA_LATENCY, + priv->latency); + INIT_WORK(&priv->qos_work, omap8250_uart_qos_work); + + device_init_wakeup(&pdev->dev, true); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, -1); + + pm_runtime_irq_safe(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + pm_runtime_get_sync(&pdev->dev); + + omap_serial_fill_features_erratas(&up, priv); + ret = serial8250_register_8250_port(&up); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register 8250 port\n"); + goto err; + } + priv->line = ret; + platform_set_drvdata(pdev, priv); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + return 0; +err: + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int omap8250_remove(struct platform_device *pdev) +{ + struct omap8250_priv *priv = platform_get_drvdata(pdev); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + serial8250_unregister_port(priv->line); + pm_qos_remove_request(&priv->pm_qos_request); + device_init_wakeup(&pdev->dev, false); + return 0; +} + +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME) + +static inline void omap8250_enable_wakeirq(struct omap8250_priv *priv, + bool enable) +{ + if (!priv->wakeirq) + return; + + if (enable) + enable_irq(priv->wakeirq); + else + disable_irq_nosync(priv->wakeirq); +} + +static void omap8250_enable_wakeup(struct omap8250_priv *priv, + bool enable) +{ + if (enable == priv->wakeups_enabled) + return; + + omap8250_enable_wakeirq(priv, enable); + priv->wakeups_enabled = enable; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int omap8250_prepare(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return 0; + priv->is_suspending = true; + return 0; +} + +static void omap8250_complete(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return; + priv->is_suspending = false; +} + +static int omap8250_suspend(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + + serial8250_suspend_port(priv->line); + flush_work(&priv->qos_work); + + if (device_may_wakeup(dev)) + omap8250_enable_wakeup(priv, true); + else + omap8250_enable_wakeup(priv, false); + return 0; +} + +static int omap8250_resume(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + omap8250_enable_wakeup(priv, false); + + serial8250_resume_port(priv->line); + return 0; +} +#else +#define omap8250_prepare NULL +#define omap8250_complete NULL +#endif + +#ifdef CONFIG_PM_RUNTIME +static int omap8250_lost_context(struct uart_8250_port *up) +{ + u32 val; + + val = serial_in(up, UART_OMAP_MDR1); + /* + * If we lose context, then MDR1 is set to its reset value which is + * UART_OMAP_MDR1_DISABLE. After set_termios() we set it either to 13x + * or 16x but never to disable again. + */ + if (val == UART_OMAP_MDR1_DISABLE) + return 1; + return 0; +} + +static int omap8250_runtime_suspend(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up; + + up = serial8250_get_port(priv->line); + /* + * When using 'no_console_suspend', the console UART must not be + * suspended. Since driver suspend is managed by runtime suspend, + * preventing runtime suspend (by returning error) will keep device + * active during suspend. + */ + if (priv->is_suspending && !console_suspend_enabled) { + if (uart_console(&up->port)) + return -EBUSY; + } + + omap8250_enable_wakeup(priv, true); + + priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + schedule_work(&priv->qos_work); + + return 0; +} + +static int omap8250_runtime_resume(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up; + int loss_cntx; + + /* In case runtime-pm tries this before we are setup */ + if (!priv) + return 0; + + up = serial8250_get_port(priv->line); + omap8250_enable_wakeup(priv, false); + loss_cntx = omap8250_lost_context(up); + + if (loss_cntx) + omap8250_restore_regs(up); + + priv->latency = priv->calc_latency; + schedule_work(&priv->qos_work); + return 0; +} +#endif + +static const struct dev_pm_ops omap8250_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume) + SET_RUNTIME_PM_OPS(omap8250_runtime_suspend, + omap8250_runtime_resume, NULL) + .prepare = omap8250_prepare, + .complete = omap8250_complete, +}; + +static const struct of_device_id omap8250_dt_ids[] = { + { .compatible = "ti,omap2-uart" }, + { .compatible = "ti,omap3-uart" }, + { .compatible = "ti,omap4-uart" }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap8250_dt_ids); + +static struct platform_driver omap8250_platform_driver = { + .driver = { + .name = "omap8250", + .pm = &omap8250_dev_pm_ops, + .of_match_table = omap8250_dt_ids, + .owner = THIS_MODULE, + }, + .probe = omap8250_probe, + .remove = omap8250_remove, +}; +module_platform_driver(omap8250_platform_driver); + +MODULE_AUTHOR("Sebastian Andrzej Siewior"); +MODULE_DESCRIPTION("OMAP 8250 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 21eca79..5d3d65c 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -299,6 +299,15 @@ config SERIAL_8250_RT288X serial port, say Y to this option. The driver can handle up to 2 serial ports. If unsure, say N. +config SERIAL_8250_OMAP + tristate "Support for OMAP internal UART (8250 based driver)" + depends on SERIAL_8250 && ARCH_OMAP2PLUS + help + If you have a machine based on an Texas Instruments OMAP CPU you + can enable its onboard serial ports by enabling this option. + + This driver uses ttyS instead of ttyO. + config SERIAL_8250_FINTEK tristate "Support for Fintek F81216A LPC to 4 UART" depends on SERIAL_8250 && PNP diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 5256b89..31e7cdc 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -20,5 +20,6 @@ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o +obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o -- cgit v0.10.2 From b220282156440a62d5e9e58c0aade0f0daaa374c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 20:06:40 +0200 Subject: tty: serial: 8250_dma: handle error on TX submit Right now it is possible that serial8250_tx_dma() fails and returns -EBUSY. The caller (serial8250_start_tx()) will then enable UART_IER_THRI which will generate an interrupt once the TX FIFO is empty. In serial8250_handle_irq() nothing will happen because up->dma is set and so serial8250_tx_chars() won't be invoked. We end up with plenty of interrupts and some "too much work for irq" output. This patch introduces dma_tx_err in struct uart_8250_port to signal that the last invocation of serial8250_tx_dma() failed so we can fill the TX FIFO manually. Should the next invocation of serial8250_start_tx() succeed then the dma_tx_err flag along with the THRI bit is removed and DMA only usage may continue. Reviewed-by: Tony Lindgren Tested-by: Tony Lindgren Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index cb51be5..2292a42 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -41,6 +41,7 @@ struct uart_8250_dma { size_t tx_size; unsigned char tx_running:1; + unsigned char tx_err: 1; }; struct old_serial_port { diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 2713664..f0f31d2 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1597,7 +1597,8 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) status = serial8250_rx_chars(up, status); } serial8250_modem_status(up); - if (!up->dma && (status & UART_LSR_THRE)) + if ((!up->dma || (up->dma && up->dma->tx_err)) && + (status & UART_LSR_THRE)) serial8250_tx_chars(up); spin_unlock_irqrestore(&port->lock, flags); diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 148ffe4..69e54ab 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -36,8 +36,16 @@ static void __dma_tx_complete(void *param) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&p->port); - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) - serial8250_tx_dma(p); + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) { + int ret; + + ret = serial8250_tx_dma(p); + if (ret) { + dma->tx_err = 1; + p->ier |= UART_IER_THRI; + serial_port_out(&p->port, UART_IER, p->ier); + } + } spin_unlock_irqrestore(&p->port.lock, flags); } @@ -69,6 +77,7 @@ int serial8250_tx_dma(struct uart_8250_port *p) struct uart_8250_dma *dma = p->dma; struct circ_buf *xmit = &p->port.state->xmit; struct dma_async_tx_descriptor *desc; + int ret; if (uart_tx_stopped(&p->port) || dma->tx_running || uart_circ_empty(xmit)) @@ -80,8 +89,10 @@ int serial8250_tx_dma(struct uart_8250_port *p) dma->tx_addr + xmit->tail, dma->tx_size, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EBUSY; + if (!desc) { + ret = -EBUSY; + goto err; + } dma->tx_running = 1; @@ -94,8 +105,17 @@ int serial8250_tx_dma(struct uart_8250_port *p) UART_XMIT_SIZE, DMA_TO_DEVICE); dma_async_issue_pending(dma->txchan); - + if (dma->tx_err) { + dma->tx_err = 0; + if (p->ier & UART_IER_THRI) { + p->ier &= ~UART_IER_THRI; + serial_out(p, UART_IER, p->ier); + } + } return 0; +err: + dma->tx_err = 1; + return ret; } EXPORT_SYMBOL_GPL(serial8250_tx_dma); -- cgit v0.10.2 From 0fcb7901f9d61a325b4c5b88c600383bcbeb97fe Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 20:06:41 +0200 Subject: tty: serial: 8250_dma: keep own book keeping about RX transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After dmaengine_terminate_all() has been invoked then both DMA drivers (edma and omap-dma) do not invoke dma_cookie_complete() to mark the transfer as complete. This dma_cookie_complete() is performed by the Synopsys DesignWare driver which is probably the only one that is used by omap8250-dma and hence don't see following problem… …which is that once a RX transfer has been terminated then following query of channel status reports DMA_IN_PROGRESS (again: the actual transfer has been canceled, there is nothing going on anymore). This means that serial8250_rx_dma() never enqueues another DMA transfer because it (wrongly) assumes that there is a transer already pending. Vinod Koul refuses to accept a patch which adds this dma_cookie_complete() to both drivers and so dmaengine_tx_status() would report DMA_COMPLETE instead (and behave like the Synopsys DesignWare driver already does). He argues that I am not allowed to use the cookie to query the status and that the driver already cleaned everything up after the invokation of dmaengine_terminate_all(). To end this I add a bookkeeping whether or not a RX-transfer has been started to the 8250-dma code. It has already been done for the TX side. *Now* we learn about the RX status based on our bookkeeping and don't need dmaengine_tx_status() for this anymore. Cc: vinod.koul@intel.com Reviewed-by: Tony Lindgren Tested-by: Tony Lindgren Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 2292a42..712d160 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -42,6 +42,7 @@ struct uart_8250_dma { unsigned char tx_running:1; unsigned char tx_err: 1; + unsigned char rx_running:1; }; struct old_serial_port { diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 69e54ab..db9eda3 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -61,6 +61,7 @@ static void __dma_rx_complete(void *param) dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, dma->rx_size, DMA_FROM_DEVICE); + dma->rx_running = 0; dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); dmaengine_terminate_all(dma->rxchan); @@ -123,10 +124,6 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) { struct uart_8250_dma *dma = p->dma; struct dma_async_tx_descriptor *desc; - struct dma_tx_state state; - int dma_status; - - dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); switch (iir & 0x3f) { case UART_IIR_RLSI: @@ -137,7 +134,7 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) * If RCVR FIFO trigger level was not reached, complete the * transfer and let 8250_core copy the remaining data. */ - if (dma_status == DMA_IN_PROGRESS) { + if (dma->rx_running) { dmaengine_pause(dma->rxchan); __dma_rx_complete(p); } @@ -146,7 +143,7 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) break; } - if (dma_status) + if (dma->rx_running) return 0; desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, @@ -155,6 +152,7 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) if (!desc) return -EBUSY; + dma->rx_running = 1; desc->callback = __dma_rx_complete; desc->callback_param = p; -- cgit v0.10.2 From f1a297bb044265efafd9b941d00d52cb66835695 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 20:06:42 +0200 Subject: tty: serial: 8250: allow to use custom DMA implementation The OMAP has a few corner cases where it needs a share of kindness of affection to do the right thing. Heikki Krogerus suggested that instead adding the quirks into the default DMA implementation, OMAP could get its own copy of the function. And Alan suggested the same thing so here we go. This patch provides callbacks for custom TX/RX DMA implementation. If there are not setup / used, then the default (current) implementation is used. Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 712d160..0dc1cb6 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -16,6 +16,9 @@ #include struct uart_8250_dma { + int (*tx_dma)(struct uart_8250_port *p); + int (*rx_dma)(struct uart_8250_port *p, unsigned int iir); + /* Filter function */ dma_filter_fn fn; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index f0f31d2..39450ba 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1353,7 +1353,7 @@ static void serial8250_start_tx(struct uart_port *port) struct uart_8250_port *up = up_to_u8250p(port); serial8250_rpm_get_tx(up); - if (up->dma && !serial8250_tx_dma(up)) { + if (up->dma && !up->dma->tx_dma(up)) { return; } else if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; @@ -1591,7 +1591,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) if (status & (UART_LSR_DR | UART_LSR_BI)) { if (up->dma) - dma_err = serial8250_rx_dma(up, iir); + dma_err = up->dma->rx_dma(up, iir); if (!up->dma || dma_err) status = serial8250_rx_chars(up, status); @@ -3627,8 +3627,13 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->dl_read = up->dl_read; if (up->dl_write) uart->dl_write = up->dl_write; - if (up->dma) + if (up->dma) { uart->dma = up->dma; + if (!uart->dma->tx_dma) + uart->dma->tx_dma = serial8250_tx_dma; + if (!uart->dma->rx_dma) + uart->dma->rx_dma = serial8250_rx_dma; + } if (serial8250_isa_config != NULL) serial8250_isa_config(0, &uart->port, diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index db9eda3..258430b 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -118,7 +118,6 @@ err: dma->tx_err = 1; return ret; } -EXPORT_SYMBOL_GPL(serial8250_tx_dma); int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) { @@ -165,7 +164,6 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) return 0; } -EXPORT_SYMBOL_GPL(serial8250_rx_dma); int serial8250_request_dma(struct uart_8250_port *p) { -- cgit v0.10.2 From 31a171328e870c9f65d01191e51a75cde5f78ffd Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 20:06:43 +0200 Subject: tty: serial: 8250_omap: add custom DMA-TX callback This patch provides mostly a copy of serial8250_tx_dma() + __dma_tx_complete() with the following extensions: - DMA bug At least on AM335x the following problem exists: Even if the TX FIFO is empty and a TX transfer is programmed (and started) the UART does not trigger the DMA transfer. After $TRESHOLD number of bytes have been written to the FIFO manually the UART reevaluates the whole situation and decides that now there is enough room in the FIFO and so the transfer begins. This problem has not been seen on DRA7 or beagle board xm (OMAP3). I am not sure if this is UART-IP core specific or DMA engine. The workaround is to use a threshold of one byte, program the DMA transfer minus one byte and then to put the first byte into the FIFO to kick start the transfer. - support for runtime PM RPM is enabled on start_tx(). We can't disable RPM on DMA complete callback because there is still data in the FIFO which is being sent. We have to wait until the FIFO is empty before we disable it. For this to happen we fake a TX sent error and enable THRI. Once the FIFO is empty we receive an interrupt and since the TTY-buffer is still empty we "put RPM" via __stop_tx(). Should it been filed then in the start_tx() path we should program the DMA transfer and remove the error flag and the THRI bit. Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 2f653c4..5f183d1 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "8250.h" @@ -29,6 +30,7 @@ #define UART_ERRATA_i202_MDR1_ACCESS (1 << 0) #define OMAP_UART_WER_HAS_TX_WAKEUP (1 << 1) +#define OMAP_DMA_TX_KICK (1 << 2) #define OMAP_UART_FCR_RX_TRIG 6 #define OMAP_UART_FCR_TX_TRIG 4 @@ -616,6 +618,148 @@ static void omap_8250_unthrottle(struct uart_port *port) pm_runtime_put_autosuspend(port->dev); } +#ifdef CONFIG_SERIAL_8250_DMA +static int omap_8250_tx_dma(struct uart_8250_port *p); + +static void omap_8250_dma_tx_complete(void *param) +{ + struct uart_8250_port *p = param; + struct uart_8250_dma *dma = p->dma; + struct circ_buf *xmit = &p->port.state->xmit; + unsigned long flags; + bool en_thri = false; + + dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + spin_lock_irqsave(&p->port.lock, flags); + + dma->tx_running = 0; + + xmit->tail += dma->tx_size; + xmit->tail &= UART_XMIT_SIZE - 1; + p->port.icount.tx += dma->tx_size; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&p->port); + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) { + int ret; + + ret = omap_8250_tx_dma(p); + if (ret) + en_thri = true; + + } else if (p->capabilities & UART_CAP_RPM) { + en_thri = true; + } + + if (en_thri) { + dma->tx_err = 1; + p->ier |= UART_IER_THRI; + serial_port_out(&p->port, UART_IER, p->ier); + } + + spin_unlock_irqrestore(&p->port.lock, flags); +} + +static int omap_8250_tx_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + struct omap8250_priv *priv = p->port.private_data; + struct circ_buf *xmit = &p->port.state->xmit; + struct dma_async_tx_descriptor *desc; + unsigned int skip_byte = 0; + int ret; + + if (dma->tx_running) + return 0; + if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) { + + /* + * Even if no data, we need to return an error for the two cases + * below so serial8250_tx_chars() is invoked and properly clears + * THRI and/or runtime suspend. + */ + if (dma->tx_err || p->capabilities & UART_CAP_RPM) { + ret = -EBUSY; + goto err; + } + if (p->ier & UART_IER_THRI) { + p->ier &= ~UART_IER_THRI; + serial_out(p, UART_IER, p->ier); + } + return 0; + } + + dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + if (priv->habit & OMAP_DMA_TX_KICK) { + u8 tx_lvl; + + /* + * We need to put the first byte into the FIFO in order to start + * the DMA transfer. For transfers smaller than four bytes we + * don't bother doing DMA at all. It seem not matter if there + * are still bytes in the FIFO from the last transfer (in case + * we got here directly from omap_8250_dma_tx_complete()). Bytes + * leaving the FIFO seem not to trigger the DMA transfer. It is + * really the byte that we put into the FIFO. + * If the FIFO is already full then we most likely got here from + * omap_8250_dma_tx_complete(). And this means the DMA engine + * just completed its work. We don't have to wait the complete + * 86us at 115200,8n1 but around 60us (not to mention lower + * baudrates). So in that case we take the interrupt and try + * again with an empty FIFO. + */ + tx_lvl = serial_in(p, UART_OMAP_TX_LVL); + if (tx_lvl == p->tx_loadsz) { + ret = -EBUSY; + goto err; + } + if (dma->tx_size < 4) { + ret = -EINVAL; + goto err; + } + skip_byte = 1; + } + + desc = dmaengine_prep_slave_single(dma->txchan, + dma->tx_addr + xmit->tail + skip_byte, + dma->tx_size - skip_byte, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + ret = -EBUSY; + goto err; + } + + dma->tx_running = 1; + + desc->callback = omap_8250_dma_tx_complete; + desc->callback_param = p; + + dma->tx_cookie = dmaengine_submit(desc); + + dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + dma_async_issue_pending(dma->txchan); + if (dma->tx_err) + dma->tx_err = 0; + + if (p->ier & UART_IER_THRI) { + p->ier &= ~UART_IER_THRI; + serial_out(p, UART_IER, p->ier); + } + if (skip_byte) + serial_out(p, UART_TX, xmit->buf[xmit->tail]); + return 0; +err: + dma->tx_err = 1; + return ret; +} + +#endif + static int omap8250_probe(struct platform_device *pdev) { struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index df6c9ab..53af3b7 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -359,6 +359,7 @@ #define UART_OMAP_SYSC 0x15 /* System configuration register */ #define UART_OMAP_SYSS 0x16 /* System status register */ #define UART_OMAP_WER 0x17 /* Wake-up enable register */ +#define UART_OMAP_TX_LVL 0x1a /* TX FIFO level register */ /* * These are the definitions for the MDR1 register -- cgit v0.10.2 From 0e31c8d173ab105f161f26d222eae6cad2aebf5f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 20:06:44 +0200 Subject: tty: serial: 8250_omap: add custom DMA-RX callback The omap needs a DMA request pending right away. If it is enqueued once the bytes are in the FIFO then nothing will happen and the FIFO will be later purged via RX-timeout interrupt. This patch enqueues RX-DMA request on completion but not if it was aborted on error. The first enqueue will happen in the driver in startup. Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 5f183d1..1659858 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -619,6 +619,102 @@ static void omap_8250_unthrottle(struct uart_port *port) } #ifdef CONFIG_SERIAL_8250_DMA +static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir); + +static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) +{ + struct uart_8250_dma *dma = p->dma; + struct tty_port *tty_port = &p->port.state->port; + struct dma_tx_state state; + int count; + + dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, + dma->rx_size, DMA_FROM_DEVICE); + + dma->rx_running = 0; + dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + dmaengine_terminate_all(dma->rxchan); + + count = dma->rx_size - state.residue; + + tty_insert_flip_string(tty_port, dma->rx_buf, count); + p->port.icount.rx += count; + if (!error) + omap_8250_rx_dma(p, 0); + + tty_flip_buffer_push(tty_port); +} + +static void __dma_rx_complete(void *param) +{ + __dma_rx_do_complete(param, false); +} + +static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +{ + struct uart_8250_dma *dma = p->dma; + struct dma_async_tx_descriptor *desc; + + switch (iir & 0x3f) { + case UART_IIR_RLSI: + /* 8250_core handles errors and break interrupts */ + if (dma->rx_running) { + dmaengine_pause(dma->rxchan); + __dma_rx_do_complete(p, true); + } + return -EIO; + case UART_IIR_RX_TIMEOUT: + /* + * If RCVR FIFO trigger level was not reached, complete the + * transfer and let 8250_core copy the remaining data. + */ + if (dma->rx_running) { + dmaengine_pause(dma->rxchan); + __dma_rx_do_complete(p, true); + } + return -ETIMEDOUT; + case UART_IIR_RDI: + /* + * The OMAP UART is a special BEAST. If we receive RDI we _have_ + * a DMA transfer programmed but it didn't work. One reason is + * that we were too slow and there were too many bytes in the + * FIFO, the UART counted wrong and never kicked the DMA engine + * to do anything. That means once we receive RDI on OMAP then + * the DMA won't do anything soon so we have to cancel the DMA + * transfer and purge the FIFO manually. + */ + if (dma->rx_running) { + dmaengine_pause(dma->rxchan); + __dma_rx_do_complete(p, true); + } + return -ETIMEDOUT; + + default: + break; + } + + if (dma->rx_running) + return 0; + + desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, + dma->rx_size, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EBUSY; + + dma->rx_running = 1; + desc->callback = __dma_rx_complete; + desc->callback_param = p; + + dma->rx_cookie = dmaengine_submit(desc); + + dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr, + dma->rx_size, DMA_FROM_DEVICE); + + dma_async_issue_pending(dma->rxchan); + return 0; +} + static int omap_8250_tx_dma(struct uart_8250_port *p); static void omap_8250_dma_tx_complete(void *param) -- cgit v0.10.2 From 77285243a68fec6852041d1db0d5587684b9dc48 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 20:06:48 +0200 Subject: tty: serial: 8250: omap: add custom irq handling We have (or will have) custom DMA callbacks in the omap driver due to the different behaviour in the RX and TX case. To make this work we need a few changes in the IRQ handler to invoke the rx_handler again after the "manual" mode or retry the tx_handler again before falling back to the manual mode. Heikki didn't want to see the extra hacks in the generic / default irq handler and Peter wasn't too happy about an OMAP-only IRQ handler. The way I planned it is to use this extra IRQ routine only in DMA case. If Peter dislike this approach then I hope Heikki doesn't block changes in the default IRQ handler :) Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Heikki Krogerus Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 0dc1cb6..458ad2833 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -119,6 +119,8 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) } struct uart_8250_port *serial8250_get_port(int line); +void serial8250_rpm_get(struct uart_8250_port *p); +void serial8250_rpm_put(struct uart_8250_port *p); #if defined(__alpha__) && !defined(CONFIG_PCI) /* diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 39450ba..7e78f30 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -541,20 +541,22 @@ void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) } EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); -static void serial8250_rpm_get(struct uart_8250_port *p) +void serial8250_rpm_get(struct uart_8250_port *p) { if (!(p->capabilities & UART_CAP_RPM)) return; pm_runtime_get_sync(p->port.dev); } +EXPORT_SYMBOL_GPL(serial8250_rpm_get); -static void serial8250_rpm_put(struct uart_8250_port *p) +void serial8250_rpm_put(struct uart_8250_port *p) { if (!(p->capabilities & UART_CAP_RPM)) return; pm_runtime_mark_last_busy(p->port.dev); pm_runtime_put_autosuspend(p->port.dev); } +EXPORT_SYMBOL_GPL(serial8250_rpm_put); /* * These two wrappers ensure that enable_runtime_pm_tx() can be called more than diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 1659858..6500547f 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -854,6 +855,60 @@ err: return ret; } +/* + * This is mostly serial8250_handle_irq(). We have a slightly different DMA + * hoook for RX/TX and need different logic for them in the ISR. Therefore we + * use the default routine in the non-DMA case and this one for with DMA. + */ +static int omap_8250_dma_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned char status; + unsigned long flags; + u8 iir; + int dma_err = 0; + + serial8250_rpm_get(up); + + iir = serial_port_in(port, UART_IIR); + if (iir & UART_IIR_NO_INT) { + serial8250_rpm_put(up); + return 0; + } + + spin_lock_irqsave(&port->lock, flags); + + status = serial_port_in(port, UART_LSR); + + if (status & (UART_LSR_DR | UART_LSR_BI)) { + + dma_err = omap_8250_rx_dma(up, iir); + if (dma_err) { + status = serial8250_rx_chars(up, status); + omap_8250_rx_dma(up, 0); + } + } + serial8250_modem_status(up); + if (status & UART_LSR_THRE && up->dma->tx_err) { + if (uart_tx_stopped(&up->port) || + uart_circ_empty(&up->port.state->xmit)) { + up->dma->tx_err = 0; + serial8250_tx_chars(up); + } else { + /* + * try again due to an earlier failer which + * might have been resolved by now. + */ + dma_err = omap_8250_tx_dma(up); + if (dma_err) + serial8250_tx_chars(up); + } + } + + spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); + return 1; +} #endif static int omap8250_probe(struct platform_device *pdev) -- cgit v0.10.2 From 0a0661ddb8fc9298a293558898e1045fbf54f256 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Sep 2014 20:06:49 +0200 Subject: tty: serial: 8250: omap: add dma support This patch adds the required pieces to 8250-OMAP UART driver for DMA support. The TX burst size is set to 1 so we can send an arbitrary amount of bytes. The RX burst is currently set to 48 which means we receive an DMA interrupt every 48 bytes and have to reprogram everything. Less bytes in the RX-FIFO mean that no DMA transfer will happen and the UART will send a RX-timeout _or_ RDI event at which point the FIFO will be manually purged. There is a workaround for TX-DMA on AM33xx where we put the first byte into the FIFO to kick start the DMA process. Haven't seen this problem on OMAP36xx (beagle board xm) or DRA7xx. On AM375x there is "Usage Note 2.7: UART: Cannot Acknowledge Idle Requests in Smartidle Mode When Configured for DMA Operations" in the errata document. This problem persists even after disabling DMA in the UART and will be addressed in the HWMOD. v10: - delay update_registers() from set_termios() until TX-DMA is done. It has been reported / proved that invoking update_registers() while TX-DMA is in progress may stall the DMA operation and it won't finish. - use the new omap DMA-TX-RX hooks and DMA only interrupt routine. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 6500547f..57a8b12 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -88,6 +88,7 @@ struct omap8250_priv { u8 wer; u8 xon; u8 xoff; + u8 delayed_restore; u16 quot; bool is_suspending; @@ -211,6 +212,18 @@ static void omap8250_update_scr(struct uart_8250_port *up, static void omap8250_restore_regs(struct uart_8250_port *up) { struct omap8250_priv *priv = up->port.private_data; + struct uart_8250_dma *dma = up->dma; + + if (dma && dma->tx_running) { + /* + * TCSANOW requests the change to occur immediately however if + * we have a TX-DMA operation in progress then it has been + * observed that it might stall and never complete. Therefore we + * delay DMA completes to prevent this hang from happen. + */ + priv->delayed_restore = 1; + return; + } serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_EFR, UART_EFR_ECB); @@ -375,6 +388,10 @@ static void omap_8250_set_termios(struct uart_port *port, priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY | OMAP_UART_SCR_TX_TRIG_GRANU1_MASK; + if (up->dma) + priv->scr |= OMAP_UART_SCR_DMAMODE_1 | + OMAP_UART_SCR_DMAMODE_CTL; + priv->xon = termios->c_cc[VSTART]; priv->xoff = termios->c_cc[VSTOP]; @@ -554,6 +571,9 @@ static int omap_8250_startup(struct uart_port *port) priv->wer |= OMAP_UART_TX_WAKEUP_EN; serial_out(up, UART_OMAP_WER, priv->wer); + if (up->dma) + up->dma->rx_dma(up, 0); + pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); return 0; @@ -572,6 +592,8 @@ static void omap_8250_shutdown(struct uart_port *port) struct omap8250_priv *priv = port->private_data; flush_work(&priv->qos_work); + if (up->dma) + up->dma->rx_dma(up, UART_IIR_RX_TIMEOUT); pm_runtime_get_sync(port->dev); @@ -725,6 +747,7 @@ static void omap_8250_dma_tx_complete(void *param) struct circ_buf *xmit = &p->port.state->xmit; unsigned long flags; bool en_thri = false; + struct omap8250_priv *priv = p->port.private_data; dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); @@ -737,6 +760,11 @@ static void omap_8250_dma_tx_complete(void *param) xmit->tail &= UART_XMIT_SIZE - 1; p->port.icount.tx += dma->tx_size; + if (priv->delayed_restore) { + priv->delayed_restore = 0; + omap8250_restore_regs(p); + } + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&p->port); @@ -909,6 +937,18 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) serial8250_rpm_put(up); return 1; } + +static bool the_no_dma_filter_fn(struct dma_chan *chan, void *param) +{ + return false; +} + +#else + +static inline int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +{ + return -EINVAL; +} #endif static int omap8250_probe(struct platform_device *pdev) @@ -1010,6 +1050,32 @@ static int omap8250_probe(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); omap_serial_fill_features_erratas(&up, priv); +#ifdef CONFIG_SERIAL_8250_DMA + if (pdev->dev.of_node) { + /* + * Oh DMA support. If there are no DMA properties in the DT then + * we will fall back to a generic DMA channel which does not + * really work here. To ensure that we do not get a generic DMA + * channel assigned, we have the the_no_dma_filter_fn() here. + * To avoid "failed to request DMA" messages we check for DMA + * properties in DT. + */ + ret = of_property_count_strings(pdev->dev.of_node, "dma-names"); + if (ret == 2) { + up.dma = &priv->omap8250_dma; + up.port.handle_irq = omap_8250_dma_handle_irq; + priv->omap8250_dma.fn = the_no_dma_filter_fn; + priv->omap8250_dma.tx_dma = omap_8250_tx_dma; + priv->omap8250_dma.rx_dma = omap_8250_rx_dma; + priv->omap8250_dma.rx_size = RX_TRIGGER; + priv->omap8250_dma.rxconf.src_maxburst = RX_TRIGGER; + priv->omap8250_dma.txconf.dst_maxburst = TX_TRIGGER; + + if (of_machine_is_compatible("ti,am33xx")) + priv->habit |= OMAP_DMA_TX_KICK; + } + } +#endif ret = serial8250_register_8250_port(&up); if (ret < 0) { dev_err(&pdev->dev, "unable to register 8250 port\n"); @@ -1146,6 +1212,8 @@ static int omap8250_runtime_suspend(struct device *dev) } omap8250_enable_wakeup(priv, true); + if (up->dma) + omap_8250_rx_dma(up, UART_IIR_RX_TIMEOUT); priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; schedule_work(&priv->qos_work); @@ -1170,6 +1238,9 @@ static int omap8250_runtime_resume(struct device *dev) if (loss_cntx) omap8250_restore_regs(up); + if (up->dma) + omap_8250_rx_dma(up, 0); + priv->latency = priv->calc_latency; schedule_work(&priv->qos_work); return 0; -- cgit v0.10.2 From b6b30d67f39b7522d606751c0dba019577f0835a Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:01:02 +0200 Subject: serial: use container_of to resolve uart_sio_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c index 5702828..8f7f83a 100644 --- a/drivers/tty/serial/m32r_sio.c +++ b/drivers/tty/serial/m32r_sio.c @@ -249,7 +249,8 @@ static void serial_out(struct uart_sio_port *up, int offset, int value) static void m32r_sio_stop_tx(struct uart_port *port) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); if (up->ier & UART_IER_THRI) { up->ier &= ~UART_IER_THRI; @@ -260,7 +261,8 @@ static void m32r_sio_stop_tx(struct uart_port *port) static void m32r_sio_start_tx(struct uart_port *port) { #ifdef CONFIG_SERIAL_M32R_PLDSIO - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); struct circ_buf *xmit = &up->port.state->xmit; if (!(up->ier & UART_IER_THRI)) { @@ -274,7 +276,8 @@ static void m32r_sio_start_tx(struct uart_port *port) } while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY); #else - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; @@ -285,7 +288,8 @@ static void m32r_sio_start_tx(struct uart_port *port) static void m32r_sio_stop_rx(struct uart_port *port) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); up->ier &= ~UART_IER_RLSI; up->port.read_status_mask &= ~UART_LSR_DR; @@ -294,7 +298,8 @@ static void m32r_sio_stop_rx(struct uart_port *port) static void m32r_sio_enable_ms(struct uart_port *port) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); up->ier |= UART_IER_MSI; serial_out(up, UART_IER, up->ier); @@ -581,7 +586,8 @@ static void m32r_sio_timeout(unsigned long data) static unsigned int m32r_sio_tx_empty(struct uart_port *port) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); unsigned long flags; unsigned int ret; @@ -609,7 +615,8 @@ static void m32r_sio_break_ctl(struct uart_port *port, int break_state) static int m32r_sio_startup(struct uart_port *port) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); int retval; sio_init(); @@ -652,7 +659,8 @@ static int m32r_sio_startup(struct uart_port *port) static void m32r_sio_shutdown(struct uart_port *port) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); /* * Disable interrupts from this port @@ -681,7 +689,8 @@ static unsigned int m32r_sio_get_divisor(struct uart_port *port, static void m32r_sio_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); unsigned char cval = 0; unsigned long flags; unsigned int baud, quot; @@ -780,7 +789,8 @@ static void m32r_sio_set_termios(struct uart_port *port, static void m32r_sio_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); if (up->pm) up->pm(port, state, oldstate); @@ -825,7 +835,8 @@ m32r_sio_request_std_resource(struct uart_sio_port *up, struct resource **res) static void m32r_sio_release_port(struct uart_port *port) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); unsigned long start, offset = 0, size = 0; size <<= up->port.regshift; @@ -862,7 +873,8 @@ static void m32r_sio_release_port(struct uart_port *port) static int m32r_sio_request_port(struct uart_port *port) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); struct resource *res = NULL; int ret = 0; @@ -889,7 +901,8 @@ static int m32r_sio_request_port(struct uart_port *port) static void m32r_sio_config_port(struct uart_port *port, int unused) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); @@ -1000,7 +1013,8 @@ static inline void wait_for_xmitr(struct uart_sio_port *up) static void m32r_sio_console_putchar(struct uart_port *port, int ch) { - struct uart_sio_port *up = (struct uart_sio_port *)port; + struct uart_sio_port *up = + container_of(port, struct uart_sio_port, port); wait_for_xmitr(up); sio_out(up, SIOTXB, ch); -- cgit v0.10.2 From c9db776b57d84588688f29f0ba11cfd00a227254 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:01:03 +0200 Subject: serial: sa1100: use container_of to resolve sa1100_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index 753d452..4eb24fe 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -142,7 +142,8 @@ static void sa1100_timeout(unsigned long data) */ static void sa1100_stop_tx(struct uart_port *port) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); u32 utcr3; utcr3 = UART_GET_UTCR3(sport); @@ -155,7 +156,8 @@ static void sa1100_stop_tx(struct uart_port *port) */ static void sa1100_start_tx(struct uart_port *port) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); u32 utcr3; utcr3 = UART_GET_UTCR3(sport); @@ -168,7 +170,8 @@ static void sa1100_start_tx(struct uart_port *port) */ static void sa1100_stop_rx(struct uart_port *port) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); u32 utcr3; utcr3 = UART_GET_UTCR3(sport); @@ -180,7 +183,8 @@ static void sa1100_stop_rx(struct uart_port *port) */ static void sa1100_enable_ms(struct uart_port *port) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); mod_timer(&sport->timer, jiffies); } @@ -323,7 +327,8 @@ static irqreturn_t sa1100_int(int irq, void *dev_id) */ static unsigned int sa1100_tx_empty(struct uart_port *port) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); return UART_GET_UTSR1(sport) & UTSR1_TBY ? 0 : TIOCSER_TEMT; } @@ -342,7 +347,8 @@ static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl) */ static void sa1100_break_ctl(struct uart_port *port, int break_state) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); unsigned long flags; unsigned int utcr3; @@ -358,7 +364,8 @@ static void sa1100_break_ctl(struct uart_port *port, int break_state) static int sa1100_startup(struct uart_port *port) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); int retval; /* @@ -387,7 +394,8 @@ static int sa1100_startup(struct uart_port *port) static void sa1100_shutdown(struct uart_port *port) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); /* * Stop our timer. @@ -409,7 +417,8 @@ static void sa1100_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); unsigned long flags; unsigned int utcr0, old_utcr3, baud, quot; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; @@ -512,7 +521,8 @@ sa1100_set_termios(struct uart_port *port, struct ktermios *termios, static const char *sa1100_type(struct uart_port *port) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); return sport->port.type == PORT_SA1100 ? "SA1100" : NULL; } @@ -522,7 +532,8 @@ static const char *sa1100_type(struct uart_port *port) */ static void sa1100_release_port(struct uart_port *port) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); release_mem_region(sport->port.mapbase, UART_PORT_SIZE); } @@ -532,7 +543,8 @@ static void sa1100_release_port(struct uart_port *port) */ static int sa1100_request_port(struct uart_port *port) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, "sa11x0-uart") != NULL ? 0 : -EBUSY; @@ -543,7 +555,8 @@ static int sa1100_request_port(struct uart_port *port) */ static void sa1100_config_port(struct uart_port *port, int flags) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); if (flags & UART_CONFIG_TYPE && sa1100_request_port(&sport->port) == 0) @@ -558,7 +571,8 @@ static void sa1100_config_port(struct uart_port *port, int flags) static int sa1100_verify_port(struct uart_port *port, struct serial_struct *ser) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); int ret = 0; if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100) @@ -691,7 +705,8 @@ void __init sa1100_register_uart(int idx, int port) #ifdef CONFIG_SERIAL_SA1100_CONSOLE static void sa1100_console_putchar(struct uart_port *port, int ch) { - struct sa1100_port *sport = (struct sa1100_port *)port; + struct sa1100_port *sport = + container_of(port, struct sa1100_port, port); while (!(UART_GET_UTSR1(sport) & UTSR1_TNF)) barrier(); -- cgit v0.10.2 From 2413b320625f0c8d5292f417923326177691b09b Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:01:04 +0200 Subject: serial: use container_of to resolve uart_ip22zilog_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c index 99b7b86..991e6dc 100644 --- a/drivers/tty/serial/ip22zilog.c +++ b/drivers/tty/serial/ip22zilog.c @@ -544,7 +544,8 @@ static unsigned int ip22zilog_get_mctrl(struct uart_port *port) /* The port lock is held and interrupts are disabled. */ static void ip22zilog_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + struct uart_ip22zilog_port *up = + container_of(port, struct uart_ip22zilog_port, port); struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); unsigned char set_bits, clear_bits; @@ -568,7 +569,8 @@ static void ip22zilog_set_mctrl(struct uart_port *port, unsigned int mctrl) /* The port lock is held and interrupts are disabled. */ static void ip22zilog_stop_tx(struct uart_port *port) { - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + struct uart_ip22zilog_port *up = + container_of(port, struct uart_ip22zilog_port, port); up->flags |= IP22ZILOG_FLAG_TX_STOPPED; } @@ -576,7 +578,8 @@ static void ip22zilog_stop_tx(struct uart_port *port) /* The port lock is held and interrupts are disabled. */ static void ip22zilog_start_tx(struct uart_port *port) { - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + struct uart_ip22zilog_port *up = + container_of(port, struct uart_ip22zilog_port, port); struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); unsigned char status; @@ -636,7 +639,8 @@ static void ip22zilog_stop_rx(struct uart_port *port) /* The port lock is held. */ static void ip22zilog_enable_ms(struct uart_port *port) { - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + struct uart_ip22zilog_port *up = + container_of(port, struct uart_ip22zilog_port, port); struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); unsigned char new_reg; @@ -652,7 +656,8 @@ static void ip22zilog_enable_ms(struct uart_port *port) /* The port lock is not held. */ static void ip22zilog_break_ctl(struct uart_port *port, int break_state) { - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + struct uart_ip22zilog_port *up = + container_of(port, struct uart_ip22zilog_port, port); struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); unsigned char set_bits, clear_bits, new_reg; unsigned long flags; @@ -873,7 +878,8 @@ static void ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + struct uart_ip22zilog_port *up = + container_of(port, struct uart_ip22zilog_port, port); unsigned long flags; int baud, brg; -- cgit v0.10.2 From 22d4d44c4ce3a860377aaaeeb4a7b8b907ab66bc Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:01:05 +0200 Subject: serial: mpsc: use container_of to resolve mpsc_port_info from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index ae49856..5d5499b 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -1246,7 +1246,8 @@ static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id) */ static uint mpsc_tx_empty(struct uart_port *port) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); ulong iflags; uint rc; @@ -1264,7 +1265,8 @@ static void mpsc_set_mctrl(struct uart_port *port, uint mctrl) static uint mpsc_get_mctrl(struct uart_port *port) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); u32 mflags, status; status = (pi->mirror_regs) ? pi->MPSC_CHR_10_m @@ -1281,7 +1283,8 @@ static uint mpsc_get_mctrl(struct uart_port *port) static void mpsc_stop_tx(struct uart_port *port) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); pr_debug("mpsc_stop_tx[%d]\n", port->line); @@ -1290,7 +1293,8 @@ static void mpsc_stop_tx(struct uart_port *port) static void mpsc_start_tx(struct uart_port *port) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); unsigned long iflags; spin_lock_irqsave(&pi->tx_lock, iflags); @@ -1316,7 +1320,8 @@ static void mpsc_start_rx(struct mpsc_port_info *pi) static void mpsc_stop_rx(struct uart_port *port) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); pr_debug("mpsc_stop_rx[%d]: Stopping...\n", port->line); @@ -1338,7 +1343,8 @@ static void mpsc_stop_rx(struct uart_port *port) static void mpsc_break_ctl(struct uart_port *port, int ctl) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); ulong flags; u32 v; @@ -1353,7 +1359,8 @@ static void mpsc_break_ctl(struct uart_port *port, int ctl) static int mpsc_startup(struct uart_port *port) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); u32 flag = 0; int rc; @@ -1383,7 +1390,8 @@ static int mpsc_startup(struct uart_port *port) static void mpsc_shutdown(struct uart_port *port) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); pr_debug("mpsc_shutdown[%d]: Shutting down MPSC\n", port->line); @@ -1394,7 +1402,8 @@ static void mpsc_shutdown(struct uart_port *port) static void mpsc_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); u32 baud; ulong flags; u32 chr_bits, stop_bits, par; @@ -1498,7 +1507,8 @@ static int mpsc_request_port(struct uart_port *port) static void mpsc_release_port(struct uart_port *port) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); if (pi->ready) { mpsc_uninit_rings(pi); @@ -1513,7 +1523,8 @@ static void mpsc_config_port(struct uart_port *port, int flags) static int mpsc_verify_port(struct uart_port *port, struct serial_struct *ser) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); int rc = 0; pr_debug("mpsc_verify_port[%d]: Verifying port data\n", pi->port.line); @@ -1548,7 +1559,8 @@ static void mpsc_put_poll_char(struct uart_port *port, static int mpsc_get_poll_char(struct uart_port *port) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); struct mpsc_rx_desc *rxre; u32 cmdstat, bytes_in, i; u8 *bp; @@ -1648,7 +1660,8 @@ static int mpsc_get_poll_char(struct uart_port *port) static void mpsc_put_poll_char(struct uart_port *port, unsigned char c) { - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_port_info *pi = + container_of(port, struct mpsc_port_info, port); u32 data; data = readl(pi->mpsc_base + MPSC_MPCR); -- cgit v0.10.2 From e789d2688720dba26079aac3e008eb6e78937753 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:01:06 +0200 Subject: serial: cpm_uart: use container_of to resolve uart_cpm_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 533852e..638afd3 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -80,7 +80,8 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo); */ static unsigned int cpm_uart_tx_empty(struct uart_port *port) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); cbd_t __iomem *bdp = pinfo->tx_bd_base; int ret = 0; @@ -102,7 +103,8 @@ static unsigned int cpm_uart_tx_empty(struct uart_port *port) static void cpm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); if (pinfo->gpios[GPIO_RTS] >= 0) gpio_set_value(pinfo->gpios[GPIO_RTS], !(mctrl & TIOCM_RTS)); @@ -113,7 +115,8 @@ static void cpm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) static unsigned int cpm_uart_get_mctrl(struct uart_port *port) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); unsigned int mctrl = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; if (pinfo->gpios[GPIO_CTS] >= 0) { @@ -144,7 +147,8 @@ static unsigned int cpm_uart_get_mctrl(struct uart_port *port) */ static void cpm_uart_stop_tx(struct uart_port *port) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); smc_t __iomem *smcp = pinfo->smcp; scc_t __iomem *sccp = pinfo->sccp; @@ -161,7 +165,8 @@ static void cpm_uart_stop_tx(struct uart_port *port) */ static void cpm_uart_start_tx(struct uart_port *port) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); smc_t __iomem *smcp = pinfo->smcp; scc_t __iomem *sccp = pinfo->sccp; @@ -189,7 +194,8 @@ static void cpm_uart_start_tx(struct uart_port *port) */ static void cpm_uart_stop_rx(struct uart_port *port) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); smc_t __iomem *smcp = pinfo->smcp; scc_t __iomem *sccp = pinfo->sccp; @@ -206,7 +212,8 @@ static void cpm_uart_stop_rx(struct uart_port *port) */ static void cpm_uart_break_ctl(struct uart_port *port, int break_state) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); pr_debug("CPM uart[%d]:break ctrl, break_state: %d\n", port->line, break_state); @@ -240,7 +247,8 @@ static void cpm_uart_int_rx(struct uart_port *port) unsigned char ch; u8 *cp; struct tty_port *tport = &port->state->port; - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); cbd_t __iomem *bdp; u16 status; unsigned int flg; @@ -397,7 +405,8 @@ static irqreturn_t cpm_uart_int(int irq, void *data) static int cpm_uart_startup(struct uart_port *port) { int retval; - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); pr_debug("CPM uart[%d]:startup\n", port->line); @@ -442,7 +451,8 @@ inline void cpm_uart_wait_until_send(struct uart_cpm_port *pinfo) */ static void cpm_uart_shutdown(struct uart_port *port) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); pr_debug("CPM uart[%d]:shutdown\n", port->line); @@ -492,7 +502,8 @@ static void cpm_uart_set_termios(struct uart_port *port, unsigned long flags; u16 cval, scval, prev_mode; int bits, sbits; - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); smc_t __iomem *smcp = pinfo->smcp; scc_t __iomem *sccp = pinfo->sccp; int maxidl; @@ -675,7 +686,8 @@ static int cpm_uart_tx_pump(struct uart_port *port) cbd_t __iomem *bdp; u8 *p; int count; - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); struct circ_buf *xmit = &port->state->xmit; /* Handle xon/xoff */ @@ -906,7 +918,8 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo) */ static int cpm_uart_request_port(struct uart_port *port) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); int ret; pr_debug("CPM uart[%d]:request port\n", port->line); @@ -938,7 +951,8 @@ static int cpm_uart_request_port(struct uart_port *port) static void cpm_uart_release_port(struct uart_port *port) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); if (!(pinfo->flags & FLAG_CONSOLE)) cpm_uart_freebuf(pinfo); @@ -1082,7 +1096,8 @@ static int poll_wait_key(char *obuf, struct uart_cpm_port *pinfo) static int cpm_get_poll_char(struct uart_port *port) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); if (!serial_polled) { serial_polled = 1; @@ -1099,7 +1114,8 @@ static int cpm_get_poll_char(struct uart_port *port) static void cpm_put_poll_char(struct uart_port *port, unsigned char c) { - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct uart_cpm_port *pinfo = + container_of(port, struct uart_cpm_port, port); static char ch[2]; ch[0] = (char)c; -- cgit v0.10.2 From a15ad34834b1b29459ae87b0f04489d0765259e8 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:01:07 +0200 Subject: TTY: jsm: use container_of to resolve jsm_channel from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 3e5c156..8814680 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -77,7 +77,8 @@ static unsigned int jsm_tty_tx_empty(struct uart_port *port) static unsigned int jsm_tty_get_mctrl(struct uart_port *port) { int result; - struct jsm_channel *channel = (struct jsm_channel *)port; + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); @@ -98,7 +99,8 @@ static unsigned int jsm_tty_get_mctrl(struct uart_port *port) */ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct jsm_channel *channel = (struct jsm_channel *)port; + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); @@ -133,7 +135,8 @@ static void jsm_tty_write(struct uart_port *port) static void jsm_tty_start_tx(struct uart_port *port) { - struct jsm_channel *channel = (struct jsm_channel *)port; + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); @@ -145,7 +148,8 @@ static void jsm_tty_start_tx(struct uart_port *port) static void jsm_tty_stop_tx(struct uart_port *port) { - struct jsm_channel *channel = (struct jsm_channel *)port; + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); @@ -157,7 +161,8 @@ static void jsm_tty_stop_tx(struct uart_port *port) static void jsm_tty_send_xchar(struct uart_port *port, char ch) { unsigned long lock_flags; - struct jsm_channel *channel = (struct jsm_channel *)port; + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); struct ktermios *termios; spin_lock_irqsave(&port->lock, lock_flags); @@ -172,7 +177,8 @@ static void jsm_tty_send_xchar(struct uart_port *port, char ch) static void jsm_tty_stop_rx(struct uart_port *port) { - struct jsm_channel *channel = (struct jsm_channel *)port; + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); channel->ch_bd->bd_ops->disable_receiver(channel); } @@ -180,7 +186,8 @@ static void jsm_tty_stop_rx(struct uart_port *port) static void jsm_tty_break(struct uart_port *port, int break_state) { unsigned long lock_flags; - struct jsm_channel *channel = (struct jsm_channel *)port; + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); spin_lock_irqsave(&port->lock, lock_flags); if (break_state == -1) @@ -194,7 +201,8 @@ static void jsm_tty_break(struct uart_port *port, int break_state) static int jsm_tty_open(struct uart_port *port) { struct jsm_board *brd; - struct jsm_channel *channel = (struct jsm_channel *)port; + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); struct ktermios *termios; /* Get board pointer from our array of majors we have allocated */ @@ -273,7 +281,8 @@ static void jsm_tty_close(struct uart_port *port) { struct jsm_board *bd; struct ktermios *ts; - struct jsm_channel *channel = (struct jsm_channel *)port; + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "start\n"); @@ -307,7 +316,8 @@ static void jsm_tty_set_termios(struct uart_port *port, struct ktermios *old_termios) { unsigned long lock_flags; - struct jsm_channel *channel = (struct jsm_channel *)port; + struct jsm_channel *channel = + container_of(port, struct jsm_channel, uart_port); spin_lock_irqsave(&port->lock, lock_flags); channel->ch_c_cflag = termios->c_cflag; -- cgit v0.10.2 From edb20c7aa5080f983af562d117c577975c9c392e Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:01:08 +0200 Subject: tty: use container_of to resolve uart_pmac_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index abbfedb..4aca322 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1352,7 +1352,8 @@ static int pmz_verify_port(struct uart_port *port, struct serial_struct *ser) static int pmz_poll_get_char(struct uart_port *port) { - struct uart_pmac_port *uap = (struct uart_pmac_port *)port; + struct uart_pmac_port *uap = + container_of(port, struct uart_pmac_port, port); int tries = 2; while (tries) { @@ -1367,7 +1368,8 @@ static int pmz_poll_get_char(struct uart_port *port) static void pmz_poll_put_char(struct uart_port *port, unsigned char c) { - struct uart_pmac_port *uap = (struct uart_pmac_port *)port; + struct uart_pmac_port *uap = + container_of(port, struct uart_pmac_port, port); /* Wait for the transmit buffer to empty. */ while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0) @@ -1954,7 +1956,8 @@ static void __exit exit_pmz(void) static void pmz_console_putchar(struct uart_port *port, int ch) { - struct uart_pmac_port *uap = (struct uart_pmac_port *)port; + struct uart_pmac_port *uap = + container_of(port, struct uart_pmac_port, port); /* Wait for the transmit buffer to empty. */ while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0) -- cgit v0.10.2 From 9a6962c531d890880b5b6a75df0678b1ba7742cc Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:01:09 +0200 Subject: serial: sunsu: use container_of to resolve uart_sunsu_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Acked-by: David S. Miller Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 5326ae1..be010f8 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -264,7 +264,8 @@ static inline void __stop_tx(struct uart_sunsu_port *p) static void sunsu_stop_tx(struct uart_port *port) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); __stop_tx(up); @@ -279,7 +280,8 @@ static void sunsu_stop_tx(struct uart_port *port) static void sunsu_start_tx(struct uart_port *port) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; @@ -297,7 +299,8 @@ static void sunsu_start_tx(struct uart_port *port) static void sunsu_stop_rx(struct uart_port *port) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); up->ier &= ~UART_IER_RLSI; up->port.read_status_mask &= ~UART_LSR_DR; @@ -306,7 +309,8 @@ static void sunsu_stop_rx(struct uart_port *port) static void sunsu_enable_ms(struct uart_port *port) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); @@ -543,7 +547,8 @@ static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id) static unsigned int sunsu_tx_empty(struct uart_port *port) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); unsigned long flags; unsigned int ret; @@ -556,7 +561,8 @@ static unsigned int sunsu_tx_empty(struct uart_port *port) static unsigned int sunsu_get_mctrl(struct uart_port *port) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); unsigned char status; unsigned int ret; @@ -576,7 +582,8 @@ static unsigned int sunsu_get_mctrl(struct uart_port *port) static void sunsu_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); unsigned char mcr = 0; if (mctrl & TIOCM_RTS) @@ -595,7 +602,8 @@ static void sunsu_set_mctrl(struct uart_port *port, unsigned int mctrl) static void sunsu_break_ctl(struct uart_port *port, int break_state) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); @@ -609,7 +617,8 @@ static void sunsu_break_ctl(struct uart_port *port, int break_state) static int sunsu_startup(struct uart_port *port) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); unsigned long flags; int retval; @@ -719,7 +728,8 @@ static int sunsu_startup(struct uart_port *port) static void sunsu_shutdown(struct uart_port *port) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); unsigned long flags; /* @@ -767,7 +777,8 @@ static void sunsu_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); unsigned char cval, fcr = 0; unsigned long flags; @@ -918,7 +929,8 @@ static int sunsu_request_port(struct uart_port *port) static void sunsu_config_port(struct uart_port *port, int flags) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); if (flags & UART_CONFIG_TYPE) { /* @@ -1277,7 +1289,8 @@ static __inline__ void wait_for_xmitr(struct uart_sunsu_port *up) static void sunsu_console_putchar(struct uart_port *port, int ch) { - struct uart_sunsu_port *up = (struct uart_sunsu_port *)port; + struct uart_sunsu_port *up = + container_of(port, struct uart_sunsu_port, port); wait_for_xmitr(up); serial_out(up, UART_TX, ch); -- cgit v0.10.2 From 726657653ff1400bd144b6f293b907d2a1fc9865 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:01:10 +0200 Subject: serial: sunsab: use container_of to resolve uart_sunsu_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Acked-by: David S. Miller Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index b339fe4..e3b43a4 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -345,7 +345,8 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id) /* port->lock is not held. */ static unsigned int sunsab_tx_empty(struct uart_port *port) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); int ret; /* Do not need a lock for a state test like this. */ @@ -360,7 +361,8 @@ static unsigned int sunsab_tx_empty(struct uart_port *port) /* port->lock held by caller. */ static void sunsab_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); if (mctrl & TIOCM_RTS) { up->cached_mode &= ~SAB82532_MODE_FRTS; @@ -383,7 +385,8 @@ static void sunsab_set_mctrl(struct uart_port *port, unsigned int mctrl) /* port->lock is held by caller and interrupts are disabled. */ static unsigned int sunsab_get_mctrl(struct uart_port *port) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); unsigned char val; unsigned int result; @@ -404,7 +407,8 @@ static unsigned int sunsab_get_mctrl(struct uart_port *port) /* port->lock held by caller. */ static void sunsab_stop_tx(struct uart_port *port) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); up->interrupt_mask1 |= SAB82532_IMR1_XPR; writeb(up->interrupt_mask1, &up->regs->w.imr1); @@ -432,7 +436,8 @@ static void sunsab_tx_idle(struct uart_sunsab_port *up) /* port->lock held by caller. */ static void sunsab_start_tx(struct uart_port *port) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); struct circ_buf *xmit = &up->port.state->xmit; int i; @@ -465,7 +470,8 @@ static void sunsab_start_tx(struct uart_port *port) /* port->lock is not held. */ static void sunsab_send_xchar(struct uart_port *port, char ch) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); unsigned long flags; if (ch == __DISABLED_CHAR) @@ -482,7 +488,8 @@ static void sunsab_send_xchar(struct uart_port *port, char ch) /* port->lock held by caller. */ static void sunsab_stop_rx(struct uart_port *port) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); up->interrupt_mask0 |= SAB82532_IMR0_TCD; writeb(up->interrupt_mask1, &up->regs->w.imr0); @@ -491,7 +498,8 @@ static void sunsab_stop_rx(struct uart_port *port) /* port->lock is not held. */ static void sunsab_break_ctl(struct uart_port *port, int break_state) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); unsigned long flags; unsigned char val; @@ -514,7 +522,8 @@ static void sunsab_break_ctl(struct uart_port *port, int break_state) /* port->lock is not held. */ static int sunsab_startup(struct uart_port *port) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); unsigned long flags; unsigned char tmp; int err = request_irq(up->port.irq, sunsab_interrupt, @@ -585,7 +594,8 @@ static int sunsab_startup(struct uart_port *port) /* port->lock is not held. */ static void sunsab_shutdown(struct uart_port *port) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); @@ -771,7 +781,8 @@ static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cfla static void sunsab_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); unsigned long flags; unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); unsigned int quot = uart_get_divisor(port, baud); @@ -840,7 +851,8 @@ static struct uart_sunsab_port *sunsab_ports; static void sunsab_console_putchar(struct uart_port *port, int c) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *)port; + struct uart_sunsab_port *up = + container_of(port, struct uart_sunsab_port, port); sunsab_tec_wait(up); writeb(c, &up->regs->w.tic); -- cgit v0.10.2 From b70e5e9d717faa334bf33c232c763be3f673138e Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:19:46 +0200 Subject: serial: amba-pl010: use container_of to resolve uart_amba_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 2064d31..00ae8dc 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -75,7 +75,8 @@ struct uart_amba_port { static void pl010_stop_tx(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int cr; cr = readb(uap->port.membase + UART010_CR); @@ -85,7 +86,8 @@ static void pl010_stop_tx(struct uart_port *port) static void pl010_start_tx(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int cr; cr = readb(uap->port.membase + UART010_CR); @@ -95,7 +97,8 @@ static void pl010_start_tx(struct uart_port *port) static void pl010_stop_rx(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int cr; cr = readb(uap->port.membase + UART010_CR); @@ -105,7 +108,8 @@ static void pl010_stop_rx(struct uart_port *port) static void pl010_enable_ms(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int cr; cr = readb(uap->port.membase + UART010_CR); @@ -259,14 +263,16 @@ static irqreturn_t pl010_int(int irq, void *dev_id) static unsigned int pl010_tx_empty(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int status = readb(uap->port.membase + UART01x_FR); return status & UART01x_FR_BUSY ? 0 : TIOCSER_TEMT; } static unsigned int pl010_get_mctrl(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int result = 0; unsigned int status; @@ -283,7 +289,8 @@ static unsigned int pl010_get_mctrl(struct uart_port *port) static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); if (uap->data) uap->data->set_mctrl(uap->dev, uap->port.membase, mctrl); @@ -291,7 +298,8 @@ static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl) static void pl010_break_ctl(struct uart_port *port, int break_state) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned long flags; unsigned int lcr_h; @@ -307,7 +315,8 @@ static void pl010_break_ctl(struct uart_port *port, int break_state) static int pl010_startup(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); int retval; /* @@ -347,7 +356,8 @@ static int pl010_startup(struct uart_port *port) static void pl010_shutdown(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); /* * Free the interrupt @@ -374,7 +384,8 @@ static void pl010_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int lcr_h, old_cr; unsigned long flags; unsigned int baud, quot; @@ -551,7 +562,8 @@ static struct uart_amba_port *amba_ports[UART_NR]; static void pl010_console_putchar(struct uart_port *port, int ch) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int status; do { -- cgit v0.10.2 From afc5438b4cc1082fc1ad6b729b15d26d327e70f2 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:19:47 +0200 Subject: serial: pnx8xxx: use container_of to resolve pnx8xxx_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/pnx8xxx_uart.c b/drivers/tty/serial/pnx8xxx_uart.c index 2ba24a4..9fd9414 100644 --- a/drivers/tty/serial/pnx8xxx_uart.c +++ b/drivers/tty/serial/pnx8xxx_uart.c @@ -126,7 +126,8 @@ static void pnx8xxx_timeout(unsigned long data) */ static void pnx8xxx_stop_tx(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); u32 ien; /* Disable TX intr */ @@ -142,7 +143,8 @@ static void pnx8xxx_stop_tx(struct uart_port *port) */ static void pnx8xxx_start_tx(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); u32 ien; /* Clear all pending TX intr */ @@ -158,7 +160,8 @@ static void pnx8xxx_start_tx(struct uart_port *port) */ static void pnx8xxx_stop_rx(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); u32 ien; /* Disable RX intr */ @@ -174,7 +177,8 @@ static void pnx8xxx_stop_rx(struct uart_port *port) */ static void pnx8xxx_enable_ms(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); mod_timer(&sport->timer, jiffies); } @@ -313,14 +317,16 @@ static irqreturn_t pnx8xxx_int(int irq, void *dev_id) */ static unsigned int pnx8xxx_tx_empty(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); return serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA ? 0 : TIOCSER_TEMT; } static unsigned int pnx8xxx_get_mctrl(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); unsigned int mctrl = TIOCM_DSR; unsigned int msr; @@ -347,7 +353,8 @@ static void pnx8xxx_set_mctrl(struct uart_port *port, unsigned int mctrl) */ static void pnx8xxx_break_ctl(struct uart_port *port, int break_state) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); unsigned long flags; unsigned int lcr; @@ -363,7 +370,8 @@ static void pnx8xxx_break_ctl(struct uart_port *port, int break_state) static int pnx8xxx_startup(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); int retval; /* @@ -397,7 +405,8 @@ static int pnx8xxx_startup(struct uart_port *port) static void pnx8xxx_shutdown(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); int lcr; /* @@ -434,7 +443,8 @@ static void pnx8xxx_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); unsigned long flags; unsigned int lcr_fcr, old_ien, baud, quot; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; @@ -551,7 +561,8 @@ pnx8xxx_set_termios(struct uart_port *port, struct ktermios *termios, static const char *pnx8xxx_type(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); return sport->port.type == PORT_PNX8XXX ? "PNX8XXX" : NULL; } @@ -561,7 +572,8 @@ static const char *pnx8xxx_type(struct uart_port *port) */ static void pnx8xxx_release_port(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); release_mem_region(sport->port.mapbase, UART_PORT_SIZE); } @@ -571,7 +583,8 @@ static void pnx8xxx_release_port(struct uart_port *port) */ static int pnx8xxx_request_port(struct uart_port *port) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, "pnx8xxx-uart") != NULL ? 0 : -EBUSY; } @@ -581,7 +594,8 @@ static int pnx8xxx_request_port(struct uart_port *port) */ static void pnx8xxx_config_port(struct uart_port *port, int flags) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); if (flags & UART_CONFIG_TYPE && pnx8xxx_request_port(&sport->port) == 0) @@ -596,7 +610,8 @@ static void pnx8xxx_config_port(struct uart_port *port, int flags) static int pnx8xxx_verify_port(struct uart_port *port, struct serial_struct *ser) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); int ret = 0; if (ser->type != PORT_UNKNOWN && ser->type != PORT_PNX8XXX) @@ -662,7 +677,8 @@ static void __init pnx8xxx_init_ports(void) static void pnx8xxx_console_putchar(struct uart_port *port, int ch) { - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + struct pnx8xxx_port *sport = + container_of(port, struct pnx8xxx_port, port); int status; do { -- cgit v0.10.2 From 6f414130e8605920d78827e2bbf79a5ce7d25e4a Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:19:48 +0200 Subject: serial: use container_of to resolve uart_sunzilog_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Acked-by: David S. Miller Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index 02df394..844aae7 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -644,7 +644,8 @@ static unsigned int sunzilog_get_mctrl(struct uart_port *port) /* The port lock is held and interrupts are disabled. */ static void sunzilog_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct uart_sunzilog_port *up = + container_of(port, struct uart_sunzilog_port, port); struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); unsigned char set_bits, clear_bits; @@ -668,7 +669,8 @@ static void sunzilog_set_mctrl(struct uart_port *port, unsigned int mctrl) /* The port lock is held and interrupts are disabled. */ static void sunzilog_stop_tx(struct uart_port *port) { - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct uart_sunzilog_port *up = + container_of(port, struct uart_sunzilog_port, port); up->flags |= SUNZILOG_FLAG_TX_STOPPED; } @@ -676,7 +678,8 @@ static void sunzilog_stop_tx(struct uart_port *port) /* The port lock is held and interrupts are disabled. */ static void sunzilog_start_tx(struct uart_port *port) { - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct uart_sunzilog_port *up = + container_of(port, struct uart_sunzilog_port, port); struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); unsigned char status; @@ -736,7 +739,8 @@ static void sunzilog_stop_rx(struct uart_port *port) /* The port lock is held. */ static void sunzilog_enable_ms(struct uart_port *port) { - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct uart_sunzilog_port *up = + container_of(port, struct uart_sunzilog_port, port); struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); unsigned char new_reg; @@ -752,7 +756,8 @@ static void sunzilog_enable_ms(struct uart_port *port) /* The port lock is not held. */ static void sunzilog_break_ctl(struct uart_port *port, int break_state) { - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct uart_sunzilog_port *up = + container_of(port, struct uart_sunzilog_port, port); struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); unsigned char set_bits, clear_bits, new_reg; unsigned long flags; @@ -938,7 +943,8 @@ static void sunzilog_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct uart_sunzilog_port *up = + container_of(port, struct uart_sunzilog_port, port); unsigned long flags; int baud, brg; @@ -998,7 +1004,8 @@ static int sunzilog_verify_port(struct uart_port *port, struct serial_struct *se static int sunzilog_get_poll_char(struct uart_port *port) { unsigned char ch, r1; - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct uart_sunzilog_port *up = + container_of(port, struct uart_sunzilog_port, port); struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(&up->port); @@ -1032,7 +1039,8 @@ static int sunzilog_get_poll_char(struct uart_port *port) static void sunzilog_put_poll_char(struct uart_port *port, unsigned char ch) { - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *)port; + struct uart_sunzilog_port *up = + container_of(port, struct uart_sunzilog_port, port); sunzilog_putchar(&up->port, ch); } -- cgit v0.10.2 From f9c1b28e8991d37df9d58026c7f7f882b7cfae23 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 5 Oct 2014 19:19:49 +0200 Subject: tty: ar933x_uart: use container_of to resolve ar933x_uart_port from uart_port Use container_of instead of casting first structure member. Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 0be1c45..2739361 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -119,7 +119,8 @@ static inline void ar933x_uart_putc(struct ar933x_uart_port *up, int ch) static unsigned int ar933x_uart_tx_empty(struct uart_port *port) { - struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + struct ar933x_uart_port *up = + container_of(port, struct ar933x_uart_port, port); unsigned long flags; unsigned int rdata; @@ -141,21 +142,24 @@ static void ar933x_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) static void ar933x_uart_start_tx(struct uart_port *port) { - struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + struct ar933x_uart_port *up = + container_of(port, struct ar933x_uart_port, port); ar933x_uart_start_tx_interrupt(up); } static void ar933x_uart_stop_tx(struct uart_port *port) { - struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + struct ar933x_uart_port *up = + container_of(port, struct ar933x_uart_port, port); ar933x_uart_stop_tx_interrupt(up); } static void ar933x_uart_stop_rx(struct uart_port *port) { - struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + struct ar933x_uart_port *up = + container_of(port, struct ar933x_uart_port, port); up->ier &= ~AR933X_UART_INT_RX_VALID; ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier); @@ -163,7 +167,8 @@ static void ar933x_uart_stop_rx(struct uart_port *port) static void ar933x_uart_break_ctl(struct uart_port *port, int break_state) { - struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + struct ar933x_uart_port *up = + container_of(port, struct ar933x_uart_port, port); unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); @@ -231,7 +236,8 @@ static void ar933x_uart_set_termios(struct uart_port *port, struct ktermios *new, struct ktermios *old) { - struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + struct ar933x_uart_port *up = + container_of(port, struct ar933x_uart_port, port); unsigned int cs; unsigned long flags; unsigned int baud, scale, step; @@ -404,7 +410,8 @@ static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id) static int ar933x_uart_startup(struct uart_port *port) { - struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + struct ar933x_uart_port *up = + container_of(port, struct ar933x_uart_port, port); unsigned long flags; int ret; @@ -430,7 +437,8 @@ static int ar933x_uart_startup(struct uart_port *port) static void ar933x_uart_shutdown(struct uart_port *port) { - struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + struct ar933x_uart_port *up = + container_of(port, struct ar933x_uart_port, port); /* Disable all interrupts */ up->ier = 0; @@ -468,7 +476,8 @@ static void ar933x_uart_config_port(struct uart_port *port, int flags) static int ar933x_uart_verify_port(struct uart_port *port, struct serial_struct *ser) { - struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + struct ar933x_uart_port *up = + container_of(port, struct ar933x_uart_port, port); if (ser->type != PORT_UNKNOWN && ser->type != PORT_AR933X) @@ -521,7 +530,8 @@ static void ar933x_uart_wait_xmitr(struct ar933x_uart_port *up) static void ar933x_uart_console_putchar(struct uart_port *port, int ch) { - struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + struct ar933x_uart_port *up = + container_of(port, struct ar933x_uart_port, port); ar933x_uart_wait_xmitr(up); ar933x_uart_putc(up, ch); -- cgit v0.10.2 From 510d483285432e45c453722f7ff7a9cf63e95bc4 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 6 Oct 2014 12:07:23 +0200 Subject: serial: samsung: Remove checks for CONFIG_SAMSUNG_CLOCK Commit 32726d2d5502 ("ARM: SAMSUNG: Remove legacy clock code") removed the Kconfig symbol SAMSUNG_CLOCK. Remove the last checks for its macro, and the dead code they hide, too. Signed-off-by: Paul Bolle Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index c78f43a..796c04c 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1226,24 +1226,6 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, return 0; } -#ifdef CONFIG_SAMSUNG_CLOCK -static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct uart_port *port = s3c24xx_dev_to_port(dev); - struct s3c24xx_uart_port *ourport = to_ourport(port); - - if (IS_ERR(ourport->baudclk)) - return -EINVAL; - - return snprintf(buf, PAGE_SIZE, "* %s\n", - ourport->baudclk->name ?: "(null)"); -} - -static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); -#endif - /* Device driver serial port probe */ static const struct of_device_id s3c24xx_uart_dt_match[]; @@ -1329,12 +1311,6 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) */ clk_disable_unprepare(ourport->clk); -#ifdef CONFIG_SAMSUNG_CLOCK - ret = device_create_file(&pdev->dev, &dev_attr_clock_source); - if (ret < 0) - dev_err(&pdev->dev, "failed to add clock source attr.\n"); -#endif - ret = s3c24xx_serial_cpufreq_register(ourport); if (ret < 0) dev_err(&pdev->dev, "failed to add cpufreq notifier\n"); @@ -1348,9 +1324,6 @@ static int s3c24xx_serial_remove(struct platform_device *dev) if (port) { s3c24xx_serial_cpufreq_deregister(to_ourport(port)); -#ifdef CONFIG_SAMSUNG_CLOCK - device_remove_file(&dev->dev, &dev_attr_clock_source); -#endif uart_remove_one_port(&s3c24xx_uart_drv, port); } -- cgit v0.10.2 From 32304d753304b1aaf79eb4957d1d1375ddc92c14 Mon Sep 17 00:00:00 2001 From: Gregory Hermant Date: Tue, 30 Sep 2014 08:59:17 +0200 Subject: max310x: max3109_detect should use indirect addressing in SPI mode for REVID register This patch allows to read the REV_ID register in SPI mode and consequently to properly detect the max3109. Indeed in SPI mode, this register is only accessible by using indirect addressing. Signed-off-by: Gregory Hermant Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 0041a64..4cdc555 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -346,10 +346,13 @@ static int max3109_detect(struct device *dev) unsigned int val = 0; int ret; - ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val); + ret = regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, + MAX310X_EXTREG_ENBL); if (ret) return ret; + regmap_read(s->regmap, MAX310X_REVID_EXTREG, &val); + regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, MAX310X_EXTREG_DSBL); if (((val & MAX310x_REV_MASK) != MAX3109_REV_ID)) { dev_err(dev, "%s ID 0x%02x does not match\n", s->devtype->name, val); -- cgit v0.10.2 From 10263f656a23e303d9d2ae1336ea6ff83f0714ef Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 8 Oct 2014 00:25:57 +0900 Subject: tty: hvcs: Remove unnecessary KERN_ERR in hvcs.c This patch remove unnecessary KERN_ERR in pr_warning() within hvcs.c Signed-off-by: Masanari Iida Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 81e939e..81ff7e1 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -1575,7 +1575,7 @@ static int __init hvcs_module_init(void) */ rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan); if (rc) - pr_warning(KERN_ERR "HVCS: Failed to create rescan file (err %d)\n", rc); + pr_warning("HVCS: Failed to create rescan file (err %d)\n", rc); return 0; } -- cgit v0.10.2 From a8b26e1af94825296f29eee6bfc001c5543d3c7e Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Tue, 7 Oct 2014 17:35:47 -0700 Subject: serial: 8250_dw: Add DMA support for non-ACPI platforms The dma pointer under struct uart_8250_port is currently left unassigned for non-ACPI platforms. It should be pointing to the dma member in struct dw8250_data like how it was done for ACPI, so the core 8250 code will try to request for DMA when registering the port If DMA is not enabled in device tree, request DMA will fail and the driver will fall back to PIO Signed-off-by: Ray Jui Reviewed-by: JD (Jiandong) Zheng Reviewed-by: Scott Branden Tested-by: Scott Branden Reviewed-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index beea6ca..73a14ae 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -290,6 +290,14 @@ static int dw8250_probe_of(struct uart_port *p, if (has_ucv) dw8250_setup_port(up); + /* if we have a valid fifosize, try hooking up DMA here */ + if (p->fifosize) { + up->dma = &data->dma; + + up->dma->rxconf.src_maxburst = p->fifosize / 4; + up->dma->txconf.dst_maxburst = p->fifosize / 4; + } + if (!of_property_read_u32(np, "reg-shift", &val)) p->regshift = val; -- cgit v0.10.2 From aad31088dbfe6016147469974f0cd6c3f3201f3e Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Wed, 8 Oct 2014 21:57:27 +0200 Subject: serial/max310x: Remove obsolete #ifset TIOC[SG]RS485 Commit e676253b19b2 ("serial/8250: Add support for RS485 IOCTLs") added references to TIOC[SG]RS48 on 8250_core.c. This change triggered the need to define them in all the arches that uses tty/serial. This made #ifdef TIOC[SG]RS48 obsolete. Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 4cdc555..ecb4667 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -880,7 +880,6 @@ static void max310x_set_termios(struct uart_port *port, static int max310x_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) { -#if defined(TIOCSRS485) && defined(TIOCGRS485) struct serial_rs485 rs485; unsigned int val; @@ -923,7 +922,6 @@ static int max310x_ioctl(struct uart_port *port, unsigned int cmd, default: break; } -#endif return -ENOIOCTLCMD; } -- cgit v0.10.2 From c015b4ad2ae57e5f07a5d4f746835356056c8e03 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Wed, 8 Oct 2014 21:57:28 +0200 Subject: serial/sc16is7xx: Remove obsolete #ifset TIOC[SG]RS485 Commit e676253b19b2 ("serial/8250: Add support for RS485 IOCTLs") added references to TIOC[SG]RS48 on 8250_core.c. This change triggered the need to define them in all the arches that uses tty/serial. This made #ifdef TIOC[SG]RS48 obsolete. Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 6246820..7a79113 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -830,7 +830,6 @@ static void sc16is7xx_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); } -#if defined(TIOCSRS485) && defined(TIOCGRS485) static void sc16is7xx_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) { @@ -848,12 +847,10 @@ static void sc16is7xx_config_rs485(struct uart_port *port, 0); } } -#endif static int sc16is7xx_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) { -#if defined(TIOCSRS485) && defined(TIOCGRS485) struct serial_rs485 rs485; switch (cmd) { @@ -872,7 +869,6 @@ static int sc16is7xx_ioctl(struct uart_port *port, unsigned int cmd, default: break; } -#endif return -ENOIOCTLCMD; } -- cgit v0.10.2 From fe655de1718e063bd0b1c7a4305b8eea54d199d9 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Fri, 17 Oct 2014 04:01:11 -0500 Subject: tty/serial: earlycon: Fix print for implied MMIO case For the case in which we just provide an address as an argument to the earlycon console type like: earlycon=msm_serial_dm,0xf991e000 We would report this as an IO port based mapping and not as MMIO. Simple fix to use the port->iotype to decide which message to print. Signed-off-by: Kumar Gala Acked-by: Mark RUtland Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index a514ee6..64fe25a 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -98,7 +98,7 @@ static int __init parse_options(struct earlycon_device *device, strlcpy(device->options, options, length); } - if (mmio || mmio32) + if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32) pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n", mmio32 ? "32" : "", (unsigned long long)port->mapbase, -- cgit v0.10.2 From c0d1c6b0f0dc731b1b17f48876f048af36ad3ce8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 Oct 2014 14:49:37 -0200 Subject: serial: imx: Fix the reporting of interrupts On a imx system with ttymxc0, ttymxc1 and ttymxc4 registered we see the following output from 'cat /proc/interrupts': $ cat /proc/interrupts CPU0 ... 58: 39 GIC 58 2020000.serial 67: 115 GIC 67 21f8000.i2c The only uart irq that appears is ttymxc0, which is the console. As ttymxc1 and ttymxc4 will only have their irq registered at imx_startup(), they are not shown right after probe. Transmitting to ttymxc1 and ttymxc4 will cause their irqs to be registered, but the output shows: $ echo "111111" > /dev/ttymxc1 $ echo "444444" > /dev/ttymxc4 $ cat /proc/interrupts CPU0 ... 58: 150 GIC 58 2020000.serial 59: 1 GIC 59 62: 1 GIC 62 67: 115 GIC 67 21f8000.i2c ,which misses printing the associated device address. In order to fix this, register all the irqs inside the probe function via devm_request_irq(), which will correctly report the serial interrupts associated with their correspondent serial device and also helps simplyfing the code by avoiding the calls to free_irq(). $ echo "111111" > /dev/ttymxc1 $ echo "444444" > /dev/ttymxc4 $ cat /proc/interrupts CPU0 .... 58: 202 GIC 58 2020000.serial 59: 1 GIC 59 21e8000.serial 62: 1 GIC 62 21f4000.serial 67: 115 GIC 67 21f8000.i2c Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 8f62a3c..e4a2846 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1109,37 +1109,6 @@ static int imx_startup(struct uart_port *port) while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0)) udelay(1); - /* - * Allocate the IRQ(s) i.MX1 has three interrupts whereas later - * chips only have one interrupt. - */ - if (sport->txirq > 0) { - retval = request_irq(sport->rxirq, imx_rxint, 0, - dev_name(port->dev), sport); - if (retval) - goto error_out1; - - retval = request_irq(sport->txirq, imx_txint, 0, - dev_name(port->dev), sport); - if (retval) - goto error_out2; - - /* do not use RTS IRQ on IrDA */ - if (!USE_IRDA(sport)) { - retval = request_irq(sport->rtsirq, imx_rtsint, 0, - dev_name(port->dev), sport); - if (retval) - goto error_out3; - } - } else { - retval = request_irq(sport->port.irq, imx_int, 0, - dev_name(port->dev), sport); - if (retval) { - free_irq(sport->port.irq, sport); - goto error_out1; - } - } - spin_lock_irqsave(&sport->port.lock, flags); /* * Finally, clear and enable interrupts @@ -1202,12 +1171,6 @@ static int imx_startup(struct uart_port *port) return 0; -error_out3: - if (sport->txirq) - free_irq(sport->txirq, sport); -error_out2: - if (sport->rxirq) - free_irq(sport->rxirq, sport); error_out1: return retval; } @@ -1255,17 +1218,6 @@ static void imx_shutdown(struct uart_port *port) del_timer_sync(&sport->timer); /* - * Free the interrupts - */ - if (sport->txirq > 0) { - if (!USE_IRDA(sport)) - free_irq(sport->rtsirq, sport); - free_irq(sport->txirq, sport); - free_irq(sport->rxirq, sport); - } else - free_irq(sport->port.irq, sport); - - /* * Disable all interrupts, port and break condition. */ @@ -1929,6 +1881,36 @@ static int serial_imx_probe(struct platform_device *pdev) sport->port.uartclk = clk_get_rate(sport->clk_per); + /* + * Allocate the IRQ(s) i.MX1 has three interrupts whereas later + * chips only have one interrupt. + */ + if (sport->txirq > 0) { + ret = devm_request_irq(&pdev->dev, sport->rxirq, imx_rxint, 0, + dev_name(&pdev->dev), sport); + if (ret) + return ret; + + ret = devm_request_irq(&pdev->dev, sport->txirq, imx_txint, 0, + dev_name(&pdev->dev), sport); + if (ret) + return ret; + + /* do not use RTS IRQ on IrDA */ + if (!USE_IRDA(sport)) { + ret = devm_request_irq(&pdev->dev, sport->rtsirq, + imx_rtsint, 0, + dev_name(&pdev->dev), sport); + if (ret) + return ret; + } + } else { + ret = devm_request_irq(&pdev->dev, sport->port.irq, imx_int, 0, + dev_name(&pdev->dev), sport); + if (ret) + return ret; + } + imx_ports[sport->port.line] = sport; platform_set_drvdata(pdev, sport); -- cgit v0.10.2 From cb0f0a5ff43c1ae05c6d696857c42dceb0cda7f8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 Oct 2014 14:49:38 -0200 Subject: serial: imx: Remove unneeded goto label Instead of jumping to 'error_out1' label we can simplify the code and return the error code directly. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index e4a2846..e7dc210 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1076,11 +1076,11 @@ static int imx_startup(struct uart_port *port) retval = clk_prepare_enable(sport->clk_per); if (retval) - goto error_out1; + return retval; retval = clk_prepare_enable(sport->clk_ipg); if (retval) { clk_disable_unprepare(sport->clk_per); - goto error_out1; + return retval; } imx_setup_ufcr(sport, 0); @@ -1170,9 +1170,6 @@ static int imx_startup(struct uart_port *port) } return 0; - -error_out1: - return retval; } static void imx_shutdown(struct uart_port *port) -- cgit v0.10.2 From f1f2b6e497170a6ab83aeb781688a65c25621836 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 Oct 2014 14:49:39 -0200 Subject: serial: imx: Remove unneeded OOM error message When kzalloc() fails the core MM will already complain about it, so there is no need to have the error message locally. Remove the unneeded error message. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index e7dc210..9e8685e 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -991,7 +991,6 @@ static int imx_uart_dma_init(struct imx_port *sport) sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!sport->rx_buf) { - dev_err(dev, "cannot alloc DMA buffer.\n"); ret = -ENOMEM; goto err; } -- cgit v0.10.2 From f0fd1b73b0f3589ff90582fbbb5eedc149caefab Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 Oct 2014 14:49:40 -0200 Subject: serial: imx: Remove unneeded registration message There is no real value in displaying "Serial: IMX driver" in every boot. The uart_register_driver() can fail and even so the "Serial: IMX driver" will be displayed, which is not really helpful. This is particularly annoying when booting multi_v7_defconfig kernel on a SoC that is not a i.MX and even though this message gets displayed. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 9e8685e..d2e6cf5 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1937,11 +1937,8 @@ static struct platform_driver serial_imx_driver = { static int __init imx_serial_init(void) { - int ret; - - pr_info("Serial: IMX driver\n"); + int ret = uart_register_driver(&imx_reg); - ret = uart_register_driver(&imx_reg); if (ret) return ret; -- cgit v0.10.2 From 7e041abf79c1b555b9d117d4a319b505ee601f55 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 26 Oct 2014 22:25:05 -0700 Subject: tty: ipwireless: Fix probable mask then right shift defects Precedence of & and >> is not the same and is not left to right. shift has higher precedence and should be done after the mask. Add parentheses around the masks. Signed-off-by: Joe Perches Acked-by: David Sterba Reviewed-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c index 2c14842..5c77e1e 100644 --- a/drivers/tty/ipwireless/hardware.c +++ b/drivers/tty/ipwireless/hardware.c @@ -378,9 +378,9 @@ static void swap_packet_bitfield_to_le(unsigned char *data) /* * transform bits from aa.bbb.ccc to ccc.bbb.aa */ - ret |= tmp & 0xc0 >> 6; - ret |= tmp & 0x38 >> 1; - ret |= tmp & 0x07 << 5; + ret |= (tmp & 0xc0) >> 6; + ret |= (tmp & 0x38) >> 1; + ret |= (tmp & 0x07) << 5; *data = ret & 0xff; #endif } @@ -393,9 +393,9 @@ static void swap_packet_bitfield_from_le(unsigned char *data) /* * transform bits from ccc.bbb.aa to aa.bbb.ccc */ - ret |= tmp & 0xe0 >> 5; - ret |= tmp & 0x1c << 1; - ret |= tmp & 0x03 << 6; + ret |= (tmp & 0xe0) >> 5; + ret |= (tmp & 0x1c) << 1; + ret |= (tmp & 0x03) << 6; *data = ret & 0xff; #endif } -- cgit v0.10.2 From 1256937f0438786cb19a7cb716b12025b30052e9 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:26:24 -0500 Subject: tty: Replace open-coded test with tty_hung_up_p() tty_hung_up_p() is equivalent to the open-coded test in tty_open(). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 4bd48f7..1262368 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2129,7 +2129,7 @@ retry_open: /* * Need to reset f_op in case a hangup happened. */ - if (filp->f_op == &hung_up_tty_fops) + if (tty_hung_up_p(filp)) filp->f_op = &tty_fops; goto retry_open; } -- cgit v0.10.2 From c961bfb17406c9fda6ba37cbba34feacdd09c6eb Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:26:25 -0500 Subject: tty: Call methods in modern style The use of older function ptr calling style, (*fn)(), makes static analysis more error-prone; replace with modern fn() style. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 1262368..01d45fd 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -981,7 +981,7 @@ void __stop_tty(struct tty_struct *tty) return; tty->stopped = 1; if (tty->ops->stop) - (tty->ops->stop)(tty); + tty->ops->stop(tty); } void stop_tty(struct tty_struct *tty) @@ -1012,7 +1012,7 @@ void __start_tty(struct tty_struct *tty) return; tty->stopped = 0; if (tty->ops->start) - (tty->ops->start)(tty); + tty->ops->start(tty); tty_wakeup(tty); } @@ -1066,7 +1066,7 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, situation */ ld = tty_ldisc_ref_wait(tty); if (ld->ops->read) - i = (ld->ops->read)(tty, file, buf, count); + i = ld->ops->read(tty, file, buf, count); else i = -EIO; tty_ldisc_deref(ld); @@ -2182,7 +2182,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) ld = tty_ldisc_ref_wait(tty); if (ld->ops->poll) - ret = (ld->ops->poll)(tty, filp, wait); + ret = ld->ops->poll(tty, filp, wait); tty_ldisc_deref(ld); return ret; } @@ -2905,7 +2905,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } if (tty->ops->ioctl) { - retval = (tty->ops->ioctl)(tty, cmd, arg); + retval = tty->ops->ioctl(tty, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } @@ -2932,7 +2932,7 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, return -EINVAL; if (tty->ops->compat_ioctl) { - retval = (tty->ops->compat_ioctl)(tty, cmd, arg); + retval = tty->ops->compat_ioctl(tty, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 24a1360..1787fa4 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -550,14 +550,14 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked); if (tty->ops->set_termios) - (*tty->ops->set_termios)(tty, &old_termios); + tty->ops->set_termios(tty, &old_termios); else tty_termios_copy_hw(&tty->termios, &old_termios); ld = tty_ldisc_ref(tty); if (ld != NULL) { if (ld->ops->set_termios) - (ld->ops->set_termios)(tty, &old_termios); + ld->ops->set_termios(tty, &old_termios); tty_ldisc_deref(ld); } up_write(&tty->termios_rwsem); -- cgit v0.10.2 From 1d597e7c266658697843352c5c030e20f48c6230 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:26:26 -0500 Subject: tty: Remove defunct pcxe_open() declaration pcxe_open() has no definition in mainline; remove declaration. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/tty.h b/include/linux/tty.h index f6835ea..97cfb31 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -621,10 +621,6 @@ extern long n_tty_compat_ioctl_helper(struct tty_struct *tty, struct file *file, extern void serial_console_init(void); -/* pcxx.c */ - -extern int pcxe_open(struct tty_struct *tty, struct file *filp); - /* vt.c */ extern int vt_ioctl(struct tty_struct *tty, -- cgit v0.10.2 From b9104f5cec3b7d8fb4068bce5ddc8240dc7b81f7 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:26:27 -0500 Subject: tty: Remove defunct serial_console_init() declaration serial_console_init() is not defined by the tty core; remove declaration. Note that the powerpc arch boot code contains a serial_console_init() declaration in arch/powerpc/boot/ops.h which is restricted to the powerpc arch boot. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/tty.h b/include/linux/tty.h index 97cfb31..c52a689 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -617,10 +617,6 @@ extern int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, extern long n_tty_compat_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); -/* serial.c */ - -extern void serial_console_init(void); - /* vt.c */ extern int vt_ioctl(struct tty_struct *tty, -- cgit v0.10.2 From 79f5ad3e1826a8b06f1dec0a85a26a407a0c4445 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:26:28 -0500 Subject: serial: hp300: Remove obsolete comments setup_serial_console() is obsolete and has been superseded by early_serial_setup() which is called at the end of the function. The IA64 arch does not call this function; only the m68k arch setup calls this function. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c index afffe4d..b488208 100644 --- a/drivers/tty/serial/8250/8250_hp300.c +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -88,10 +88,6 @@ extern int hp300_uart_scode; /* * Parse the bootinfo to find descriptions for headless console and * debug serial ports and register them with the 8250 driver. - * This function should be called before serial_console_init() is called - * to make sure the serial console will be available for use. IA-64 kernel - * calls this function from setup_arch() after the EFI and ACPI tables have - * been parsed. */ int __init hp300_setup_serial_console(void) { -- cgit v0.10.2 From f5bce77300eac7f56772e1664788a2df6ba88858 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:26:29 -0500 Subject: cris: Remove obsolete ASYNC_SPLIT_TERMIOS behavior ASYNC_SPLIT_TERMIOS behavior is a remnant of the long-dead /dev/cuaXX callout device. Split termios handling was removed tree-wide in v2.5.71 by: commit 99a21edebbfd8c29e39ee7fcc8a1ffa423657290 Author: Alexander Viro Date: Wed Jun 11 07:41:28 2003 -0700 [PATCH] tty_driver refcounting killed the last remnants of callout stuff - we don't need to mess with storing termios privately anymore. which pre-dated the re-introduction into the cris serial driver in v2.6.7 by: commit 311a5ffeda8ccb3f1f3840069f37234e043092d4 Author: Andrew Morton Date: Mon May 31 18:52:29 2004 -0700 [PATCH] CRIS architecture update From: "Mikael Starvik" - Lots of fixes from 2.4. - Updated for 2.6.6. - Added IDE driver Cc: Mikael Starvik Cc: Jesper Nilsson Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 58e6f61..0c1825b 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3676,12 +3676,6 @@ rs_close(struct tty_struct *tty, struct file * filp) } info->port.flags |= ASYNC_CLOSING; /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->port.flags & ASYNC_NORMAL_ACTIVE) - info->normal_termios = tty->termios; - /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ @@ -4076,11 +4070,6 @@ rs_open(struct tty_struct *tty, struct file * filp) return retval; } - if ((info->port.count == 1) && (info->port.flags & ASYNC_SPLIT_TERMIOS)) { - tty->termios = info->normal_termios; - change_speed(info); - } - #ifdef SERIAL_DEBUG_OPEN printk("rs_open ttyS%d successful...\n", info->line); #endif @@ -4327,7 +4316,6 @@ static int __init rs_init(void) info->custom_divisor = 0; info->x_char = 0; info->event = 0; - info->normal_termios = driver->init_termios; info->xmit.buf = NULL; info->xmit.tail = info->xmit.head = 0; info->first_recv_buffer = info->last_recv_buffer = NULL; diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h index 7599014..15a52ee 100644 --- a/drivers/tty/serial/crisv10.h +++ b/drivers/tty/serial/crisv10.h @@ -98,7 +98,6 @@ struct e100_serial { struct work_struct work; struct async_icount icount; /* error-statistics etc.*/ - struct ktermios normal_termios; unsigned long char_time_usec; /* The time for 1 char, in usecs */ unsigned long flush_time_usec; /* How often we should flush */ -- cgit v0.10.2 From 5ac9c05d789044d30735402f387355a394ec8e18 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:26:30 -0500 Subject: tty: Document defunct ASYNC_SPLIT_TERMIOS flag in uapi header The last vestige of ASYNC_SPLIT_TERMIOS was removed by commit 'cris: Remove obsolete ASYNC_SPLIT_TERMIOS behavior'. Mark the flag as defunct in the uapi header. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/uapi/linux/tty_flags.h b/include/uapi/linux/tty_flags.h index 4e021e3..d2bc4ff 100644 --- a/include/uapi/linux/tty_flags.h +++ b/include/uapi/linux/tty_flags.h @@ -14,7 +14,7 @@ * on the callout port */ #define ASYNCB_FOURPORT 1 /* Set OU1, OUT2 per AST Fourport settings */ #define ASYNCB_SAK 2 /* Secure Attention Key (Orange book) */ -#define ASYNCB_SPLIT_TERMIOS 3 /* Separate termios for dialin/callout */ +#define ASYNCB_SPLIT_TERMIOS 3 /* [x] Separate termios for dialin/callout */ #define ASYNCB_SPD_HI 4 /* Use 56000 instead of 38400 bps */ #define ASYNCB_SPD_VHI 5 /* Use 115200 instead of 38400 bps */ #define ASYNCB_SKIP_TEST 6 /* Skip UART test during autoconfiguration */ -- cgit v0.10.2 From 68952076e9226cc23ebce66d3fc2fdb8b6c04c30 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:26:31 -0500 Subject: vt: Remove vt_get_kmsg_redirect() from uapi header vt_get_kmsg_redirect() only has meaning to the console driver as an alias for calling vt_kmsg_redirect(). Move the macro definition to the only source file which uses it; remove from uapi/linux/vt.h Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index b33b00b..3dc5d56 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -2509,6 +2509,8 @@ int vt_kmsg_redirect(int new) return kmsg_con; } +#define vt_get_kmsg_redirect() vt_kmsg_redirect(-1) + /* * Console on virtual terminal * diff --git a/include/uapi/linux/vt.h b/include/uapi/linux/vt.h index 4b59a26..978578b 100644 --- a/include/uapi/linux/vt.h +++ b/include/uapi/linux/vt.h @@ -84,7 +84,4 @@ struct vt_setactivate { #define VT_SETACTIVATE 0x560F /* Activate and set the mode of a console */ - -#define vt_get_kmsg_redirect() vt_kmsg_redirect(-1) - #endif /* _UAPI_LINUX_VT_H */ -- cgit v0.10.2 From 86b20dfe9821060f880f6fa4881d51cabce78166 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:26:32 -0500 Subject: serial: 8250_em: Remove out-of-memory message devm_kzalloc() already warns if allocation fails; remove duplicate message. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index 56c8723..478599d 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -102,10 +102,8 @@ static int serial8250_em_probe(struct platform_device *pdev) } priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "unable to allocate private data\n"); + if (!priv) return -ENOMEM; - } priv->sclk = devm_clk_get(&pdev->dev, "sclk"); if (IS_ERR(priv->sclk)) { -- cgit v0.10.2 From 085f4d84137c6481cdbc12d4c9d7256137b1e9b1 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Sun, 26 Oct 2014 20:08:02 +0800 Subject: tty:vt remove obsolete struct initializer This patch fixes sparse warning: "obsolete struct initializer, use C99 syntax" Signed-off-by: Peng Fan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 3dc5d56..43c2c24 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1367,9 +1367,11 @@ static void csi_m(struct vc_data *vc) rgb_from_256(vc->vc_par[i])); } else if (vc->vc_par[i] == 2 && /* 24 bit */ i <= vc->vc_npar + 3) {/* extremely rare */ - struct rgb c = {r:vc->vc_par[i+1], - g:vc->vc_par[i+2], - b:vc->vc_par[i+3]}; + struct rgb c = { + .r = vc->vc_par[i + 1], + .g = vc->vc_par[i + 2], + .b = vc->vc_par[i + 3], + }; rgb_foreground(vc, c); i += 3; } @@ -1388,9 +1390,11 @@ static void csi_m(struct vc_data *vc) rgb_from_256(vc->vc_par[i])); } else if (vc->vc_par[i] == 2 && /* 24 bit */ i <= vc->vc_npar + 3) { - struct rgb c = {r:vc->vc_par[i+1], - g:vc->vc_par[i+2], - b:vc->vc_par[i+3]}; + struct rgb c = { + .r = vc->vc_par[i + 1], + .g = vc->vc_par[i + 2], + .b = vc->vc_par[i + 3], + }; rgb_background(vc, c); i += 3; } -- cgit v0.10.2 From 36f339d136f3b6e91235be619a88289a5f980450 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 6 Nov 2014 09:06:12 -0500 Subject: serial: pl011: Fix build breakage with !CONFIG_DMA_ENGINE Commit 479e9b94fdce7bc4 ("serial: Refactor uart_flush_buffer() from uart_close") refactored the uart_flush_buffer() in uart_close() into those drivers that define a flush_buffer() method. However, in the amba-pl011 driver configured without CONFIG_DMA_ENGINE, flush_buffer() is a NULL method, so the direct call fails to compile. Check and call the flush_buffer() method through the ops table instead. Reported-by: Fengguang Wu Cc: Russell King Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 8469b66..83c8f72 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1689,7 +1689,8 @@ static void pl011_shutdown(struct uart_port *port) plat->exit(); } - pl011_dma_flush_buffer(port); + if (uap->port.ops->flush_buffer) + uap->port.ops->flush_buffer(port); } static void -- cgit v0.10.2 From 7af0ea5dee68c18259b07b86835d2648156d47f4 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 22 Oct 2014 07:46:50 -0500 Subject: tty: serial: omap: Increase max consoles and add check to prevent crash Increase the maximum number of consoles possible to 10 since DRA7 now has the maximum number of consoles possible. without doing this, for example, enabling DRA7 UART10 results in internal data structures and console cannot match up and we endup with a crash as follows: [ 1.903503] omap_uart 48424000.serial: [UART-1]: failure [serial_omap_probe]: -22 [ 1.911437] omap_uart: probe of 48424000.serial failed with error -22 [ 1.920379] Unable to handle kernel NULL pointer dereference at virtual address 00000004 [ 1.928894] pgd = c0004000 [ 1.931732] [00000004] *pgd=00000000 [ 1.935485] Internal error: Oops: 5 [#1] SMP ARM [ 1.940338] Modules linked in: [ 1.943542] CPU: 1 PID: 12 Comm: kworker/1:0 Tainted: G W 3.18.0-rc1-00001-g8821bc4-dirty #6 [ 1.953521] task: ed8a2d00 ti: ed8a4000 task.ti: ed8a4000 [ 1.959197] PC is at process_one_work+0x38/0x4a4 [ 1.964050] LR is at 0x0 [ 1.966705] pc : [] lr : [<00000000>] psr: 40000093 [ 1.966705] sp : ed8a5ea8 ip : ed8a5ec8 fp : eeb9abc0 [ 1.978759] r10: ed8a4000 r9 : 00000008 r8 : ed842458 [ 1.984252] r7 : 00000000 r6 : eeb9abc0 r5 : ed842440 r4 : edbf26a8 [ 1.991119] r3 : 00000000 r2 : 00000000 r1 : 00000000 r0 : 00000000 [ 1.997985] Flags: nZcv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel [ 2.005767] Control: 10c5387d Table: 8000406a DAC: 00000015 [ 2.011810] Process kworker/1:0 (pid: 12, stack limit = 0xed8a4248) [ 2.018371] Stack: (0xed8a5ea8 to 0xed8a6000) [ 2.022949] 5ea0: 00000001 00000000 60000093 c008346c 00000001 ed8a5ec8 [ 2.031555] 5ec0: c0054de4 eeb9abc0 ed8a4000 ed842458 00000008 ed8a4000 eeb9abc0 ed842440 [ 2.040161] 5ee0: eeb9abc0 eeb9abf0 ed8a4000 ed842458 00000008 ed8a4000 eeb9abc0 c0054ec4 [ 2.048767] 5f00: ed8a4000 eeb9ac4c a0000053 00000000 ed845bc0 ed842440 c0054d80 00000000 [ 2.057342] 5f20: 00000000 00000000 00000000 c005999c 00000001 00000000 c05eaa64 ed842440 [ 2.065948] 5f40: 00000000 00000000 dead4ead ffffffff ffffffff c097c680 00000000 00000000 [ 2.074554] 5f60: c078acd4 ed8a5f64 ed8a5f64 00000000 00000000 dead4ead ffffffff ffffffff [ 2.083160] 5f80: c097c680 00000000 00000000 c078acd4 ed8a5f90 ed8a5f90 600000d3 ed845bc0 [ 2.091766] 5fa0: c00598d4 00000000 00000000 c000e668 00000000 00000000 00000000 00000000 [ 2.100341] 5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 2.108947] 5fe0: 00000000 00000000 00000000 00000000 00000013 00000000 90005148 11010482 [ 2.117553] [] (process_one_work) from [] (worker_thread+0x144/0x498) [ 2.126159] [] (worker_thread) from [] (kthread+0xc8/0xe4) [ 2.133758] [] (kthread) from [] (ret_from_fork+0x14/0x2c) [ 2.141357] Code: e1a04001 e1a05000 e893000f e31e0004 (e5978004) [ 2.147766] ---[ end trace 5798e2803311b69f ]--- The final solution is to transition off to use 8250 driver and no dependency on console structures and move away from omap-serial driver, hence no major cleanups are done for this driver, so add a simple check to prevent future crashes in a similar manner in case of platform with additional ports. Signed-off-by: Nishanth Menon Acked-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 6cda5cd..9d22d33 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -46,7 +46,7 @@ #include -#define OMAP_MAX_HSUART_PORTS 6 +#define OMAP_MAX_HSUART_PORTS 10 #define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) @@ -1693,6 +1693,13 @@ static int serial_omap_probe(struct platform_device *pdev) goto err_port_line; } + if (up->port.line >= OMAP_MAX_HSUART_PORTS) { + dev_err(&pdev->dev, "uart ID %d > MAX %d.\n", up->port.line, + OMAP_MAX_HSUART_PORTS); + ret = -ENXIO; + goto err_port_line; + } + ret = serial_omap_probe_rs485(up, pdev->dev.of_node); if (ret < 0) goto err_rs485; -- cgit v0.10.2 From 9b8777e3473e31b2aabd669e5f34866d4a3afb6a Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 16 Oct 2014 21:48:21 +0200 Subject: serial: of: add a PORT_RT2880 definition The Ralink RT2880 SoC and its successors have an internal 8250 core. This core needs the same quirks applied as the AMD AU1xxx uart. In addition to these quirks, the ports memory region is only 0x100 unlike the AU1xxx which has a size of 0x1000. Signed-off-by: John Crispin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 7e78f30..2235032 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2640,8 +2640,11 @@ serial8250_pm(struct uart_port *port, unsigned int state, static unsigned int serial8250_port_size(struct uart_8250_port *pt) { - if (pt->port.iotype == UPIO_AU) + if (pt->port.iotype == UPIO_AU) { + if (pt->port.type == PORT_RT2880) + return 0x100; return 0x1000; + } if (is_omap1_8250(pt)) return 0x16 << pt->port.regshift; diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 9c64ad2..8749fb8 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -130,8 +130,15 @@ static int of_platform_serial_setup(struct platform_device *ofdev, port->dev = &ofdev->dev; - if (type == PORT_TEGRA) + switch (type) { + case PORT_TEGRA: port->handle_break = tegra_serial_handle_break; + break; + + case PORT_RT2880: + port->iotype = UPIO_AU; + break; + } return 0; out: @@ -317,6 +324,7 @@ static struct of_device_id of_platform_serial_table[] = { { .compatible = "ns16850", .data = (void *)PORT_16850, }, { .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, }, { .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, }, + { .compatible = "ralink,rt2880-uart", .data = (void *)PORT_RT2880, }, { .compatible = "altr,16550-FIFO32", .data = (void *)PORT_ALTR_16550_F32, }, { .compatible = "altr,16550-FIFO64", diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 16ad852..c172180 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -54,7 +54,8 @@ #define PORT_ALTR_16550_F32 26 /* Altera 16550 UART with 32 FIFOs */ #define PORT_ALTR_16550_F64 27 /* Altera 16550 UART with 64 FIFOs */ #define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */ -#define PORT_MAX_8250 28 /* max port ID */ +#define PORT_RT2880 29 /* Ralink RT2880 internal UART */ +#define PORT_MAX_8250 29 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed -- cgit v0.10.2 From e8ec41921e46d82f7cc1fe7ee30a821bd536890d Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 16 Oct 2014 21:48:22 +0200 Subject: serial: ralink: adds Mediatek MT7620 serial Add the config symbol for Mediatek MT7620 SoC to the SERIAL_8250_RT288X dependencies. Signed-off-by: John Crispin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 5d3d65c..0fcbcd2 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -293,7 +293,7 @@ config SERIAL_8250_EM config SERIAL_8250_RT288X bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support" - depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883) + depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620) help If you have a Ralink RT288x/RT305x SoC based board and want to use the serial port, say Y to this option. The driver can handle up to 2 serial -- cgit v0.10.2 From a7ae7f8243b15cd65b5811de031eb4f8413b3e6f Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 16 Oct 2014 22:43:27 +0200 Subject: serial: of_serial: add "ralink,rt2880-uart" to the binding documentation Signed-off-by: John Crispin Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/of-serial.txt b/Documentation/devicetree/bindings/serial/of-serial.txt index 8c4fd03..b52b982 100644 --- a/Documentation/devicetree/bindings/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/serial/of-serial.txt @@ -10,6 +10,7 @@ Required properties: - "ns16850" - "nvidia,tegra20-uart" - "nxp,lpc3220-uart" + - "ralink,rt2880-uart" - "ibm,qpace-nwp-serial" - "altr,16550-FIFO32" - "altr,16550-FIFO64" -- cgit v0.10.2 From 66f37aafd6a6177287334397c59d7a727d16cd24 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Mon, 20 Oct 2014 19:12:20 +0200 Subject: tty/serial: at91: fix rx ring buffer management This patch swaps the use "tail" and "head" to fit the semantic of the linux circular buffer documentation: - head: the point at which the producer (the DMA controller) inserts items. - tail: the point at which the consumer (the serial framework) finds the next item. Besides the former code of the rx ring buffer didn't manage the case where head < tail, which might lead to loss of data. To fix this bug the data are now sent from the DMA buffer to the serial framework in two steps: 1 - First, we test if head < tail. If so, we copy the data from tail to the end of the DMA buffer then reset tail to zero. 2 - Finally, we copy data from tail to head then set tail to head. In addition, since tty_insert_flip_string() may now be called twice, atmel_flip_buffer_rx_dma() becomes less efficient than moving the calls dma_sync_sg_for_cpu(), dma_sync_sg_for_device(), tty_insert_flip_string() and tty_flip_buffer_push() directly into atmel_rx_from_dma(). Signed-off-by: Cyrille Pitchen Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 8a84034..9eb624a 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -880,32 +880,6 @@ chan_err: return -EINVAL; } -static void atmel_flip_buffer_rx_dma(struct uart_port *port, - char *buf, size_t count) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct tty_port *tport = &port->state->port; - - dma_sync_sg_for_cpu(port->dev, - &atmel_port->sg_rx, - 1, - DMA_DEV_TO_MEM); - - tty_insert_flip_string(tport, buf, count); - - dma_sync_sg_for_device(port->dev, - &atmel_port->sg_rx, - 1, - DMA_DEV_TO_MEM); - /* - * Drop the lock here since it might end up calling - * uart_start(), which takes the lock. - */ - spin_unlock(&port->lock); - tty_flip_buffer_push(tport); - spin_lock(&port->lock); -} - static void atmel_complete_rx_dma(void *arg) { struct uart_port *port = arg; @@ -934,11 +908,12 @@ static void atmel_release_rx_dma(struct uart_port *port) static void atmel_rx_from_dma(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct tty_port *tport = &port->state->port; struct circ_buf *ring = &atmel_port->rx_ring; struct dma_chan *chan = atmel_port->chan_rx; struct dma_tx_state state; enum dma_status dmastat; - size_t pending, count; + size_t count; /* Reset the UART timeout early so that we don't miss one */ @@ -953,27 +928,68 @@ static void atmel_rx_from_dma(struct uart_port *port) tasklet_schedule(&atmel_port->tasklet); return; } - /* current transfer size should no larger than dma buffer */ - pending = sg_dma_len(&atmel_port->sg_rx) - state.residue; - BUG_ON(pending > sg_dma_len(&atmel_port->sg_rx)); + /* CPU claims ownership of RX DMA buffer */ + dma_sync_sg_for_cpu(port->dev, + &atmel_port->sg_rx, + 1, + DMA_DEV_TO_MEM); + + /* + * ring->head points to the end of data already written by the DMA. + * ring->tail points to the beginning of data to be read by the + * framework. + * The current transfer size should not be larger than the dma buffer + * length. + */ + ring->head = sg_dma_len(&atmel_port->sg_rx) - state.residue; + BUG_ON(ring->head > sg_dma_len(&atmel_port->sg_rx)); /* - * This will take the chars we have so far, - * ring->head will record the transfer size, only new bytes come - * will insert into the framework. + * At this point ring->head may point to the first byte right after the + * last byte of the dma buffer: + * 0 <= ring->head <= sg_dma_len(&atmel_port->sg_rx) + * + * However ring->tail must always points inside the dma buffer: + * 0 <= ring->tail <= sg_dma_len(&atmel_port->sg_rx) - 1 + * + * Since we use a ring buffer, we have to handle the case + * where head is lower than tail. In such a case, we first read from + * tail to the end of the buffer then reset tail. */ - if (pending > ring->head) { - count = pending - ring->head; + if (ring->head < ring->tail) { + count = sg_dma_len(&atmel_port->sg_rx) - ring->tail; - atmel_flip_buffer_rx_dma(port, ring->buf + ring->head, count); + tty_insert_flip_string(tport, ring->buf + ring->tail, count); + ring->tail = 0; + port->icount.rx += count; + } - ring->head += count; - if (ring->head == sg_dma_len(&atmel_port->sg_rx)) - ring->head = 0; + /* Finally we read data from tail to head */ + if (ring->tail < ring->head) { + count = ring->head - ring->tail; + tty_insert_flip_string(tport, ring->buf + ring->tail, count); + /* Wrap ring->head if needed */ + if (ring->head >= sg_dma_len(&atmel_port->sg_rx)) + ring->head = 0; + ring->tail = ring->head; port->icount.rx += count; } + /* USART retreives ownership of RX DMA buffer */ + dma_sync_sg_for_device(port->dev, + &atmel_port->sg_rx, + 1, + DMA_DEV_TO_MEM); + + /* + * Drop the lock here since it might end up calling + * uart_start(), which takes the lock. + */ + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + spin_lock(&port->lock); + UART_PUT_IER(port, ATMEL_US_TIMEOUT); } -- cgit v0.10.2 From 42b4eba0612f87632a3e4d58c03f00e108e05dbc Mon Sep 17 00:00:00 2001 From: Janusz Uzycki Date: Fri, 10 Oct 2014 18:53:24 +0200 Subject: serial: mxs-auart: clean get_mctrl and set_mctrl Russell King: The only thing which the .get_mctrl method is supposed to do is to return the state of the /input/ lines, which are CTS, DCD, DSR, RI. The output line state is stored in port->mctrl, and is added to the returned value by serial_core when it's required. RTS output state should not be returned from the .get_mctrl method. This patch removes ctrl variable from mxs_auart_port and removes useless reading back RTS line. The ctrl variable in mxs_auart_port duplicated mctrl, member of uart_port structure in serial_core.h. The removed code from mxs_auart_set_mctrl() and mxs_auart_get_mctrl duplicated uart_update_mctrl() and uart_tiocmget() in serial_core.c. Signed-off-by: Janusz Uzycki Reviewed-by: Richard Genoud Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 0296c1c..907c3e5 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -143,7 +143,6 @@ struct mxs_auart_port { #define MXS_AUART_DMA_RX_READY 3 /* bit 3 */ #define MXS_AUART_RTSCTS 4 /* bit 4 */ unsigned long flags; - unsigned int ctrl; enum mxs_auart_type devtype; unsigned int irq; @@ -406,8 +405,6 @@ static void mxs_auart_release_port(struct uart_port *u) static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) { - struct mxs_auart_port *s = to_auart_port(u); - u32 ctrl = readl(u->membase + AUART_CTRL2); ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS); @@ -418,24 +415,17 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) ctrl |= AUART_CTRL2_RTS; } - s->ctrl = mctrl; writel(ctrl, u->membase + AUART_CTRL2); } static u32 mxs_auart_get_mctrl(struct uart_port *u) { - struct mxs_auart_port *s = to_auart_port(u); u32 stat = readl(u->membase + AUART_STAT); - int ctrl2 = readl(u->membase + AUART_CTRL2); - u32 mctrl = s->ctrl; + u32 mctrl = 0; - mctrl &= ~TIOCM_CTS; if (stat & AUART_STAT_CTS) mctrl |= TIOCM_CTS; - if (ctrl2 & AUART_CTRL2_RTS) - mctrl |= TIOCM_RTS; - return mctrl; } @@ -1071,8 +1061,6 @@ static int mxs_auart_probe(struct platform_device *pdev) s->port.type = PORT_IMX; s->port.dev = s->dev = &pdev->dev; - s->ctrl = 0; - s->irq = platform_get_irq(pdev, 0); s->port.irq = s->irq; ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s); -- cgit v0.10.2 From 7c573d7ea6c5a866cf89b7bf45fc5ab82d289cf1 Mon Sep 17 00:00:00 2001 From: Janusz Uzycki Date: Fri, 10 Oct 2014 18:53:25 +0200 Subject: serial: mxs-auart: use mctrl_gpio helpers for handling modem signals Dedicated CTS and RTS pins are unusable together with a lot of other peripherals because they share the same line. Pinctrl is limited. Moreover, the AUART controller doesn't handle DTR/DSR/DCD/RI signals, so we have to control them via GPIO. This patch permits to use GPIOs to control the CTS/RTS/DTR/DSR/DCD/RI signals. Signed-off-by: Janusz Uzycki Reviewed-by: Richard Genoud Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/fsl-mxs-auart.txt b/Documentation/devicetree/bindings/serial/fsl-mxs-auart.txt index 59a40f1..7c408c8 100644 --- a/Documentation/devicetree/bindings/serial/fsl-mxs-auart.txt +++ b/Documentation/devicetree/bindings/serial/fsl-mxs-auart.txt @@ -11,8 +11,13 @@ Required properties: - dma-names: "rx" for RX channel, "tx" for TX channel. Optional properties: -- fsl,uart-has-rtscts : Indicate the UART has RTS and CTS lines, +- fsl,uart-has-rtscts : Indicate the UART has RTS and CTS lines + for hardware flow control, it also means you enable the DMA support for this UART. +- {rts,cts,dtr,dsr,rng,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD + line respectively. It will use specified PIO instead of the peripheral + function pin for the USART feature. + If unsure, don't specify this property. Example: auart0: serial@8006a000 { @@ -21,6 +26,9 @@ auart0: serial@8006a000 { interrupts = <112>; dmas = <&dma_apbx 8>, <&dma_apbx 9>; dma-names = "rx", "tx"; + cts-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; + dsr-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>; + dcd-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>; }; Note: Each auart port should have an alias correctly numbered in "aliases" diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 649b784..4e6a0ba 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1408,6 +1408,7 @@ config SERIAL_MXS_AUART depends on ARCH_MXS tristate "MXS AUART support" select SERIAL_CORE + select SERIAL_MCTRL_GPIO if GPIOLIB help This driver supports the MXS Application UART (AUART) port. diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 907c3e5..5922cb4 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -42,6 +42,9 @@ #include +#include +#include "serial_mctrl_gpio.h" + #define MXS_AUART_PORTS 5 #define MXS_AUART_FIFO_SIZE 16 @@ -158,6 +161,8 @@ struct mxs_auart_port { struct scatterlist rx_sgl; struct dma_chan *rx_dma_chan; void *rx_dma_buf; + + struct mctrl_gpios *gpios; }; static struct platform_device_id mxs_auart_devtype[] = { @@ -405,6 +410,8 @@ static void mxs_auart_release_port(struct uart_port *u) static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) { + struct mxs_auart_port *s = to_auart_port(u); + u32 ctrl = readl(u->membase + AUART_CTRL2); ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS); @@ -416,17 +423,20 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) } writel(ctrl, u->membase + AUART_CTRL2); + + mctrl_gpio_set(s->gpios, mctrl); } static u32 mxs_auart_get_mctrl(struct uart_port *u) { + struct mxs_auart_port *s = to_auart_port(u); u32 stat = readl(u->membase + AUART_STAT); u32 mctrl = 0; if (stat & AUART_STAT_CTS) mctrl |= TIOCM_CTS; - return mctrl; + return mctrl_gpio_get(s->gpios, &mctrl); } static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s); @@ -554,6 +564,10 @@ err_out: } +#define RTS_AT_AUART() IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(s->gpios, \ + UART_GPIO_RTS)) +#define CTS_AT_AUART() IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(s->gpios, \ + UART_GPIO_CTS)) static void mxs_auart_settermios(struct uart_port *u, struct ktermios *termios, struct ktermios *old) @@ -630,6 +644,7 @@ static void mxs_auart_settermios(struct uart_port *u, ctrl |= AUART_LINECTRL_STP2; /* figure out the hardware flow control settings */ + ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN); if (cflag & CRTSCTS) { /* * The DMA has a bug(see errata:2836) in mx23. @@ -644,9 +659,11 @@ static void mxs_auart_settermios(struct uart_port *u, ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR; } - ctrl2 |= AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN; - } else { - ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN); + /* Even if RTS is GPIO line RTSEN can be enabled because + * the pinctrl configuration decides about RTS pin function */ + ctrl2 |= AUART_CTRL2_RTSEN; + if (CTS_AT_AUART()) + ctrl2 |= AUART_CTRL2_CTSEN; } /* set baud rate */ @@ -690,7 +707,9 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) s->port.membase + AUART_INTR_CLR); if (istat & AUART_INTR_CTSMIS) { - uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS); + if (CTS_AT_AUART()) + uart_handle_cts_change(&s->port, + stat & AUART_STAT_CTS); writel(AUART_INTR_CTSMIS, s->port.membase + AUART_INTR_CLR); istat &= ~AUART_INTR_CTSMIS; @@ -1014,6 +1033,23 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, return 0; } +static bool mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev) +{ + s->gpios = mctrl_gpio_init(dev, 0); + if (IS_ERR_OR_NULL(s->gpios)) + return false; + + /* Block (enabled before) DMA option if RTS or CTS is GPIO line */ + if (!RTS_AT_AUART() || !CTS_AT_AUART()) { + if (test_bit(MXS_AUART_RTSCTS, &s->flags)) + dev_warn(dev, + "DMA and flow control via gpio may cause some problems. DMA disabled!\n"); + clear_bit(MXS_AUART_RTSCTS, &s->flags); + } + + return true; +} + static int mxs_auart_probe(struct platform_device *pdev) { const struct of_device_id *of_id = @@ -1069,6 +1105,10 @@ static int mxs_auart_probe(struct platform_device *pdev) platform_set_drvdata(pdev, s); + if (!mxs_auart_init_gpios(s, &pdev->dev)) + dev_err(&pdev->dev, + "Failed to initialize GPIOs. The serial port may not work as expected\n"); + auart_port[s->port.line] = s; mxs_auart_reset(&s->port); -- cgit v0.10.2 From f9e42397d79b6e810437ba1130b0b4b594f5e56c Mon Sep 17 00:00:00 2001 From: Janusz Uzycki Date: Fri, 10 Oct 2014 18:53:26 +0200 Subject: serial: mxs-auart: add interrupts for modem control lines Handle CTS/DSR/RI/DCD GPIO interrupts in mxs-auart. Signed-off-by: Janusz Uzycki Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 5922cb4..088f1e1 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -42,7 +42,10 @@ #include +#include +#include #include +#include #include "serial_mctrl_gpio.h" #define MXS_AUART_PORTS 5 @@ -146,6 +149,7 @@ struct mxs_auart_port { #define MXS_AUART_DMA_RX_READY 3 /* bit 3 */ #define MXS_AUART_RTSCTS 4 /* bit 4 */ unsigned long flags; + unsigned int mctrl_prev; enum mxs_auart_type devtype; unsigned int irq; @@ -163,6 +167,8 @@ struct mxs_auart_port { void *rx_dma_buf; struct mctrl_gpios *gpios; + int gpio_irq[UART_GPIO_MAX]; + bool ms_irq_enabled; }; static struct platform_device_id mxs_auart_devtype[] = { @@ -427,6 +433,29 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) mctrl_gpio_set(s->gpios, mctrl); } +#define MCTRL_ANY_DELTA (TIOCM_RI | TIOCM_DSR | TIOCM_CD | TIOCM_CTS) +static u32 mxs_auart_modem_status(struct mxs_auart_port *s, u32 mctrl) +{ + u32 mctrl_diff; + + mctrl_diff = mctrl ^ s->mctrl_prev; + s->mctrl_prev = mctrl; + if (mctrl_diff & MCTRL_ANY_DELTA && s->ms_irq_enabled && + s->port.state != NULL) { + if (mctrl_diff & TIOCM_RI) + s->port.icount.rng++; + if (mctrl_diff & TIOCM_DSR) + s->port.icount.dsr++; + if (mctrl_diff & TIOCM_CD) + uart_handle_dcd_change(&s->port, mctrl & TIOCM_CD); + if (mctrl_diff & TIOCM_CTS) + uart_handle_cts_change(&s->port, mctrl & TIOCM_CTS); + + wake_up_interruptible(&s->port.state->port.delta_msr_wait); + } + return mctrl; +} + static u32 mxs_auart_get_mctrl(struct uart_port *u) { struct mxs_auart_port *s = to_auart_port(u); @@ -439,6 +468,64 @@ static u32 mxs_auart_get_mctrl(struct uart_port *u) return mctrl_gpio_get(s->gpios, &mctrl); } +/* + * Enable modem status interrupts + */ +static void mxs_auart_enable_ms(struct uart_port *port) +{ + struct mxs_auart_port *s = to_auart_port(port); + + /* + * Interrupt should not be enabled twice + */ + if (s->ms_irq_enabled) + return; + + s->ms_irq_enabled = true; + + if (s->gpio_irq[UART_GPIO_CTS] >= 0) + enable_irq(s->gpio_irq[UART_GPIO_CTS]); + /* TODO: enable AUART_INTR_CTSMIEN otherwise */ + + if (s->gpio_irq[UART_GPIO_DSR] >= 0) + enable_irq(s->gpio_irq[UART_GPIO_DSR]); + + if (s->gpio_irq[UART_GPIO_RI] >= 0) + enable_irq(s->gpio_irq[UART_GPIO_RI]); + + if (s->gpio_irq[UART_GPIO_DCD] >= 0) + enable_irq(s->gpio_irq[UART_GPIO_DCD]); +} + +/* + * Disable modem status interrupts + */ +static void mxs_auart_disable_ms(struct uart_port *port) +{ + struct mxs_auart_port *s = to_auart_port(port); + + /* + * Interrupt should not be disabled twice + */ + if (!s->ms_irq_enabled) + return; + + s->ms_irq_enabled = false; + + if (s->gpio_irq[UART_GPIO_CTS] >= 0) + disable_irq(s->gpio_irq[UART_GPIO_CTS]); + /* TODO: disable AUART_INTR_CTSMIEN otherwise */ + + if (s->gpio_irq[UART_GPIO_DSR] >= 0) + disable_irq(s->gpio_irq[UART_GPIO_DSR]); + + if (s->gpio_irq[UART_GPIO_RI] >= 0) + disable_irq(s->gpio_irq[UART_GPIO_RI]); + + if (s->gpio_irq[UART_GPIO_DCD] >= 0) + disable_irq(s->gpio_irq[UART_GPIO_DCD]); +} + static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s); static void dma_rx_callback(void *arg) { @@ -689,6 +776,12 @@ static void mxs_auart_settermios(struct uart_port *u, dev_err(s->dev, "We can not start up the DMA.\n"); } } + + /* CTS flow-control and modem-status interrupts */ + if (UART_ENABLE_MS(u, termios->c_cflag)) + mxs_auart_enable_ms(u); + else + mxs_auart_disable_ms(u); } static irqreturn_t mxs_auart_irq_handle(int irq, void *context) @@ -706,8 +799,18 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) | AUART_INTR_CTSMIS), s->port.membase + AUART_INTR_CLR); + /* + * Dealing with GPIO interrupt + */ + if (irq == s->gpio_irq[UART_GPIO_CTS] || + irq == s->gpio_irq[UART_GPIO_DCD] || + irq == s->gpio_irq[UART_GPIO_DSR] || + irq == s->gpio_irq[UART_GPIO_RI]) + mxs_auart_modem_status(s, + mctrl_gpio_get(s->gpios, &s->mctrl_prev)); + if (istat & AUART_INTR_CTSMIS) { - if (CTS_AT_AUART()) + if (CTS_AT_AUART() && s->ms_irq_enabled) uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS); writel(AUART_INTR_CTSMIS, @@ -770,6 +873,10 @@ static int mxs_auart_startup(struct uart_port *u) */ writel(AUART_LINECTRL_FEN, u->membase + AUART_LINECTRL_SET); + /* get initial status of modem lines */ + mctrl_gpio_get(s->gpios, &s->mctrl_prev); + + s->ms_irq_enabled = false; return 0; } @@ -777,6 +884,8 @@ static void mxs_auart_shutdown(struct uart_port *u) { struct mxs_auart_port *s = to_auart_port(u); + mxs_auart_disable_ms(u); + if (auart_dma_enabled(s)) mxs_auart_dma_exit(s); @@ -833,6 +942,7 @@ static struct uart_ops mxs_auart_ops = { .start_tx = mxs_auart_start_tx, .stop_tx = mxs_auart_stop_tx, .stop_rx = mxs_auart_stop_rx, + .enable_ms = mxs_auart_enable_ms, .break_ctl = mxs_auart_break_ctl, .set_mctrl = mxs_auart_set_mctrl, .get_mctrl = mxs_auart_get_mctrl, @@ -1035,6 +1145,9 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, static bool mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev) { + enum mctrl_gpio_idx i; + struct gpio_desc *gpiod; + s->gpios = mctrl_gpio_init(dev, 0); if (IS_ERR_OR_NULL(s->gpios)) return false; @@ -1047,9 +1160,54 @@ static bool mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev) clear_bit(MXS_AUART_RTSCTS, &s->flags); } + for (i = 0; i < UART_GPIO_MAX; i++) { + gpiod = mctrl_gpio_to_gpiod(s->gpios, i); + if (gpiod && (gpiod_get_direction(gpiod) == GPIOF_DIR_IN)) + s->gpio_irq[i] = gpiod_to_irq(gpiod); + else + s->gpio_irq[i] = -EINVAL; + } + return true; } +static void mxs_auart_free_gpio_irq(struct mxs_auart_port *s) +{ + enum mctrl_gpio_idx i; + + for (i = 0; i < UART_GPIO_MAX; i++) + if (s->gpio_irq[i] >= 0) + free_irq(s->gpio_irq[i], s); +} + +static int mxs_auart_request_gpio_irq(struct mxs_auart_port *s) +{ + int *irq = s->gpio_irq; + enum mctrl_gpio_idx i; + int err = 0; + + for (i = 0; (i < UART_GPIO_MAX) && !err; i++) { + if (irq[i] < 0) + continue; + + irq_set_status_flags(irq[i], IRQ_NOAUTOEN); + err = request_irq(irq[i], mxs_auart_irq_handle, + IRQ_TYPE_EDGE_BOTH, dev_name(s->dev), s); + if (err) + dev_err(s->dev, "%s - Can't get %d irq\n", + __func__, irq[i]); + } + + /* + * If something went wrong, rollback. + */ + while (err && (--i >= 0)) + if (irq[i] >= 0) + free_irq(irq[i], s); + + return err; +} + static int mxs_auart_probe(struct platform_device *pdev) { const struct of_device_id *of_id = @@ -1097,6 +1255,8 @@ static int mxs_auart_probe(struct platform_device *pdev) s->port.type = PORT_IMX; s->port.dev = s->dev = &pdev->dev; + s->mctrl_prev = 0; + s->irq = platform_get_irq(pdev, 0); s->port.irq = s->irq; ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s); @@ -1109,13 +1269,20 @@ static int mxs_auart_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to initialize GPIOs. The serial port may not work as expected\n"); + /* + * Get the GPIO lines IRQ + */ + ret = mxs_auart_request_gpio_irq(s); + if (ret) + goto out_free_irq; + auart_port[s->port.line] = s; mxs_auart_reset(&s->port); ret = uart_add_one_port(&auart_driver, &s->port); if (ret) - goto out_free_irq; + goto out_free_gpio_irq; version = readl(s->port.membase + AUART_VERSION); dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n", @@ -1124,6 +1291,8 @@ static int mxs_auart_probe(struct platform_device *pdev) return 0; +out_free_gpio_irq: + mxs_auart_free_gpio_irq(s); out_free_irq: auart_port[pdev->id] = NULL; free_irq(s->irq, s); @@ -1143,6 +1312,7 @@ static int mxs_auart_remove(struct platform_device *pdev) auart_port[pdev->id] = NULL; + mxs_auart_free_gpio_irq(s); clk_put(s->clk); free_irq(s->irq, s); kfree(s); -- cgit v0.10.2 From 36a262782b045220bfccf89bd2c4d78cbe152c8e Mon Sep 17 00:00:00 2001 From: Janusz Uzycki Date: Fri, 10 Oct 2014 18:53:27 +0200 Subject: serial: mxs-auart: enable PPS support Enables PPS support in mxs-auart serial driver to make PPS API working. Signed-off-by: Janusz Uzycki Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 088f1e1..386b528 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -784,6 +784,16 @@ static void mxs_auart_settermios(struct uart_port *u, mxs_auart_disable_ms(u); } +static void mxs_auart_set_ldisc(struct uart_port *port, int new) +{ + if (new == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + mxs_auart_enable_ms(port); + } else { + port->flags &= ~UPF_HARDPPS_CD; + } +} + static irqreturn_t mxs_auart_irq_handle(int irq, void *context) { u32 istat; @@ -949,6 +959,7 @@ static struct uart_ops mxs_auart_ops = { .startup = mxs_auart_startup, .shutdown = mxs_auart_shutdown, .set_termios = mxs_auart_settermios, + .set_ldisc = mxs_auart_set_ldisc, .type = mxs_auart_type, .release_port = mxs_auart_release_port, .request_port = mxs_auart_request_port, -- cgit v0.10.2 From 660beb0e94ad81cfcb6b7846606c0378b0152064 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 29 Oct 2014 11:14:37 -0700 Subject: tty: serial: msm: Fix sysrq spinlock recursion on non-DM The handle_rx() path calls uart_handle_sysrq_char() with the port lock held. This causes a spinlock recursion. Release and reacquire the lock here to avoid this. BUG: spinlock recursion on CPU#0, swapper/0 lock: msm_uart_ports+0x1e0/0x2d0, .magic: dead4ead, .owner: swapper/0, .owner_cpu: 0 CPU: 0 PID: 0 Comm: swapper Not tainted 3.17.0-rc7-00012-gb38ee8265941 #69 [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (do_raw_spin_lock+0x11c/0x13c) [] (do_raw_spin_lock) from [] (msm_console_write+0x78/0x188) [] (msm_console_write) from [] (call_console_drivers.constprop.22+0xb4/0x144) [] (call_console_drivers.constprop.22) from [] (console_unlock+0x27c/0x4ac) [] (console_unlock) from [] (vprintk_emit+0x1f4/0x5a8) [] (vprintk_emit) from [] (printk+0x30/0x40) [] (printk) from [] (__handle_sysrq+0x58/0x1b8) [] (__handle_sysrq) from [] (msm_irq+0x694/0x6f8) [] (msm_irq) from [] (handle_irq_event_percpu+0x58/0x270) [] (handle_irq_event_percpu) from [] (handle_irq_event+0x3c/0x5c) [] (handle_irq_event) from [] (handle_level_irq+0x9c/0x138) [] (handle_level_irq) from [] (generic_handle_irq+0x24/0x38) [] (generic_handle_irq) from [] (handle_IRQ+0x44/0xb0) [] (handle_IRQ) from [] (msm_vic_handle_irq+0x44/0x64) [] (msm_vic_handle_irq) from [] (__irq_svc+0x44/0x7c) Exception stack(0xc0719f68 to 0xc0719fb0) 9f60: 00000001 00000001 00000000 c0722938 c0718000 c0769acc 9f80: 00000000 c0720098 c0769305 4117b362 c0769acc 00000000 01000000 c0719fb0 9fa0: c004cab0 c000f880 20000013 ffffffff [] (__irq_svc) from [] (arch_cpu_idle+0x20/0x30) [] (arch_cpu_idle) from [] (cpu_startup_entry+0xf4/0x23c) [] (cpu_startup_entry) from [] (start_kernel+0x32c/0x394) Cc: Frank Rowand Cc: Daniel Thompson Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 4b6c783..cedcc36 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -174,6 +174,7 @@ static void handle_rx(struct uart_port *port) while ((sr = msm_read(port, UART_SR)) & UART_SR_RX_READY) { unsigned int c; char flag = TTY_NORMAL; + int sysrq; c = msm_read(port, UART_RF); @@ -195,7 +196,10 @@ static void handle_rx(struct uart_port *port) else if (sr & UART_SR_PAR_FRAME_ERR) flag = TTY_FRAME; - if (!uart_handle_sysrq_char(port, c)) + spin_unlock(&port->lock); + sysrq = uart_handle_sysrq_char(port, c); + spin_lock(&port->lock); + if (!sysrq) tty_insert_flip_char(tport, c, flag); } -- cgit v0.10.2 From 0896d4d4fb162297d7199410bae386a96a2e473b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 29 Oct 2014 11:14:38 -0700 Subject: tty: serial: msm: Support sysrq on uartDM devices To properly support sysrq on uartDM hardware we need to properly handle break characters. With the DM hardware the fifo can pack 4 characters at a time, where a break is indicated by an all zero byte. Unfortunately, we can't differentiate between an all zero byte for a break and an all zero byte of data, so try and do as best we can. First unmask the RX break start interrupt and record the interrupt when it arrives. Then while processing the fifo, detect the break by searching for an all zero character as long as we recently received an RX break start interrupt. This should make sysrq work fairly well. Cc: Frank Rowand Cc: Daniel Thompson Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index cedcc36..d44c049 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -54,6 +54,7 @@ struct msm_port { unsigned int imr; int is_uartdm; unsigned int old_snap_state; + bool break_detected; }; static inline void wait_for_xmitr(struct uart_port *port) @@ -126,23 +127,38 @@ static void handle_rx_dm(struct uart_port *port, unsigned int misr) while (count > 0) { unsigned char buf[4]; + int sysrq, r_count, i; sr = msm_read(port, UART_SR); if ((sr & UART_SR_RX_READY) == 0) { msm_port->old_snap_state -= count; break; } + ioread32_rep(port->membase + UARTDM_RF, buf, 1); - if (sr & UART_SR_RX_BREAK) { - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (sr & UART_SR_PAR_FRAME_ERR) - port->icount.frame++; + r_count = min_t(int, count, sizeof(buf)); + + for (i = 0; i < r_count; i++) { + char flag = TTY_NORMAL; - /* TODO: handle sysrq */ - tty_insert_flip_string(tport, buf, min(count, 4)); - count -= 4; + if (msm_port->break_detected && buf[i] == 0) { + port->icount.brk++; + flag = TTY_BREAK; + msm_port->break_detected = false; + if (uart_handle_break(port)) + continue; + } + + if (!(port->read_status_mask & UART_SR_RX_BREAK)) + flag = TTY_NORMAL; + + spin_unlock(&port->lock); + sysrq = uart_handle_sysrq_char(port, buf[i]); + spin_lock(&port->lock); + if (!sysrq) + tty_insert_flip_char(tport, buf[i], flag); + } + count -= r_count; } spin_unlock(&port->lock); @@ -291,6 +307,11 @@ static irqreturn_t msm_irq(int irq, void *dev_id) misr = msm_read(port, UART_MISR); msm_write(port, 0, UART_IMR); /* disable interrupt */ + if (misr & UART_IMR_RXBREAK_START) { + msm_port->break_detected = true; + msm_write(port, UART_CR_CMD_RESET_RXBREAK_START, UART_CR); + } + if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) { if (msm_port->is_uartdm) handle_rx_dm(port, misr); @@ -496,7 +517,7 @@ static int msm_startup(struct uart_port *port) /* turn on RX and CTS interrupts */ msm_port->imr = UART_IMR_RXLEV | UART_IMR_RXSTALE | - UART_IMR_CURRENT_CTS; + UART_IMR_CURRENT_CTS | UART_IMR_RXBREAK_START; if (msm_port->is_uartdm) { msm_write(port, 0xFFFFFF, UARTDM_DMRX); diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index 73d3abe..3e1c713 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -65,6 +65,7 @@ #define UART_CR_TX_ENABLE (1 << 2) #define UART_CR_RX_DISABLE (1 << 1) #define UART_CR_RX_ENABLE (1 << 0) +#define UART_CR_CMD_RESET_RXBREAK_START ((1 << 11) | (2 << 4)) #define UART_IMR 0x0014 #define UART_IMR_TXLEV (1 << 0) @@ -72,6 +73,7 @@ #define UART_IMR_RXLEV (1 << 4) #define UART_IMR_DELTA_CTS (1 << 5) #define UART_IMR_CURRENT_CTS (1 << 6) +#define UART_IMR_RXBREAK_START (1 << 10) #define UART_IPR_RXSTALE_LAST 0x20 #define UART_IPR_STALE_LSB 0x1F -- cgit v0.10.2 From a12f1b406f2d3c3e3bba5300b6e420d004ca263b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 29 Oct 2014 18:47:01 -0700 Subject: tty: serial: msm: Reset uartdm after baud rate change We need to issue a reset if we ever change the value of the IPR register on DM hardware. If we don't reset the hardware the RX stale interrupt never triggers and the only way to trigger an RX handling event is by filling up the fifo. This causes things like getty to not work so well considering it might change the baud rate a few times. Fix this by moving the reset on startup and any reprogramming required after the reset to be after we change the baud rate. Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index d44c049..b507f5a 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -427,9 +427,6 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) entry = msm_find_best_baud(port, baud); - if (msm_port->is_uartdm) - msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); - msm_write(port, entry->code, UART_CSR); /* RX stale watermark */ @@ -446,6 +443,18 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) /* set TX watermark */ msm_write(port, 10, UART_TFWR); + msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR); + msm_reset(port); + + /* Enable RX and TX */ + msm_write(port, UART_CR_TX_ENABLE | UART_CR_RX_ENABLE, UART_CR); + + /* turn on RX and CTS interrupts */ + msm_port->imr = UART_IMR_RXLEV | UART_IMR_RXSTALE | + UART_IMR_CURRENT_CTS | UART_IMR_RXBREAK_START; + + msm_write(port, msm_port->imr, UART_IMR); + if (msm_port->is_uartdm) { msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR); msm_write(port, 0xFFFFFF, UARTDM_DMRX); @@ -492,40 +501,6 @@ static int msm_startup(struct uart_port *port) data |= UART_MR1_AUTO_RFR_LEVEL1 & (rfr_level << 2); data |= UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; msm_write(port, data, UART_MR1); - - /* make sure that RXSTALE count is non-zero */ - data = msm_read(port, UART_IPR); - if (unlikely(!data)) { - data |= UART_IPR_RXSTALE_LAST; - data |= UART_IPR_STALE_LSB; - msm_write(port, data, UART_IPR); - } - - data = 0; - if (!port->cons || (port->cons && !(port->cons->flags & CON_ENABLED))) { - msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR); - msm_reset(port); - data = UART_CR_TX_ENABLE; - } - - data |= UART_CR_RX_ENABLE; - msm_write(port, data, UART_CR); /* enable TX & RX */ - - /* Make sure IPR is not 0 to start with*/ - if (msm_port->is_uartdm) - msm_write(port, UART_IPR_STALE_LSB, UART_IPR); - - /* turn on RX and CTS interrupts */ - msm_port->imr = UART_IMR_RXLEV | UART_IMR_RXSTALE | - UART_IMR_CURRENT_CTS | UART_IMR_RXBREAK_START; - - if (msm_port->is_uartdm) { - msm_write(port, 0xFFFFFF, UARTDM_DMRX); - msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR); - msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR); - } - - msm_write(port, msm_port->imr, UART_IMR); return 0; } -- cgit v0.10.2 From 97f754710304382607417c700476007443cd96a4 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 22 Oct 2014 17:33:01 -0700 Subject: tty: serial: msm_serial: Use DT aliases We rely on probe order of this driver to determine the line number for the uart port. This makes it impossible to know the line number when these devices are populated via DT. Use the DT alias mechanism to assign the line based on the aliases node. Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index b507f5a..09364dd 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1044,17 +1044,22 @@ static int msm_serial_probe(struct platform_device *pdev) struct resource *resource; struct uart_port *port; const struct of_device_id *id; - int irq; + int irq, line; if (pdev->id == -1) pdev->id = atomic_inc_return(&msm_uart_next_id) - 1; - if (unlikely(pdev->id < 0 || pdev->id >= UART_NR)) + if (pdev->dev.of_node) + line = of_alias_get_id(pdev->dev.of_node, "serial"); + else + line = pdev->id; + + if (unlikely(line < 0 || line >= UART_NR)) return -ENXIO; - dev_info(&pdev->dev, "msm_serial: detected port #%d\n", pdev->id); + dev_info(&pdev->dev, "msm_serial: detected port #%d\n", line); - port = get_port_from_line(pdev->id); + port = get_port_from_line(line); port->dev = &pdev->dev; msm_port = UART_TO_MSM(port); -- cgit v0.10.2 From f968ef34945f8e606c5d98ff330a5903785ea76a Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 28 Oct 2014 09:28:07 +0100 Subject: serial: imx: clean up imx_poll_put_char() imx_put_poll_char() has been simplified to remove the code to disable interrupts. The present code can corrupt register state when re-entered from FIQ handler. Switch to _relaxed() MMIO functions (which are safe for polled I/O and needed to avoid taking spin locks). Signed-off-by: Daniel Thompson Signed-off-by: Dirk Behme Cc: Jiri Slaby Cc: Huang Shijie Cc: linux-serial@vger.kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index d2e6cf5..8d61aa9 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1457,42 +1457,28 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser) #if defined(CONFIG_CONSOLE_POLL) static int imx_poll_get_char(struct uart_port *port) { - if (!(readl(port->membase + USR2) & USR2_RDR)) + if (!(readl_relaxed(port->membase + USR2) & USR2_RDR)) return NO_POLL_CHAR; - return readl(port->membase + URXD0) & URXD_RX_DATA; + return readl_relaxed(port->membase + URXD0) & URXD_RX_DATA; } 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); + status = readl_relaxed(port->membase + USR1); } while (~status & USR1_TRDY); /* write */ - writel(c, port->membase + URTX0); + writel_relaxed(c, port->membase + URTX0); /* flush */ do { - status = readl(port->membase + USR2); + status = readl_relaxed(port->membase + USR2); } while (~status & USR2_TXDC); - - /* restore control registers */ - imx_port_ucrs_restore(port, &old_ucr); } #endif -- cgit v0.10.2 From 6b8bdad961647ebbc3fec8130f6487ab9fddaef4 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 28 Oct 2014 09:28:08 +0100 Subject: serial: imx: add imx_poll_init() For the console poll usage, .poll_init() will perform deeper hardware initialization to ensure the serial port is always active. Signed-off-by: Daniel Thompson Signed-off-by: Dirk Behme Cc: Jiri Slaby Cc: Huang Shijie Cc: linux-serial@vger.kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 8d61aa9..062e761 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1455,6 +1455,41 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser) } #if defined(CONFIG_CONSOLE_POLL) + +static int imx_poll_init(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + unsigned long flags; + unsigned long temp; + int retval; + + retval = clk_prepare_enable(sport->clk_ipg); + if (retval) + return retval; + retval = clk_prepare_enable(sport->clk_per); + if (retval) + clk_disable_unprepare(sport->clk_ipg); + + imx_setup_ufcr(sport, 0); + + spin_lock_irqsave(&sport->port.lock, flags); + + temp = readl(sport->port.membase + UCR1); + if (is_imx1_uart(sport)) + temp |= IMX1_UCR1_UARTCLKEN; + temp |= UCR1_UARTEN | UCR1_RRDYEN; + temp &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN); + writel(temp, sport->port.membase + UCR1); + + temp = readl(sport->port.membase + UCR2); + temp |= UCR2_RXEN; + writel(temp, sport->port.membase + UCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); + + return 0; +} + static int imx_poll_get_char(struct uart_port *port) { if (!(readl_relaxed(port->membase + USR2) & USR2_RDR)) @@ -1499,6 +1534,7 @@ static struct uart_ops imx_pops = { .config_port = imx_config_port, .verify_port = imx_verify_port, #if defined(CONFIG_CONSOLE_POLL) + .poll_init = imx_poll_init, .poll_get_char = imx_poll_get_char, .poll_put_char = imx_poll_put_char, #endif -- cgit v0.10.2 From c0ec3fd123e9e64e095fb221ace841e00c04e40b Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 21 Oct 2014 15:22:57 -0700 Subject: tty: serial: bcm63xx: Allow bcm63xx_uart to be built on other platforms This device was originally supported on bcm63xx only, but it shows up on a wide variety of MIPS and ARM chipsets spanning multiple product lines. Now that the driver has eliminated dependencies on bcm63xx-specific header files, we can build it on any non-bcm63xx kernel. Compile-tested on x86, both statically and as a module. Tested for functionality on bcm3384 (a new MIPS platform under active development). Signed-off-by: Kevin Cernekee Acked-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 4e6a0ba..9503789 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1283,7 +1283,7 @@ config SERIAL_TIMBERDALE config SERIAL_BCM63XX tristate "bcm63xx serial port support" select SERIAL_CORE - depends on BCM63XX + depends on MIPS || ARM || COMPILE_TEST help If you have a bcm63xx CPU, you can enable its onboard serial port by enabling this options. -- cgit v0.10.2 From 048c1df7e22b2d40c639837440527ba43b6a591c Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 21 Oct 2014 15:22:58 -0700 Subject: tty: serial: bcm63xx: Add support for unnamed clock outputs from DT The original non-DT bcm63xx clk code ignores the struct device argument and looks up a global clock name. DT platforms, by contrast, often just use a phandle to reference a clock node with no "clock-output-names" property. Modify the UART driver to support both schemes. Signed-off-by: Kevin Cernekee Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 2315190..de95573 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -824,7 +824,8 @@ static int bcm_uart_probe(struct platform_device *pdev) if (!res_irq) return -ENODEV; - clk = clk_get(&pdev->dev, "periph"); + clk = pdev->dev.of_node ? of_clk_get(pdev->dev.of_node, 0) : + clk_get(&pdev->dev, "periph"); if (IS_ERR(clk)) return -ENODEV; -- cgit v0.10.2 From 4b65208f3ab62e0eb2a8052c7ceec090d6d5617d Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 21 Oct 2014 15:22:59 -0700 Subject: tty: serial: bcm63xx: Update the Kconfig help text Remove incorrect "bcm963xx_uart" module name; add a list of known users; tweak grammar/indentation/capitalization. Signed-off-by: Kevin Cernekee Acked-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 9503789..5210c9f 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1281,22 +1281,24 @@ config SERIAL_TIMBERDALE Add support for UART controller on timberdale. config SERIAL_BCM63XX - tristate "bcm63xx serial port support" + tristate "Broadcom BCM63xx/BCM33xx UART support" select SERIAL_CORE depends on MIPS || ARM || COMPILE_TEST help - If you have a bcm63xx CPU, you can enable its onboard - serial port by enabling this options. + This enables the driver for the onchip UART core found on + the following chipsets: - To compile this driver as a module, choose M here: the - module will be called bcm963xx_uart. + BCM33xx (cable modem) + BCM63xx/BCM63xxx (DSL) + BCM68xx (PON) + BCM7xxx (STB) - DOCSIS console config SERIAL_BCM63XX_CONSOLE - bool "Console on bcm63xx serial port" + bool "Console on BCM63xx serial port" depends on SERIAL_BCM63XX=y select SERIAL_CORE_CONSOLE help - If you have enabled the serial port on the bcm63xx CPU + If you have enabled the serial port on the BCM63xx CPU you can make it the console by answering Y to this option. config SERIAL_GRLIB_GAISLER_APBUART -- cgit v0.10.2 From 4137cd9b5c79ff213731865b0ac227c6d8c32e4b Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 21 Oct 2014 15:23:00 -0700 Subject: tty: serial: bcm63xx: Fix typo in MODULE_DESCRIPTION Remove the extra '<' character. Signed-off-by: Kevin Cernekee Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index de95573..b615af2 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -906,5 +906,5 @@ module_init(bcm_uart_init); module_exit(bcm_uart_exit); MODULE_AUTHOR("Maxime Bizon "); -MODULE_DESCRIPTION("Broadcom 63 Date: Tue, 21 Oct 2014 15:23:01 -0700 Subject: Documentation: DT: Add entries for bcm63xx UART This squashes a checkpatch warning on my new bcm3384 dts submission. Signed-off-by: Kevin Cernekee Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/bcm63xx-uart.txt b/Documentation/devicetree/bindings/serial/bcm63xx-uart.txt new file mode 100644 index 0000000..5c52e5e --- /dev/null +++ b/Documentation/devicetree/bindings/serial/bcm63xx-uart.txt @@ -0,0 +1,30 @@ +* BCM63xx UART + +Required properties: + +- compatible: "brcm,bcm6345-uart" + +- reg: The base address of the UART register bank. + +- interrupts: A single interrupt specifier. + +- clocks: Clock driving the hardware; used to figure out the baud rate + divisor. + +Example: + + uart0: serial@14e00520 { + compatible = "brcm,bcm6345-uart"; + reg = <0x14e00520 0x18>; + interrupt-parent = <&periph_intc>; + interrupts = <2>; + clocks = <&periph_clk>; + }; + + clocks { + periph_clk: periph_clk@0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <54000000>; + }; + }; -- cgit v0.10.2 From 1ab8e4b1acdf39ebe6002bbe07ef6e578cff5d74 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 21 Oct 2014 15:23:02 -0700 Subject: tty: serial: bcm63xx: Enable DT earlycon support This enables early console output if there is a chosen/stdout-path property referencing a UART node with the "brcm,bcm6345-uart" compatible string. The bootloader sets up the pinmux and baud/parity/etc. Tested on bcm3384 (MIPS, DT). Signed-off-by: Kevin Cernekee Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 5210c9f..7088373 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1297,6 +1297,7 @@ config SERIAL_BCM63XX_CONSOLE bool "Console on BCM63xx serial port" depends on SERIAL_BCM63XX=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help If you have enabled the serial port on the BCM63xx CPU you can make it the console by answering Y to this option. diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index b615af2..109dea7 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -782,6 +782,26 @@ static int __init bcm63xx_console_init(void) console_initcall(bcm63xx_console_init); +static void bcm_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, bcm_console_putchar); + wait_for_xmitr(&dev->port); +} + +static int __init bcm_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = bcm_early_write; + return 0; +} + +OF_EARLYCON_DECLARE(bcm63xx_uart, "brcm,bcm6345-uart", bcm_early_console_setup); + #define BCM63XX_CONSOLE (&bcm63xx_console) #else #define BCM63XX_CONSOLE NULL -- cgit v0.10.2 From e979f3b712c8b8ae44bab591427f1647dd25aa0d Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 21 Oct 2014 15:23:03 -0700 Subject: tty: serial: bcm63xx: Eliminate unnecessary request/release functions We don't really need to perform the ioremap "on demand" so it's simpler just to do it from the probe function. This also lets us eliminate the UART_REG_SIZE constant and rely on the resource information passed in from the DT or platform code. Signed-off-by: Kevin Cernekee Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 109dea7..e04e580 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -588,20 +588,7 @@ static void bcm_uart_set_termios(struct uart_port *port, */ static int bcm_uart_request_port(struct uart_port *port) { - unsigned int size; - - size = UART_REG_SIZE; - if (!request_mem_region(port->mapbase, size, "bcm63xx")) { - dev_err(port->dev, "Memory region busy\n"); - return -EBUSY; - } - - port->membase = ioremap(port->mapbase, size); - if (!port->membase) { - dev_err(port->dev, "Unable to map registers\n"); - release_mem_region(port->mapbase, size); - return -EBUSY; - } + /* UARTs always present */ return 0; } @@ -610,8 +597,7 @@ static int bcm_uart_request_port(struct uart_port *port) */ static void bcm_uart_release_port(struct uart_port *port) { - release_mem_region(port->mapbase, UART_REG_SIZE); - iounmap(port->membase); + /* Nothing to release ... */ } /* @@ -833,13 +819,20 @@ static int bcm_uart_probe(struct platform_device *pdev) if (pdev->id < 0 || pdev->id >= BCM63XX_NR_UARTS) return -EINVAL; - if (ports[pdev->id].membase) + port = &ports[pdev->id]; + if (port->membase) return -EBUSY; + memset(port, 0, sizeof(*port)); res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res_mem) return -ENODEV; + port->mapbase = res_mem->start; + port->membase = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res_irq) return -ENODEV; @@ -849,10 +842,7 @@ static int bcm_uart_probe(struct platform_device *pdev) if (IS_ERR(clk)) return -ENODEV; - port = &ports[pdev->id]; - memset(port, 0, sizeof(*port)); port->iotype = UPIO_MEM; - port->mapbase = res_mem->start; port->irq = res_irq->start; port->ops = &bcm_uart_ops; port->flags = UPF_BOOT_AUTOCONF; diff --git a/include/linux/serial_bcm63xx.h b/include/linux/serial_bcm63xx.h index a80aa1a..570e964 100644 --- a/include/linux/serial_bcm63xx.h +++ b/include/linux/serial_bcm63xx.h @@ -116,6 +116,4 @@ UART_FIFO_PARERR_MASK | \ UART_FIFO_BRKDET_MASK) -#define UART_REG_SIZE 24 - #endif /* _LINUX_SERIAL_BCM63XX_H */ -- cgit v0.10.2 From 7645c2f4677198df449228a4736d7f94e5c14156 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 21 Oct 2014 15:23:06 -0700 Subject: MAINTAINERS: Add entry for rp2 (Rocketport Express/Infinity) driver I wrote this driver and use it daily on several machines for work, so why not. Signed-off-by: Kevin Cernekee Acked-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman diff --git a/MAINTAINERS b/MAINTAINERS index 3c64271..878a560 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7813,6 +7813,12 @@ S: Maintained F: Documentation/serial/rocket.txt F: drivers/tty/rocket* +ROCKETPORT EXPRESS/INFINITY DRIVER +M: Kevin Cernekee +L: linux-serial@vger.kernel.org +S: Odd Fixes +F: drivers/tty/serial/rp2.* + ROSE NETWORK LAYER M: Ralf Baechle L: linux-hams@vger.kernel.org -- cgit v0.10.2 From 2a768264eef098cc1355b02ca5023b6cfb183a1f Mon Sep 17 00:00:00 2001 From: Eddie Huang Date: Wed, 22 Oct 2014 21:12:07 +0800 Subject: tty: serial: Fix mediatek UART driver setting baudrate issue In mtk8250_set_termios function, calculating quot value can not be zero, otherwise, using DIV_ROUND_CLOSEST(port->uartclk, quot * baud) will fail due to divisor is zero. Signed-off-by: Eddie Huang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 8f37d57..6f93123 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -74,14 +74,14 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, /* Set to next lower baudrate supported */ if ((baud == 500000) || (baud == 576000)) baud = 460800; - quot = DIV_ROUND_CLOSEST(port->uartclk, 4 * baud); + quot = DIV_ROUND_UP(port->uartclk, 4 * baud); } else { serial_port_out(port, UART_MTK_HIGHS, 0x3); /* Set to highest baudrate supported */ if (baud >= 1152000) baud = 921600; - quot = DIV_ROUND_CLOSEST(port->uartclk, 256 * baud); + quot = DIV_ROUND_UP(port->uartclk, 256 * baud); } /* -- cgit v0.10.2 From 5483c10e03c6e37b81b79bf88749bcfbe474a639 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 22 Oct 2014 17:43:16 +0200 Subject: serial: at91: Use dmaengine_slave_config API We are removing the dmaengine_device_control API, that shouldn't even have been exposed in the first place. Change the callers to use the proper API. Signed-off-by: Maxime Ripard Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 9eb624a..b998710 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -862,9 +862,8 @@ static int atmel_prepare_tx_dma(struct uart_port *port) config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; config.dst_addr = port->mapbase + ATMEL_US_THR; - ret = dmaengine_device_control(atmel_port->chan_tx, - DMA_SLAVE_CONFIG, - (unsigned long)&config); + ret = dmaengine_slave_config(atmel_port->chan_tx, + &config); if (ret) { dev_err(port->dev, "DMA tx slave configuration failed\n"); goto chan_err; @@ -1042,9 +1041,8 @@ static int atmel_prepare_rx_dma(struct uart_port *port) config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; config.src_addr = port->mapbase + ATMEL_US_RHR; - ret = dmaengine_device_control(atmel_port->chan_rx, - DMA_SLAVE_CONFIG, - (unsigned long)&config); + ret = dmaengine_slave_config(atmel_port->chan_rx, + &config); if (ret) { dev_err(port->dev, "DMA rx slave configuration failed\n"); goto chan_err; -- cgit v0.10.2 From 08de1014ffd49f156b8b9a0a88ed6dff9a52a275 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 24 Oct 2014 17:20:49 +0800 Subject: serial: fsl-lpuart: add lpuart32 power management support This adds 32-bit register lpuart32 power management support, this also updates the 8-bit register lpuart resume function. Signed-off-by: Jingchang Lu Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 6dd53af..4e25772 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1862,6 +1862,20 @@ static int lpuart_suspend(struct device *dev) static int lpuart_resume(struct device *dev) { struct lpuart_port *sport = dev_get_drvdata(dev); + unsigned long temp; + + if (sport->lpuart32) { + lpuart32_setup_watermark(sport); + temp = lpuart32_read(sport->port.membase + UARTCTRL); + temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | + UARTCTRL_TE | UARTCTRL_ILIE); + lpuart32_write(temp, sport->port.membase + UARTCTRL); + } else { + lpuart_setup_watermark(sport); + temp = readb(sport->port.membase + UARTCR2); + temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE); + writeb(temp, sport->port.membase + UARTCR2); + } uart_resume_port(&lpuart_reg, &sport->port); -- cgit v0.10.2 From 398a9db655efcc208285997c351fe73702735d88 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Thu, 30 Oct 2014 19:49:45 -0500 Subject: serial: 8250_pci: Handle devices mapped above 4 GiB Several init/setup functions passed the PCI BAR resource start address to ioremap_nocache() via an unsigned long. This caused address truncation for a 32-bit device mapped above 4 GiB (i.e. the CPU interacts with the device via a translated address), which resulted in a kernel panic. This patch replaces all of the instances of intermediate variable use with pci_ioremap_bar() to ensure the full resource_size_t start address is used and that ioremap_nocache() is still called. The kernel panic (Exar XR17V358 PCIe device on a Freescale P2020 SBC): Machine check in kernel mode. Caused by (from MCSR=10008): Bus - Read Data Bus Error Oops: Machine check, sig: 7 [#1] SMP NR_CPUS=2 X-ES P2020 Modules linked in: CPU: 1 PID: 1 Comm: swapper/0 Not tainted 3.14.15-xes_r2-00002-g560e401 #978 task: bf850000 ti: bffee000 task.ti: bf84c000 NIP: 80318e10 LR: 80319ecc CTR: 80318dfc REGS: bffeff10 TRAP: 0204 Not tainted (3.14.15-xes_r2-00002-g560e401) MSR: 00021000 CR: 20adbe42 XER: 00000000 DEAR: c1058001 ESR: 00000000 GPR00: 00000000 bf84db30 bf850000 80cb4af8 00000001 00000000 80000007 80000000 GPR08: bf837c9c c1058001 00000001 00000000 80000007 00000000 80002a10 00000000 GPR16: 00000000 00000000 00000000 00000000 00000000 00000000 80cb0000 80c72dc4 GPR24: 80cb4900 fffffffe 00029000 00000001 bf8c11e8 ffffffea 80c72ce4 80cb4af8 NIP [80318e10] mem_serial_in+0x14/0x28 LR [80319ecc] serial8250_config_port+0x160/0xe38 Call Trace: [bf84db30] [80319d94] serial8250_config_port+0x28/0xe38 (unreliable) [bf84db60] [80315e3c] uart_add_one_port+0x148/0x3a4 [bf84dbf0] [8031bf40] serial8250_register_8250_port+0x2dc/0x3c8 [bf84dc20] [8032111c] pciserial_init_ports+0xd4/0x1c0 [bf84dd50] [803212f8] pciserial_init_one+0xf0/0x224 [bf84dd90] [802d8ff4] local_pci_probe+0x34/0x8c [bf84dda0] [802d92c8] pci_device_probe+0x84/0xa0 [bf84ddc0] [80329ee0] driver_probe_device+0xac/0x26c [bf84dde0] [8032a15c] __driver_attach+0xbc/0xc0 [bf84de00] [80328388] bus_for_each_dev+0x90/0xcc [bf84de30] [80329cd0] driver_attach+0x24/0x34 [bf84de40] [80328e28] bus_add_driver+0x104/0x1fc [bf84de60] [8032a8c8] driver_register+0x70/0x138 [bf84de70] [802d93c0] __pci_register_driver+0x48/0x58 [bf84de80] [8077e0e4] serial_pci_driver_init+0x24/0x34 [bf84de90] [80002228] do_one_initcall+0x34/0x1b0 [bf84df00] [80764294] kernel_init_freeable+0x138/0x1e8 [bf84df30] [80002a24] kernel_init+0x14/0x108 [bf84df40] [8000ef94] ret_from_kernel_thread+0x5c/0x64 Instruction dump: 800800c4 7d290214 39290001 7c0004ac 7ca049ae 7c0004ac 4e800020 88030035 81230008 7c840030 7d292214 7c0004ac <88690000> 0c030000 4c00012c 5463063e ---[ end trace e3c16443b5d573c6 ]--- Signed-off-by: Aaron Sierra Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index a9b935a..50f7570 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -79,29 +79,24 @@ setup_port(struct serial_private *priv, struct uart_8250_port *port, int bar, int offset, int regshift) { struct pci_dev *dev = priv->dev; - unsigned long base, len; if (bar >= PCI_NUM_BAR_RESOURCES) return -EINVAL; - base = pci_resource_start(dev, bar); - if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { - len = pci_resource_len(dev, bar); - if (!priv->remapped_bar[bar]) - priv->remapped_bar[bar] = ioremap_nocache(base, len); + priv->remapped_bar[bar] = pci_ioremap_bar(dev, bar); if (!priv->remapped_bar[bar]) return -ENOMEM; port->port.iotype = UPIO_MEM; port->port.iobase = 0; - port->port.mapbase = base + offset; + port->port.mapbase = pci_resource_start(dev, bar) + offset; port->port.membase = priv->remapped_bar[bar] + offset; port->port.regshift = regshift; } else { port->port.iotype = UPIO_PORT; - port->port.iobase = base + offset; + port->port.iobase = pci_resource_start(dev, bar) + offset; port->port.mapbase = 0; port->port.membase = NULL; port->port.regshift = 0; @@ -317,7 +312,6 @@ static void pci_plx9050_exit(struct pci_dev *dev) static void pci_ni8420_exit(struct pci_dev *dev) { void __iomem *p; - unsigned long base, len; unsigned int bar = 0; if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { @@ -325,9 +319,7 @@ static void pci_ni8420_exit(struct pci_dev *dev) return; } - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); + p = pci_ioremap_bar(dev, bar); if (p == NULL) return; @@ -349,7 +341,6 @@ static void pci_ni8420_exit(struct pci_dev *dev) static void pci_ni8430_exit(struct pci_dev *dev) { void __iomem *p; - unsigned long base, len; unsigned int bar = 0; if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { @@ -357,9 +348,7 @@ static void pci_ni8430_exit(struct pci_dev *dev) return; } - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); + p = pci_ioremap_bar(dev, bar); if (p == NULL) return; @@ -682,7 +671,6 @@ static int pci_xircom_init(struct pci_dev *dev) static int pci_ni8420_init(struct pci_dev *dev) { void __iomem *p; - unsigned long base, len; unsigned int bar = 0; if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { @@ -690,9 +678,7 @@ static int pci_ni8420_init(struct pci_dev *dev) return 0; } - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); + p = pci_ioremap_bar(dev, bar); if (p == NULL) return -ENOMEM; @@ -714,7 +700,7 @@ static int pci_ni8420_init(struct pci_dev *dev) static int pci_ni8430_init(struct pci_dev *dev) { void __iomem *p; - unsigned long base, len; + struct pci_bus_region region; u32 device_window; unsigned int bar = 0; @@ -723,14 +709,17 @@ static int pci_ni8430_init(struct pci_dev *dev) return 0; } - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); + p = pci_ioremap_bar(dev, bar); if (p == NULL) return -ENOMEM; - /* Set device window address and size in BAR0 */ - device_window = ((base + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00) + /* + * Set device window address and size in BAR0, while acknowledging that + * the resource structure may contain a translated address that differs + * from the address the device responds to. + */ + pcibios_resource_to_bus(dev->bus, ®ion, &dev->resource[bar]); + device_window = ((region.start + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00) | MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE; writel(device_window, p + MITE_IOWBSR1); @@ -757,8 +746,8 @@ pci_ni8430_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) { + struct pci_dev *dev = priv->dev; void __iomem *p; - unsigned long base, len; unsigned int bar, offset = board->first_offset; if (idx >= board->num_ports) @@ -767,9 +756,7 @@ pci_ni8430_setup(struct serial_private *priv, bar = FL_GET_BASE(board->flags); offset += idx * board->uart_offset; - base = pci_resource_start(priv->dev, bar); - len = pci_resource_len(priv->dev, bar); - p = ioremap_nocache(base, len); + p = pci_ioremap_bar(dev, bar); /* enable the transceiver */ writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE, -- cgit v0.10.2 From 5d14bba91d8c350b21d28834fab95f67a46a264e Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Thu, 30 Oct 2014 19:49:52 -0500 Subject: serial: 8250_pci: Check mapping in pci_ni8430_init Check the return value of ioremap_nocache to make sure we got a valid mapping. Signed-off-by: Aaron Sierra Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 50f7570..003679f 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -757,6 +757,8 @@ pci_ni8430_setup(struct serial_private *priv, offset += idx * board->uart_offset; p = pci_ioremap_bar(dev, bar); + if (!p) + return -ENOMEM; /* enable the transceiver */ writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE, -- cgit v0.10.2 From 1bd8324535ec1ff44aef55c0e40b9e7d56b310fb Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Mon, 3 Nov 2014 23:16:54 +0100 Subject: serial: of-serial: fetch line number from DT The general agreed way to specify a fixed line number for a serial console is to provide a "serial" alias in the devicetree. Start parsing this property in of_serial. Signed-off-by: Lucas Stach Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 8749fb8..7bc1634 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -102,6 +102,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (of_property_read_u32(np, "fifo-size", &prop) == 0) port->fifosize = prop; + /* Check for a fixed line number */ + if ((ret = of_alias_get_id(np, "serial")) >= 0) + port->line = ret; + port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { -- cgit v0.10.2 From f77d55a3b56a98d14b6e7dc549c24b33011d175d Mon Sep 17 00:00:00 2001 From: Julien CHAUVEAU Date: Tue, 4 Nov 2014 11:45:55 +0100 Subject: serial: 8250_dw: get index of serial line from DT aliases Get index of serial line from device tree using function of_alias_get_id(). If no alias is found, the 8250 core takes care of incrementing the line number. Signed-off-by: Julien CHAUVEAU Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 73a14ae..18ff532 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -258,6 +258,7 @@ static int dw8250_probe_of(struct uart_port *p, struct uart_8250_port *up = up_to_u8250p(p); u32 val; bool has_ucv = true; + int id; if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { #ifdef __BIG_ENDIAN @@ -301,6 +302,11 @@ static int dw8250_probe_of(struct uart_port *p, if (!of_property_read_u32(np, "reg-shift", &val)) p->regshift = val; + /* get index of serial line, if found in DT aliases */ + id = of_alias_get_id(np, "serial"); + if (id >= 0) + p->line = id; + /* clock got configured through clk api, all done */ if (p->uartclk) return 0; -- cgit v0.10.2 From 19038ad9f08c96dcff870b18af8fd5ae5141dec1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Nov 2014 13:35:16 +0100 Subject: tty: xuartps: Add support for setting modem control signals Add support for setting the state of the DTR and RTS signals. Acked-by: Soren Brinkmann Signed-off-by: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 200c1af..542bab3 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -133,6 +133,15 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_IXR_BRK 0x80000000 /* + * Modem Control register: + * The read/write Modem Control register controls the interface with the modem + * or data set, or a peripheral device emulating a modem. + */ +#define CDNS_UART_MODEMCR_FCM 0x00000020 /* Automatic flow control mode */ +#define CDNS_UART_MODEMCR_RTS 0x00000002 /* Request to send output control */ +#define CDNS_UART_MODEMCR_DTR 0x00000001 /* Data Terminal Ready */ + +/* * Channel Status Register: * The channel status register (CSR) is provided to enable the control logic * to monitor the status of bits in the channel interrupt status register, @@ -915,7 +924,18 @@ static unsigned int cdns_uart_get_mctrl(struct uart_port *port) static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { - /* N/A */ + u32 val; + + val = cdns_uart_readl(CDNS_UART_MODEMCR_OFFSET); + + val &= ~(CDNS_UART_MODEMCR_RTS | CDNS_UART_MODEMCR_DTR); + + if (mctrl & TIOCM_RTS) + val |= CDNS_UART_MODEMCR_RTS; + if (mctrl & TIOCM_DTR) + val |= CDNS_UART_MODEMCR_DTR; + + cdns_uart_writel(val, CDNS_UART_MODEMCR_OFFSET); } #ifdef CONFIG_CONSOLE_POLL -- cgit v0.10.2 From 633caba8932d9ba2f79bbcb7573e58eae3fdac68 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:40:03 -0500 Subject: tty: Move tty hung up check from port->lock critical section The port->lock does not protect the filp->f_op field; move the tty_hung_up_p() test outside the port->lock critical section in tty_port_close_start(). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 1b93357..3b641d1 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -473,12 +473,10 @@ int tty_port_close_start(struct tty_port *port, { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&port->lock, flags); + if (tty_hung_up_p(filp)) return 0; - } + spin_lock_irqsave(&port->lock, flags); if (tty->count == 1 && port->count != 1) { printk(KERN_WARNING "tty_port_close_start: tty->count = 1 port count = %d.\n", -- cgit v0.10.2 From 413ba6385382bc80e4bf2f58efa900e82e810b11 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:40:04 -0500 Subject: tty: Convert tty->closing to int tty->closing is a bitfield member; prevent corruption from non-atomic update by assigning a unique memory location. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/tty.h b/include/linux/tty.h index c52a689..7d66ae5 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -284,7 +284,7 @@ struct tty_struct { #define N_TTY_BUF_SIZE 4096 - unsigned char closing:1; + int closing; unsigned char *write_buf; int write_cnt; /* If the tty has a pending do_SAK, queue it here - akpm */ -- cgit v0.10.2 From 3f40f5b2a22110754ce469f5722bade8aeb241e5 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 12:40:05 -0500 Subject: tty: Flush tty buffers after hardware shutdown The line discipline buffer and the tty buffers must be flushed again after hardware shutdown; otherwise, a brief window exists between the ldisc flush in tty_port_close_start() and the subsequent tty_port_shutdown(), during which more data could be received into the tty buffers. A racing open might then be able to receive data from the previous session. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 3b641d1..4d9abaa 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -520,6 +520,7 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) { unsigned long flags; + tty_ldisc_flush(tty); tty->closing = 0; spin_lock_irqsave(&port->lock, flags); -- cgit v0.10.2 From 9191aaaa82d3149767125d58b2d260a3749d9f5e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 13:11:41 -0500 Subject: tty: Allow safe access to termios for set_ldisc() handlers Allow a tty driver to safely access termios settings while handling the set_ldisc() notification. UART drivers use the set_ldisc() notification to check if the N_PPS line discipline is being enabled; if so, modem status interrupts may also need to be enabled. Conversely, modem status interrupts may need to be disabled if switching away from the N_PPS line discipline. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index b66a81d..3737f55 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -572,8 +572,11 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_ldisc_restore(tty, old_ldisc); } - if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) + if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) { + down_read(&tty->termios_rwsem); tty->ops->set_ldisc(tty); + up_read(&tty->termios_rwsem); + } /* At this point we hold a reference to the new ldisc and a reference to the old ldisc, or we hold two references to -- cgit v0.10.2 From db1b9dfcd622604b268234e01c539927093abce7 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 13:11:42 -0500 Subject: serial: core: Claim port mutex for set_ldisc() Three UART drivers (8250, atmel & amba-pl010) enable modem status interrupts if the line discipline is changed to N_PPS. However, the uart port flags may only be safely modified while holding the port mutex. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 1a2d90f..5209eaa 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1245,8 +1245,11 @@ static void uart_set_ldisc(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *uport = state->uart_port; - if (uport->ops->set_ldisc) + if (uport->ops->set_ldisc) { + mutex_lock(&state->port.mutex); uport->ops->set_ldisc(uport, tty->termios.c_line); + mutex_unlock(&state->port.mutex); + } } static void uart_set_termios(struct tty_struct *tty, -- cgit v0.10.2 From 732a84a037a4de29b54e0b4e6cb6f9b3813e29e5 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 13:11:43 -0500 Subject: serial: core: Pass termios to set_ldisc() notifications UART drivers which enable modem status interrupts when switching to N_PPS line discipline need to determine if modem status interrupts should be disabled when switching from N_PPS. Specifically, the set_ldisc() notification needs to evaluate UART_ENABLE_MS() which requires termios->c_cflag. Convert in-tree UART drivers to new interface. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 2235032..116b5a7 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2609,9 +2609,9 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios, } static void -serial8250_set_ldisc(struct uart_port *port, int new) +serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios) { - if (new == N_PPS) { + if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; serial8250_enable_ms(port); } else diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 00ae8dc..230bc7a 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -479,9 +479,9 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios, spin_unlock_irqrestore(&uap->port.lock, flags); } -static void pl010_set_ldisc(struct uart_port *port, int new) +static void pl010_set_ldisc(struct uart_port *port, struct ktermios *termios) { - if (new == N_PPS) { + if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; pl010_enable_ms(port); } else diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index b998710..6a1d960 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2056,9 +2056,9 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, spin_unlock_irqrestore(&port->lock, flags); } -static void atmel_set_ldisc(struct uart_port *port, int new) +static void atmel_set_ldisc(struct uart_port *port, struct ktermios *termios) { - if (new == N_PPS) { + if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; atmel_enable_ms(port); } else { diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 7da9911..44b27ec 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -944,12 +944,13 @@ bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser) * Enable the IrDA function if tty->ldisc.num is N_IRDA. * In other cases, disable IrDA function. */ -static void bfin_serial_set_ldisc(struct uart_port *port, int ld) +static void bfin_serial_set_ldisc(struct uart_port *port, + struct ktermios *termios) { struct bfin_serial_port *uart = (struct bfin_serial_port *)port; unsigned int val; - switch (ld) { + switch (termios->c_line) { case N_IRDA: val = UART_GET_GCTL(uart); val |= (UMOD_IRDA | RPOLC); diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index acfe317..f963c4c 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -225,13 +225,14 @@ static void uart_clps711x_break_ctl(struct uart_port *port, int break_state) writel(ubrlcr, port->membase + UBRLCR_OFFSET); } -static void uart_clps711x_set_ldisc(struct uart_port *port, int ld) +static void uart_clps711x_set_ldisc(struct uart_port *port, + struct ktermios *termios) { if (!port->line) { struct clps711x_port *s = dev_get_drvdata(port->dev); regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON1_SIREN, - (ld == N_IRDA) ? SYSCON1_SIREN : 0); + (termios->c_line == N_IRDA) ? SYSCON1_SIREN : 0); } } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 5209eaa..c5fb08c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1247,7 +1247,7 @@ static void uart_set_ldisc(struct tty_struct *tty) if (uport->ops->set_ldisc) { mutex_lock(&state->port.mutex); - uport->ops->set_ldisc(uport, tty->termios.c_line); + uport->ops->set_ldisc(uport, &tty->termios); mutex_unlock(&state->port.mutex); } } diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index ad93296..40b4cc4 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -63,7 +63,7 @@ struct uart_ops { void (*flush_buffer)(struct uart_port *); void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); - void (*set_ldisc)(struct uart_port *, int new); + void (*set_ldisc)(struct uart_port *, struct ktermios *); void (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate); -- cgit v0.10.2 From d41510ce2f071c9ccb1903d7a5135443a57dbe4e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 13:11:44 -0500 Subject: serial: Take uart port lock for direct *_enable_ms() Three UART drivers (8250, atmel & amba-pl010) directly call their enable_ms() method; the uart port lock must be acquired before any h/w programming. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 116b5a7..f0f35e7 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2613,7 +2613,9 @@ serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios) { if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; + spin_lock_irq(&port->lock); serial8250_enable_ms(port); + spin_unlock_irq(&port->lock); } else port->flags &= ~UPF_HARDPPS_CD; } diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 230bc7a..194108b 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -483,7 +483,9 @@ static void pl010_set_ldisc(struct uart_port *port, struct ktermios *termios) { if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; + spin_lock_irq(&port->lock); pl010_enable_ms(port); + spin_unlock_irq(&port->lock); } else port->flags &= ~UPF_HARDPPS_CD; } diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 6a1d960..e67cb38 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2060,7 +2060,9 @@ static void atmel_set_ldisc(struct uart_port *port, struct ktermios *termios) { if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; + spin_lock_irq(&port->lock); atmel_enable_ms(port); + spin_unlock_irq(&port->lock); } else { port->flags &= ~UPF_HARDPPS_CD; } -- cgit v0.10.2 From cab68f89546ba5a04bf28aaeaca841d4ccc2fd52 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 5 Nov 2014 13:11:45 -0500 Subject: serial: Test/disable MSIs if switching from N_PPS Switching to the N_PPS line discipline may require enabling modem status interrupts; conversely switching from N_PPS may require disabling modem status interrupts. Affected drivers: 8250 amba-pl010 atmel Cc: Nicolas Ferre Cc: Russell King Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index f0f35e7..25eb8e9 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1402,6 +1402,19 @@ static void serial8250_stop_rx(struct uart_port *port) serial8250_rpm_put(up); } +static void serial8250_disable_ms(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + /* no MSR capabilities */ + if (up->bugs & UART_BUG_NOMSR) + return; + + up->ier &= ~UART_IER_MSI; + serial_port_out(port, UART_IER, up->ier); +} + static void serial8250_enable_ms(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2616,8 +2629,14 @@ serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios) spin_lock_irq(&port->lock); serial8250_enable_ms(port); spin_unlock_irq(&port->lock); - } else + } else { port->flags &= ~UPF_HARDPPS_CD; + if (!UART_ENABLE_MS(port, termios->c_cflag)) { + spin_lock_irq(&port->lock); + serial8250_disable_ms(port); + spin_unlock_irq(&port->lock); + } + } } diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 194108b..5d41d5b 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -106,6 +106,16 @@ static void pl010_stop_rx(struct uart_port *port) writel(cr, uap->port.membase + UART010_CR); } +static void pl010_disable_ms(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int cr; + + cr = readb(uap->port.membase + UART010_CR); + cr &= ~UART010_CR_MSIE; + writel(cr, uap->port.membase + UART010_CR); +} + static void pl010_enable_ms(struct uart_port *port) { struct uart_amba_port *uap = @@ -486,8 +496,14 @@ static void pl010_set_ldisc(struct uart_port *port, struct ktermios *termios) spin_lock_irq(&port->lock); pl010_enable_ms(port); spin_unlock_irq(&port->lock); - } else + } else { port->flags &= ~UPF_HARDPPS_CD; + if (!UART_ENABLE_MS(port, termios->c_cflag)) { + spin_lock_irq(&port->lock); + pl010_disable_ms(port); + spin_unlock_irq(&port->lock); + } + } } static const char *pl010_type(struct uart_port *port) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index e67cb38..b1d61dc 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2065,6 +2065,11 @@ static void atmel_set_ldisc(struct uart_port *port, struct ktermios *termios) spin_unlock_irq(&port->lock); } else { port->flags &= ~UPF_HARDPPS_CD; + if (!UART_ENABLE_MS(port, termios->c_cflag)) { + spin_lock_irq(&port->lock); + atmel_disable_ms(port); + spin_unlock_irq(&port->lock); + } } } -- cgit v0.10.2 From 2fdd8c8c5304901fa7dbb2ce5dbc90a1984cee3d Mon Sep 17 00:00:00 2001 From: Sergej Pupykin Date: Thu, 6 Nov 2014 14:36:31 +0300 Subject: parport: Add support for the WCH382 2S/1P multi-IO card WCH382 is a PCI-E card with 1 LPT and 2 DB9 COM ports detected as Serial controller: Device 1c00:3250 (rev 10) (prog-if 05 [16850]) Signed-off-by: Sergej Pupykin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index ee93200..e15b484 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -64,6 +64,7 @@ enum parport_pc_pci_cards { timedia_9079c, wch_ch353_1s1p, wch_ch353_2s1p, + wch_ch382_2s1p, sunix_2s1p, }; @@ -151,6 +152,7 @@ static struct parport_pc_pci cards[] = { /* timedia_9079c */ { 1, { { 2, 3 }, } }, /* wch_ch353_1s1p*/ { 1, { { 1, -1}, } }, /* wch_ch353_2s1p*/ { 1, { { 2, -1}, } }, + /* wch_ch382_2s1p*/ { 1, { { 2, -1}, } }, /* sunix_2s1p */ { 1, { { 3, -1 }, } }, }; @@ -257,6 +259,7 @@ static struct pci_device_id parport_serial_pci_tbl[] = { /* WCH CARDS */ { 0x4348, 0x5053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p}, { 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p}, + { 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p}, /* * More SUNIX variations. At least one of these has part number @@ -494,6 +497,13 @@ static struct pciserial_board pci_parport_serial_boards[] = { .base_baud = 115200, .uart_offset = 8, }, + [wch_ch382_2s1p] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + .first_offset = 0xC0, + }, [sunix_2s1p] = { .flags = FL_BASE0|FL_BASE_BARS, .num_ports = 2, diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 003679f..0468e15 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1783,6 +1783,16 @@ pci_wch_ch353_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +static int +pci_wch_ch382_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + port->port.flags |= UPF_FIXED_TYPE; + port->port.type = PORT_16850; + return pci_default_setup(priv, board, port, idx); +} + #define PCI_VENDOR_ID_SBSMODULARIO 0x124B #define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B #define PCI_DEVICE_ID_OCTPRO 0x0001 @@ -1837,6 +1847,8 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_VENDOR_ID_SUNIX 0x1fd4 #define PCI_DEVICE_ID_SUNIX_1999 0x1999 +#define PCIE_VENDOR_ID_WCH 0x1c00 +#define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -2528,6 +2540,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, + /* WCH CH382 2S1P card (16750 clone) */ + { + .vendor = PCIE_VENDOR_ID_WCH, + .device = PCIE_DEVICE_ID_WCH_CH382_2S1P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch382_setup, + }, /* * ASIX devices with FIFO bug */ @@ -3635,6 +3655,7 @@ static const struct pci_device_id blacklist[] = { /* multi-io cards handled by parport_serial */ { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */ { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */ + { PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */ }; /* -- cgit v0.10.2 From 7f1dc2f384792f271833cd89a8608d189b63ea6d Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Thu, 16 Oct 2014 14:16:22 +0530 Subject: serial: 8250: sparse warning of incorrect type fixed a sparse warning in 8250_core.c : incorrect type in assignment (different address spaces) the warning was because an unsigned char pointer was being assigned to a pointer of unsigned char __iomem type . Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 458ad2833..b008368 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -56,7 +56,7 @@ struct old_serial_port { unsigned int flags; unsigned char hub6; unsigned char io_type; - unsigned char *iomem_base; + unsigned char __iomem *iomem_base; unsigned short iomem_reg_shift; unsigned long irqflags; }; -- cgit v0.10.2 From a5f276f10ff70da89b349df445e944c8cd87956c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 22:46:13 +0100 Subject: serial_core: Handle TIOC[GS]RS485 ioctls. The following drivers: 8250_core, atmel_serial, max310x, mcf, omap-serial and sci16is7xx implement code to handle RS485 ioctls. In order to avoid code duplication, we implement a simple ioctl handler on the serial_core layer. This handler can be used by all the other drivers instead of duplicating code. Until this is the only RS485 ioctl handler, it will try first the rs485_config callback and if it is not present it will call the driver specific ioctl. Reviewed-by: Alan Cox Cc: Greg Kroah-Hartman Cc: Jiri Slaby Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index c5fb08c..ab4db1d 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1152,6 +1152,39 @@ static int uart_get_icount(struct tty_struct *tty, return 0; } +static int uart_get_rs485_config(struct uart_port *port, + struct serial_rs485 __user *rs485) +{ + if (!port->rs485_config) + return -ENOIOCTLCMD; + + if (copy_to_user(rs485, &port->rs485, sizeof(port->rs485))) + return -EFAULT; + return 0; +} + +static int uart_set_rs485_config(struct uart_port *port, + struct serial_rs485 __user *rs485_user) +{ + struct serial_rs485 rs485; + int ret; + + if (!port->rs485_config) + return -ENOIOCTLCMD; + + if (copy_from_user(&rs485, rs485_user, sizeof(*rs485_user))) + return -EFAULT; + + ret = port->rs485_config(port, &rs485); + if (ret) + return ret; + + if (copy_to_user(rs485_user, &port->rs485, sizeof(port->rs485))) + return -EFAULT; + + return 0; +} + /* * Called via sys_ioctl. We can use spin_lock_irq() here. */ @@ -1223,6 +1256,18 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, * protected against the tty being hung up. */ switch (cmd) { + case TIOCGRS485: + ret = uart_get_rs485_config(state->uart_port, uarg); + break; + + case TIOCSRS485: + ret = uart_set_rs485_config(state->uart_port, uarg); + break; + } + if (ret != -ENOIOCTLCMD) + goto out; + + switch (cmd) { case TIOCSERGETLSR: /* Get line status register */ ret = uart_get_lsr_info(tty, state, uarg); break; diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 40b4cc4..3231a43 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -131,6 +131,8 @@ struct uart_port { void (*pm)(struct uart_port *, unsigned int state, unsigned int old); void (*handle_break)(struct uart_port *); + int (*rs485_config)(struct uart_port *, + struct serial_rs485 *rs485); unsigned int irq; /* irq number */ unsigned long irqflags; /* irq flags */ unsigned int uartclk; /* base uart clock */ @@ -231,6 +233,7 @@ struct uart_port { unsigned char unused[2]; struct attribute_group *attr_group; /* port specific attributes */ const struct attribute_group **tty_groups; /* all attributes (serial core use only) */ + struct serial_rs485 rs485; void *private_data; /* generic platform data pointer */ }; -- cgit v0.10.2 From 46c55b4bb9b55b7b09b6879668a209a5657814b3 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 09:22:51 +0100 Subject: serial/8250: Copy RS485 fields to serial_core Initialize recently added rs485 fields on serial_core Reviewed-by: Alan Cox Cc: Jiri Slaby Cc: Sebastian Andrzej Siewior Cc: Alan Cox Cc: Tony Lindgren Cc: Peter Hurley Cc: Yoshihiro YUNOMAE Cc: Andy Shevchenko Cc: Ingo Molnar Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 25eb8e9..3f2c657 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3619,6 +3619,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->rs485 = up->rs485; uart->port.throttle = up->port.throttle; uart->port.unthrottle = up->port.unthrottle; + uart->port.rs485_config = up->port.rs485_config; + uart->port.rs485 = up->port.rs485; /* Take tx_loadsz from fifosize if it wasn't set separately */ if (uart->port.fifosize && !uart->tx_loadsz) -- cgit v0.10.2 From 41e69093fd608c0348fb12c0879ab464f986d2a8 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 09:22:52 +0100 Subject: 8250/fintek: Use rs485 handler from serial_core In order to remove the handler for rs485 ioctls on serial_8250, all the drivers must use the implementation on serial_core. Reviewed-by: Alan Cox Cc: Jiri Slaby Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 1bb28cb..1e6899bc 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -89,11 +89,11 @@ static int fintek_8250_check_id(void) return 0; } -static int fintek_8250_rs4850_config(struct uart_8250_port *uart, +static int fintek_8250_rs485_config(struct uart_port *port, struct serial_rs485 *rs485) { uint8_t config = 0; - int index = fintek_8250_get_index(uart->port.iobase); + int index = fintek_8250_get_index(port->iobase); if (index < 0) return -EINVAL; @@ -134,6 +134,8 @@ static int fintek_8250_rs4850_config(struct uart_8250_port *uart, outb(config, DATA_PORT); fintek_8250_exit_key(); + port->rs485 = *rs485; + return 0; } @@ -166,7 +168,7 @@ fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) uart.port.irq = pnp_irq(dev, 0); uart.port.iobase = pnp_port_start(dev, 0); uart.port.iotype = UPIO_PORT; - uart.rs485_config = fintek_8250_rs4850_config; + uart.port.rs485_config = fintek_8250_rs485_config; uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) -- cgit v0.10.2 From 039ec1f010e6b058f497381d5a6bb840e160b4ac Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 09:22:53 +0100 Subject: serial/8250: Remove obsolete handling of rs485 ioctls There is no more users for this functions. All the 8250 drivers are using the rs485 handler on serial_core instead. Reviewed-by: Alan Cox Cc: Jiri Slaby Cc: Sebastian Andrzej Siewior Cc: Alan Cox Cc: Tony Lindgren Cc: Peter Hurley Cc: Yoshihiro YUNOMAE Cc: Andy Shevchenko Cc: Ingo Molnar Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 3f2c657..30522d6 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3005,42 +3005,6 @@ serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) return 0; } -static int serial8250_ioctl(struct uart_port *port, unsigned int cmd, - unsigned long arg) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - int ret; - struct serial_rs485 rs485_config; - - if (!up->rs485_config) - return -ENOIOCTLCMD; - - switch (cmd) { - case TIOCSRS485: - if (copy_from_user(&rs485_config, (void __user *)arg, - sizeof(rs485_config))) - return -EFAULT; - - ret = up->rs485_config(up, &rs485_config); - if (ret) - return ret; - - memcpy(&up->rs485, &rs485_config, sizeof(rs485_config)); - - return 0; - case TIOCGRS485: - if (copy_to_user((void __user *)arg, &up->rs485, - sizeof(up->rs485))) - return -EFAULT; - return 0; - default: - break; - } - - return -ENOIOCTLCMD; -} - static const char * serial8250_type(struct uart_port *port) { @@ -3072,7 +3036,6 @@ static struct uart_ops serial8250_pops = { .request_port = serial8250_request_port, .config_port = serial8250_config_port, .verify_port = serial8250_verify_port, - .ioctl = serial8250_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = serial8250_get_poll_char, .poll_put_char = serial8250_put_poll_char, @@ -3615,8 +3578,6 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->port.fifosize = up->port.fifosize; uart->tx_loadsz = up->tx_loadsz; uart->capabilities = up->capabilities; - uart->rs485_config = up->rs485_config; - uart->rs485 = up->rs485; uart->port.throttle = up->port.throttle; uart->port.unthrottle = up->port.unthrottle; uart->port.rs485_config = up->port.rs485_config; diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 3df10d5..e02acf0 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -97,13 +97,10 @@ struct uart_8250_port { unsigned char msr_saved_flags; struct uart_8250_dma *dma; - struct serial_rs485 rs485; /* 8250 specific callbacks */ int (*dl_read)(struct uart_8250_port *); void (*dl_write)(struct uart_8250_port *, int); - int (*rs485_config)(struct uart_8250_port *, - struct serial_rs485 *rs485); }; static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up) -- cgit v0.10.2 From b57d15fe8b37fef8f374afa08e3a557898d5d517 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 09:22:54 +0100 Subject: serial/sc16is7xx: Use the rs485 functions on serial_core In order to unify all the rs485 ioctl handling. Use the implementation of TIOC[GS]RS485 ioctl handling on serial_core. Reviewed-by: Alan Cox Cc: Jiri Slaby Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 7a79113..df9a384 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -304,8 +304,6 @@ struct sc16is7xx_one { struct uart_port port; struct work_struct tx_work; struct work_struct md_work; - - struct serial_rs485 rs485; }; struct sc16is7xx_port { @@ -657,15 +655,15 @@ static void sc16is7xx_stop_tx(struct uart_port* port) struct circ_buf *xmit = &one->port.state->xmit; /* handle rs485 */ - if (one->rs485.flags & SER_RS485_ENABLED) { + if (port->rs485.flags & SER_RS485_ENABLED) { /* do nothing if current tx not yet completed */ int lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); if (!(lsr & SC16IS7XX_LSR_TEMT_BIT)) return; if (uart_circ_empty(xmit) && - (one->rs485.delay_rts_after_send > 0)) - mdelay(one->rs485.delay_rts_after_send); + (port->rs485.delay_rts_after_send > 0)) + mdelay(port->rs485.delay_rts_after_send); } sc16is7xx_port_update(port, SC16IS7XX_IER_REG, @@ -688,9 +686,9 @@ static void sc16is7xx_start_tx(struct uart_port *port) struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); /* handle rs485 */ - if ((one->rs485.flags & SER_RS485_ENABLED) && - (one->rs485.delay_rts_before_send > 0)) { - mdelay(one->rs485.delay_rts_before_send); + if ((port->rs485.flags & SER_RS485_ENABLED) && + (port->rs485.delay_rts_before_send > 0)) { + mdelay(port->rs485.delay_rts_before_send); } if (!work_pending(&one->tx_work)) @@ -830,47 +828,20 @@ static void sc16is7xx_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); } -static void sc16is7xx_config_rs485(struct uart_port *port, +static int sc16is7xx_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) { - struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - - one->rs485 = *rs485; - - if (one->rs485.flags & SER_RS485_ENABLED) { + if (port->rs485.flags & SER_RS485_ENABLED) sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, SC16IS7XX_EFCR_AUTO_RS485_BIT, SC16IS7XX_EFCR_AUTO_RS485_BIT); - } else { + else sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, SC16IS7XX_EFCR_AUTO_RS485_BIT, 0); - } -} - -static int sc16is7xx_ioctl(struct uart_port *port, unsigned int cmd, - unsigned long arg) -{ - struct serial_rs485 rs485; + port->rs485 = *rs485; - switch (cmd) { - case TIOCSRS485: - if (copy_from_user(&rs485, (void __user *)arg, sizeof(rs485))) - return -EFAULT; - - sc16is7xx_config_rs485(port, &rs485); - return 0; - case TIOCGRS485: - if (copy_to_user((void __user *)arg, - &(to_sc16is7xx_one(port, port)->rs485), - sizeof(rs485))) - return -EFAULT; - return 0; - default: - break; - } - - return -ENOIOCTLCMD; + return 0; } static int sc16is7xx_startup(struct uart_port *port) @@ -996,7 +967,6 @@ static const struct uart_ops sc16is7xx_ops = { .release_port = sc16is7xx_null_void, .config_port = sc16is7xx_config_port, .verify_port = sc16is7xx_verify_port, - .ioctl = sc16is7xx_ioctl, .pm = sc16is7xx_pm, }; @@ -1126,6 +1096,7 @@ static int sc16is7xx_probe(struct device *dev, s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; s->p[i].port.iotype = UPIO_PORT; s->p[i].port.uartclk = freq; + s->p[i].port.rs485_config = sc16is7xx_config_rs485; s->p[i].port.ops = &sc16is7xx_ops; /* Disable all interrupts */ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_IER_REG, 0); -- cgit v0.10.2 From 2fc0184dae7be565e4ad47c720e6014cd5543a21 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 09:22:55 +0100 Subject: serial/mcf: Use the rs485 functions on serial_core In order to unify all the rs485 ioctl handling. Use the implementation of TIOC[GS]RS485 ioctl handling on serial_core. Reviewed-by: Alan Cox Cc: Jiri Slaby Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index bc896dc..d7be1f1 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -57,7 +57,6 @@ struct mcf_uart { struct uart_port port; unsigned int sigs; /* Local copy of line sigs */ unsigned char imr; /* Local IMR mirror */ - struct serial_rs485 rs485; /* RS485 settings */ }; /****************************************************************************/ @@ -104,7 +103,7 @@ static void mcf_start_tx(struct uart_port *port) { struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - if (pp->rs485.flags & SER_RS485_ENABLED) { + if (port->rs485.flags & SER_RS485_ENABLED) { /* Enable Transmitter */ writeb(MCFUART_UCR_TXENABLE, port->membase + MCFUART_UCR); /* Manually assert RTS */ @@ -258,7 +257,7 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, mr2 |= MCFUART_MR2_TXCTS; } - if (pp->rs485.flags & SER_RS485_ENABLED) { + if (port->rs485.flags & SER_RS485_ENABLED) { dev_dbg(port->dev, "Setting UART to RS485\n"); mr2 |= MCFUART_MR2_TXRTS; } @@ -360,7 +359,7 @@ static void mcf_tx_chars(struct mcf_uart *pp) pp->imr &= ~MCFUART_UIR_TXREADY; writeb(pp->imr, port->membase + MCFUART_UIMR); /* Disable TX to negate RTS automatically */ - if (pp->rs485.flags & SER_RS485_ENABLED) + if (port->rs485.flags & SER_RS485_ENABLED) writeb(MCFUART_UCR_TXDISABLE, port->membase + MCFUART_UCR); } @@ -440,7 +439,7 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) /****************************************************************************/ /* Enable or disable the RS485 support */ -static void mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) +static int mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) { struct mcf_uart *pp = container_of(port, struct mcf_uart, port); unsigned long flags; @@ -460,32 +459,9 @@ static void mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) } writeb(mr1, port->membase + MCFUART_UMR); writeb(mr2, port->membase + MCFUART_UMR); - pp->rs485 = *rs485; + port->rs485 = *rs485; spin_unlock_irqrestore(&port->lock, flags); -} -static int mcf_ioctl(struct uart_port *port, unsigned int cmd, - unsigned long arg) -{ - switch (cmd) { - case TIOCSRS485: { - struct serial_rs485 rs485; - if (copy_from_user(&rs485, (struct serial_rs485 *)arg, - sizeof(struct serial_rs485))) - return -EFAULT; - mcf_config_rs485(port, &rs485); - break; - } - case TIOCGRS485: { - struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - if (copy_to_user((struct serial_rs485 *)arg, &pp->rs485, - sizeof(struct serial_rs485))) - return -EFAULT; - break; - } - default: - return -ENOIOCTLCMD; - } return 0; } @@ -510,7 +486,6 @@ static const struct uart_ops mcf_uart_ops = { .release_port = mcf_release_port, .config_port = mcf_config_port, .verify_port = mcf_verify_port, - .ioctl = mcf_ioctl, }; static struct mcf_uart mcf_ports[4]; @@ -538,6 +513,7 @@ int __init early_mcf_setup(struct mcf_platform_uart *platp) port->irq = platp[i].irq; port->uartclk = MCF_BUSCLK; port->flags = UPF_BOOT_AUTOCONF; + port->rs485_config = mcf_config_rs485; port->ops = &mcf_uart_ops; } @@ -663,6 +639,7 @@ static int mcf_probe(struct platform_device *pdev) port->uartclk = MCF_BUSCLK; port->ops = &mcf_uart_ops; port->flags = UPF_BOOT_AUTOCONF; + port->rs485_config = mcf_config_rs485; uart_add_one_port(&mcf_driver, port); } -- cgit v0.10.2 From 13bd3e6fa177883914fa64b609651e56d58eea65 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 09:22:56 +0100 Subject: serial/atmel: Use the rs485 functions on serial_core In order to unify all the rs485 ioctl handling. Use the implementation of TIOC[GS]RS485 ioctl handling on serial_core. Reviewed-by: Alan Cox Cc: Jiri Slaby Signed-off-by: Ricardo Ribalda Delgado Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index b1d61dc..80c4bfc 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -167,7 +167,6 @@ struct atmel_uart_port { struct circ_buf rx_ring; - struct serial_rs485 rs485; /* rs485 settings */ struct mctrl_gpios *gpios; int gpio_irq[UART_GPIO_MAX]; unsigned int tx_done_mask; @@ -290,7 +289,8 @@ static unsigned int atmel_get_lines_status(struct uart_port *port) } /* Enable or disable the rs485 support */ -void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +static int 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; @@ -306,7 +306,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) /* Resetting serial mode to RS232 (0x0) */ mode &= ~ATMEL_US_USMODE; - atmel_port->rs485 = *rs485conf; + port->rs485 = *rs485conf; if (rs485conf->flags & SER_RS485_ENABLED) { dev_dbg(port->dev, "Setting UART to RS485\n"); @@ -329,6 +329,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) spin_unlock_irqrestore(&port->lock, flags); + return 0; } /* @@ -372,11 +373,10 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) /* Resetting serial mode to RS232 (0x0) */ mode &= ~ATMEL_US_USMODE; - if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + if (port->rs485.flags & SER_RS485_ENABLED) { dev_dbg(port->dev, "Setting UART to RS485\n"); - if ((atmel_port->rs485.delay_rts_after_send) > 0) - UART_PUT_TTGR(port, - atmel_port->rs485.delay_rts_after_send); + if ((port->rs485.delay_rts_after_send) > 0) + UART_PUT_TTGR(port, port->rs485.delay_rts_after_send); mode |= ATMEL_US_USMODE_RS485; } else { dev_dbg(port->dev, "Setting UART to RS232\n"); @@ -423,8 +423,8 @@ static void atmel_stop_tx(struct uart_port *port) /* Disable interrupts */ UART_PUT_IDR(port, atmel_port->tx_done_mask); - if ((atmel_port->rs485.flags & SER_RS485_ENABLED) && - !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) + if ((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) atmel_start_rx(port); } @@ -441,8 +441,8 @@ static void atmel_start_tx(struct uart_port *port) really need this.*/ return; - if ((atmel_port->rs485.flags & SER_RS485_ENABLED) && - !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) + if ((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) atmel_stop_rx(port); /* re-enable PDC transmit */ @@ -807,7 +807,7 @@ static void atmel_tx_dma(struct uart_port *port) atmel_port->cookie_tx = dmaengine_submit(desc); } else { - if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + if (port->rs485.flags & SER_RS485_ENABLED) { /* DMA done, stop TX, start RX for RS485 */ atmel_start_rx(port); } @@ -1254,8 +1254,8 @@ static void atmel_tx_pdc(struct uart_port *port) /* Enable interrupts */ UART_PUT_IER(port, atmel_port->tx_done_mask); } else { - if ((atmel_port->rs485.flags & SER_RS485_ENABLED) && - !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) { + if ((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { /* DMA done, stop TX, start RX for RS485 */ atmel_start_rx(port); } @@ -1566,7 +1566,7 @@ static int atmel_init_property(struct atmel_uart_port *atmel_port, return 0; } -static void atmel_init_rs485(struct atmel_uart_port *atmel_port, +static void atmel_init_rs485(struct uart_port *port, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1577,7 +1577,7 @@ static void atmel_init_rs485(struct atmel_uart_port *atmel_port, /* rs485 properties */ if (of_property_read_u32_array(np, "rs485-rts-delay", rs485_delay, 2) == 0) { - struct serial_rs485 *rs485conf = &atmel_port->rs485; + struct serial_rs485 *rs485conf = &port->rs485; rs485conf->delay_rts_before_send = rs485_delay[0]; rs485conf->delay_rts_after_send = rs485_delay[1]; @@ -1591,7 +1591,7 @@ static void atmel_init_rs485(struct atmel_uart_port *atmel_port, rs485conf->flags |= SER_RS485_ENABLED; } } else { - atmel_port->rs485 = pdata->rs485; + port->rs485 = pdata->rs485; } } @@ -1927,7 +1927,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, { unsigned long flags; unsigned int mode, imr, quot, baud; - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); /* Get current mode register */ mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL @@ -2029,10 +2028,9 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, /* Resetting serial mode to RS232 (0x0) */ mode &= ~ATMEL_US_USMODE; - if (atmel_port->rs485.flags & SER_RS485_ENABLED) { - if ((atmel_port->rs485.delay_rts_after_send) > 0) - UART_PUT_TTGR(port, - atmel_port->rs485.delay_rts_after_send); + if (port->rs485.flags & SER_RS485_ENABLED) { + if ((port->rs485.delay_rts_after_send) > 0) + UART_PUT_TTGR(port, port->rs485.delay_rts_after_send); mode |= ATMEL_US_USMODE_RS485; } @@ -2171,35 +2169,6 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) } #endif -static int -atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) -{ - struct serial_rs485 rs485conf; - - switch (cmd) { - case TIOCSRS485: - if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, - sizeof(rs485conf))) - return -EFAULT; - - atmel_config_rs485(port, &rs485conf); - break; - - case TIOCGRS485: - if (copy_to_user((struct serial_rs485 *) arg, - &(to_atmel_uart_port(port)->rs485), - sizeof(rs485conf))) - return -EFAULT; - break; - - default: - return -ENOIOCTLCMD; - } - return 0; -} - - - static struct uart_ops atmel_pops = { .tx_empty = atmel_tx_empty, .set_mctrl = atmel_set_mctrl, @@ -2220,7 +2189,6 @@ static struct uart_ops atmel_pops = { .config_port = atmel_config_port, .verify_port = atmel_verify_port, .pm = atmel_serial_pm, - .ioctl = atmel_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = atmel_poll_get_char, .poll_put_char = atmel_poll_put_char, @@ -2240,7 +2208,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, if (!atmel_init_property(atmel_port, pdev)) atmel_set_ops(port); - atmel_init_rs485(atmel_port, pdev); + atmel_init_rs485(port, pdev); port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; @@ -2249,6 +2217,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; + port->rs485_config = atmel_config_rs485; tasklet_init(&atmel_port->tasklet, atmel_tasklet_func, (unsigned long)port); @@ -2283,7 +2252,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, } /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ - if (atmel_port->rs485.flags & SER_RS485_ENABLED) + if (port->rs485.flags & SER_RS485_ENABLED) atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; else if (atmel_use_pdc_tx(port)) { port->fifosize = PDC_BUFFER_SIZE; @@ -2629,7 +2598,7 @@ static int atmel_serial_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, port); - if (port->rs485.flags & SER_RS485_ENABLED) { + if (port->uart.rs485.flags & SER_RS485_ENABLED) { UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL); UART_PUT_CR(&port->uart, ATMEL_US_RTSEN); } -- cgit v0.10.2 From dadd7ecbff4bf01ec446c4390cfeab20124b5708 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 22:46:14 +0100 Subject: serial/omap: Use the rs485 functions on serial_core In order to unify all the rs485 ioctl handling Use the implementation of TIOC[GS]RS485 ioctl handling on serial_core. Reviewed-by: Alan Cox Cc: Greg Kroah-Hartman Cc: Jiri Slaby Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 9d22d33..050d1b5 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -163,7 +163,6 @@ struct uart_omap_port { u8 wakeups_enabled; u32 features; - struct serial_rs485 rs485; int rts_gpio; struct pm_qos_request pm_qos_request; @@ -316,7 +315,7 @@ static void serial_omap_stop_tx(struct uart_port *port) pm_runtime_get_sync(up->dev); /* Handle RS-485 */ - if (up->rs485.flags & SER_RS485_ENABLED) { + if (port->rs485.flags & SER_RS485_ENABLED) { if (up->scr & OMAP_UART_SCR_TX_EMPTY) { /* THR interrupt is fired when both TX FIFO and TX * shift register are empty. This means there's nothing @@ -327,10 +326,12 @@ static void serial_omap_stop_tx(struct uart_port *port) */ up->scr &= ~OMAP_UART_SCR_TX_EMPTY; serial_out(up, UART_OMAP_SCR, up->scr); - res = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + res = (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? + 1 : 0; if (gpio_get_value(up->rts_gpio) != res) { - if (up->rs485.delay_rts_after_send > 0) - mdelay(up->rs485.delay_rts_after_send); + if (port->rs485.delay_rts_after_send > 0) + mdelay( + port->rs485.delay_rts_after_send); gpio_set_value(up->rts_gpio, res); } } else { @@ -353,8 +354,8 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_IER, up->ier); } - if ((up->rs485.flags & SER_RS485_ENABLED) && - !(up->rs485.flags & SER_RS485_RX_DURING_TX)) { + if ((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { /* * Empty the RX FIFO, we are not interested in anything * received during the half-duplex transmission. @@ -429,22 +430,22 @@ static void serial_omap_start_tx(struct uart_port *port) pm_runtime_get_sync(up->dev); /* Handle RS-485 */ - if (up->rs485.flags & SER_RS485_ENABLED) { + if (port->rs485.flags & SER_RS485_ENABLED) { /* Fire THR interrupts when FIFO is below trigger level */ up->scr &= ~OMAP_UART_SCR_TX_EMPTY; serial_out(up, UART_OMAP_SCR, up->scr); /* if rts not already enabled */ - res = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0; + res = (port->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0; if (gpio_get_value(up->rts_gpio) != res) { gpio_set_value(up->rts_gpio, res); - if (up->rs485.delay_rts_before_send > 0) - mdelay(up->rs485.delay_rts_before_send); + if (port->rs485.delay_rts_before_send > 0) + mdelay(port->rs485.delay_rts_before_send); } } - if ((up->rs485.flags & SER_RS485_ENABLED) && - !(up->rs485.flags & SER_RS485_RX_DURING_TX)) + if ((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) serial_omap_stop_rx(port); serial_omap_enable_ier_thri(up); @@ -1355,7 +1356,7 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up) #endif /* Enable or disable the rs485 support */ -static void +static int serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) { struct uart_omap_port *up = to_uart_omap_port(port); @@ -1372,7 +1373,7 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) serial_out(up, UART_IER, 0); /* store new config */ - up->rs485 = *rs485conf; + port->rs485 = *rs485conf; /* * Just as a precaution, only allow rs485 @@ -1380,12 +1381,12 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) */ if (gpio_is_valid(up->rts_gpio)) { /* enable / disable rts */ - val = (up->rs485.flags & SER_RS485_ENABLED) ? + val = (port->rs485.flags & SER_RS485_ENABLED) ? SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND; - val = (up->rs485.flags & val) ? 1 : 0; + val = (port->rs485.flags & val) ? 1 : 0; gpio_set_value(up->rts_gpio, val); } else - up->rs485.flags &= ~SER_RS485_ENABLED; + port->rs485.flags &= ~SER_RS485_ENABLED; /* Enable interrupts */ up->ier = mode; @@ -1394,7 +1395,7 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) /* If RS-485 is disabled, make sure the THR interrupt is fired when * TX FIFO is below the trigger level. */ - if (!(up->rs485.flags & SER_RS485_ENABLED) && + if (!(port->rs485.flags & SER_RS485_ENABLED) && (up->scr & OMAP_UART_SCR_TX_EMPTY)) { up->scr &= ~OMAP_UART_SCR_TX_EMPTY; serial_out(up, UART_OMAP_SCR, up->scr); @@ -1403,36 +1404,10 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) spin_unlock_irqrestore(&up->port.lock, flags); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); -} - -static int -serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) -{ - struct serial_rs485 rs485conf; - - switch (cmd) { - case TIOCSRS485: - if (copy_from_user(&rs485conf, (void __user *) arg, - sizeof(rs485conf))) - return -EFAULT; - serial_omap_config_rs485(port, &rs485conf); - break; - - case TIOCGRS485: - if (copy_to_user((void __user *) arg, - &(to_uart_omap_port(port)->rs485), - sizeof(rs485conf))) - return -EFAULT; - break; - - default: - return -ENOIOCTLCMD; - } return 0; } - static struct uart_ops serial_omap_pops = { .tx_empty = serial_omap_tx_empty, .set_mctrl = serial_omap_set_mctrl, @@ -1453,7 +1428,6 @@ static struct uart_ops serial_omap_pops = { .request_port = serial_omap_request_port, .config_port = serial_omap_config_port, .verify_port = serial_omap_verify_port, - .ioctl = serial_omap_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_put_char = serial_omap_poll_put_char, .poll_get_char = serial_omap_poll_get_char, @@ -1587,7 +1561,7 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) static int serial_omap_probe_rs485(struct uart_omap_port *up, struct device_node *np) { - struct serial_rs485 *rs485conf = &up->rs485; + struct serial_rs485 *rs485conf = &up->port.rs485; u32 rs485_delay[2]; enum of_gpio_flags flags; int ret; @@ -1709,6 +1683,7 @@ static int serial_omap_probe(struct platform_device *pdev) up->port.membase = base; up->port.flags = omap_up_info->flags; up->port.uartclk = omap_up_info->uartclk; + up->port.rs485_config = serial_omap_config_rs485; if (!up->port.uartclk) { up->port.uartclk = DEFAULT_CLK_SPEED; dev_warn(&pdev->dev, -- cgit v0.10.2 From c267d679cfd9699b9349fd714f63f6b4ee59dda2 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 09:22:58 +0100 Subject: drivers/max310: Use the rs485 functions on serial_core In order to unify all the rs485 ioctl handling Use the implementation of TIOC[GS]RS485 ioctl handling on serial_core. Reviewed-by: Alan Cox Cc: Jiri Slaby Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index ecb4667..182549f 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -877,53 +877,37 @@ static void max310x_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); } -static int max310x_ioctl(struct uart_port *port, unsigned int cmd, - unsigned long arg) +static int max310x_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485) { - struct serial_rs485 rs485; unsigned int val; - switch (cmd) { - case TIOCSRS485: - if (copy_from_user(&rs485, (void __user *)arg, sizeof(rs485))) - return -EFAULT; - if (rs485.delay_rts_before_send > 0x0f || - rs485.delay_rts_after_send > 0x0f) - return -ERANGE; - val = (rs485.delay_rts_before_send << 4) | - rs485.delay_rts_after_send; - max310x_port_write(port, MAX310X_HDPIXDELAY_REG, val); - if (rs485.flags & SER_RS485_ENABLED) { - max310x_port_update(port, MAX310X_MODE1_REG, - MAX310X_MODE1_TRNSCVCTRL_BIT, - MAX310X_MODE1_TRNSCVCTRL_BIT); - max310x_port_update(port, MAX310X_MODE2_REG, - MAX310X_MODE2_ECHOSUPR_BIT, - MAX310X_MODE2_ECHOSUPR_BIT); - } else { - max310x_port_update(port, MAX310X_MODE1_REG, - MAX310X_MODE1_TRNSCVCTRL_BIT, 0); - max310x_port_update(port, MAX310X_MODE2_REG, - MAX310X_MODE2_ECHOSUPR_BIT, 0); - } - return 0; - case TIOCGRS485: - memset(&rs485, 0, sizeof(rs485)); - val = max310x_port_read(port, MAX310X_MODE1_REG); - rs485.flags = (val & MAX310X_MODE1_TRNSCVCTRL_BIT) ? - SER_RS485_ENABLED : 0; - rs485.flags |= SER_RS485_RTS_ON_SEND; - val = max310x_port_read(port, MAX310X_HDPIXDELAY_REG); - rs485.delay_rts_before_send = val >> 4; - rs485.delay_rts_after_send = val & 0x0f; - if (copy_to_user((void __user *)arg, &rs485, sizeof(rs485))) - return -EFAULT; - return 0; - default: - break; + if (rs485->delay_rts_before_send > 0x0f || + rs485->delay_rts_after_send > 0x0f) + return -ERANGE; + + val = (rs485->delay_rts_before_send << 4) | + rs485->delay_rts_after_send; + max310x_port_write(port, MAX310X_HDPIXDELAY_REG, val); + if (rs485->flags & SER_RS485_ENABLED) { + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, + MAX310X_MODE1_TRNSCVCTRL_BIT); + max310x_port_update(port, MAX310X_MODE2_REG, + MAX310X_MODE2_ECHOSUPR_BIT, + MAX310X_MODE2_ECHOSUPR_BIT); + } else { + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, 0); + max310x_port_update(port, MAX310X_MODE2_REG, + MAX310X_MODE2_ECHOSUPR_BIT, 0); } - return -ENOIOCTLCMD; + rs485->flags &= SER_RS485_RTS_ON_SEND | SER_RS485_ENABLED; + memset(rs485->padding, 0, sizeof(rs485->padding)); + port->rs485 = *rs485; + + return 0; } static int max310x_startup(struct uart_port *port) @@ -1018,7 +1002,6 @@ static const struct uart_ops max310x_ops = { .release_port = max310x_null_void, .config_port = max310x_config_port, .verify_port = max310x_verify_port, - .ioctl = max310x_ioctl, }; static int __maybe_unused max310x_suspend(struct device *dev) @@ -1219,6 +1202,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, s->p[i].port.iobase = i * 0x20; s->p[i].port.membase = (void __iomem *)~0; s->p[i].port.uartclk = uartclk; + s->p[i].port.rs485_config = max310x_rs485_config; s->p[i].port.ops = &max310x_ops; /* Disable all interrupts */ max310x_port_write(&s->p[i].port, MAX310X_IRQEN_REG, 0); -- cgit v0.10.2 From a9c20a9cf3190a517b88d8e08d93157256f97673 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 09:22:59 +0100 Subject: serial_core: Remove call to driver-specific TIO[GS]RS485] Once there is no more handlers for TIOC[GS]RS485 there is no need to call the driver specific ioctl when the generic implementation is missing. Reviewed-by: Alan Cox Cc: Jiri Slaby Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index ab4db1d..99fcdba 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1155,9 +1155,6 @@ static int uart_get_icount(struct tty_struct *tty, static int uart_get_rs485_config(struct uart_port *port, struct serial_rs485 __user *rs485) { - if (!port->rs485_config) - return -ENOIOCTLCMD; - if (copy_to_user(rs485, &port->rs485, sizeof(port->rs485))) return -EFAULT; return 0; @@ -1255,7 +1252,12 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, * All these rely on hardware being present and need to be * protected against the tty being hung up. */ + switch (cmd) { + case TIOCSERGETLSR: /* Get line status register */ + ret = uart_get_lsr_info(tty, state, uarg); + break; + case TIOCGRS485: ret = uart_get_rs485_config(state->uart_port, uarg); break; @@ -1263,15 +1265,6 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, case TIOCSRS485: ret = uart_set_rs485_config(state->uart_port, uarg); break; - } - if (ret != -ENOIOCTLCMD) - goto out; - - switch (cmd) { - case TIOCSERGETLSR: /* Get line status register */ - ret = uart_get_lsr_info(tty, state, uarg); - break; - default: { struct uart_port *uport = state->uart_port; if (uport->ops->ioctl) -- cgit v0.10.2 From bd737f8738b7e15930aa7b47c94c28a8d83148ac Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Thu, 6 Nov 2014 09:23:00 +0100 Subject: tty/serial_core: Introduce lock mechanism for RS485 Introduce an homogeneous lock system between setting and using the rs485 data of the uart_port. This patch should not be split into multiple ones in order to avoid leaving the tree in an unstable state. Acked-by: Nicolas Ferre Reviewed-by: Alan Cox Suggested-by: Alan Cox Cc: Nicolas Ferre Cc: Jiri Slaby Cc: One Thousand Gnomes Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 80c4bfc..2cb0413 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -294,9 +294,6 @@ static int atmel_config_rs485(struct uart_port *port, { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned int mode; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); /* Disable interrupts */ UART_PUT_IDR(port, atmel_port->tx_done_mask); @@ -327,8 +324,6 @@ static int atmel_config_rs485(struct uart_port *port, /* Enable interrupts */ UART_PUT_IER(port, atmel_port->tx_done_mask); - spin_unlock_irqrestore(&port->lock, flags); - return 0; } @@ -2533,6 +2528,7 @@ static int atmel_serial_probe(struct platform_device *pdev) struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); void *data; int ret = -ENODEV; + bool rs485_enabled; BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); @@ -2580,6 +2576,8 @@ static int atmel_serial_probe(struct platform_device *pdev) port->rx_ring.buf = data; } + rs485_enabled = port->uart.rs485.flags & SER_RS485_ENABLED; + ret = uart_add_one_port(&atmel_uart, &port->uart); if (ret) goto err_add_port; @@ -2598,7 +2596,7 @@ static int atmel_serial_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, port); - if (port->uart.rs485.flags & SER_RS485_ENABLED) { + if (rs485_enabled) { UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL); UART_PUT_CR(&port->uart, ATMEL_US_RTSEN); } diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index d7be1f1..fdd5c7b 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -257,12 +257,12 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, mr2 |= MCFUART_MR2_TXCTS; } + spin_lock_irqsave(&port->lock, flags); if (port->rs485.flags & SER_RS485_ENABLED) { dev_dbg(port->dev, "Setting UART to RS485\n"); mr2 |= MCFUART_MR2_TXRTS; } - spin_lock_irqsave(&port->lock, flags); uart_update_timeout(port, termios->c_cflag, baud); writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); @@ -442,10 +442,8 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) static int mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) { struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - unsigned long flags; unsigned char mr1, mr2; - spin_lock_irqsave(&port->lock, flags); /* Get mode registers */ mr1 = readb(port->membase + MCFUART_UMR); mr2 = readb(port->membase + MCFUART_UMR); @@ -460,7 +458,6 @@ static int mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) writeb(mr1, port->membase + MCFUART_UMR); writeb(mr2, port->membase + MCFUART_UMR); port->rs485 = *rs485; - spin_unlock_irqrestore(&port->lock, flags); return 0; } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 050d1b5..e0bec06 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1360,12 +1360,10 @@ static int serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) { struct uart_omap_port *up = to_uart_omap_port(port); - unsigned long flags; unsigned int mode; int val; pm_runtime_get_sync(up->dev); - spin_lock_irqsave(&up->port.lock, flags); /* Disable interrupts from this port */ mode = up->ier; @@ -1401,7 +1399,6 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) serial_out(up, UART_OMAP_SCR, up->scr); } - spin_unlock_irqrestore(&up->port.lock, flags); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 99fcdba..5c8b8f5 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1155,8 +1155,16 @@ static int uart_get_icount(struct tty_struct *tty, static int uart_get_rs485_config(struct uart_port *port, struct serial_rs485 __user *rs485) { - if (copy_to_user(rs485, &port->rs485, sizeof(port->rs485))) + unsigned long flags; + struct serial_rs485 aux; + + spin_lock_irqsave(&port->lock, flags); + aux = port->rs485; + spin_unlock_irqrestore(&port->lock, flags); + + if (copy_to_user(rs485, &aux, sizeof(aux))) return -EFAULT; + return 0; } @@ -1165,6 +1173,7 @@ static int uart_set_rs485_config(struct uart_port *port, { struct serial_rs485 rs485; int ret; + unsigned long flags; if (!port->rs485_config) return -ENOIOCTLCMD; @@ -1172,7 +1181,9 @@ static int uart_set_rs485_config(struct uart_port *port, if (copy_from_user(&rs485, rs485_user, sizeof(*rs485_user))) return -EFAULT; + spin_lock_irqsave(&port->lock, flags); ret = port->rs485_config(port, &rs485); + spin_unlock_irqrestore(&port->lock, flags); if (ret) return ret; -- cgit v0.10.2 From 2dc98946d4d4398ab07d569c414c1f839907ea0b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Nov 2014 13:28:16 +0200 Subject: serial: 8250_dma: call serial8250_tx_dma unconditionally Since we have the same check inside the function we may drop it away in __dma_tx_complete(). Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 258430b..b16f9b1 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -21,6 +21,7 @@ static void __dma_tx_complete(void *param) struct uart_8250_dma *dma = p->dma; struct circ_buf *xmit = &p->port.state->xmit; unsigned long flags; + int ret; dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); @@ -36,15 +37,11 @@ static void __dma_tx_complete(void *param) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&p->port); - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) { - int ret; - - ret = serial8250_tx_dma(p); - if (ret) { - dma->tx_err = 1; - p->ier |= UART_IER_THRI; - serial_port_out(&p->port, UART_IER, p->ier); - } + ret = serial8250_tx_dma(p); + if (ret) { + dma->tx_err = 1; + p->ier |= UART_IER_THRI; + serial_port_out(&p->port, UART_IER, p->ier); } spin_unlock_irqrestore(&p->port.lock, flags); -- cgit v0.10.2 From b1835d238ad3dc6ad8fe23172b1995d1fb5bdd39 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Nov 2014 13:28:17 +0200 Subject: serial: 8250_dma: no need to set tx_err twice In the serial8250_tx_dma() the tx_err flag is set in case of error. Thus, there is no need to repeat this in __dma_tx_complete(). Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index b16f9b1..fcd7ac6 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -39,7 +39,6 @@ static void __dma_tx_complete(void *param) ret = serial8250_tx_dma(p); if (ret) { - dma->tx_err = 1; p->ier |= UART_IER_THRI; serial_port_out(&p->port, UART_IER, p->ier); } @@ -93,7 +92,6 @@ int serial8250_tx_dma(struct uart_8250_port *p) } dma->tx_running = 1; - desc->callback = __dma_tx_complete; desc->callback_param = p; -- cgit v0.10.2 From 1511316fbd1c690ad0a16324d88fb5ec45d6be36 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Thu, 6 Nov 2014 19:00:42 +0530 Subject: serial: MIPS: lantiq: use devm_iounmap instead of iounmap port->membase was allocated using devm_ioremap_nocache, so ideally we should unmap it using devm_iounmap. but it was using iounmap. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index 4675fe1..18c3844 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -497,8 +497,10 @@ lqasc_type(struct uart_port *port) static void lqasc_release_port(struct uart_port *port) { + struct platform_device *pdev = to_platform_device(port->dev); + if (port->flags & UPF_IOREMAP) { - iounmap(port->membase); + devm_iounmap(&pdev->dev, port->membase); port->membase = NULL; } } -- cgit v0.10.2 From d7685ca7c4f5d22dd9b045992e82c1c444a92187 Mon Sep 17 00:00:00 2001 From: Konrad Zapalowicz Date: Mon, 3 Nov 2014 19:52:37 +0100 Subject: drivers: serial: jsm: Add Classic board UART structure This commit adds the UART structure for the Digi Classic cards. This code comes from the staging/dgnc driver. Signed-off-by: Konrad Zapalowicz Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index af70134..9861639 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -222,7 +222,10 @@ struct jsm_channel { u8 ch_mostat; /* FEP output modem status */ u8 ch_mistat; /* FEP input modem status */ - struct neo_uart_struct __iomem *ch_neo_uart; /* Pointer to the "mapped" UART struct */ + /* Pointers to the "mapped" UART structs */ + struct neo_uart_struct __iomem *ch_neo_uart; /* NEO card */ + struct cls_uart_struct __iomem *ch_cls_uart; /* Classic card */ + u8 ch_cached_lsr; /* Cached value of the LSR register */ u8 *ch_rqueue; /* Our read queue buffer - malloc'ed */ @@ -254,6 +257,60 @@ struct jsm_channel { u64 ch_xoff_sends; /* Count of xoffs transmitted */ }; +/************************************************************************ + * Per channel/port Classic UART structures * + ************************************************************************ + * Base Structure Entries Usage Meanings to Host * + * * + * W = read write R = read only * + * U = Unused. * + ************************************************************************/ + +struct cls_uart_struct { + u8 txrx; /* WR RHR/THR - Holding Reg */ + u8 ier; /* WR IER - Interrupt Enable Reg */ + u8 isr_fcr; /* WR ISR/FCR - Interrupt Status Reg/Fifo Control Reg*/ + u8 lcr; /* WR LCR - Line Control Reg */ + u8 mcr; /* WR MCR - Modem Control Reg */ + u8 lsr; /* WR LSR - Line Status Reg */ + u8 msr; /* WR MSR - Modem Status Reg */ + u8 spr; /* WR SPR - Scratch Pad Reg */ +}; + +/* Where to read the interrupt register (8bits) */ +#define UART_CLASSIC_POLL_ADDR_OFFSET 0x40 + +#define UART_EXAR654_ENHANCED_REGISTER_SET 0xBF + +#define UART_16654_FCR_TXTRIGGER_8 0x0 +#define UART_16654_FCR_TXTRIGGER_16 0x10 +#define UART_16654_FCR_TXTRIGGER_32 0x20 +#define UART_16654_FCR_TXTRIGGER_56 0x30 + +#define UART_16654_FCR_RXTRIGGER_8 0x0 +#define UART_16654_FCR_RXTRIGGER_16 0x40 +#define UART_16654_FCR_RXTRIGGER_56 0x80 +#define UART_16654_FCR_RXTRIGGER_60 0xC0 + +#define UART_IIR_CTSRTS 0x20 /* Received CTS/RTS change of state */ +#define UART_IIR_RDI_TIMEOUT 0x0C /* Receiver data TIMEOUT */ + +/* + * These are the EXTENDED definitions for the Exar 654's Interrupt + * Enable Register. + */ +#define UART_EXAR654_EFR_ECB 0x10 /* Enhanced control bit */ +#define UART_EXAR654_EFR_IXON 0x2 /* Receiver compares Xon1/Xoff1 */ +#define UART_EXAR654_EFR_IXOFF 0x8 /* Transmit Xon1/Xoff1 */ +#define UART_EXAR654_EFR_RTSDTR 0x40 /* Auto RTS/DTR Flow Control Enable */ +#define UART_EXAR654_EFR_CTSDSR 0x80 /* Auto CTS/DSR Flow COntrol Enable */ + +#define UART_EXAR654_XOFF_DETECT 0x1 /* Indicates whether chip saw an incoming XOFF char */ +#define UART_EXAR654_XON_DETECT 0x2 /* Indicates whether chip saw an incoming XON char */ + +#define UART_EXAR654_IER_XOFF 0x20 /* Xoff Interrupt Enable */ +#define UART_EXAR654_IER_RTSDTR 0x40 /* Output Interrupt Enable */ +#define UART_EXAR654_IER_CTSDSR 0x80 /* Input Interrupt Enable */ /************************************************************************ * Per channel/port NEO UART structure * -- cgit v0.10.2 From 8a8ae62f8296760a2a1eee7009a1444c327603e0 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 6 Nov 2014 16:56:33 +0100 Subject: tty: warn on deprecated serial flags When somebody calls TIOCSSERIAL ioctl with serial flags to set one of * ASYNC_SESSION_LOCKOUT * ASYNC_PGRP_LOCKOUT * ASYNC_CALLOUT_NOHUP * ASYNC_AUTOPROBE nothing happens. We actually ignore the flags for over a decade at least (I checked 2.6.0). So start yelling at users who use those flags, that they shouldn't. Signed-off-by: Jiri Slaby Cc: Peter Hurley Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 01d45fd..7058858 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2760,6 +2760,24 @@ static int tty_tiocgicount(struct tty_struct *tty, void __user *arg) return 0; } +static void tty_warn_deprecated_flags(struct serial_struct __user *ss) +{ + static DEFINE_RATELIMIT_STATE(depr_flags, + DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + char comm[TASK_COMM_LEN]; + int flags; + + if (get_user(flags, &ss->flags)) + return; + + flags &= ASYNC_DEPRECATED; + + if (flags && __ratelimit(&depr_flags)) + pr_warning("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n", + __func__, get_task_comm(comm, current), flags); +} + /* * if pty, return the slave side (real_tty) * otherwise, return self @@ -2903,6 +2921,9 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } break; + case TIOCSSERIAL: + tty_warn_deprecated_flags(p); + break; } if (tty->ops->ioctl) { retval = tty->ops->ioctl(tty, cmd, arg); diff --git a/include/uapi/linux/tty_flags.h b/include/uapi/linux/tty_flags.h index d2bc4ff..fae4864 100644 --- a/include/uapi/linux/tty_flags.h +++ b/include/uapi/linux/tty_flags.h @@ -64,6 +64,8 @@ #define ASYNC_MAGIC_MULTIPLIER (1U << ASYNCB_MAGIC_MULTIPLIER) #define ASYNC_FLAGS ((1U << (ASYNCB_LAST_USER + 1)) - 1) +#define ASYNC_DEPRECATED (ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | \ + ASYNC_CALLOUT_NOHUP | ASYNC_AUTOPROBE) #define ASYNC_USR_MASK (ASYNC_SPD_MASK|ASYNC_CALLOUT_NOHUP| \ ASYNC_LOW_LATENCY) #define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) -- cgit v0.10.2 From c904375d4b15122c225c1073fd7a56bd541b0ce5 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Thu, 6 Nov 2014 21:10:17 +0100 Subject: serial: 8250_core: actually limit char reads to max_count In serial8250_rx_chars(), max_count is set to 256. Due to the post-decrement operator used in the while() condition, the maximum number of iterations actually 257. This is not a problem, but it is mildly surprising if you're debugging. Use pre-decrement instead. Signed-off-by: Frans Klaver Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 30522d6..d0d0c3b 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1501,7 +1501,7 @@ serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) ignore_char: lsr = serial_in(up, UART_LSR); - } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); + } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (--max_count > 0)); spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); spin_lock(&port->lock); -- cgit v0.10.2 From 95db1ccb15ae3703f751891ce1ee8f7734c21c96 Mon Sep 17 00:00:00 2001 From: Konrad Zapalowicz Date: Fri, 7 Nov 2014 00:05:32 +0100 Subject: drivers: serial: jsm: Add the Classic board implementation This commit adds the Digi Classic board implementation to the staging/jsm driver. The code here is taken from the staging/dgnc driver and modified to match the serial/jsm state. This work is mostly based on the changes that has been done to the code handling the Digi Neo cards with the inspiration coming from the diff between staging/dgnc and serial/jsm as well as the LKML history for the jsm_neo.c The code compiles now and has no sparse and checkpatch errors or warnings. Signed-off-by: Konrad Zapalowicz Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/Makefile b/drivers/tty/serial/jsm/Makefile index e46b6e0..705d1ff 100644 --- a/drivers/tty/serial/jsm/Makefile +++ b/drivers/tty/serial/jsm/Makefile @@ -4,5 +4,5 @@ obj-$(CONFIG_SERIAL_JSM) += jsm.o -jsm-objs := jsm_driver.o jsm_neo.o jsm_tty.o +jsm-objs := jsm_driver.o jsm_neo.o jsm_tty.o jsm_cls.o diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 9861639..3094282 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -189,7 +189,7 @@ struct jsm_board #define CH_LOOPBACK 0x2000 /* Channel is in lookback mode */ #define CH_BAUD0 0x08000 /* Used for checking B0 transitions */ -/* Our Read/Error/Write queue sizes */ +/* Our Read/Error queue sizes */ #define RQUEUEMASK 0x1FFF /* 8 K - 1 */ #define EQUEUEMASK 0x1FFF /* 8 K - 1 */ #define RQUEUESIZE (RQUEUEMASK + 1) @@ -431,6 +431,7 @@ struct neo_uart_struct { */ extern struct uart_driver jsm_uart_driver; extern struct board_ops jsm_neo_ops; +extern struct board_ops jsm_cls_ops; extern int jsm_debug; /************************************************************************* diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c new file mode 100644 index 0000000..b3a508d --- /dev/null +++ b/drivers/tty/serial/jsm/jsm_cls.c @@ -0,0 +1,990 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + */ + +#include /* For udelay */ +#include /* For read[bwl]/write[bwl] */ +#include /* For struct async_serial */ +#include /* For the various UART offsets */ +#include +#include + +#include "jsm.h" /* Driver main header file */ + +static struct { + unsigned int rate; + unsigned int cflag; +} baud_rates[] = { + { 921600, B921600 }, + { 460800, B460800 }, + { 230400, B230400 }, + { 115200, B115200 }, + { 57600, B57600 }, + { 38400, B38400 }, + { 19200, B19200 }, + { 9600, B9600 }, + { 4800, B4800 }, + { 2400, B2400 }, + { 1200, B1200 }, + { 600, B600 }, + { 300, B300 }, + { 200, B200 }, + { 150, B150 }, + { 134, B134 }, + { 110, B110 }, + { 75, B75 }, + { 50, B50 }, +}; + +static void cls_set_cts_flow_control(struct jsm_channel *ch) +{ + u8 lcrb = readb(&ch->ch_cls_uart->lcr); + u8 ier = readb(&ch->ch_cls_uart->ier); + u8 isr_fcr = 0; + + /* + * The Enhanced Register Set may only be accessed when + * the Line Control Register is set to 0xBFh. + */ + writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); + + isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); + + /* Turn on CTS flow control, turn off IXON flow control */ + isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_CTSDSR); + isr_fcr &= ~(UART_EXAR654_EFR_IXON); + + writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); + + /* Write old LCR value back out, which turns enhanced access off */ + writeb(lcrb, &ch->ch_cls_uart->lcr); + + /* + * Enable interrupts for CTS flow, turn off interrupts for + * received XOFF chars + */ + ier |= (UART_EXAR654_IER_CTSDSR); + ier &= ~(UART_EXAR654_IER_XOFF); + writeb(ier, &ch->ch_cls_uart->ier); + + /* Set the usual FIFO values */ + writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); + + writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 | + UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), + &ch->ch_cls_uart->isr_fcr); + + ch->ch_t_tlevel = 16; +} + +static void cls_set_ixon_flow_control(struct jsm_channel *ch) +{ + u8 lcrb = readb(&ch->ch_cls_uart->lcr); + u8 ier = readb(&ch->ch_cls_uart->ier); + u8 isr_fcr = 0; + + /* + * The Enhanced Register Set may only be accessed when + * the Line Control Register is set to 0xBFh. + */ + writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); + + isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); + + /* Turn on IXON flow control, turn off CTS flow control */ + isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXON); + isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR); + + writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); + + /* Now set our current start/stop chars while in enhanced mode */ + writeb(ch->ch_startc, &ch->ch_cls_uart->mcr); + writeb(0, &ch->ch_cls_uart->lsr); + writeb(ch->ch_stopc, &ch->ch_cls_uart->msr); + writeb(0, &ch->ch_cls_uart->spr); + + /* Write old LCR value back out, which turns enhanced access off */ + writeb(lcrb, &ch->ch_cls_uart->lcr); + + /* + * Disable interrupts for CTS flow, turn on interrupts for + * received XOFF chars + */ + ier &= ~(UART_EXAR654_IER_CTSDSR); + ier |= (UART_EXAR654_IER_XOFF); + writeb(ier, &ch->ch_cls_uart->ier); + + /* Set the usual FIFO values */ + writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); + + writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | + UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), + &ch->ch_cls_uart->isr_fcr); +} + +static void cls_set_no_output_flow_control(struct jsm_channel *ch) +{ + u8 lcrb = readb(&ch->ch_cls_uart->lcr); + u8 ier = readb(&ch->ch_cls_uart->ier); + u8 isr_fcr = 0; + + /* + * The Enhanced Register Set may only be accessed when + * the Line Control Register is set to 0xBFh. + */ + writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); + + isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); + + /* Turn off IXON flow control, turn off CTS flow control */ + isr_fcr |= (UART_EXAR654_EFR_ECB); + isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR | UART_EXAR654_EFR_IXON); + + writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); + + /* Write old LCR value back out, which turns enhanced access off */ + writeb(lcrb, &ch->ch_cls_uart->lcr); + + /* + * Disable interrupts for CTS flow, turn off interrupts for + * received XOFF chars + */ + ier &= ~(UART_EXAR654_IER_CTSDSR); + ier &= ~(UART_EXAR654_IER_XOFF); + writeb(ier, &ch->ch_cls_uart->ier); + + /* Set the usual FIFO values */ + writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); + + writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | + UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), + &ch->ch_cls_uart->isr_fcr); + + ch->ch_r_watermark = 0; + ch->ch_t_tlevel = 16; + ch->ch_r_tlevel = 16; +} + +static void cls_set_rts_flow_control(struct jsm_channel *ch) +{ + u8 lcrb = readb(&ch->ch_cls_uart->lcr); + u8 ier = readb(&ch->ch_cls_uart->ier); + u8 isr_fcr = 0; + + /* + * The Enhanced Register Set may only be accessed when + * the Line Control Register is set to 0xBFh. + */ + writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); + + isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); + + /* Turn on RTS flow control, turn off IXOFF flow control */ + isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_RTSDTR); + isr_fcr &= ~(UART_EXAR654_EFR_IXOFF); + + writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); + + /* Write old LCR value back out, which turns enhanced access off */ + writeb(lcrb, &ch->ch_cls_uart->lcr); + + /* Enable interrupts for RTS flow */ + ier |= (UART_EXAR654_IER_RTSDTR); + writeb(ier, &ch->ch_cls_uart->ier); + + /* Set the usual FIFO values */ + writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); + + writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 | + UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), + &ch->ch_cls_uart->isr_fcr); + + ch->ch_r_watermark = 4; + ch->ch_r_tlevel = 8; +} + +static void cls_set_ixoff_flow_control(struct jsm_channel *ch) +{ + u8 lcrb = readb(&ch->ch_cls_uart->lcr); + u8 ier = readb(&ch->ch_cls_uart->ier); + u8 isr_fcr = 0; + + /* + * The Enhanced Register Set may only be accessed when + * the Line Control Register is set to 0xBFh. + */ + writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); + + isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); + + /* Turn on IXOFF flow control, turn off RTS flow control */ + isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXOFF); + isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR); + + writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); + + /* Now set our current start/stop chars while in enhanced mode */ + writeb(ch->ch_startc, &ch->ch_cls_uart->mcr); + writeb(0, &ch->ch_cls_uart->lsr); + writeb(ch->ch_stopc, &ch->ch_cls_uart->msr); + writeb(0, &ch->ch_cls_uart->spr); + + /* Write old LCR value back out, which turns enhanced access off */ + writeb(lcrb, &ch->ch_cls_uart->lcr); + + /* Disable interrupts for RTS flow */ + ier &= ~(UART_EXAR654_IER_RTSDTR); + writeb(ier, &ch->ch_cls_uart->ier); + + /* Set the usual FIFO values */ + writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); + + writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | + UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), + &ch->ch_cls_uart->isr_fcr); +} + +static void cls_set_no_input_flow_control(struct jsm_channel *ch) +{ + u8 lcrb = readb(&ch->ch_cls_uart->lcr); + u8 ier = readb(&ch->ch_cls_uart->ier); + u8 isr_fcr = 0; + + /* + * The Enhanced Register Set may only be accessed when + * the Line Control Register is set to 0xBFh. + */ + writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); + + isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); + + /* Turn off IXOFF flow control, turn off RTS flow control */ + isr_fcr |= (UART_EXAR654_EFR_ECB); + isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR | UART_EXAR654_EFR_IXOFF); + + writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); + + /* Write old LCR value back out, which turns enhanced access off */ + writeb(lcrb, &ch->ch_cls_uart->lcr); + + /* Disable interrupts for RTS flow */ + ier &= ~(UART_EXAR654_IER_RTSDTR); + writeb(ier, &ch->ch_cls_uart->ier); + + /* Set the usual FIFO values */ + writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); + + writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | + UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), + &ch->ch_cls_uart->isr_fcr); + + ch->ch_t_tlevel = 16; + ch->ch_r_tlevel = 16; +} + +/* + * cls_clear_break. + * Determines whether its time to shut off break condition. + * + * No locks are assumed to be held when calling this function. + * channel lock is held and released in this function. + */ +static void cls_clear_break(struct jsm_channel *ch, int force) +{ + unsigned long lock_flags; + + spin_lock_irqsave(&ch->ch_lock, lock_flags); + + /* Turn break off, and unset some variables */ + if (ch->ch_flags & CH_BREAK_SENDING) { + u8 temp = readb(&ch->ch_cls_uart->lcr); + + writeb((temp & ~UART_LCR_SBC), &ch->ch_cls_uart->lcr); + + ch->ch_flags &= ~(CH_BREAK_SENDING); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, + "clear break Finishing UART_LCR_SBC! finished: %lx\n", + jiffies); + } + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); +} + +static void cls_disable_receiver(struct jsm_channel *ch) +{ + u8 tmp = readb(&ch->ch_cls_uart->ier); + + tmp &= ~(UART_IER_RDI); + writeb(tmp, &ch->ch_cls_uart->ier); +} + +static void cls_enable_receiver(struct jsm_channel *ch) +{ + u8 tmp = readb(&ch->ch_cls_uart->ier); + + tmp |= (UART_IER_RDI); + writeb(tmp, &ch->ch_cls_uart->ier); +} + +/* Make the UART raise any of the output signals we want up */ +static void cls_assert_modem_signals(struct jsm_channel *ch) +{ + if (!ch) + return; + + writeb(ch->ch_mostat, &ch->ch_cls_uart->mcr); +} + +static void cls_copy_data_from_uart_to_queue(struct jsm_channel *ch) +{ + int qleft = 0; + u8 linestatus = 0; + u8 error_mask = 0; + u16 head; + u16 tail; + unsigned long flags; + + if (!ch) + return; + + spin_lock_irqsave(&ch->ch_lock, flags); + + /* cache head and tail of queue */ + head = ch->ch_r_head & RQUEUEMASK; + tail = ch->ch_r_tail & RQUEUEMASK; + + /* Get our cached LSR */ + linestatus = ch->ch_cached_lsr; + ch->ch_cached_lsr = 0; + + /* Store how much space we have left in the queue */ + qleft = tail - head - 1; + if (qleft < 0) + qleft += RQUEUEMASK + 1; + + /* + * Create a mask to determine whether we should + * insert the character (if any) into our queue. + */ + if (ch->ch_c_iflag & IGNBRK) + error_mask |= UART_LSR_BI; + + while (1) { + /* + * Grab the linestatus register, we need to + * check to see if there is any data to read + */ + linestatus = readb(&ch->ch_cls_uart->lsr); + + /* Break out if there is no data to fetch */ + if (!(linestatus & UART_LSR_DR)) + break; + + /* + * Discard character if we are ignoring the error mask + * which in this case is the break signal. + */ + if (linestatus & error_mask) { + u8 discard; + + linestatus = 0; + discard = readb(&ch->ch_cls_uart->txrx); + continue; + } + + /* + * If our queue is full, we have no choice but to drop some + * data. The assumption is that HWFLOW or SWFLOW should have + * stopped things way way before we got to this point. + * + * I decided that I wanted to ditch the oldest data first, + * I hope thats okay with everyone? Yes? Good. + */ + while (qleft < 1) { + tail = (tail + 1) & RQUEUEMASK; + ch->ch_r_tail = tail; + ch->ch_err_overrun++; + qleft++; + } + + ch->ch_equeue[head] = linestatus & (UART_LSR_BI | UART_LSR_PE + | UART_LSR_FE); + ch->ch_rqueue[head] = readb(&ch->ch_cls_uart->txrx); + + qleft--; + + if (ch->ch_equeue[head] & UART_LSR_PE) + ch->ch_err_parity++; + if (ch->ch_equeue[head] & UART_LSR_BI) + ch->ch_err_break++; + if (ch->ch_equeue[head] & UART_LSR_FE) + ch->ch_err_frame++; + + /* Add to, and flip head if needed */ + head = (head + 1) & RQUEUEMASK; + ch->ch_rxcount++; + } + + /* + * Write new final heads to channel structure. + */ + ch->ch_r_head = head & RQUEUEMASK; + ch->ch_e_head = head & EQUEUEMASK; + + spin_unlock_irqrestore(&ch->ch_lock, flags); +} + +static void cls_copy_data_from_queue_to_uart(struct jsm_channel *ch) +{ + u16 tail; + int n; + int qlen; + u32 len_written = 0; + struct circ_buf *circ; + + if (!ch) + return; + + circ = &ch->uart_port.state->xmit; + + /* No data to write to the UART */ + if (uart_circ_empty(circ)) + return; + + /* If port is "stopped", don't send any data to the UART */ + if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING)) + return; + + /* We have to do it this way, because of the EXAR TXFIFO count bug. */ + if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM))) + return; + + n = 32; + + /* cache tail of queue */ + tail = circ->tail & (UART_XMIT_SIZE - 1); + qlen = uart_circ_chars_pending(circ); + + /* Find minimum of the FIFO space, versus queue length */ + n = min(n, qlen); + + while (n > 0) { + writeb(circ->buf[tail], &ch->ch_cls_uart->txrx); + tail = (tail + 1) & (UART_XMIT_SIZE - 1); + n--; + ch->ch_txcount++; + len_written++; + } + + /* Update the final tail */ + circ->tail = tail & (UART_XMIT_SIZE - 1); + + if (len_written > ch->ch_t_tlevel) + ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + + if (uart_circ_empty(circ)) + uart_write_wakeup(&ch->uart_port); +} + +static void cls_parse_modem(struct jsm_channel *ch, u8 signals) +{ + u8 msignals = signals; + + jsm_dbg(MSIGS, &ch->ch_bd->pci_dev, + "neo_parse_modem: port: %d msignals: %x\n", + ch->ch_portnum, msignals); + + /* + * Scrub off lower bits. + * They signify delta's, which I don't care about + * Keep DDCD and DDSR though + */ + msignals &= 0xf8; + + if (msignals & UART_MSR_DDCD) + uart_handle_dcd_change(&ch->uart_port, msignals & UART_MSR_DCD); + if (msignals & UART_MSR_DDSR) + uart_handle_dcd_change(&ch->uart_port, msignals & UART_MSR_CTS); + + if (msignals & UART_MSR_DCD) + ch->ch_mistat |= UART_MSR_DCD; + else + ch->ch_mistat &= ~UART_MSR_DCD; + + if (msignals & UART_MSR_DSR) + ch->ch_mistat |= UART_MSR_DSR; + else + ch->ch_mistat &= ~UART_MSR_DSR; + + if (msignals & UART_MSR_RI) + ch->ch_mistat |= UART_MSR_RI; + else + ch->ch_mistat &= ~UART_MSR_RI; + + if (msignals & UART_MSR_CTS) + ch->ch_mistat |= UART_MSR_CTS; + else + ch->ch_mistat &= ~UART_MSR_CTS; + + jsm_dbg(MSIGS, &ch->ch_bd->pci_dev, + "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n", + ch->ch_portnum, + !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR), + !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS), + !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS), + !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR), + !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI), + !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD)); +} + +/* Parse the ISR register for the specific port */ +static inline void cls_parse_isr(struct jsm_board *brd, uint port) +{ + struct jsm_channel *ch; + u8 isr = 0; + unsigned long flags; + + /* + * No need to verify board pointer, it was already + * verified in the interrupt routine. + */ + + if (port > brd->nasync) + return; + + ch = brd->channels[port]; + if (!ch) + return; + + /* Here we try to figure out what caused the interrupt to happen */ + while (1) { + isr = readb(&ch->ch_cls_uart->isr_fcr); + + /* Bail if no pending interrupt on port */ + if (isr & UART_IIR_NO_INT) + break; + + /* Receive Interrupt pending */ + if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) { + /* Read data from uart -> queue */ + cls_copy_data_from_uart_to_queue(ch); + jsm_check_queue_flow_control(ch); + } + + /* Transmit Hold register empty pending */ + if (isr & UART_IIR_THRI) { + /* Transfer data (if any) from Write Queue -> UART. */ + spin_lock_irqsave(&ch->ch_lock, flags); + ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + spin_unlock_irqrestore(&ch->ch_lock, flags); + cls_copy_data_from_queue_to_uart(ch); + } + + /* + * CTS/RTS change of state: + * Don't need to do anything, the cls_parse_modem + * below will grab the updated modem signals. + */ + + /* Parse any modem signal changes */ + cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr)); + } +} + +/* Channel lock MUST be held before calling this function! */ +static void cls_flush_uart_write(struct jsm_channel *ch) +{ + u8 tmp = 0; + u8 i = 0; + + if (!ch) + return; + + writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), + &ch->ch_cls_uart->isr_fcr); + + for (i = 0; i < 10; i++) { + /* Check to see if the UART feels it completely flushed FIFO */ + tmp = readb(&ch->ch_cls_uart->isr_fcr); + if (tmp & UART_FCR_CLEAR_XMIT) { + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, + "Still flushing TX UART... i: %d\n", i); + udelay(10); + } else + break; + } + + ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); +} + +/* Channel lock MUST be held before calling this function! */ +static void cls_flush_uart_read(struct jsm_channel *ch) +{ + if (!ch) + return; + + /* + * For complete POSIX compatibility, we should be purging the + * read FIFO in the UART here. + * + * However, clearing the read FIFO (UART_FCR_CLEAR_RCVR) also + * incorrectly flushes write data as well as just basically trashing the + * FIFO. + * + * Presumably, this is a bug in this UART. + */ + + udelay(10); +} + +static void cls_send_start_character(struct jsm_channel *ch) +{ + if (!ch) + return; + + if (ch->ch_startc != __DISABLED_CHAR) { + ch->ch_xon_sends++; + writeb(ch->ch_startc, &ch->ch_cls_uart->txrx); + } +} + +static void cls_send_stop_character(struct jsm_channel *ch) +{ + if (!ch) + return; + + if (ch->ch_stopc != __DISABLED_CHAR) { + ch->ch_xoff_sends++; + writeb(ch->ch_stopc, &ch->ch_cls_uart->txrx); + } +} + +/* + * cls_param() + * Send any/all changes to the line to the UART. + */ +static void cls_param(struct jsm_channel *ch) +{ + u8 lcr = 0; + u8 uart_lcr = 0; + u8 ier = 0; + u32 baud = 9600; + int quot = 0; + struct jsm_board *bd; + int i; + unsigned int cflag; + + bd = ch->ch_bd; + if (!bd) + return; + + /* + * If baud rate is zero, flush queues, and set mval to drop DTR. + */ + if ((ch->ch_c_cflag & (CBAUD)) == 0) { + ch->ch_r_head = 0; + ch->ch_r_tail = 0; + ch->ch_e_head = 0; + ch->ch_e_tail = 0; + + cls_flush_uart_write(ch); + cls_flush_uart_read(ch); + + /* The baudrate is B0 so all modem lines are to be dropped. */ + ch->ch_flags |= (CH_BAUD0); + ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR); + cls_assert_modem_signals(ch); + return; + } + + cflag = C_BAUD(ch->uart_port.state->port.tty); + baud = 9600; + for (i = 0; i < ARRAY_SIZE(baud_rates); i++) { + if (baud_rates[i].cflag == cflag) { + baud = baud_rates[i].rate; + break; + } + } + + if (ch->ch_flags & CH_BAUD0) + ch->ch_flags &= ~(CH_BAUD0); + + if (ch->ch_c_cflag & PARENB) + lcr |= UART_LCR_PARITY; + + if (!(ch->ch_c_cflag & PARODD)) + lcr |= UART_LCR_EPAR; + + /* + * Not all platforms support mark/space parity, + * so this will hide behind an ifdef. + */ +#ifdef CMSPAR + if (ch->ch_c_cflag & CMSPAR) + lcr |= UART_LCR_SPAR; +#endif + + if (ch->ch_c_cflag & CSTOPB) + lcr |= UART_LCR_STOP; + + switch (ch->ch_c_cflag & CSIZE) { + case CS5: + lcr |= UART_LCR_WLEN5; + break; + case CS6: + lcr |= UART_LCR_WLEN6; + break; + case CS7: + lcr |= UART_LCR_WLEN7; + break; + case CS8: + default: + lcr |= UART_LCR_WLEN8; + break; + } + + ier = readb(&ch->ch_cls_uart->ier); + uart_lcr = readb(&ch->ch_cls_uart->lcr); + + if (baud == 0) + baud = 9600; + + quot = ch->ch_bd->bd_dividend / baud; + + if (quot != 0) { + writeb(UART_LCR_DLAB, &ch->ch_cls_uart->lcr); + writeb((quot & 0xff), &ch->ch_cls_uart->txrx); + writeb((quot >> 8), &ch->ch_cls_uart->ier); + writeb(lcr, &ch->ch_cls_uart->lcr); + } + + if (uart_lcr != lcr) + writeb(lcr, &ch->ch_cls_uart->lcr); + + if (ch->ch_c_cflag & CREAD) + ier |= (UART_IER_RDI | UART_IER_RLSI); + + ier |= (UART_IER_THRI | UART_IER_MSI); + + writeb(ier, &ch->ch_cls_uart->ier); + + if (ch->ch_c_cflag & CRTSCTS) + cls_set_cts_flow_control(ch); + else if (ch->ch_c_iflag & IXON) { + /* + * If start/stop is set to disable, + * then we should disable flow control. + */ + if ((ch->ch_startc == __DISABLED_CHAR) || + (ch->ch_stopc == __DISABLED_CHAR)) + cls_set_no_output_flow_control(ch); + else + cls_set_ixon_flow_control(ch); + } else + cls_set_no_output_flow_control(ch); + + if (ch->ch_c_cflag & CRTSCTS) + cls_set_rts_flow_control(ch); + else if (ch->ch_c_iflag & IXOFF) { + /* + * If start/stop is set to disable, + * then we should disable flow control. + */ + if ((ch->ch_startc == __DISABLED_CHAR) || + (ch->ch_stopc == __DISABLED_CHAR)) + cls_set_no_input_flow_control(ch); + else + cls_set_ixoff_flow_control(ch); + } else + cls_set_no_input_flow_control(ch); + + cls_assert_modem_signals(ch); + + /* get current status of the modem signals now */ + cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr)); +} + +/* + * cls_intr() + * + * Classic specific interrupt handler. + */ +static irqreturn_t cls_intr(int irq, void *voidbrd) +{ + struct jsm_board *brd = voidbrd; + unsigned long lock_flags; + unsigned char uart_poll; + uint i = 0; + + /* Lock out the slow poller from running on this board. */ + spin_lock_irqsave(&brd->bd_intr_lock, lock_flags); + + /* + * Check the board's global interrupt offset to see if we + * acctually do have an interrupt pending on us. + */ + uart_poll = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET); + + jsm_dbg(INTR, &brd->pci_dev, "%s:%d uart_poll: %x\n", + __FILE__, __LINE__, uart_poll); + + if (!uart_poll) { + jsm_dbg(INTR, &brd->pci_dev, + "Kernel interrupted to me, but no pending interrupts...\n"); + spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); + return IRQ_NONE; + } + + /* At this point, we have at least SOMETHING to service, dig further. */ + + /* Parse each port to find out what caused the interrupt */ + for (i = 0; i < brd->nasync; i++) + cls_parse_isr(brd, i); + + spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); + + return IRQ_HANDLED; +} + +/* Inits UART */ +static void cls_uart_init(struct jsm_channel *ch) +{ + unsigned char lcrb = readb(&ch->ch_cls_uart->lcr); + unsigned char isr_fcr = 0; + + writeb(0, &ch->ch_cls_uart->ier); + + /* + * The Enhanced Register Set may only be accessed when + * the Line Control Register is set to 0xBFh. + */ + writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); + + isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); + + /* Turn on Enhanced/Extended controls */ + isr_fcr |= (UART_EXAR654_EFR_ECB); + + writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); + + /* Write old LCR value back out, which turns enhanced access off */ + writeb(lcrb, &ch->ch_cls_uart->lcr); + + /* Clear out UART and FIFO */ + readb(&ch->ch_cls_uart->txrx); + + writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), + &ch->ch_cls_uart->isr_fcr); + udelay(10); + + ch->ch_flags |= (CH_FIFO_ENABLED | CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + + readb(&ch->ch_cls_uart->lsr); + readb(&ch->ch_cls_uart->msr); +} + +/* + * Turns off UART. + */ +static void cls_uart_off(struct jsm_channel *ch) +{ + /* Stop all interrupts from accurring. */ + writeb(0, &ch->ch_cls_uart->ier); +} + +/* + * cls_get_uarts_bytes_left. + * Returns 0 is nothing left in the FIFO, returns 1 otherwise. + * + * The channel lock MUST be held by the calling function. + */ +static u32 cls_get_uart_bytes_left(struct jsm_channel *ch) +{ + u8 left = 0; + u8 lsr = readb(&ch->ch_cls_uart->lsr); + + /* Determine whether the Transmitter is empty or not */ + if (!(lsr & UART_LSR_TEMT)) + left = 1; + else { + ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + left = 0; + } + + return left; +} + +/* + * cls_send_break. + * Starts sending a break thru the UART. + * + * The channel lock MUST be held by the calling function. + */ +static void cls_send_break(struct jsm_channel *ch) +{ + /* Tell the UART to start sending the break */ + if (!(ch->ch_flags & CH_BREAK_SENDING)) { + u8 temp = readb(&ch->ch_cls_uart->lcr); + + writeb((temp | UART_LCR_SBC), &ch->ch_cls_uart->lcr); + ch->ch_flags |= (CH_BREAK_SENDING); + } +} + +/* + * cls_send_immediate_char. + * Sends a specific character as soon as possible to the UART, + * jumping over any bytes that might be in the write queue. + * + * The channel lock MUST be held by the calling function. + */ +static void cls_send_immediate_char(struct jsm_channel *ch, unsigned char c) +{ + writeb(c, &ch->ch_cls_uart->txrx); +} + +struct board_ops jsm_cls_ops = { + .intr = cls_intr, + .uart_init = cls_uart_init, + .uart_off = cls_uart_off, + .param = cls_param, + .assert_modem_signals = cls_assert_modem_signals, + .flush_uart_write = cls_flush_uart_write, + .flush_uart_read = cls_flush_uart_read, + .disable_receiver = cls_disable_receiver, + .enable_receiver = cls_enable_receiver, + .send_break = cls_send_break, + .clear_break = cls_clear_break, + .send_start_character = cls_send_start_character, + .send_stop_character = cls_send_stop_character, + .copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart, + .get_uart_bytes_left = cls_get_uart_bytes_left, + .send_immediate_char = cls_send_immediate_char +}; + -- cgit v0.10.2 From 03a8482c17dd0ae3251451d54cbbc03f930f21d5 Mon Sep 17 00:00:00 2001 From: Konrad Zapalowicz Date: Fri, 7 Nov 2014 00:05:33 +0100 Subject: drivers: serial: jsm: Enable support for Digi Classic adapters This commit enables support for the Digi Classic adapters line in the jsm driver. This means changes in: - device probing code - device cleanup code - driver description (Kconfig) The init/cleanup code is based on the staging/dgnc driver. Signed-off-by: Konrad Zapalowicz Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 7088373..e71a28b 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1029,11 +1029,11 @@ config SERIAL_VR41XX_CONSOLE a console on a serial port, say Y. Otherwise, say N. config SERIAL_JSM - tristate "Digi International NEO PCI Support" + tristate "Digi International NEO and Classic PCI Support" depends on PCI select SERIAL_CORE help - This is a driver for Digi International's Neo series + This is a driver for Digi International's Neo and Classic series of cards which provide multiple serial ports. You would need something like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. This driver diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 3094282..662523d 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -68,6 +68,10 @@ do { \ #define MAX_STOPS_SENT 5 /* Board ids */ +#define PCI_DEVICE_ID_CLASSIC_4 0x0028 +#define PCI_DEVICE_ID_CLASSIC_8 0x0029 +#define PCI_DEVICE_ID_CLASSIC_4_422 0x00D0 +#define PCI_DEVICE_ID_CLASSIC_8_422 0x00D1 #define PCI_DEVICE_ID_NEO_4 0x00B0 #define PCI_DEVICE_ID_NEO_1_422 0x00CC #define PCI_DEVICE_ID_NEO_1_422_485 0x00CD diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index d2885a7..0927ddf 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -31,8 +31,7 @@ #include "jsm.h" MODULE_AUTHOR("Digi International, http://www.digi.com"); -MODULE_DESCRIPTION("Driver for the Digi International " - "Neo PCI based product line"); +MODULE_DESCRIPTION("Driver for the Digi International Neo and Classic PCI based product line"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("jsm"); @@ -50,7 +49,7 @@ struct uart_driver jsm_uart_driver = { }; static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev, - pci_channel_state_t state); + pci_channel_state_t state); static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev); static void jsm_io_resume(struct pci_dev *pdev); @@ -68,7 +67,7 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) { int rc = 0; struct jsm_board *brd; - static int adapter_count = 0; + static int adapter_count; rc = pci_enable_device(pdev); if (rc) { @@ -82,10 +81,8 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_disable_device; } - brd = kzalloc(sizeof(struct jsm_board), GFP_KERNEL); + brd = kzalloc(sizeof(*brd), GFP_KERNEL); if (!brd) { - dev_err(&pdev->dev, - "memory allocation for board structure failed\n"); rc = -ENOMEM; goto out_release_regions; } @@ -95,7 +92,6 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) brd->pci_dev = pdev; switch (pdev->device) { - case PCI_DEVICE_ID_NEO_2DB9: case PCI_DEVICE_ID_NEO_2DB9PRI: case PCI_DEVICE_ID_NEO_2RJ45: @@ -104,6 +100,8 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) brd->maxports = 2; break; + case PCI_DEVICE_ID_CLASSIC_4: + case PCI_DEVICE_ID_CLASSIC_4_422: case PCI_DEVICE_ID_NEO_4: case PCIE_DEVICE_ID_NEO_4: case PCIE_DEVICE_ID_NEO_4RJ45: @@ -111,6 +109,8 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) brd->maxports = 4; break; + case PCI_DEVICE_ID_CLASSIC_8: + case PCI_DEVICE_ID_CLASSIC_8_422: case PCI_DEVICE_ID_DIGI_NEO_8: case PCIE_DEVICE_ID_NEO_8: case PCIE_DEVICE_ID_NEO_8RJ45: @@ -129,36 +129,109 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) brd->irq = pdev->irq; - jsm_dbg(INIT, &brd->pci_dev, "jsm_found_board - NEO adapter\n"); + switch (pdev->device) { + case PCI_DEVICE_ID_CLASSIC_4: + case PCI_DEVICE_ID_CLASSIC_4_422: + case PCI_DEVICE_ID_CLASSIC_8: + case PCI_DEVICE_ID_CLASSIC_8_422: + + jsm_dbg(INIT, &brd->pci_dev, + "jsm_found_board - Classic adapter\n"); + + /* + * For PCI ClassicBoards + * PCI Local Address (.i.e. "resource" number) space + * 0 PLX Memory Mapped Config + * 1 PLX I/O Mapped Config + * 2 I/O Mapped UARTs and Status + * 3 Memory Mapped VPD + * 4 Memory Mapped UARTs and Status + */ + + /* Get the PCI Base Address Registers */ + brd->membase = pci_resource_start(pdev, 4); + brd->membase_end = pci_resource_end(pdev, 4); + + if (brd->membase & 0x1) + brd->membase &= ~0x3; + else + brd->membase &= ~0xF; + + brd->iobase = pci_resource_start(pdev, 1); + brd->iobase_end = pci_resource_end(pdev, 1); + brd->iobase = ((unsigned int)(brd->iobase)) & 0xFFFE; + + /* Assign the board_ops struct */ + brd->bd_ops = &jsm_cls_ops; + + brd->bd_uart_offset = 0x8; + brd->bd_dividend = 921600; + + brd->re_map_membase = ioremap(brd->membase, + pci_resource_len(pdev, 4)); + if (!brd->re_map_membase) { + dev_err(&pdev->dev, + "Card has no PCI Memory resources, failing board.\n"); + rc = -ENOMEM; + goto out_kfree_brd; + } - /* get the PCI Base Address Registers */ - brd->membase = pci_resource_start(pdev, 0); - brd->membase_end = pci_resource_end(pdev, 0); + /* + * Enable Local Interrupt 1 (0x1), + * Local Interrupt 1 Polarity Active high (0x2), + * Enable PCI interrupt (0x43) + */ + outb(0x43, brd->iobase + 0x4c); - if (brd->membase & 1) - brd->membase &= ~3; - else - brd->membase &= ~15; + break; - /* Assign the board_ops struct */ - brd->bd_ops = &jsm_neo_ops; + case PCI_DEVICE_ID_NEO_2DB9: + case PCI_DEVICE_ID_NEO_2DB9PRI: + case PCI_DEVICE_ID_NEO_2RJ45: + case PCI_DEVICE_ID_NEO_2RJ45PRI: + case PCI_DEVICE_ID_NEO_2_422_485: + case PCI_DEVICE_ID_NEO_4: + case PCIE_DEVICE_ID_NEO_4: + case PCIE_DEVICE_ID_NEO_4RJ45: + case PCIE_DEVICE_ID_NEO_4_IBM: + case PCI_DEVICE_ID_DIGI_NEO_8: + case PCIE_DEVICE_ID_NEO_8: + case PCIE_DEVICE_ID_NEO_8RJ45: - brd->bd_uart_offset = 0x200; - brd->bd_dividend = 921600; + jsm_dbg(INIT, &brd->pci_dev, "jsm_found_board - NEO adapter\n"); - brd->re_map_membase = ioremap(brd->membase, pci_resource_len(pdev, 0)); - if (!brd->re_map_membase) { - dev_err(&pdev->dev, - "card has no PCI Memory resources, " - "failing board.\n"); - rc = -ENOMEM; - goto out_kfree_brd; + /* get the PCI Base Address Registers */ + brd->membase = pci_resource_start(pdev, 0); + brd->membase_end = pci_resource_end(pdev, 0); + + if (brd->membase & 1) + brd->membase &= ~0x3; + else + brd->membase &= ~0xF; + + /* Assign the board_ops struct */ + brd->bd_ops = &jsm_neo_ops; + + brd->bd_uart_offset = 0x200; + brd->bd_dividend = 921600; + + brd->re_map_membase = ioremap(brd->membase, + pci_resource_len(pdev, 0)); + if (!brd->re_map_membase) { + dev_err(&pdev->dev, + "Card has no PCI Memory resources, failing board.\n"); + rc = -ENOMEM; + goto out_kfree_brd; + } + + break; + default: + return -ENXIO; } - rc = request_irq(brd->irq, brd->bd_ops->intr, - IRQF_SHARED, "JSM", brd); + rc = request_irq(brd->irq, brd->bd_ops->intr, IRQF_SHARED, "JSM", brd); if (rc) { - printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq); + dev_warn(&pdev->dev, "Failed to hook IRQ %d\n", brd->irq); goto out_iounmap; } @@ -178,7 +251,7 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) } /* Log the information about the board */ - dev_info(&pdev->dev, "board %d: Digi Neo (rev %d), irq %d\n", + dev_info(&pdev->dev, "board %d: Digi Classic/Neo (rev %d), irq %d\n", adapter_count, brd->rev, brd->irq); pci_set_drvdata(pdev, brd); @@ -205,6 +278,18 @@ static void jsm_remove_one(struct pci_dev *pdev) struct jsm_board *brd = pci_get_drvdata(pdev); int i = 0; + switch (pdev->device) { + case PCI_DEVICE_ID_CLASSIC_4: + case PCI_DEVICE_ID_CLASSIC_4_422: + case PCI_DEVICE_ID_CLASSIC_8: + case PCI_DEVICE_ID_CLASSIC_8_422: + /* Tell card not to interrupt anymore. */ + outb(0x0, brd->iobase + 0x4c); + break; + default: + break; + } + jsm_remove_uart_port(brd); free_irq(brd->irq, brd); @@ -239,6 +324,10 @@ static struct pci_device_id jsm_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4), 0, 0, 11 }, { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4RJ45), 0, 0, 12 }, { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_8RJ45), 0, 0, 13 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_CLASSIC_4), 0, 0, 14 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_CLASSIC_4_422), 0, 0, 15 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_CLASSIC_8), 0, 0, 16 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_CLASSIC_8_422), 0, 0, 17 }, { 0, } }; MODULE_DEVICE_TABLE(pci, jsm_pci_tbl); diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 8814680..4259e5a 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -425,6 +425,8 @@ int jsm_tty_init(struct jsm_board *brd) if (brd->bd_uart_offset == 0x200) ch->ch_neo_uart = vaddr + (brd->bd_uart_offset * i); + else + ch->ch_cls_uart = vaddr + (brd->bd_uart_offset * i); ch->ch_bd = brd; ch->ch_portnum = i; -- cgit v0.10.2 From ad5708f67e0f5aa78915715c660b4f2ca9130279 Mon Sep 17 00:00:00 2001 From: Konrad Zapalowicz Date: Fri, 7 Nov 2014 00:05:35 +0100 Subject: drivers: serial: jsm: Remove FSF address from the file documentation/header This commit removes the address of Free Software Foundation from each of the mentioned file in order to suppress the checkpatch warning. Signed-off-by: Konrad Zapalowicz Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 662523d..72ed5c1 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -13,11 +13,6 @@ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 * Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - * * Contact Information: * Scott H Kilau * Wendy Xiong diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c index b3a508d..4cb9275 100644 --- a/drivers/tty/serial/jsm/jsm_cls.c +++ b/drivers/tty/serial/jsm/jsm_cls.c @@ -12,11 +12,6 @@ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! * * This is shared code between Digi's CVS archive and the diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 0927ddf..efbd87a 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -13,11 +13,6 @@ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 * Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - * * Contact Information: * Scott H Kilau * Wendy Xiong diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index dfaf488..dc2cd90 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -13,11 +13,6 @@ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 * Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - * * Contact Information: * Scott H Kilau * Wendy Xiong diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 4259e5a..c60e454 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -13,11 +13,6 @@ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 * Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - * * Contact Information: * Scott H Kilau * Ananda Venkatarman -- cgit v0.10.2 From 4ae612a3260336872e4f858d2d226a3721ccfd89 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 7 Nov 2014 00:23:13 -0200 Subject: serial: fsl_lpuart: Remove unneeded check for 'res' 'res' will be automatically checked inside devm_ioremap_resource(), so there is no need to explicitly perform a NULL check. Signed-off-by: Fabio Estevam Acked-by: Jingchang Lu Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 4e25772..73d939c 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1786,15 +1786,13 @@ static int lpuart_probe(struct platform_device *pdev) } sport->port.line = ret; sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart"); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - sport->port.mapbase = res->start; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sport->port.membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(sport->port.membase)) return PTR_ERR(sport->port.membase); + sport->port.mapbase = res->start; sport->port.dev = &pdev->dev; sport->port.type = PORT_LPUART; sport->port.iotype = UPIO_MEM; -- cgit v0.10.2 From 144c29ed5fa361b890523358d791e6481d55e5a6 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 7 Nov 2014 00:23:14 -0200 Subject: serial: fsl_lpuart: Remove unneeded registration message There is no real value in displaying "serial: Freescale lpuart driver" in every boot. The uart_register_driver() can fail and even so the "serial: Freescale lpuart driver" will be displayed, which is not really helpful. This is particularly annoying when booting multi_v7_defconfig kernel on a SoC that is not a Vybrid/Layerscape and even though this message gets displayed. Signed-off-by: Fabio Estevam Acked-by: Jingchang Lu Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 73d939c..4f58077 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1896,11 +1896,8 @@ static struct platform_driver lpuart_driver = { static int __init lpuart_serial_init(void) { - int ret; - - pr_info("serial: Freescale lpuart driver\n"); + int ret = uart_register_driver(&lpuart_reg); - ret = uart_register_driver(&lpuart_reg); if (ret) return ret; -- cgit v0.10.2 From 4c0be45b18a3bd5c215589c4b320648e35024f6e Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 7 Nov 2014 14:14:35 +0000 Subject: serial: pl011: Allocate TX DMA buffer from DMA capable memory Allocating with __GFP_DMA avoids the need for bounce buffers Signed-off-by: Andrew Jackson Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 83c8f72..8654049 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -988,7 +988,7 @@ static void pl011_dma_startup(struct uart_amba_port *uap) if (!uap->dmatx.chan) return; - uap->dmatx.buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL); + uap->dmatx.buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL | __GFP_DMA); if (!uap->dmatx.buf) { dev_err(uap->port.dev, "no memory for DMA TX buffer\n"); uap->port.fifosize = uap->fifosize; -- cgit v0.10.2 From e2a545a6aee935e44db08797f7155be38f3c18f9 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 7 Nov 2014 14:14:39 +0000 Subject: serial: pl011: Avoid assumptions about buffer size when refilling TX DMA The existing code assumed that PL011_DMA_BUFFER_SIZE == UART_XMIT_SIZE, which may not always be the case. This allows for these two being different sizes and not copying too much data. Signed-off-by: Andrew Jackson Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 8654049..6153a42 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -501,7 +501,11 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count); else { size_t first = UART_XMIT_SIZE - xmit->tail; - size_t second = xmit->head; + size_t second; + + if (first > count) + first = count; + second = count - first; memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], first); if (second) -- cgit v0.10.2 From c64be9231e089350fa55c7e03adc79e341ab8e89 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 7 Nov 2014 14:14:43 +0000 Subject: serial: pl011: Set length of DMA transfer The DMA engines on some systems require that the dma_length is set when using scatter gather lists. Signed-off-by: Andrew Jackson Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 6153a42..55dc00d 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -246,6 +246,7 @@ static int pl011_sgbuf_init(struct dma_chan *chan, struct pl011_sgbuf *sg, sg_set_page(&sg->sg, phys_to_page(dma_addr), PL011_DMA_BUFFER_SIZE, offset_in_page(dma_addr)); sg_dma_address(&sg->sg) = dma_addr; + sg_dma_len(&sg->sg) = PL011_DMA_BUFFER_SIZE; return 0; } -- cgit v0.10.2 From 2d3b7d6e7dc8785f9a6c0e4d38188dd1a0859d11 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 7 Nov 2014 14:14:47 +0000 Subject: serial: pl011: Don't enable RX DMA if residue processing not supported If the DMA engine doesn't support residue processing then the RX DMA handling won't work terribly well if polling is enabled. So, disable RX DMA if residue handling isn't available. Signed-off-by: Andrew Jackson Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 55dc00d..d984a97 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -322,7 +322,22 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port * .src_maxburst = uap->fifosize >> 2, .device_fc = false, }; + struct dma_slave_caps caps; + /* + * Some DMA controllers provide information on their capabilities. + * If the controller does, check for suitable residue processing + * otherwise assime all is well. + */ + if (0 == dma_get_slave_caps(chan, &caps)) { + if (caps.residue_granularity == + DMA_RESIDUE_GRANULARITY_DESCRIPTOR) { + dma_release_channel(chan); + dev_info(uap->port.dev, + "RX DMA disabled - no residue processing\n"); + return; + } + } dmaengine_slave_config(chan, &rx_conf); uap->dmarx.chan = chan; -- cgit v0.10.2 From 98267d33e2da8cd386212856a22f4a64b32834ab Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 7 Nov 2014 14:14:23 +0000 Subject: serial: pl011: Add device tree support for RX DMA polling Add equivalent attributes to those provided in the platform data for use when RX DMA is enabled. Signed-off-by: Andrew Jackson Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/pl011.txt b/Documentation/devicetree/bindings/serial/pl011.txt index 5d2e840..0e05340 100644 --- a/Documentation/devicetree/bindings/serial/pl011.txt +++ b/Documentation/devicetree/bindings/serial/pl011.txt @@ -6,12 +6,17 @@ Required properties: - interrupts: exactly one interrupt specifier Optional properties: -- pinctrl: When present, must have one state named "sleep" - and one state named "default" -- clocks: When present, must refer to exactly one clock named - "apb_pclk" -- dmas: When present, may have one or two dma channels. - The first one must be named "rx", the second one - must be named "tx". +- pinctrl: When present, must have one state named "sleep" + and one state named "default" +- clocks: When present, must refer to exactly one clock named + "apb_pclk" +- dmas: When present, may have one or two dma channels. + The first one must be named "rx", the second one + must be named "tx". +- auto-poll: Enables polling when using RX DMA. +- poll-rate-ms: Rate at which poll occurs when auto-poll is set, + default 100ms. +- poll-timeout-ms: Poll timeout when auto-poll is set, default + 3000ms. See also bindings/arm/primecell.txt diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index d984a97..8d94c19 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -341,6 +341,7 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port * dmaengine_slave_config(chan, &rx_conf); uap->dmarx.chan = chan; + uap->dmarx.auto_poll_rate = false; if (plat && plat->dma_rx_poll_enable) { /* Set poll rate if specified. */ if (plat->dma_rx_poll_rate) { @@ -361,9 +362,24 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port * plat->dma_rx_poll_timeout; else uap->dmarx.poll_timeout = 3000; - } else - uap->dmarx.auto_poll_rate = false; - + } else if (!plat && dev->of_node) { + uap->dmarx.auto_poll_rate = of_property_read_bool( + dev->of_node, "auto-poll"); + if (uap->dmarx.auto_poll_rate) { + u32 x; + + if (0 == of_property_read_u32(dev->of_node, + "poll-rate-ms", &x)) + uap->dmarx.poll_rate = x; + else + uap->dmarx.poll_rate = 100; + if (0 == of_property_read_u32(dev->of_node, + "poll-timeout-ms", &x)) + uap->dmarx.poll_timeout = x; + else + uap->dmarx.poll_timeout = 3000; + } + } dev_info(uap->port.dev, "DMA channel RX %s\n", dma_chan_name(uap->dmarx.chan)); } -- cgit v0.10.2 From e4c787dac21e50ac9ef53fac91b9182d4fede465 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Nov 2014 14:22:01 +0900 Subject: Revert "serial: of-serial: fetch line number from DT" This reverts commit 1bd8324535ec1ff44aef55c0e40b9e7d56b310fb. To quote Olof: This commit broke a whole lot of tegra boards in last night's -next here. In particular, I've been looking at tegra20-seaboard, which now doesn't boot with console any more. Breaking existing systems is bad. Reported-by: Olof Johansson Cc: Lucas Stach Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 22f06a6..fd00e25 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -102,10 +102,6 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (of_property_read_u32(np, "fifo-size", &prop) == 0) port->fifosize = prop; - /* Check for a fixed line number */ - if ((ret = of_alias_get_id(np, "serial")) >= 0) - port->line = ret; - port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { -- cgit v0.10.2 From 52c40fca1a80abaffd9f7315de568d89a2064734 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 11 Nov 2014 11:20:56 -0500 Subject: vt: Fix build breakage when !CONFIG_VT_CONSOLE Commit 68952076e9226cc23ebce66d3fc2fdb8b6c04c30 ("vt: Remove vt_get_kmsg_redirect() from uapi header") fails to compile if !CONFIG_VT_CONSOLE. Move macro definition for vt_get_kmsg_redirect() up with file-scope function declarations. Reported-by: Jim Davis Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 43c2c24..ed01365 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -156,6 +156,8 @@ static void console_callback(struct work_struct *ignored); static void blank_screen_t(unsigned long dummy); static void set_palette(struct vc_data *vc); +#define vt_get_kmsg_redirect() vt_kmsg_redirect(-1) + static int printable; /* Is console ready for printing? */ int default_utf8 = true; module_param(default_utf8, int, S_IRUGO | S_IWUSR); @@ -2513,8 +2515,6 @@ int vt_kmsg_redirect(int new) return kmsg_con; } -#define vt_get_kmsg_redirect() vt_kmsg_redirect(-1) - /* * Console on virtual terminal * -- cgit v0.10.2 From 7920408b0c8292831f064ea33f56aa307b221a2c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 14 Nov 2014 10:39:21 -0800 Subject: tty: serial: msm_serial: Don't require DT aliases If there isn't a DT alias then of_alias_get_id() will return -ENODEV. This will cause the msm_serial driver to fail probe, when we want to keep the previous behavior where we generated a dynamic line number at probe time. Restore this behavior by generating a dynamic id if the line number is still negative after checking for an alias or in the non-DT case looking at the .id field of the platform device. Reported-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 09364dd..d1bc6b6 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1046,14 +1046,14 @@ static int msm_serial_probe(struct platform_device *pdev) const struct of_device_id *id; int irq, line; - if (pdev->id == -1) - pdev->id = atomic_inc_return(&msm_uart_next_id) - 1; - if (pdev->dev.of_node) line = of_alias_get_id(pdev->dev.of_node, "serial"); else line = pdev->id; + if (line < 0) + line = atomic_inc_return(&msm_uart_next_id) - 1; + if (unlikely(line < 0 || line >= UART_NR)) return -ENXIO; -- cgit v0.10.2 From bbac3297c6712f061853e3e11d5e21aa4164968f Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Fri, 14 Nov 2014 14:07:15 -0800 Subject: tty: serial: msm_serial: document DT alias Update devicetree binding for msm_serial to reflect msm_serial_probe() getting line id (port number) from the serialN alias. Signed-off-by: Frank Rowand Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt b/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt index ffa5b78..a2114c2 100644 --- a/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt +++ b/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt @@ -27,27 +27,52 @@ Optional properties: - dmas: Should contain dma specifiers for transmit and receive channels - dma-names: Should contain "tx" for transmit and "rx" for receive channels +Note: Aliases may be defined to ensure the correct ordering of the UARTs. +The alias serialN will result in the UART being assigned port N. If any +serialN alias exists, then an alias must exist for each enabled UART. The +serialN aliases should be in a .dts file instead of in a .dtsi file. + Examples: -A uartdm v1.4 device with dma capabilities. - -serial@f991e000 { - compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; - reg = <0xf991e000 0x1000>; - interrupts = <0 108 0x0>; - clocks = <&blsp1_uart2_apps_cxc>, <&blsp1_ahb_cxc>; - clock-names = "core", "iface"; - dmas = <&dma0 0>, <&dma0 1>; - dma-names = "tx", "rx"; -}; - -A uartdm v1.3 device without dma capabilities and part of a GSBI complex. - -serial@19c40000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x19c40000 0x1000>, - <0x19c00000 0x1000>; - interrupts = <0 195 0x0>; - clocks = <&gsbi5_uart_cxc>, <&gsbi5_ahb_cxc>; - clock-names = "core", "iface"; -}; +- A uartdm v1.4 device with dma capabilities. + + serial@f991e000 { + compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; + reg = <0xf991e000 0x1000>; + interrupts = <0 108 0x0>; + clocks = <&blsp1_uart2_apps_cxc>, <&blsp1_ahb_cxc>; + clock-names = "core", "iface"; + dmas = <&dma0 0>, <&dma0 1>; + dma-names = "tx", "rx"; + }; + +- A uartdm v1.3 device without dma capabilities and part of a GSBI complex. + + serial@19c40000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x19c40000 0x1000>, + <0x19c00000 0x1000>; + interrupts = <0 195 0x0>; + clocks = <&gsbi5_uart_cxc>, <&gsbi5_ahb_cxc>; + clock-names = "core", "iface"; + }; + +- serialN alias. + + aliases { + serial0 = &uarta; + serial1 = &uartc; + serial2 = &uartb; + }; + + uarta: serial@12490000 { + status = "ok"; + }; + + uartb: serial@16340000 { + status = "ok"; + }; + + uartc: serial@1a240000 { + status = "ok"; + }; -- cgit v0.10.2 From c556522e89fb00ee1e1efd2e1d29ed092243cf66 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Fri, 14 Nov 2014 16:59:31 +0100 Subject: serial: sh-sci: Add device tree support for r8a7794 Simply document the new compat string. There appears to be no need for a driver update. Signed-off-by: Ulrich Hecht [geert: Reworded to match previous commits] Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index b355660..7b87eb2 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -20,6 +20,10 @@ Required properties: - "renesas,scifa-r8a7791" for R8A7791 (R-Car M2) SCIFA compatible UART. - "renesas,scifb-r8a7791" for R8A7791 (R-Car M2) SCIFB compatible UART. - "renesas,hscif-r8a7791" for R8A7791 (R-Car M2) HSCIF compatible UART. + - "renesas,scif-r8a7794" for R8A7794 (R-Car E2) SCIF compatible UART. + - "renesas,scifa-r8a7794" for R8A7794 (R-Car E2) SCIFA compatible UART. + - "renesas,scifb-r8a7794" for R8A7794 (R-Car E2) SCIFB compatible UART. + - "renesas,hscif-r8a7794" for R8A7794 (R-Car E2) HSCIF compatible UART. - "renesas,scif" for generic SCIF compatible UART. - "renesas,scifa" for generic SCIFA compatible UART. - "renesas,scifb" for generic SCIFB compatible UART. -- cgit v0.10.2 From 681b05f58f0cd13ea21ee46600a9ad679199214c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 14 Nov 2014 16:59:32 +0100 Subject: serial: sh-sci: Add device tree support for r7s72100 Simply document the new compat string (and keep the list sorted by SoC). There appears to be no need for a driver update. Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index 7b87eb2..ae73bb0 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -4,8 +4,7 @@ Required properties: - compatible: Must contain one of the following: - - "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART. - - "renesas,scifb-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFB compatible UART. + - "renesas,scif-r7s72100" for R7S72100 (RZ/A1H) SCIF compatible UART. - "renesas,scifa-r8a73a4" for R8A73A4 (R-Mobile APE6) SCIFA compatible UART. - "renesas,scifb-r8a73a4" for R8A73A4 (R-Mobile APE6) SCIFB compatible UART. - "renesas,scifa-r8a7740" for R8A7740 (R-Mobile A1) SCIFA compatible UART. @@ -24,6 +23,8 @@ Required properties: - "renesas,scifa-r8a7794" for R8A7794 (R-Car E2) SCIFA compatible UART. - "renesas,scifb-r8a7794" for R8A7794 (R-Car E2) SCIFB compatible UART. - "renesas,hscif-r8a7794" for R8A7794 (R-Car E2) HSCIF compatible UART. + - "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART. + - "renesas,scifb-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFB compatible UART. - "renesas,scif" for generic SCIF compatible UART. - "renesas,scifa" for generic SCIFA compatible UART. - "renesas,scifb" for generic SCIFB compatible UART. -- cgit v0.10.2 From f53297fb99c8d2381a94cbaacc3e81c0c5db4cfd Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Thu, 20 Nov 2014 07:58:27 +0900 Subject: serial: sh-sci: Change checking for error rate of HSCIF This changes negative values of error rate to be checked, because these values are valid as error rate. And this changes in the process of adopting a value close to 0. Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index eb17c71..fccebbd 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1812,9 +1812,6 @@ static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq, err = DIV_ROUND_CLOSEST(freq, ((br + 1) * bps * sr * (1 << (2 * c + 1)) / 1000)) - 1000; - if (err < 0) - continue; - /* Calc recv margin * M: Receive margin (%) * N: Ratio of bit rate to clock (N = sampling rate) @@ -1829,7 +1826,7 @@ static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq, */ recv_margin = abs((500 - DIV_ROUND_CLOSEST(1000, sr << 1)) / 10); - if (min_err > err) { + if (abs(min_err) > abs(err)) { min_err = err; recv_max_margin = recv_margin; } else if ((min_err == err) && -- cgit v0.10.2 From 333f4eb1ba46b2d75fb3dc46ebed13aa1d0c9639 Mon Sep 17 00:00:00 2001 From: Konrad Zapalowicz Date: Sun, 9 Nov 2014 02:22:16 +0100 Subject: serial: jsm: Remove unnecessary parameter from clear_break() The 'force' parameter to the {cls,neo}_send_break() function has been removed because it has not been used. The client to this API (the tty code) always called this function with only one value. Signed-off-by: Konrad Zapalowicz Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 72ed5c1..39b325b 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -120,7 +120,7 @@ struct board_ops { void (*disable_receiver) (struct jsm_channel *ch); void (*enable_receiver) (struct jsm_channel *ch); void (*send_break) (struct jsm_channel *ch); - void (*clear_break) (struct jsm_channel *ch, int); + void (*clear_break) (struct jsm_channel *ch); void (*send_start_character) (struct jsm_channel *ch); void (*send_stop_character) (struct jsm_channel *ch); void (*copy_data_from_queue_to_uart) (struct jsm_channel *ch); diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c index 4cb9275..3df9112 100644 --- a/drivers/tty/serial/jsm/jsm_cls.c +++ b/drivers/tty/serial/jsm/jsm_cls.c @@ -311,7 +311,7 @@ static void cls_set_no_input_flow_control(struct jsm_channel *ch) * No locks are assumed to be held when calling this function. * channel lock is held and released in this function. */ -static void cls_clear_break(struct jsm_channel *ch, int force) +static void cls_clear_break(struct jsm_channel *ch) { unsigned long lock_flags; diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index dc2cd90..b9faee7 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -689,7 +689,7 @@ static void neo_flush_uart_read(struct jsm_channel *ch) /* * No locks are assumed to be held when calling this function. */ -static void neo_clear_break(struct jsm_channel *ch, int force) +static void neo_clear_break(struct jsm_channel *ch) { unsigned long lock_flags; diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index c60e454..524e86a 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -188,7 +188,7 @@ static void jsm_tty_break(struct uart_port *port, int break_state) if (break_state == -1) channel->ch_bd->bd_ops->send_break(channel); else - channel->ch_bd->bd_ops->clear_break(channel, 0); + channel->ch_bd->bd_ops->clear_break(channel); spin_unlock_irqrestore(&port->lock, lock_flags); } -- cgit v0.10.2 From b6501dd86ff085a8687d157c016f46a34e46bf5b Mon Sep 17 00:00:00 2001 From: Konrad Zapalowicz Date: Sun, 9 Nov 2014 02:22:17 +0100 Subject: serial: jsm: Remove unnecessary if statement The flow of {neo,cls}_param() shows that at this stage the baud rate has a non-zero value. This fact makes the if clausule obsolete and acknowledges it's removal. Signed-off-by: Konrad Zapalowicz Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c index 3df9112..bfb0681 100644 --- a/drivers/tty/serial/jsm/jsm_cls.c +++ b/drivers/tty/serial/jsm/jsm_cls.c @@ -767,9 +767,6 @@ static void cls_param(struct jsm_channel *ch) ier = readb(&ch->ch_cls_uart->ier); uart_lcr = readb(&ch->ch_cls_uart->lcr); - if (baud == 0) - baud = 9600; - quot = ch->ch_bd->bd_dividend / baud; if (quot != 0) { diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index b9faee7..4b8196d 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -1037,9 +1037,6 @@ static void neo_param(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); uart_lcr = readb(&ch->ch_neo_uart->lcr); - if (baud == 0) - baud = 9600; - quot = ch->ch_bd->bd_dividend / baud; if (quot != 0) { -- cgit v0.10.2 From 245ae51cec684aa1a3e6e35afdf2a6c93d371b32 Mon Sep 17 00:00:00 2001 From: Konrad Zapalowicz Date: Sun, 9 Nov 2014 02:22:18 +0100 Subject: serial: jsm: Fix the alignment of the switch satement This commit fixes the alignment of the 'case's i the switch statement. Signed-off-by: Konrad Zapalowicz Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 4b8196d..5de6dffb 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -1019,19 +1019,19 @@ static void neo_param(struct jsm_channel *ch) lcr |= UART_LCR_STOP; switch (ch->ch_c_cflag & CSIZE) { - case CS5: - lcr |= UART_LCR_WLEN5; - break; - case CS6: - lcr |= UART_LCR_WLEN6; - break; - case CS7: - lcr |= UART_LCR_WLEN7; - break; - case CS8: - default: - lcr |= UART_LCR_WLEN8; + case CS5: + lcr |= UART_LCR_WLEN5; + break; + case CS6: + lcr |= UART_LCR_WLEN6; + break; + case CS7: + lcr |= UART_LCR_WLEN7; break; + case CS8: + default: + lcr |= UART_LCR_WLEN8; + break; } ier = readb(&ch->ch_neo_uart->ier); -- cgit v0.10.2 From 5b05e2cbf5b38dfb2410d23adc26703fd37f84f9 Mon Sep 17 00:00:00 2001 From: Konrad Zapalowicz Date: Sun, 9 Nov 2014 02:22:19 +0100 Subject: serial: jsm: Replace magic value with the proper define The changed function flushes the tx UART and the '4' corresponds to the UART_FCR_CLEAR_XMIT value. This commit replaces the magic number with this define. Signed-off-by: Konrad Zapalowicz Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 5de6dffb..7291c21 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -644,7 +644,7 @@ static void neo_flush_uart_write(struct jsm_channel *ch) /* Check to see if the UART feels it completely flushed the FIFO. */ tmp = readb(&ch->ch_neo_uart->isr_fcr); - if (tmp & 4) { + if (tmp & UART_FCR_CLEAR_XMIT) { jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "Still flushing TX UART... i: %d\n", i); udelay(10); -- cgit v0.10.2 From 483e91a503064c104326a6bb55d4505ff467cd92 Mon Sep 17 00:00:00 2001 From: Konrad Zapalowicz Date: Sun, 9 Nov 2014 02:22:20 +0100 Subject: serial: jsm: Fix unnecessary space before function ptr arguments This commit deals with the checkpatch warning "Unnecessary space before function pointer arguments". Signed-off-by: Konrad Zapalowicz Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 39b325b..0b79b87 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -111,21 +111,21 @@ struct jsm_channel; ************************************************************************/ struct board_ops { irq_handler_t intr; - void (*uart_init) (struct jsm_channel *ch); - void (*uart_off) (struct jsm_channel *ch); - void (*param) (struct jsm_channel *ch); - void (*assert_modem_signals) (struct jsm_channel *ch); - void (*flush_uart_write) (struct jsm_channel *ch); - void (*flush_uart_read) (struct jsm_channel *ch); - void (*disable_receiver) (struct jsm_channel *ch); - void (*enable_receiver) (struct jsm_channel *ch); - void (*send_break) (struct jsm_channel *ch); - void (*clear_break) (struct jsm_channel *ch); - void (*send_start_character) (struct jsm_channel *ch); - void (*send_stop_character) (struct jsm_channel *ch); - void (*copy_data_from_queue_to_uart) (struct jsm_channel *ch); - u32 (*get_uart_bytes_left) (struct jsm_channel *ch); - void (*send_immediate_char) (struct jsm_channel *ch, unsigned char); + void (*uart_init)(struct jsm_channel *ch); + void (*uart_off)(struct jsm_channel *ch); + void (*param)(struct jsm_channel *ch); + void (*assert_modem_signals)(struct jsm_channel *ch); + void (*flush_uart_write)(struct jsm_channel *ch); + void (*flush_uart_read)(struct jsm_channel *ch); + void (*disable_receiver)(struct jsm_channel *ch); + void (*enable_receiver)(struct jsm_channel *ch); + void (*send_break)(struct jsm_channel *ch); + void (*clear_break)(struct jsm_channel *ch); + void (*send_start_character)(struct jsm_channel *ch); + void (*send_stop_character)(struct jsm_channel *ch); + void (*copy_data_from_queue_to_uart)(struct jsm_channel *ch); + u32 (*get_uart_bytes_left)(struct jsm_channel *ch); + void (*send_immediate_char)(struct jsm_channel *ch, unsigned char); }; -- cgit v0.10.2 From e620e54884ceb983c38f979a22fd04ae0820c725 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 9 Nov 2014 22:46:35 -0800 Subject: tty: pr_warning->pr_warn and logging neatening Convert the pr_warning to the more common pr_warn. Other miscellanea: o Convert unusual PR_FMT define and uses to pr_fmt o Remove unnecessary OOM message o Fix grammar in an error message o Convert a pr_warning with a KERN_ERR to pr_err Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 4f485e8..9d29d7e 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -309,8 +309,8 @@ static int __init ehv_bc_console_init(void) * handle for udbg. */ if (stdout_bc != CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE) - pr_warning("ehv-bc: udbg handle %u is not the stdout handle\n", - CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE); + pr_warn("ehv-bc: udbg handle %u is not the stdout handle\n", + CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE); #endif /* add_preferred_console() must be called before register_console(), diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index 858291c..59ed783 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -249,7 +249,7 @@ static int lock_card(struct isi_board *card) spin_unlock_irqrestore(&card->card_lock, card->flags); msleep(10); } - pr_warning("Failed to lock Card (0x%lx)\n", card->base); + pr_warn("Failed to lock Card (0x%lx)\n", card->base); return 0; /* Failed to acquire the card! */ } @@ -378,13 +378,13 @@ static inline int __isicom_paranoia_check(struct isi_port const *port, char *name, const char *routine) { if (!port) { - pr_warning("Warning: bad isicom magic for dev %s in %s.\n", - name, routine); + pr_warn("Warning: bad isicom magic for dev %s in %s\n", + name, routine); return 1; } if (port->magic != ISICOM_MAGIC) { - pr_warning("Warning: NULL isicom port for dev %s in %s.\n", - name, routine); + pr_warn("Warning: NULL isicom port for dev %s in %s\n", + name, routine); return 1; } @@ -546,8 +546,8 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) byte_count = header & 0xff; if (channel + 1 > card->port_count) { - pr_warning("%s(0x%lx): %d(channel) > port_count.\n", - __func__, base, channel+1); + pr_warn("%s(0x%lx): %d(channel) > port_count\n", + __func__, base, channel + 1); outw(0x0000, base+0x04); /* enable interrupts */ spin_unlock(&card->card_lock); return IRQ_HANDLED; diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index d62d8da..984e1c0 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -517,14 +517,15 @@ static void sport_set_termios(struct uart_port *port, up->csize = 5; break; default: - pr_warning("requested word length not supported\n"); + pr_warn("requested word length not supported\n"); + break; } if (termios->c_cflag & CSTOPB) { up->stopb = 1; } if (termios->c_cflag & PARENB) { - pr_warning("PAREN bits is not supported yet\n"); + pr_warn("PAREN bit is not supported yet\n"); /* up->parib = 1; */ } diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index 445799d..e1f4fda 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -1371,7 +1371,7 @@ static void hsu_global_init(void) hsu->iolen = 0x1000; if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global"))) - pr_warning("HSU: error in request mem region\n"); + pr_warn("HSU: error in request mem region\n"); hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen); if (!hsu->reg) { diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 1504a14..77239d5 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -27,6 +27,8 @@ * interrupt for a low speed UART device */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #ifdef CONFIG_MAGIC_SYSRQ #define SUPPORT_SYSRQ #endif @@ -47,8 +49,6 @@ #include "mrst_max3110.h" -#define PR_FMT "mrst_max3110: " - #define UART_TX_NEEDED 1 #define CON_TX_NEEDED 2 #define BIT_IRQ_PENDING 3 @@ -127,8 +127,8 @@ static int max3110_out(struct uart_max3110 *max, const u16 out) *obuf = out; ret = max3110_write_then_read(max, obuf, ibuf, 2, 1); if (ret) { - pr_warning(PR_FMT "%s(): get err msg %d when sending 0x%x\n", - __func__, ret, out); + pr_warn("%s: get err msg %d when sending 0x%x\n", + __func__, ret, out); goto exit; } @@ -153,10 +153,8 @@ static int max3110_read_multi(struct uart_max3110 *max) blen = M3110_RX_FIFO_DEPTH * sizeof(u16); buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA); - if (!buf) { - pr_warning(PR_FMT "%s(): fail to alloc dma buffer\n", __func__); + if (!buf) return 0; - } /* tx/rx always have the same length */ obuf = buf; @@ -212,13 +210,13 @@ serial_m3110_con_setup(struct console *co, char *options) int parity = 'n'; int flow = 'n'; - pr_info(PR_FMT "setting up console\n"); + pr_info("setting up console\n"); if (co->index == -1) co->index = 0; if (!max) { - pr_err(PR_FMT "pmax is NULL, return"); + pr_err("pmax is NULL, return\n"); return -ENODEV; } @@ -296,8 +294,7 @@ static void send_circ_buf(struct uart_max3110 *max, ret = max3110_write_then_read(max, obuf, ibuf, blen, 0); if (ret) - pr_warning(PR_FMT "%s(): get err msg %d\n", - __func__, ret); + pr_warn("%s: get err msg %d\n", __func__, ret); receive_chars(max, ibuf, len); @@ -411,7 +408,7 @@ static int max3110_main_thread(void *_max) int ret = 0; struct circ_buf *xmit = &max->con_xmit; - pr_info(PR_FMT "start main thread\n"); + pr_info("start main thread\n"); do { wait_event_interruptible(*wq, @@ -455,7 +452,7 @@ static int max3110_read_thread(void *_max) { struct uart_max3110 *max = _max; - pr_info(PR_FMT "start read thread\n"); + pr_info("start read thread\n"); do { /* * If can't acquire the mutex, it means the main thread @@ -481,7 +478,7 @@ static int serial_m3110_startup(struct uart_port *port) int ret = 0; if (port->line != 0) { - pr_err(PR_FMT "uart port startup failed\n"); + pr_err("uart port startup failed\n"); return -1; } @@ -504,7 +501,7 @@ static int serial_m3110_startup(struct uart_port *port) if (IS_ERR(max->read_thread)) { ret = PTR_ERR(max->read_thread); max->read_thread = NULL; - pr_err(PR_FMT "Can't create read thread!\n"); + pr_err("Can't create read thread!\n"); return ret; } } diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index c039cfe..8a89f6e 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -924,7 +924,7 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) if (kbd->kbdmode != VC_UNICODE) { if (!up_flag) - pr_warning("keyboard mode must be unicode for braille patterns\n"); + pr_warn("keyboard mode must be unicode for braille patterns\n"); return; } @@ -1253,8 +1253,8 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) if (raw_mode && !hw_raw) if (emulate_raw(vc, keycode, !down << 7)) if (keycode < BTN_MISC && printk_ratelimit()) - pr_warning("can't emulate rawmode for keycode %d\n", - keycode); + pr_warn("can't emulate rawmode for keycode %d\n", + keycode); #ifdef CONFIG_SPARC if (keycode == KEY_A && sparc_l1_a_state) { diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index ed01365..f3fbbbc 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3855,8 +3855,8 @@ void do_unblank_screen(int leaving_gfx) return; if (!vc_cons_allocated(fg_console)) { /* impossible */ - pr_warning("unblank_screen: tty %d not allocated ??\n", - fg_console+1); + pr_warn("unblank_screen: tty %d not allocated ??\n", + fg_console + 1); return; } vc = vc_cons[fg_console].d; -- cgit v0.10.2 From 52bec4ed4ef83f1a14dbcfd1a97e35f77c6e261e Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Tue, 11 Nov 2014 20:44:58 +0800 Subject: serial: sirf: add a new uart type support in CSR A7DA SoC, uart6 located at BT module and it need multiple clock sources, so for "sirf,marco-bt-uart" compatible uarts, drivers take 3 clock sources and enable them. this patch also replaces clk_get by devm_clk_get function and fix DT binding document in which we missed to fix when we added marco platform in commit 909102db44f "serial: sirf: add support for Marco chip". Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/sirf-uart.txt b/Documentation/devicetree/bindings/serial/sirf-uart.txt index a2dfc65..3acdd96 100644 --- a/Documentation/devicetree/bindings/serial/sirf-uart.txt +++ b/Documentation/devicetree/bindings/serial/sirf-uart.txt @@ -1,7 +1,9 @@ * CSR SiRFprimaII/atlasVI Universal Synchronous Asynchronous Receiver/Transmitter * Required properties: -- compatible : Should be "sirf,prima2-uart" or "sirf, prima2-usp-uart" +- compatible : Should be "sirf,prima2-uart", "sirf, prima2-usp-uart", + "sirf,marco-uart" or "sirf,marco-bt-uart" which means + uart located in BT module and used for BT. - reg : Offset and length of the register set for the device - interrupts : Should contain uart interrupt - fifosize : Should define hardware rx/tx fifo size @@ -31,3 +33,15 @@ usp@b0090000 { rts-gpios = <&gpio 15 0>; cts-gpios = <&gpio 46 0>; }; + +for uart use in BT module, +uart6: uart@11000000 { + cell-index = <6>; + compatible = "sirf,marco-bt-uart", "sirf,marco-uart"; + reg = <0x11000000 0x1000>; + interrupts = <0 100 0>; + clocks = <&clks 138>, <&clks 140>, <&clks 141>; + clock-names = "uart", "general", "noc"; + fifosize = <128>; + status = "disabled"; +} diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 4102192..2f6c6b0 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1032,10 +1032,19 @@ static void sirfsoc_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - if (!state) + if (!state) { + if (sirfport->is_bt_uart) { + clk_prepare_enable(sirfport->clk_noc); + clk_prepare_enable(sirfport->clk_general); + } clk_prepare_enable(sirfport->clk); - else + } else { clk_disable_unprepare(sirfport->clk); + if (sirfport->is_bt_uart) { + clk_disable_unprepare(sirfport->clk_general); + clk_disable_unprepare(sirfport->clk_noc); + } + } } static int sirfsoc_uart_startup(struct uart_port *port) @@ -1378,12 +1387,26 @@ usp_no_flow_control: } port->irq = res->start; - sirfport->clk = clk_get(&pdev->dev, NULL); + sirfport->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sirfport->clk)) { ret = PTR_ERR(sirfport->clk); goto err; } port->uartclk = clk_get_rate(sirfport->clk); + if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-bt-uart")) { + sirfport->clk_general = devm_clk_get(&pdev->dev, "general"); + if (IS_ERR(sirfport->clk_general)) { + ret = PTR_ERR(sirfport->clk_general); + goto err; + } + sirfport->clk_noc = devm_clk_get(&pdev->dev, "noc"); + if (IS_ERR(sirfport->clk_noc)) { + ret = PTR_ERR(sirfport->clk_noc); + goto err; + } + sirfport->is_bt_uart = true; + } else + sirfport->is_bt_uart = false; port->ops = &sirfsoc_uart_ops; spin_lock_init(&port->lock); @@ -1392,7 +1415,7 @@ usp_no_flow_control: 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; + goto err; } sirfport->rx_dma_chan = dma_request_slave_channel(port->dev, "rx"); @@ -1421,8 +1444,6 @@ alloc_coherent_err: sirfport->rx_dma_items[j].xmit.buf, sirfport->rx_dma_items[j].dma_addr); dma_release_channel(sirfport->rx_dma_chan); -port_err: - clk_put(sirfport->clk); err: return ret; } @@ -1431,7 +1452,6 @@ static int sirfsoc_uart_remove(struct platform_device *pdev) { struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); struct uart_port *port = &sirfport->port; - clk_put(sirfport->clk); uart_remove_one_port(&sirfsoc_uart_drv, port); if (sirfport->rx_dma_chan) { int i; diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index 6a7ebf7..275d038 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -417,6 +417,10 @@ struct sirfsoc_uart_port { struct uart_port port; struct clk *clk; + /* UART6 for BT usage in A7DA platform need multi-clock source */ + bool is_bt_uart; + struct clk *clk_general; + struct clk *clk_noc; /* for SiRFmarco, there are SET/CLR for UART_INT_EN */ bool is_marco; struct sirfsoc_uart_register *uart_reg; -- cgit v0.10.2 From 6b1f40cf4840820051d69646af0b6503878cb1bc Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 10 Nov 2014 16:05:03 +0800 Subject: tty: serial: men_z135_uart: Add terminating entry for men_z135_ids The mcb_device_id table is supposed to be zero-terminated. Signed-off-by: Axel Lin Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index 30e9e60..517cd07 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -809,6 +809,7 @@ static void men_z135_remove(struct mcb_device *mdev) static const struct mcb_device_id men_z135_ids[] = { { .device = 0x87 }, + { } }; MODULE_DEVICE_TABLE(mcb, men_z135_ids); -- cgit v0.10.2 From 54178fe6b3443249ec2dcd6eec9f2c3ebcdc3142 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 12 Nov 2014 10:28:33 +0100 Subject: tty: serial: 8250: omap: line is unsigned, don't check < 0 Dan Carpenter reported: |drivers/tty/serial/8250/8250_omap.c:1025 omap8250_probe() |warn: unsigned 'up.port.line' is never less than zero. |1025 if (up.port.line < 0) { I (wrongly) assumed that line is an int and compiler didn't complain nor did sparse. Since of_alias_get_id() and pdev->id can get negative I check for the error via ret variable. Reported-by: Dan Carpenter Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 57a8b12..336602e 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1014,19 +1014,20 @@ static int omap8250_probe(struct platform_device *pdev) up.port.unthrottle = omap_8250_unthrottle; if (pdev->dev.of_node) { - up.port.line = of_alias_get_id(pdev->dev.of_node, "serial"); + ret = of_alias_get_id(pdev->dev.of_node, "serial"); + of_property_read_u32(pdev->dev.of_node, "clock-frequency", &up.port.uartclk); priv->wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1); } else { - up.port.line = pdev->id; + ret = pdev->id; } - - if (up.port.line < 0) { - dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", - up.port.line); - return -ENODEV; + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias/pdev id\n"); + return ret; } + up.port.line = ret; + if (!up.port.uartclk) { up.port.uartclk = DEFAULT_CLK_SPEED; dev_warn(&pdev->dev, -- cgit v0.10.2 From 3c59958d5868f77741930402dfcb27e9dc8968d2 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 12 Nov 2014 10:28:34 +0100 Subject: tty: serial: omap_serial: line is unsigned, don't check < 0 Dan Carpenter reported: |drivers/tty/serial/8250/8250_omap.c:1025 omap8250_probe() |warn: unsigned 'up.port.line' is never less than zero. |1025 if (up.port.line < 0) { Since of_alias_get_id() and pdev->id can get negative I check for the error via ret variable. Reported-by: Dan Carpenter Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index e0bec06..435478a 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1653,16 +1653,16 @@ static int serial_omap_probe(struct platform_device *pdev) up->port.ops = &serial_omap_pops; if (pdev->dev.of_node) - up->port.line = of_alias_get_id(pdev->dev.of_node, "serial"); + ret = of_alias_get_id(pdev->dev.of_node, "serial"); else - up->port.line = pdev->id; + ret = pdev->id; - if (up->port.line < 0) { + if (ret < 0) { dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", - up->port.line); - ret = -ENODEV; + ret); goto err_port_line; } + up->port.line = ret; if (up->port.line >= OMAP_MAX_HSUART_PORTS) { dev_err(&pdev->dev, "uart ID %d > MAX %d.\n", up->port.line, -- cgit v0.10.2 From b725680f4818e1244449f2db2e9892b26784ed16 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 12 Nov 2014 11:55:25 +0100 Subject: tty: bfin, define inlined structures struct bfin_serial_port contains structs circ_buf, timer_list, and work_struct directly (not pointers). This means that these structures have to be defined completely to be inlined in struct bfin_serial_port. So instead of struct declarations, define the structures by including proper headers. They were pulled in by linux/serial_core.h or others until now. But experimenting with circ_buf removal from serial_core, struct circ_buf becomes undefined in bfin_serial: In file included from arch/blackfin/kernel/debug-mmrs.c:21:0: arch/blackfin/include/asm/bfin_serial.h:44:18: error: field 'rx_dma_buf' has incomplete type Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman diff --git a/arch/blackfin/include/asm/bfin_serial.h b/arch/blackfin/include/asm/bfin_serial.h index 2d90d62..d00d732 100644 --- a/arch/blackfin/include/asm/bfin_serial.h +++ b/arch/blackfin/include/asm/bfin_serial.h @@ -9,8 +9,11 @@ #ifndef __BFIN_ASM_SERIAL_H__ #define __BFIN_ASM_SERIAL_H__ +#include #include #include +#include +#include #include #include @@ -25,10 +28,6 @@ # endif #endif -struct circ_buf; -struct timer_list; -struct work_struct; - struct bfin_serial_port { struct uart_port port; unsigned int old_status; -- cgit v0.10.2 From 93d94b37fd044674e4e495bed82a45c38bde865a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 12 Nov 2014 15:55:07 -0200 Subject: serial: imx: Fix warning when building 'allmodconfig' When CONFIG_SERIAL_IMX_CONSOLE=n and CONFIG_CONSOLE_POLL=y we get the following build warning: drivers/tty/serial/imx.c:306:13: warning: 'imx_port_ucrs_save' defined but not used [-Wunused-function] drivers/tty/serial/imx.c:315:13: warning: 'imx_port_ucrs_restore' defined but not used [-Wunused-function] imx_port_ucrs_save/restore are only used under CONFIG_SERIAL_IMX_CONSOLE, so their definitions should be also be protected only by CONFIG_SERIAL_IMX_CONSOLE. This was detected when building 'allmodconfig'. Reported-by: Olof's autobuilder Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 062e761..c53968e 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -302,7 +302,7 @@ static inline int is_imx6q_uart(struct imx_port *sport) /* * Save and restore functions for UCR1, UCR2 and UCR3 registers */ -#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_IMX_CONSOLE) +#if defined(CONFIG_SERIAL_IMX_CONSOLE) static void imx_port_ucrs_save(struct uart_port *port, struct imx_port_ucrs *ucr) { -- cgit v0.10.2 From bca2092d78970df3959462d2aae1941e6b24ebdf Mon Sep 17 00:00:00 2001 From: David Daney Date: Fri, 14 Nov 2014 17:26:19 +0300 Subject: serial: 8250_dw: Use 64-bit access for OCTEON. Although the existing code appears to work on most hardware, the hardware designers tell us that 8-bit access to the registers is not guaranteed to be reliable. Also the OCTEON simulation environments prohibit 8-bit accesses. For these reasons, we use __raw_readq/__raw_writeq for OCTEON. This code is protected with #ifdef CONFIG_64BIT so it still builds under configurations lacking readq/writeq. We can get rid of the #ifdef __BIG_ENDIAN, as under 64-bit accesses, OCTEON is byte order invariant. Signed-off-by: David Daney Signed-off-by: Aleksey Makarov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 18ff532..f4fd362f 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -122,13 +122,44 @@ static unsigned int dw8250_serial_in(struct uart_port *p, int offset) return dw8250_modify_msr(p, offset, value); } -/* Read Back (rb) version to ensure register access ording. */ -static void dw8250_serial_out_rb(struct uart_port *p, int offset, int value) +#ifdef CONFIG_64BIT +static unsigned int dw8250_serial_inq(struct uart_port *p, int offset) { - dw8250_serial_out(p, offset, value); - dw8250_serial_in(p, UART_LCR); + unsigned int value; + + value = (u8)__raw_readq(p->membase + (offset << p->regshift)); + + return dw8250_modify_msr(p, offset, value); } +static void dw8250_serial_outq(struct uart_port *p, int offset, int value) +{ + struct dw8250_data *d = p->private_data; + + if (offset == UART_MCR) + d->last_mcr = value; + + value &= 0xff; + __raw_writeq(value, p->membase + (offset << p->regshift)); + /* Read back to ensure register write ordering. */ + __raw_readq(p->membase + (UART_LCR << p->regshift)); + + /* Make sure LCR write wasn't ignored */ + if (offset == UART_LCR) { + int tries = 1000; + while (tries--) { + unsigned int lcr = p->serial_in(p, UART_LCR); + if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) + return; + dw8250_force_idle(p); + __raw_writeq(value & 0xff, + p->membase + (UART_LCR << p->regshift)); + } + dev_err(p->dev, "Couldn't set LCR to %d\n", value); + } +} +#endif /* CONFIG_64BIT */ + static void dw8250_serial_out32(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; @@ -260,21 +291,17 @@ static int dw8250_probe_of(struct uart_port *p, bool has_ucv = true; int id; +#ifdef CONFIG_64BIT if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { -#ifdef __BIG_ENDIAN - /* - * Low order bits of these 64-bit registers, when - * accessed as a byte, are 7 bytes further down in the - * address space in big endian mode. - */ - p->membase += 7; -#endif - p->serial_out = dw8250_serial_out_rb; + p->serial_in = dw8250_serial_inq; + p->serial_out = dw8250_serial_outq; p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; p->type = PORT_OCTEON; data->usr_reg = 0x27; has_ucv = false; - } else if (!of_property_read_u32(np, "reg-io-width", &val)) { + } else +#endif + if (!of_property_read_u32(np, "reg-io-width", &val)) { switch (val) { case 1: break; -- cgit v0.10.2 From f3006e44dd3dc0f2d0ce636585ed95d5b4ad6f35 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 12 Nov 2014 20:32:49 -0200 Subject: serial: mxs-auart: Fix mxs_auart_set_ldisc() Commit 732a84a037a4 ("serial: core: Pass termios to set_ldisc() notifications") changed the set_ldisc prototype. At the time of this commit the mxs_auart driver did not implement set_ldisc, so that's why it has not been converted. Adapt also mxs_auart_set_ldisc() so that the following build warning can be fixed: drivers/tty/serial/mxs-auart.c:962:2: warning: initialization from incompatible pointer type .set_ldisc = mxs_auart_set_ldisc, ^ drivers/tty/serial/mxs-auart.c:962:2: warning: (near initialization for 'mxs_auart_ops.set_ldisc') Signed-off-by: Fabio Estevam Tested-by: Janusz Uzycki Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 386b528..0262a6a 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -784,9 +784,10 @@ static void mxs_auart_settermios(struct uart_port *u, mxs_auart_disable_ms(u); } -static void mxs_auart_set_ldisc(struct uart_port *port, int new) +static void mxs_auart_set_ldisc(struct uart_port *port, + struct ktermios *termios) { - if (new == N_PPS) { + if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; mxs_auart_enable_ms(port); } else { -- cgit v0.10.2 From 08f937f4bcbe156773834935018c9bf7874a42f3 Mon Sep 17 00:00:00 2001 From: Janusz Uzycki Date: Fri, 14 Nov 2014 23:24:33 +0100 Subject: serial: mxs-auart: fix gpio change detection on interrupt mxs_auart_modem_status() did't detect gpio's state change because s->mctrl_prev was modified before by mctrl_gpio_get(). The patch introduces mctrl_temp variable to fix the bug. Signed-off-by: Janusz Uzycki Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 0262a6a..21b3b35 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -799,6 +799,7 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) { u32 istat; struct mxs_auart_port *s = context; + u32 mctrl_temp = s->mctrl_prev; u32 stat = readl(s->port.membase + AUART_STAT); istat = readl(s->port.membase + AUART_INTR); @@ -818,7 +819,7 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) irq == s->gpio_irq[UART_GPIO_DSR] || irq == s->gpio_irq[UART_GPIO_RI]) mxs_auart_modem_status(s, - mctrl_gpio_get(s->gpios, &s->mctrl_prev)); + mctrl_gpio_get(s->gpios, &mctrl_temp)); if (istat & AUART_INTR_CTSMIS) { if (CTS_AT_AUART() && s->ms_irq_enabled) -- cgit v0.10.2 From 2b310ec7934f69c6e9a49dd675f413e112b82e5c Mon Sep 17 00:00:00 2001 From: Janusz Uzycki Date: Tue, 18 Nov 2014 18:37:13 +0100 Subject: serial: mxs-auart: fix tx_empty against shift register tx_empty() should test whether both the transmitter fifo and shifter for the port is empty, ie. the whole last char was transmitted. The shift register is empty if AUART_STAT_BUSY is cleared. The patch fixes the function against the shift register. According to i.MX23 and i.MX28 Reference Manual: AUART_STAT_TXFE: TX FIFO or transmit holding register is empty. AUART_STAT_BUSY: AUART still transmits bits. The BUSY signal goes HIGH as soon as the data is written to the transmit FIFO (that is, the FIFO is non-empty) and remains asserted HIGH while data is being transmitted. BUSY is negated only when the transmit FIFO is empty, and the last character has been transmitted from the shift register, including the stop bits. Signed-off-by: Janusz Uzycki Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 21b3b35..b7a5aaa 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -913,10 +913,11 @@ static void mxs_auart_shutdown(struct uart_port *u) static unsigned int mxs_auart_tx_empty(struct uart_port *u) { - if (readl(u->membase + AUART_STAT) & AUART_STAT_TXFE) + if ((readl(u->membase + AUART_STAT) & + (AUART_STAT_TXFE | AUART_STAT_BUSY)) == AUART_STAT_TXFE) return TIOCSER_TEMT; - else - return 0; + + return 0; } static void mxs_auart_start_tx(struct uart_port *u) -- cgit v0.10.2 From cb8ee9f08c4abfd8744eabffc467c06795c835d9 Mon Sep 17 00:00:00 2001 From: Peter Hung Date: Wed, 19 Nov 2014 13:22:27 +0800 Subject: serial: Fix io address assign flow with Fintek PCI-to-UART Product The original driver fixed the io address with 0xe000+idx*8, but real io address assigned from BIOS is dynamically from read PCI configure space 0x24, 0x20, 0x1c. The Fintek F81504/F81508/F81512 maybe malfunction without this patch and malfunction surely when more then 1 PCI card. Signed-off-by: Peter Hung Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 0468e15..31feeb2 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1554,25 +1554,48 @@ static int pci_fintek_setup(struct serial_private *priv, unsigned long iobase; unsigned long ciobase = 0; u8 config_base; + u32 bar_data[3]; /* - * We are supposed to be able to read these from the PCI config space, - * but the values there don't seem to match what we need to use, so - * just use these hard-coded values for now, as they are correct. + * Find each UARTs offset in PCI configuraion space */ switch (idx) { - case 0: iobase = 0xe000; config_base = 0x40; break; - case 1: iobase = 0xe008; config_base = 0x48; break; - case 2: iobase = 0xe010; config_base = 0x50; break; - case 3: iobase = 0xe018; config_base = 0x58; break; - case 4: iobase = 0xe020; config_base = 0x60; break; - case 5: iobase = 0xe028; config_base = 0x68; break; - case 6: iobase = 0xe030; config_base = 0x70; break; - case 7: iobase = 0xe038; config_base = 0x78; break; - case 8: iobase = 0xe040; config_base = 0x80; break; - case 9: iobase = 0xe048; config_base = 0x88; break; - case 10: iobase = 0xe050; config_base = 0x90; break; - case 11: iobase = 0xe058; config_base = 0x98; break; + case 0: + config_base = 0x40; + break; + case 1: + config_base = 0x48; + break; + case 2: + config_base = 0x50; + break; + case 3: + config_base = 0x58; + break; + case 4: + config_base = 0x60; + break; + case 5: + config_base = 0x68; + break; + case 6: + config_base = 0x70; + break; + case 7: + config_base = 0x78; + break; + case 8: + config_base = 0x80; + break; + case 9: + config_base = 0x88; + break; + case 10: + config_base = 0x90; + break; + case 11: + config_base = 0x98; + break; default: /* Unknown number of ports, get out of here */ return -EINVAL; @@ -1583,6 +1606,14 @@ static int pci_fintek_setup(struct serial_private *priv, ciobase = (int)(base + (0x8 * idx)); } + /* Get the io address dispatch from the BIOS */ + pci_read_config_dword(pdev, 0x24, &bar_data[0]); + pci_read_config_dword(pdev, 0x20, &bar_data[1]); + pci_read_config_dword(pdev, 0x1c, &bar_data[2]); + + /* Calculate Real IO Port */ + iobase = (bar_data[idx/4] & 0xffffffe0) + (idx % 4) * 8; + dev_dbg(&pdev->dev, "%s: idx=%d iobase=0x%lx ciobase=0x%lx config_base=0x%2x\n", __func__, idx, iobase, ciobase, config_base); -- cgit v0.10.2 From f0c1e460452138df03cb7a7f0e7546b74950b783 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 18 Nov 2014 11:18:02 +0100 Subject: serial: tegra: clean up tty-flag assignments The tty break and error flags are not bit masks so do not to use bitwise OR when assigning them. Note that there is no functional change due to the if-else construct and flag having been initialised to zero (TTY_NORMAL). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 78a5cf6..48e6e41 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -319,16 +319,16 @@ static char tegra_uart_decode_rx_error(struct tegra_uart_port *tup, if (unlikely(lsr & TEGRA_UART_LSR_ANY)) { if (lsr & UART_LSR_OE) { /* Overrrun error */ - flag |= TTY_OVERRUN; + flag = TTY_OVERRUN; tup->uport.icount.overrun++; dev_err(tup->uport.dev, "Got overrun errors\n"); } else if (lsr & UART_LSR_PE) { /* Parity error */ - flag |= TTY_PARITY; + flag = TTY_PARITY; tup->uport.icount.parity++; dev_err(tup->uport.dev, "Got Parity errors\n"); } else if (lsr & UART_LSR_FE) { - flag |= TTY_FRAME; + flag = TTY_FRAME; tup->uport.icount.frame++; dev_err(tup->uport.dev, "Got frame errors\n"); } else if (lsr & UART_LSR_BI) { -- cgit v0.10.2 From ddcbad929510bcad640693fe3cf299632823f61c Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 20 Nov 2014 18:33:59 +0100 Subject: serial: icom: fix error return code Return a negative error code on failure. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier ret; expression e1,e2; @@ ( if (\(ret < 0\|ret != 0\)) { ... return ret; } | ret = 0 ) ... when != ret = e1 when != &ret *if(...) { ... when != ret = e2 when forall return ret; } // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index d4620fe..45fc323 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -1550,8 +1550,10 @@ static int icom_probe(struct pci_dev *dev, icom_adapter->base_addr = pci_ioremap_bar(dev, 0); - if (!icom_adapter->base_addr) + if (!icom_adapter->base_addr) { + retval = -ENOMEM; goto probe_exit1; + } /* save off irq and request irq line */ if ( (retval = request_irq(dev->irq, icom_interrupt, -- cgit v0.10.2 From 1ff383a4c3eda8893ec61b02831826e1b1f46b41 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 24 Nov 2014 07:56:21 +0100 Subject: serial: samsung: wait for transfer completion before clock disable This patch adds waiting until transmit buffer and shifter will be empty before clock disabling. Without this fix it's possible to have clock disabled while data was not transmited yet, which causes unproper state of TX line and problems in following data transfers. Cc: # v2.6.26+ Signed-off-by: Robert Baldyga Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 796c04c..d36b2bd 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -559,11 +559,15 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, unsigned int old) { struct s3c24xx_uart_port *ourport = to_ourport(port); + int timeout = 10000; ourport->pm_level = level; switch (level) { case 3: + while (--timeout && !s3c24xx_serial_txempty_nofifo(port)) + udelay(100); + if (!IS_ERR(ourport->baudclk)) clk_disable_unprepare(ourport->baudclk); -- cgit v0.10.2 From ef4aca704836920fe16f50e4d859e22162f6e8f7 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 24 Nov 2014 07:56:22 +0100 Subject: serial: samsung: fix style problems Split lines longer than 80 chars and remove unnecessary whitespaces. Signed-off-by: Robert Baldyga Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index d36b2bd..33a953e 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -199,12 +199,14 @@ static void s3c24xx_serial_stop_rx(struct uart_port *port) } } -static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port) +static inline struct s3c24xx_uart_info + *s3c24xx_port_to_info(struct uart_port *port) { return to_ourport(port)->info; } -static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port) +static inline struct s3c2410_uartcfg + *s3c24xx_port_to_cfg(struct uart_port *port) { struct s3c24xx_uart_port *ourport; @@ -311,14 +313,14 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag); - ignore_char: +ignore_char: continue; } spin_unlock_irqrestore(&port->lock, flags); tty_flip_buffer_push(&port->state->port); - out: +out: return IRQ_HANDLED; } @@ -368,7 +370,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) if (uart_circ_empty(xmit)) s3c24xx_serial_stop_tx(port); - out: +out: spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; } @@ -519,7 +521,7 @@ static int s3c24xx_serial_startup(struct uart_port *port) return ret; - err: +err: s3c24xx_serial_shutdown(port); return ret; } @@ -845,8 +847,8 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, */ port->read_status_mask = S3C2410_UERSTAT_OVERRUN; if (termios->c_iflag & INPCK) - port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY; - + port->read_status_mask |= S3C2410_UERSTAT_FRAME | + S3C2410_UERSTAT_PARITY; /* * Which character status flags should we ignore? */ @@ -973,10 +975,13 @@ static struct uart_driver s3c24xx_uart_drv = { .minor = S3C24XX_SERIAL_MINOR, }; -static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { +#define __PORT_LOCK_UNLOCKED(i) \ + __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[i].port.lock) +static struct s3c24xx_uart_port +s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { [0] = { .port = { - .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), + .lock = __PORT_LOCK_UNLOCKED(0), .iotype = UPIO_MEM, .uartclk = 0, .fifosize = 16, @@ -987,7 +992,7 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS }, [1] = { .port = { - .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock), + .lock = __PORT_LOCK_UNLOCKED(1), .iotype = UPIO_MEM, .uartclk = 0, .fifosize = 16, @@ -1000,7 +1005,7 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS [2] = { .port = { - .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock), + .lock = __PORT_LOCK_UNLOCKED(2), .iotype = UPIO_MEM, .uartclk = 0, .fifosize = 16, @@ -1013,7 +1018,7 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS #if CONFIG_SERIAL_SAMSUNG_UARTS > 3 [3] = { .port = { - .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), + .lock = __PORT_LOCK_UNLOCKED(3), .iotype = UPIO_MEM, .uartclk = 0, .fifosize = 16, @@ -1024,6 +1029,7 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS } #endif }; +#undef __PORT_LOCK_UNLOCKED /* s3c24xx_serial_resetport * @@ -1106,11 +1112,12 @@ static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, s3c24xx_serial_set_termios(uport, termios, NULL); } - exit: +exit: return 0; } -static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) +static inline int +s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) { port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition; @@ -1118,19 +1125,22 @@ static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port CPUFREQ_TRANSITION_NOTIFIER); } -static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) +static inline void +s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) { cpufreq_unregister_notifier(&port->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); } #else -static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) +static inline int +s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) { return 0; } -static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) +static inline void +s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) { } #endif -- cgit v0.10.2 From 2f1ba72d23a2fa0cdcb4f034a059ce97e6a17696 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 24 Nov 2014 07:56:23 +0100 Subject: serial: samsung: prefer to use fifosize from driver data If we have fifosize set in driver data we prefer to use it instead of default fifosize value (which is always 16). If there is defined fifosize for particular serial we prefer to use it, otherwise we use value from info, which is common for all serials on given platform. Signed-off-by: Robert Baldyga Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 33a953e..34db167 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1292,11 +1292,10 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) of_property_read_u32(np, "samsung,uart-fifosize", &ourport->port.fifosize); - if (!ourport->port.fifosize) { - ourport->port.fifosize = (ourport->info->fifosize) ? - ourport->info->fifosize : - ourport->drv_data->fifosize[index]; - } + if (ourport->drv_data->fifosize[index]) + ourport->port.fifosize = ourport->drv_data->fifosize[index]; + else if (ourport->info->fifosize) + ourport->port.fifosize = ourport->info->fifosize; probe_index++; -- cgit v0.10.2 From 57850a50c1f91c6136fe0b4b27301ebbe848b2ce Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 24 Nov 2014 07:56:24 +0100 Subject: serial: samsung: use port->fifosize instead of hardcoded values Hardcoded FIFO size can cause hardware performance limitation. Using real size value provides better FIFO usage. Signed-off-by: Robert Baldyga Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 34db167..7ed7677 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -239,7 +239,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) struct uart_port *port = &ourport->port; unsigned int ufcon, ch, flag, ufstat, uerstat; unsigned long flags; - int max_count = 64; + int max_count = port->fifosize; spin_lock_irqsave(&port->lock, flags); @@ -330,7 +330,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) struct uart_port *port = &ourport->port; struct circ_buf *xmit = &port->state->xmit; unsigned long flags; - int count = 256; + int count = port->fifosize; spin_lock_irqsave(&port->lock, flags); -- cgit v0.10.2 From 3ffb1a8193bead7bf4ef0fec2dae050c70e4c1c1 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 12 Nov 2014 12:53:59 -0800 Subject: serial: core: Add big-endian iotype Since most drivers interpret UPIO_MEM32 to mean "little-endian" and use readl/writel to access the registers, add a parallel UPIO_MEM32BE to request the use of big-endian MMIO accessors (ioread32be/iowrite32be). Signed-off-by: Kevin Cernekee Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 5c8b8f5..57ca61b 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2153,6 +2153,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) break; case UPIO_MEM: case UPIO_MEM32: + case UPIO_MEM32BE: case UPIO_AU: case UPIO_TSI: snprintf(address, sizeof(address), @@ -2796,6 +2797,7 @@ int uart_match_port(struct uart_port *port1, struct uart_port *port2) (port1->hub6 == port2->hub6); case UPIO_MEM: case UPIO_MEM32: + case UPIO_MEM32BE: case UPIO_AU: case UPIO_TSI: return (port1->mapbase == port2->mapbase); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 3231a43..057038c 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -142,12 +142,13 @@ struct uart_port { unsigned char iotype; /* io access style */ unsigned char unused1; -#define UPIO_PORT (0) -#define UPIO_HUB6 (1) -#define UPIO_MEM (2) -#define UPIO_MEM32 (3) -#define UPIO_AU (4) /* Au1x00 and RT288x type IO */ -#define UPIO_TSI (5) /* Tsi108/109 type IO */ +#define UPIO_PORT (0) /* 8b I/O port access */ +#define UPIO_HUB6 (1) /* Hub6 ISA card */ +#define UPIO_MEM (2) /* 8b MMIO access */ +#define UPIO_MEM32 (3) /* 32b little endian */ +#define UPIO_MEM32BE (4) /* 32b big endian */ +#define UPIO_AU (5) /* Au1x00 and RT288x type IO */ +#define UPIO_TSI (6) /* Tsi108/109 type IO */ unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ -- cgit v0.10.2 From 6fad18fa51392c9847a4e3a95134b2e57155e563 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Fri, 14 Nov 2014 19:00:41 +0100 Subject: serial: 8250: don't attempt a trylock if in sysrq Attempting to use SysRq via the 8250 serial port with spin lock debugging on on a uniprocessor system results in the following splat: SysRq : BUG: spinlock trylock failure on UP on CPU#0, swapper/0 lock: serial8250_ports+0x0/0x8c0, .magic: dead4ead, .owner: swapper/0, .owner_cpu: 0 CPU: 0 PID: 0 Comm: swapper Not tainted 3.18.0-rc4+ #37 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014 ffffffff8245ba00 ffffffff81628b28 ffffffff812c8d27 ffffffff81628b48 ffffffff8106812e ffffffff8245ba00 ffffffff814e22ed ffffffff81628b68 ffffffff810681a6 0000000000000000 0000000000000000 ffffffff81628b88 Call Trace: [] dump_stack+0x19/0x1b [] spin_dump+0x7e/0xd0 [] spin_bug+0x26/0x30 [] do_raw_spin_trylock+0x4c/0x60 [] _raw_spin_trylock+0x1d/0x60 [] serial8250_console_write+0x68/0x190 [] ? sprintf+0x40/0x50 [] call_console_drivers.constprop.11+0x9e/0xf0 [] console_unlock+0x3e6/0x490 [] vprintk_emit+0x275/0x530 [] printk+0x4d/0x4f [] __handle_sysrq+0x62/0x1b0 [] ? __handle_sysrq+0x5/0x1b0 [] handle_sysrq+0x26/0x30 [] serial8250_rx_chars+0x1d7/0x250 [] serial8250_handle_irq+0x7b/0x90 [] serial8250_default_handle_irq+0x23/0x30 [] serial8250_interrupt+0x63/0xe0 [] handle_irq_event_percpu+0x4e/0x200 [] handle_irq_event+0x41/0x70 [] ? handle_edge_irq+0x1e/0x110 [] handle_edge_irq+0x9e/0x110 [] handle_irq+0x22/0x40 [] do_IRQ+0x4e/0xf0 [] common_interrupt+0x6d/0x6d [] ? default_idle+0x1f/0xd0 [] ? default_idle+0x1d/0xd0 [] arch_cpu_idle+0xf/0x20 [] cpu_startup_entry+0x25b/0x360 [] rest_init+0xbe/0xd0 [] start_kernel+0x339/0x346 [] x86_64_start_reservations+0x2a/0x2c [] x86_64_start_kernel+0xf2/0xf6 HELP : loglevel(0-9) reboot(b) crash(c) show-all-locks(d) te... Before ebade5e833eda30 ("serial: 8250: Clean up the locking for -rt") this was handled by not even attempting to try the lock if port->sysrq, since it is known to be taken by the interrupt handler; see https://bugzilla.kernel.org/show_bug.cgi?id=6716#c1. Restore that behavior. Signed-off-by: Rabin Vincent Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index d0d0c3b..9515924 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3191,7 +3191,9 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) serial8250_rpm_get(up); - if (port->sysrq || oops_in_progress) + if (port->sysrq) + locked = 0; + else if (oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags); else spin_lock_irqsave(&port->lock, flags); -- cgit v0.10.2 From 962b0a884a4d18326596e0434d813c17d7f688ef Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 25 Nov 2014 12:58:50 -0800 Subject: Revert "serial: of-serial: fix up PM ops on no_console_suspend and port type" This reverts commit 513e438581020334e0345561adeeeaefa36701be. It's broken :( Cc: Jingchang Lu Cc: Joseph Lo Cc: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index fd00e25..0889070 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -9,7 +9,6 @@ * 2 of the License, or (at your option) any later version. * */ -#include #include #include #include @@ -249,48 +248,13 @@ static int of_platform_serial_remove(struct platform_device *ofdev) } #ifdef CONFIG_PM_SLEEP -#ifdef CONFIG_SERIAL_8250 -static void of_serial_suspend_8250(struct of_serial_info *info) -{ - struct uart_8250_port *port8250 = serial8250_get_port(info->line); - struct uart_port *port = &port8250->port; - - serial8250_suspend_port(info->line); - if (info->clk && (!uart_console(port) || console_suspend_enabled)) - clk_disable_unprepare(info->clk); -} - -static void of_serial_resume_8250(struct of_serial_info *info) -{ - struct uart_8250_port *port8250 = serial8250_get_port(info->line); - struct uart_port *port = &port8250->port; - - if (info->clk && (!uart_console(port) || console_suspend_enabled)) - clk_prepare_enable(info->clk); - - serial8250_resume_port(info->line); -} -#else -static inline void of_serial_suspend_8250(struct of_serial_info *info) -{ -} - -static inline void of_serial_resume_8250(struct of_serial_info *info) -{ -} -#endif - static int of_serial_suspend(struct device *dev) { struct of_serial_info *info = dev_get_drvdata(dev); - switch(info->type) { - case PORT_8250 ... PORT_MAX_8250: - of_serial_suspend_8250(info); - break; - default: - break; - } + serial8250_suspend_port(info->line); + if (info->clk) + clk_disable_unprepare(info->clk); return 0; } @@ -299,13 +263,10 @@ static int of_serial_resume(struct device *dev) { struct of_serial_info *info = dev_get_drvdata(dev); - switch(info->type) { - case PORT_8250 ... PORT_MAX_8250: - of_serial_resume_8250(info); - break; - default: - break; - } + if (info->clk) + clk_prepare_enable(info->clk); + + serial8250_resume_port(info->line); return 0; } -- cgit v0.10.2 From 261119f727a4c7cfc001eed0d94cea70e662433a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 25 Nov 2014 12:46:39 -0800 Subject: Revert "serial: of-serial: add PM suspend/resume support" This reverts commit 2dea53bf57783f243c892e99c10c6921e956aa7e. Turns out to be broken :( Cc: Jingchang Lu Cc: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 0889070..12c95cc 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -247,32 +247,6 @@ static int of_platform_serial_remove(struct platform_device *ofdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int of_serial_suspend(struct device *dev) -{ - struct of_serial_info *info = dev_get_drvdata(dev); - - serial8250_suspend_port(info->line); - if (info->clk) - clk_disable_unprepare(info->clk); - - return 0; -} - -static int of_serial_resume(struct device *dev) -{ - struct of_serial_info *info = dev_get_drvdata(dev); - - if (info->clk) - clk_prepare_enable(info->clk); - - serial8250_resume_port(info->line); - - return 0; -} -#endif -static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); - /* * A few common types, add more as needed. */ @@ -305,7 +279,6 @@ static struct platform_driver of_platform_serial_driver = { .name = "of_serial", .owner = THIS_MODULE, .of_match_table = of_platform_serial_table, - .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, .remove = of_platform_serial_remove, -- cgit v0.10.2 From 8ad3b1352693972b737607c7b9c89b56d45fea9b Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Tue, 11 Nov 2014 15:09:05 +0800 Subject: serial: of-serial: add PM suspend/resume support This adds suspend/resume support for the of-serial driver to provide power management support on devices attatched. The handling may vary since not every of_serial device is an 8250 port. Currently only 8250 port handling is added in the suspend/resume function based on the type switch. Signed-off-by: Jingchang Lu Acked-by: Arnd Bergmann Tested-by: Joseph Lo Reviewed-by: Peter Hurley Tested-by: Florina Fainelli Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 12c95cc..58830bc 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -9,6 +9,7 @@ * 2 of the License, or (at your option) any later version. * */ +#include #include #include #include @@ -247,6 +248,70 @@ static int of_platform_serial_remove(struct platform_device *ofdev) return 0; } +#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_SERIAL_8250 +static void of_serial_suspend_8250(struct of_serial_info *info) +{ + struct uart_8250_port *port8250 = serial8250_get_port(info->line); + struct uart_port *port = &port8250->port; + + serial8250_suspend_port(info->line); + if (info->clk && (!uart_console(port) || console_suspend_enabled)) + clk_disable_unprepare(info->clk); +} + +static void of_serial_resume_8250(struct of_serial_info *info) +{ + struct uart_8250_port *port8250 = serial8250_get_port(info->line); + struct uart_port *port = &port8250->port; + + if (info->clk && (!uart_console(port) || console_suspend_enabled)) + clk_prepare_enable(info->clk); + + serial8250_resume_port(info->line); +} +#else +static inline void of_serial_suspend_8250(struct of_serial_info *info) +{ +} + +static inline void of_serial_resume_8250(struct of_serial_info *info) +{ +} +#endif + +static int of_serial_suspend(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + switch (info->type) { + case PORT_8250 ... PORT_MAX_8250: + of_serial_suspend_8250(info); + break; + default: + break; + } + + return 0; +} + +static int of_serial_resume(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + switch (info->type) { + case PORT_8250 ... PORT_MAX_8250: + of_serial_resume_8250(info); + break; + default: + break; + } + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); + /* * A few common types, add more as needed. */ @@ -279,6 +344,7 @@ static struct platform_driver of_platform_serial_driver = { .name = "of_serial", .owner = THIS_MODULE, .of_match_table = of_platform_serial_table, + .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, .remove = of_platform_serial_remove, -- cgit v0.10.2 From 8bfbe2de769afda051c56aba5450391670e769fc Mon Sep 17 00:00:00 2001 From: Christian Riesch Date: Thu, 13 Nov 2014 05:53:26 +0100 Subject: n_tty: Fix read_buf race condition, increment read_head after pushing data Commit 19e2ad6a09f0c06dbca19c98e5f4584269d913dd ("n_tty: Remove overflow tests from receive_buf() path") moved the increment of read_head into the arguments list of read_buf_addr(). Function calls represent a sequence point in C. Therefore read_head is incremented before the character c is placed in the buffer. Since the circular read buffer is a lock-less design since commit 6d76bd2618535c581f1673047b8341fd291abc67 ("n_tty: Make N_TTY ldisc receive path lockless"), this creates a race condition that leads to communication errors. This patch modifies the code to increment read_head _after_ the data is placed in the buffer and thus fixes the race for non-SMP machines. To fix the problem for SMP machines, memory barriers must be added in a separate patch. Signed-off-by: Christian Riesch Cc: Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 8358daa..9e3b216 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -321,7 +321,8 @@ static void n_tty_check_unthrottle(struct tty_struct *tty) static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata) { - *read_buf_addr(ldata, ldata->read_head++) = c; + *read_buf_addr(ldata, ldata->read_head) = c; + ldata->read_head++; } /** -- cgit v0.10.2 From a211b1af1933a3b6019d985762f5237d1d4c4213 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 21 Nov 2014 13:42:29 +0100 Subject: tty: Deletion of unnecessary checks before two function calls The functions put_device() and tty_kref_put() test whether their argument is NULL and then return immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 7058858..4f35b43 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -167,8 +167,7 @@ void free_tty_struct(struct tty_struct *tty) { if (!tty) return; - if (tty->dev) - put_device(tty->dev); + put_device(tty->dev); kfree(tty->write_buf); tty->magic = 0xDEADDEAD; kfree(tty); @@ -1688,8 +1687,7 @@ static void release_tty(struct tty_struct *tty, int idx) tty->link->port->itty = NULL; cancel_work_sync(&tty->port->buf.work); - if (tty->link) - tty_kref_put(tty->link); + tty_kref_put(tty->link); tty_kref_put(tty); } diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 4d9abaa..40b3183 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -193,8 +193,7 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) unsigned long flags; spin_lock_irqsave(&port->lock, flags); - if (port->tty) - tty_kref_put(port->tty); + tty_kref_put(port->tty); port->tty = tty_kref_get(tty); spin_unlock_irqrestore(&port->lock, flags); } -- cgit v0.10.2 From 48f5cde566201f987125ec783f40dc84b69ce300 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 21 Nov 2014 12:40:32 +0100 Subject: tty-hvsi_lib: Deletion of an unnecessary check before the function call "tty_kref_put" The tty_kref_put() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index 7ae6c29..a270f04 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -405,8 +405,7 @@ void hvsilib_close(struct hvsi_priv *pv, struct hvc_struct *hp) hvsi_send_close(pv); } - if (pv->tty) - tty_kref_put(pv->tty); + tty_kref_put(pv->tty); pv->tty = NULL; } -- cgit v0.10.2 From 50d1e7d1077b276e8faa9eebf8b710edf31fdeea Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Wed, 26 Nov 2014 12:21:56 +0300 Subject: serial: pxa: hold port.lock when reporting modem line changes Both uart_handle_dcd_change() and uart_handle_cts_change() require a port lock to be held and will emit a warning when lockdep is enabled. Held corresponding lock. This fixes the following warnings: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at drivers/tty/serial/serial_core.c:2760 uart_handle_dcd_change+0xc8/0xf8() Modules linked in: CPU: 0 PID: 0 Comm: swapper Not tainted 3.18.0-rc5+ #26 [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (warn_slowpath_common+0x6c/0x8c) [] (warn_slowpath_common) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null) from [] (uart_handle_dcd_change+0xc8/0xf8) [] (uart_handle_dcd_change) from [] (serial_pxa_irq+0x268/0x3b0) [] (serial_pxa_irq) from [] (handle_irq_event_percpu+0x50/0x16c) [] (handle_irq_event_percpu) from [] (handle_irq_event+0x3c/0x5c) [] (handle_irq_event) from [] (handle_level_irq+0x94/0x118) [] (handle_level_irq) from [] (generic_handle_irq+0x20/0x30) [] (generic_handle_irq) from [] (__handle_domain_irq+0x38/0x70) [] (__handle_domain_irq) from [] (ichp_handle_irq+0x24/0x34) [] (ichp_handle_irq) from [] (__irq_svc+0x44/0x54) Exception stack(0xc07c7f58 to 0xc07c7fa0) 7f40: 00000001 00000001 7f60: 00000000 20000013 c07c6000 00000000 00000000 c07ce0a4 c07d7798 00000000 7f80: c07e8fb8 0000004c 00000000 c07c7fa0 c0044798 c0009f20 20000013 ffffffff [] (__irq_svc) from [] (arch_cpu_idle+0x28/0x38) [] (arch_cpu_idle) from [] (cpu_startup_entry+0x1b8/0x220) [] (cpu_startup_entry) from [] (start_kernel+0x39c/0x40c) ---[ end trace 4c1b7ae03f6d9d30 ]--- ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at drivers/tty/serial/serial_core.c:2791 uart_handle_cts_change+0xa0/0xdc() Modules linked in: CPU: 0 PID: 0 Comm: swapper Tainted: G W 3.18.0-rc5+ #26 [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (warn_slowpath_common+0x6c/0x8c) [] (warn_slowpath_common) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null) from [] (uart_handle_cts_change+0xa0/0xdc) [] (uart_handle_cts_change) from [] (serial_pxa_irq+0x258/0x3b0) [] (serial_pxa_irq) from [] (handle_irq_event_percpu+0x50/0x16c) [] (handle_irq_event_percpu) from [] (handle_irq_event+0x3c/0x5c) [] (handle_irq_event) from [] (handle_level_irq+0x94/0x118) [] (handle_level_irq) from [] (generic_handle_irq+0x20/0x30) [] (generic_handle_irq) from [] (__handle_domain_irq+0x38/0x70) [] (__handle_domain_irq) from [] (ichp_handle_irq+0x24/0x34) [] (ichp_handle_irq) from [] (__irq_svc+0x44/0x54) Exception stack(0xc07c7f58 to 0xc07c7fa0) 7f40: 00000001 00000001 7f60: 00000000 20000013 c07c6000 00000000 00000000 c07ce0a4 c07d7798 00000000 7f80: c07e8fb8 0000004c 00000000 c07c7fa0 c0044798 c0009f20 20000013 ffffffff [] (__irq_svc) from [] (arch_cpu_idle+0x28/0x38) [] (arch_cpu_idle) from [] (cpu_startup_entry+0x1b8/0x220) [] (cpu_startup_entry) from [] (start_kernel+0x39c/0x40c) ---[ end trace 4c1b7ae03f6d9d31 ]--- Signed-off-by: Dmitry Eremin-Solenikov Acked-by: Robert Jarzmik Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 21b7d8b..7788d53 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -223,6 +223,7 @@ static void serial_pxa_start_tx(struct uart_port *port) } } +/* should hold up->port.lock */ static inline void check_modem_status(struct uart_pxa_port *up) { int status; @@ -255,12 +256,14 @@ static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id) iir = serial_in(up, UART_IIR); if (iir & UART_IIR_NO_INT) return IRQ_NONE; + spin_lock(&up->port.lock); lsr = serial_in(up, UART_LSR); if (lsr & UART_LSR_DR) receive_chars(up, &lsr); check_modem_status(up); if (lsr & UART_LSR_THRE) transmit_chars(up); + spin_unlock(&up->port.lock); return IRQ_HANDLED; } -- cgit v0.10.2