From 42b6a1baa3ec18de2eb15baa250da6203eeb2d39 Mon Sep 17 00:00:00 2001 From: Randy Witt Date: Thu, 17 Oct 2013 16:56:47 -0400 Subject: serial_core: Don't re-initialize a previously initialized spinlock. The uart_set_options() code unconditionally initalizes the spinlock on the port. This can cause a deadlock in some situations. One instance that exposed the problem, was when writing to /sys/module/kgdboc/parameters/kgdboc to use ttyS0 when the console is already running on ttyS0. If the spinlock is re-initialized while the lock is held due to output to the console, there is a deadlock. Assume the spinlock is initialized if the port is a console. Signed-off-by: Randy Witt Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0f02351..ece2049 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1830,9 +1830,13 @@ uart_set_options(struct uart_port *port, struct console *co, /* * Ensure that the serial console lock is initialised * early. + * If this port is a console, then the spinlock is already + * initialised. */ - spin_lock_init(&port->lock); - lockdep_set_class(&port->lock, &port_lock_key); + if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) { + spin_lock_init(&port->lock); + lockdep_set_class(&port->lock, &port_lock_key); + } memset(&termios, 0, sizeof(struct ktermios)); -- cgit v0.10.2 From 8913a316e3f74b800c3a4e0d4c85cab26bbb9c53 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 26 Nov 2013 10:30:06 +0200 Subject: serial: 8250_dw: remove ACPI ifdef ACPI now provides stubs for the functions the driver uses. Signed-off-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 4658e3e..816ec88 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -272,7 +272,6 @@ static int dw8250_probe_of(struct uart_port *p, return 0; } -#ifdef CONFIG_ACPI static int dw8250_probe_acpi(struct uart_8250_port *up, struct dw8250_data *data) { @@ -300,13 +299,6 @@ static int dw8250_probe_acpi(struct uart_8250_port *up, return 0; } -#else -static inline int dw8250_probe_acpi(struct uart_8250_port *up, - struct dw8250_data *data) -{ - return -ENODEV; -} -#endif /* CONFIG_ACPI */ static int dw8250_probe(struct platform_device *pdev) { -- cgit v0.10.2 From 187094feeb77b5f49513f6a1cfe12749c080bd96 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 3 Nov 2013 18:38:12 +0200 Subject: serial: pl011: remove redundant early amba_ports declaration This early amba_ports declaration was introduced by commit c16d51a32 (amba pl011: workaround for uart registers lockup) for use in the pl011_lockup_wa() routine. This routine was later removed by commit 4fd0690bb (serial: pl011: implement workaround for CTS clear event issue). Signed-off-by: Baruch Siach Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 7203864..742c8bf 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -112,8 +112,6 @@ static struct vendor_data vendor_st = { .get_fifosize = get_fifosize_st, }; -static struct uart_amba_port *amba_ports[UART_NR]; - /* Deals with DMA transactions */ struct pl011_sgbuf { -- cgit v0.10.2 From 5c32d12378313e0096ea0d450462aa638a90fb6e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 2 Dec 2013 14:24:41 -0500 Subject: n_tty: Merge .receive_buf() flavors N_TTY's direct and flow-controlled flavors of the .receive_buf() method are nearly identical; fold together. 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 0f74945..cf22bef6 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1674,32 +1674,9 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, } } -static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - int room, n; - - down_read(&tty->termios_rwsem); - - while (1) { - room = receive_room(tty); - n = min(count, room); - if (!n) - break; - __receive_buf(tty, cp, fp, n); - cp += n; - if (fp) - fp += n; - count -= n; - } - - tty->receive_room = room; - n_tty_check_throttle(tty); - up_read(&tty->termios_rwsem); -} - -static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static int +n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count, int flow) { struct n_tty_data *ldata = tty->disc_data; int room, n, rcvd = 0; @@ -1710,7 +1687,7 @@ static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, room = receive_room(tty); n = min(count, room); if (!n) { - if (!room) + if (flow && !room) ldata->no_room = 1; break; } @@ -1729,6 +1706,18 @@ static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, return rcvd; } +static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + n_tty_receive_buf_common(tty, cp, fp, count, 0); +} + +static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + return n_tty_receive_buf_common(tty, cp, fp, count, 1); +} + int is_ignored(int sig) { return (sigismember(¤t->blocked, sig) || -- cgit v0.10.2 From eb3e4668bd9e0bbda592e830e889f137e44ec9e4 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 2 Dec 2013 14:24:42 -0500 Subject: n_tty: Un-inline slow-path n_tty_receive_char() Commit e60d27c4d8b33ba20896b76b6558f061bc6460ff, n_tty: Factor LNEXT processing from per-char i/o path, mistakenly inlined the non-inline alias, n_tty_receive_char(), for the inline function, n_tty_receive_char_inline(). As n_tty_receive_char() is intended for slow-path char processing only, un-inline it. 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 cf22bef6..8c20a52 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1417,7 +1417,7 @@ n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c) put_tty_queue(c, ldata); } -static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) +static void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { n_tty_receive_char_inline(tty, c); } -- cgit v0.10.2 From 8dc4b25d2325002f716fffe2e35c9af43639c60f Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 2 Dec 2013 14:24:43 -0500 Subject: n_tty: Un-inline slow-path n_tty_receive_char_closing() Although n_tty_receive_char_closing() only has one call-site, let the compiler inline instead. 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 8c20a52..582a05c 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1442,8 +1442,7 @@ n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c) put_tty_queue(c, ldata); } -static inline void -n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c) +static void n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c) { if (I_ISTRIP(tty)) c &= 0x7f; -- cgit v0.10.2 From 001ba923719bc555552ac4c9fe8df4d8c888df39 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 2 Dec 2013 14:24:44 -0500 Subject: n_tty: Refactor PARMRK doubling checks Perform PARMRK doubling checks explicitly; remove ternary idiom and local variable. 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 582a05c..649fd94 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1257,7 +1257,6 @@ static int n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) { struct n_tty_data *ldata = tty->disc_data; - int parmrk; if (I_IXON(tty)) { if (c == START_CHAR(tty)) { @@ -1342,8 +1341,6 @@ n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) } if ((c == EOL_CHAR(tty)) || (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { - parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) - ? 1 : 0; /* * XXX are EOL_CHAR and EOL2_CHAR echoed?!? */ @@ -1358,7 +1355,7 @@ n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) * XXX does PARMRK doubling happen for * EOL_CHAR and EOL2_CHAR? */ - if (parmrk) + if (c == (unsigned char) '\377' && I_PARMRK(tty)) put_tty_queue(c, ldata); handle_newline: @@ -1372,7 +1369,6 @@ handle_newline: } } - parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; if (L_ECHO(tty)) { finish_erasing(ldata); if (c == '\n') @@ -1386,7 +1382,8 @@ handle_newline: commit_echoes(tty); } - if (parmrk) + /* PARMRK doubling check */ + if (c == (unsigned char) '\377' && I_PARMRK(tty)) put_tty_queue(c, ldata); put_tty_queue(c, ldata); @@ -1397,7 +1394,6 @@ static inline void n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c) { struct n_tty_data *ldata = tty->disc_data; - int parmrk; if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { start_tty(tty); @@ -1411,8 +1407,8 @@ n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c) echo_char(c, tty); commit_echoes(tty); } - parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (parmrk) + /* PARMRK doubling check */ + if (c == (unsigned char) '\377' && I_PARMRK(tty)) put_tty_queue(c, ldata); put_tty_queue(c, ldata); } -- cgit v0.10.2 From eafbe67f84761d787802e5113d895a316b6292fe Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 2 Dec 2013 14:24:45 -0500 Subject: n_tty: Refactor input_available_p() by call site Distinguish if caller is n_tty_poll() or n_tty_read(), and set the read/wakeup threshold accordingly. 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 649fd94..2caa293 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1869,14 +1869,15 @@ err: return -ENOMEM; } -static inline int input_available_p(struct tty_struct *tty, int amt) +static inline int input_available_p(struct tty_struct *tty, int poll) { struct n_tty_data *ldata = tty->disc_data; + int amt = poll && !TIME_CHAR(tty) ? MIN_CHAR(tty) : 1; if (ldata->icanon && !L_EXTPROC(tty)) { if (ldata->canon_head != ldata->read_tail) return 1; - } else if (read_cnt(ldata) >= (amt ? amt : 1)) + } else if (read_cnt(ldata) >= amt) return 1; return 0; @@ -2375,7 +2376,7 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_wait(file, &tty->read_wait, wait); poll_wait(file, &tty->write_wait, wait); - if (input_available_p(tty, TIME_CHAR(tty) ? 0 : MIN_CHAR(tty))) + if (input_available_p(tty, 1)) mask |= POLLIN | POLLRDNORM; if (tty->packet && tty->link->ctrl_status) mask |= POLLPRI | POLLIN | POLLRDNORM; -- cgit v0.10.2 From 6c67716d64103e5a8e23c45dcdfc76520033d479 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 2 Dec 2013 14:24:46 -0500 Subject: n_tty: Only perform wakeups for waiters Only wakeup the _waiting_ reader, polls and/or writer(s). 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 2caa293..c7f8f7b 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -274,7 +274,8 @@ static void n_tty_check_unthrottle(struct tty_struct *tty) return; n_tty_set_room(tty); n_tty_write_wakeup(tty->link); - wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT); + if (waitqueue_active(&tty->link->write_wait)) + wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT); return; } @@ -349,7 +350,8 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty) spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->link->packet) { tty->ctrl_status |= TIOCPKT_FLUSHREAD; - wake_up_interruptible(&tty->link->read_wait); + if (waitqueue_active(&tty->link->read_wait)) + wake_up_interruptible(&tty->link->read_wait); } spin_unlock_irqrestore(&tty->ctrl_lock, flags); } @@ -1155,7 +1157,8 @@ static void n_tty_receive_break(struct tty_struct *tty) put_tty_queue('\0', ldata); } put_tty_queue('\0', ldata); - wake_up_interruptible(&tty->read_wait); + if (waitqueue_active(&tty->read_wait)) + wake_up_interruptible(&tty->read_wait); } /** @@ -1213,7 +1216,8 @@ static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) put_tty_queue('\0', ldata); else put_tty_queue(c, ldata); - wake_up_interruptible(&tty->read_wait); + if (waitqueue_active(&tty->read_wait)) + wake_up_interruptible(&tty->read_wait); } static void @@ -1802,8 +1806,10 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) start_tty(tty); /* The termios change make the tty ready for I/O */ - wake_up_interruptible(&tty->write_wait); - wake_up_interruptible(&tty->read_wait); + if (waitqueue_active(&tty->write_wait)) + wake_up_interruptible(&tty->write_wait); + if (waitqueue_active(&tty->read_wait)) + wake_up_interruptible(&tty->read_wait); } /** -- cgit v0.10.2 From 82f91fe092b6eacd82e976b8955443f9fd97d07e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 2 Dec 2013 13:56:03 -0500 Subject: tty: Always handle NULL flag ptr Most line disciplines already handle the undocumented NULL flag ptr in their .receive_buf method; however, several don't. Document the NULL flag ptr, and correct handling in the N_MOUSE, N_GSM0710 and N_R394 line disciplines. Signed-off-by: Peter Hurley Acked-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 8755f5f..0cb7ef5 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -124,7 +124,7 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c { struct serport *serport = (struct serport*) tty->disc_data; unsigned long flags; - unsigned int ch_flags; + unsigned int ch_flags = 0; int i; spin_lock_irqsave(&serport->lock, flags); @@ -133,18 +133,20 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c goto out; for (i = 0; i < count; i++) { - switch (fp[i]) { - case TTY_FRAME: - ch_flags = SERIO_FRAME; - break; - - case TTY_PARITY: - ch_flags = SERIO_PARITY; - break; - - default: - ch_flags = 0; - break; + if (fp) { + switch (fp[i]) { + case TTY_FRAME: + ch_flags = SERIO_FRAME; + break; + + case TTY_PARITY: + ch_flags = SERIO_PARITY; + break; + + default: + ch_flags = 0; + break; + } } serio_interrupt(serport->serio, cp[i], ch_flags); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index c0f76da..c09db11 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2269,14 +2269,15 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *f; int i; char buf[64]; - char flags; + char flags = TTY_NORMAL; if (debug & 4) print_hex_dump_bytes("gsmld_receive: ", DUMP_PREFIX_OFFSET, cp, count); for (i = count, dp = cp, f = fp; i; i--, dp++) { - flags = *f++; + if (f) + flags = *f++; switch (flags) { case TTY_NORMAL: gsm->receive(gsm, *dp); diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 1e64050..8b157d6 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -1244,7 +1244,7 @@ static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, { struct r3964_info *pInfo = tty->disc_data; const unsigned char *p; - char *f, flags = 0; + char *f, flags = TTY_NORMAL; int i; for (i = count, p = cp, f = fp; i; i--, p++) { diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index f15c898..b8347c2 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -84,7 +84,8 @@ * processing. is a pointer to the buffer of input * character received by the device. is a pointer to a * pointer of flag bytes which indicate whether a character was - * received with a parity error, etc. + * received with a parity error, etc. may be NULL to indicate + * all data received is TTY_NORMAL. * * void (*write_wakeup)(struct tty_struct *); * @@ -118,7 +119,8 @@ * processing. is a pointer to the buffer of input * character received by the device. is a pointer to a * pointer of flag bytes which indicate whether a character was - * received with a parity error, etc. + * received with a parity error, etc. may be NULL to indicate + * all data received is TTY_NORMAL. * If assigned, prefer this function for automatic flow control. */ -- cgit v0.10.2 From 4d18e6eff81e1d4d81d0942d5b7e96904b3b32df Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 22 Nov 2013 12:09:55 -0500 Subject: tty: Enable configurable tty flip buffer limit Allow driver to configure its maximum flip buffer memory consumption/limit. This is necessary for very-high speed line rates (in excess of 10MB/sec) because the flip buffers can be saturated before the line discipline has a chance to throttle the input. 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 c043136f..57eb34b 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -26,7 +26,7 @@ * Byte threshold to limit memory consumption for flip buffers. * The actual memory limit is > 2x this amount. */ -#define TTYB_MEM_LIMIT 65536 +#define TTYB_DEFAULT_MEM_LIMIT 65536 /* * We default to dicing tty buffer allocations to this many characters @@ -89,7 +89,7 @@ void tty_buffer_unlock_exclusive(struct tty_port *port) int tty_buffer_space_avail(struct tty_port *port) { - int space = TTYB_MEM_LIMIT - atomic_read(&port->buf.memory_used); + int space = port->buf.mem_limit - atomic_read(&port->buf.memory_used); return max(space, 0); } @@ -162,7 +162,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) /* Should possibly check if this fails for the largest buffer we have queued and recycle that ? */ - if (atomic_read(&port->buf.memory_used) > TTYB_MEM_LIMIT) + if (atomic_read(&port->buf.memory_used) > port->buf.mem_limit) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) @@ -536,4 +536,22 @@ void tty_buffer_init(struct tty_port *port) atomic_set(&buf->memory_used, 0); atomic_set(&buf->priority, 0); INIT_WORK(&buf->work, flush_to_ldisc); + buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT; } + +/** + * tty_buffer_set_limit - change the tty buffer memory limit + * @port: tty port to change + * + * Change the tty buffer memory limit. + * Must be called before the other tty buffer functions are used. + */ + +int tty_buffer_set_limit(struct tty_port *port, int limit) +{ + if (limit < MIN_TTYB_SIZE) + return -EINVAL; + port->buf.mem_limit = limit; + return 0; +} +EXPORT_SYMBOL_GPL(tty_buffer_set_limit); diff --git a/include/linux/tty.h b/include/linux/tty.h index 97d660e..2225745 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -61,6 +61,7 @@ struct tty_bufhead { struct tty_buffer sentinel; struct llist_head free; /* Free queue head */ atomic_t memory_used; /* In-use buffers excluding free list */ + int mem_limit; struct tty_buffer *tail; /* Active buffer */ }; /* diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 21ddd7d..2da8bc2 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -1,6 +1,7 @@ #ifndef _LINUX_TTY_FLIP_H #define _LINUX_TTY_FLIP_H +extern int tty_buffer_set_limit(struct tty_port *port, int limit); extern int tty_buffer_space_avail(struct tty_port *port); extern int tty_buffer_request_room(struct tty_port *port, size_t size); extern int tty_insert_flip_string_flags(struct tty_port *port, -- cgit v0.10.2 From 5dda4ca5585270c7d6854da5f83e8e8d3e157094 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 22 Nov 2013 12:09:56 -0500 Subject: tty: Rename tty buffer memory_used field Trim up the memory_used field name to mem_used. 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 57eb34b..e8f22f8 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -89,7 +89,7 @@ void tty_buffer_unlock_exclusive(struct tty_port *port) int tty_buffer_space_avail(struct tty_port *port) { - int space = port->buf.mem_limit - atomic_read(&port->buf.memory_used); + int space = port->buf.mem_limit - atomic_read(&port->buf.mem_used); return max(space, 0); } @@ -129,7 +129,7 @@ void tty_buffer_free_all(struct tty_port *port) buf->head = &buf->sentinel; buf->tail = &buf->sentinel; - atomic_set(&buf->memory_used, 0); + atomic_set(&buf->mem_used, 0); } /** @@ -162,7 +162,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) /* Should possibly check if this fails for the largest buffer we have queued and recycle that ? */ - if (atomic_read(&port->buf.memory_used) > port->buf.mem_limit) + if (atomic_read(&port->buf.mem_used) > port->buf.mem_limit) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) @@ -170,7 +170,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) found: tty_buffer_reset(p, size); - atomic_add(size, &port->buf.memory_used); + atomic_add(size, &port->buf.mem_used); return p; } @@ -188,7 +188,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) struct tty_bufhead *buf = &port->buf; /* Dumb strategy for now - should keep some stats */ - WARN_ON(atomic_sub_return(b->size, &buf->memory_used) < 0); + WARN_ON(atomic_sub_return(b->size, &buf->mem_used) < 0); if (b->size > MIN_TTYB_SIZE) kfree(b); @@ -533,7 +533,7 @@ void tty_buffer_init(struct tty_port *port) buf->head = &buf->sentinel; buf->tail = &buf->sentinel; init_llist_head(&buf->free); - atomic_set(&buf->memory_used, 0); + atomic_set(&buf->mem_used, 0); atomic_set(&buf->priority, 0); INIT_WORK(&buf->work, flush_to_ldisc); buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT; diff --git a/include/linux/tty.h b/include/linux/tty.h index 2225745..38fcc05 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -60,7 +60,7 @@ struct tty_bufhead { atomic_t priority; struct tty_buffer sentinel; struct llist_head free; /* Free queue head */ - atomic_t memory_used; /* In-use buffers excluding free list */ + atomic_t mem_used; /* In-use buffers excluding free list */ int mem_limit; struct tty_buffer *tail; /* Active buffer */ }; -- cgit v0.10.2 From 7e1e71d1546a28ea0ccc06320987e6c4ab2e1dbe Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 22 Nov 2013 12:09:57 -0500 Subject: tty: Remove tty_prepare_flip_string_flags() There is no in-tree user of tty_prepare_flip_string_flags(); remove. 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 e8f22f8..6a3620e 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -373,34 +373,6 @@ int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, } EXPORT_SYMBOL_GPL(tty_prepare_flip_string); -/** - * tty_prepare_flip_string_flags - make room for characters - * @port: tty port - * @chars: return pointer for character write area - * @flags: return pointer for status flag write area - * @size: desired size - * - * Prepare a block of space in the buffer for data. Returns the length - * available and buffer pointer to the space which is now allocated and - * accounted for as ready for characters. This is used for drivers - * that need their own block copy routines into the buffer. There is no - * guarantee the buffer is a DMA target! - */ - -int tty_prepare_flip_string_flags(struct tty_port *port, - unsigned char **chars, char **flags, size_t size) -{ - int space = tty_buffer_request_room(port, size); - if (likely(space)) { - struct tty_buffer *tb = port->buf.tail; - *chars = char_buf_ptr(tb, tb->used); - *flags = flag_buf_ptr(tb, tb->used); - tb->used += space; - } - return space; -} -EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); - static int receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 2da8bc2..3f821e9 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -10,8 +10,6 @@ extern int tty_insert_flip_string_fixed_flag(struct tty_port *port, const unsigned char *chars, char flag, size_t size); extern int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, size_t size); -extern int tty_prepare_flip_string_flags(struct tty_port *port, - unsigned char **chars, char **flags, size_t size); extern void tty_flip_buffer_push(struct tty_port *port); void tty_schedule_flip(struct tty_port *port); -- cgit v0.10.2 From c4a8dab5806188fb2a752467b63a7fd19bcbf0ec Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 22 Nov 2013 13:06:08 -0500 Subject: staging/fwserial: Rip out rx buffering Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index 62df009..ed7806b 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -456,16 +456,27 @@ static int fwtty_write_port_status(struct fwtty_port *port) return err; } -static void __fwtty_throttle(struct fwtty_port *port, struct tty_struct *tty) +static void fwtty_throttle_port(struct fwtty_port *port) { + struct tty_struct *tty; unsigned old; + tty = tty_port_tty_get(&port->port); + if (!tty) + return; + + spin_lock_bh(&port->lock); + old = port->mctrl; port->mctrl |= OOB_RX_THROTTLE; if (C_CRTSCTS(tty)) port->mctrl &= ~TIOCM_RTS; if (~old & OOB_RX_THROTTLE) __fwtty_write_port_status(port); + + spin_unlock_bh(&port->lock); + + tty_kref_put(tty); } /** @@ -532,74 +543,8 @@ static void fwtty_emit_breaks(struct work_struct *work) port->icount.brk += brk; } -static void fwtty_pushrx(struct work_struct *work) -{ - struct fwtty_port *port = to_port(work, push); - struct tty_struct *tty; - struct buffered_rx *buf, *next; - int n, c = 0; - - spin_lock_bh(&port->lock); - list_for_each_entry_safe(buf, next, &port->buf_list, list) { - n = tty_insert_flip_string_fixed_flag(&port->port, buf->data, - TTY_NORMAL, buf->n); - c += n; - port->buffered -= n; - if (n < buf->n) { - if (n > 0) { - memmove(buf->data, buf->data + n, buf->n - n); - buf->n -= n; - } - tty = tty_port_tty_get(&port->port); - if (tty) { - __fwtty_throttle(port, tty); - tty_kref_put(tty); - } - break; - } else { - list_del(&buf->list); - kfree(buf); - } - } - if (c > 0) - tty_flip_buffer_push(&port->port); - - if (list_empty(&port->buf_list)) - clear_bit(BUFFERING_RX, &port->flags); - spin_unlock_bh(&port->lock); -} - -static int fwtty_buffer_rx(struct fwtty_port *port, unsigned char *d, size_t n) -{ - struct buffered_rx *buf; - size_t size = (n + sizeof(struct buffered_rx) + 0xFF) & ~0xFF; - - if (port->buffered + n > HIGH_WATERMARK) { - fwtty_err_ratelimited(port, "overflowed rx buffer: buffered: %d new: %zu wtrmk: %d\n", - port->buffered, n, HIGH_WATERMARK); - return 0; - } - buf = kmalloc(size, GFP_ATOMIC); - if (!buf) - return 0; - INIT_LIST_HEAD(&buf->list); - buf->n = n; - memcpy(buf->data, d, n); - - spin_lock_bh(&port->lock); - list_add_tail(&buf->list, &port->buf_list); - port->buffered += n; - if (port->buffered > port->stats.watermark) - port->stats.watermark = port->buffered; - set_bit(BUFFERING_RX, &port->flags); - spin_unlock_bh(&port->lock); - - return n; -} - static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len) { - struct tty_struct *tty; int c, n = len; unsigned lsr; int err = 0; @@ -636,31 +581,24 @@ static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len) goto out; } - if (!test_bit(BUFFERING_RX, &port->flags)) { - c = tty_insert_flip_string_fixed_flag(&port->port, data, - TTY_NORMAL, n); - if (c > 0) - tty_flip_buffer_push(&port->port); - n -= c; - - if (n) { - /* start buffering and throttling */ - n -= fwtty_buffer_rx(port, &data[c], n); - - tty = tty_port_tty_get(&port->port); - if (tty) { - spin_lock_bh(&port->lock); - __fwtty_throttle(port, tty); - spin_unlock_bh(&port->lock); - tty_kref_put(tty); - } - } - } else - n -= fwtty_buffer_rx(port, data, n); + c = tty_insert_flip_string_fixed_flag(&port->port, data, TTY_NORMAL, n); + if (c > 0) + tty_flip_buffer_push(&port->port); + n -= c; if (n) { port->overrun = true; err = -EIO; + fwtty_err_ratelimited(port, "flip buffer overrun\n"); + + } else { + /* throttle the sender if remaining flip buffer space has + * reached high watermark to avoid losing data which may be + * in-flight. Since the AR request context is 32k, that much + * data may have _already_ been acked. + */ + if (tty_buffer_space_avail(&port->port) < HIGH_WATERMARK) + fwtty_throttle_port(port); } out: @@ -1101,20 +1039,13 @@ static int fwtty_port_activate(struct tty_port *tty_port, static void fwtty_port_shutdown(struct tty_port *tty_port) { struct fwtty_port *port = to_port(tty_port, port); - struct buffered_rx *buf, *next; /* TODO: cancel outstanding transactions */ cancel_delayed_work_sync(&port->emit_breaks); cancel_delayed_work_sync(&port->drain); - cancel_work_sync(&port->push); spin_lock_bh(&port->lock); - list_for_each_entry_safe(buf, next, &port->buf_list, list) { - list_del(&buf->list); - kfree(buf); - } - port->buffered = 0; port->flags = 0; port->break_ctl = 0; port->overrun = 0; @@ -1264,8 +1195,6 @@ static void fwtty_unthrottle(struct tty_struct *tty) profile_fifo_avail(port, port->stats.unthrottle); - schedule_work(&port->push); - spin_lock_bh(&port->lock); port->mctrl &= ~OOB_RX_THROTTLE; if (C_CRTSCTS(tty)) @@ -1523,8 +1452,7 @@ static void fwtty_debugfs_show_port(struct seq_file *m, struct fwtty_port *port) seq_printf(m, " dr:%d st:%d err:%d lost:%d", stats.dropped, stats.tx_stall, stats.fifo_errs, stats.lost); - seq_printf(m, " pkts:%d thr:%d wtrmk:%d", stats.sent, stats.throttled, - stats.watermark); + seq_printf(m, " pkts:%d thr:%d", stats.sent, stats.throttled); if (port->port.console) { seq_puts(m, "\n "); @@ -2302,8 +2230,6 @@ static int fwserial_create(struct fw_unit *unit) INIT_DELAYED_WORK(&port->drain, fwtty_drain_tx); INIT_DELAYED_WORK(&port->emit_breaks, fwtty_emit_breaks); INIT_WORK(&port->hangup, fwtty_do_hangup); - INIT_WORK(&port->push, fwtty_pushrx); - INIT_LIST_HEAD(&port->buf_list); init_waitqueue_head(&port->wait_tx); port->max_payload = link_speed_to_max_payload(SCODE_100); dma_fifo_init(&port->tx_fifo); diff --git a/drivers/staging/fwserial/fwserial.h b/drivers/staging/fwserial/fwserial.h index 2463501..149b24d 100644 --- a/drivers/staging/fwserial/fwserial.h +++ b/drivers/staging/fwserial/fwserial.h @@ -166,7 +166,6 @@ struct stats { unsigned sent; unsigned lost; unsigned throttled; - unsigned watermark; unsigned reads[DISTRIBUTION_MAX_INDEX + 1]; unsigned writes[DISTRIBUTION_MAX_INDEX + 1]; unsigned txns[DISTRIBUTION_MAX_INDEX + 1]; @@ -183,12 +182,6 @@ struct fwconsole_ops { #define FWCON_NOTIFY_ATTACH 1 #define FWCON_NOTIFY_DETACH 2 -struct buffered_rx { - struct list_head list; - size_t n; - unsigned char data[0]; -}; - /** * fwtty_port: structure used to track/represent underlying tty_port * @port: underlying tty_port @@ -223,11 +216,6 @@ struct buffered_rx { * The work can race with the writer but concurrent sending is * prevented with the IN_TX flag. Scheduled under lock to * limit scheduling when fifo has just been drained. - * @push: work responsible for pushing buffered rx to the ldisc. - * rx can become buffered if the tty buffer is filled before the - * ldisc throttles the sender. - * @buf_list: list of buffered rx yet to be sent to ldisc - * @buffered: byte count of buffered rx * @tx_fifo: fifo used to store & block-up writes for dma to remote * @max_payload: max bytes transmissable per dma (based on peer's max_payload) * @status_mask: UART_LSR_* bitmask significant to rx (based on termios) @@ -267,9 +255,6 @@ struct fwtty_port { spinlock_t lock; unsigned mctrl; struct delayed_work drain; - struct work_struct push; - struct list_head buf_list; - int buffered; struct dma_fifo tx_fifo; int max_payload; unsigned status_mask; @@ -291,7 +276,6 @@ struct fwtty_port { /* bit #s for flags field */ #define IN_TX 0 #define STOP_TX 1 -#define BUFFERING_RX 2 /* bitmasks for special mctrl/mstatus bits */ #define OOB_RX_THROTTLE 0x00010000 diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 6a3620e..6454c0a 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -92,6 +92,7 @@ int tty_buffer_space_avail(struct tty_port *port) int space = port->buf.mem_limit - atomic_read(&port->buf.mem_used); return max(space, 0); } +EXPORT_SYMBOL_GPL(tty_buffer_space_avail); static void tty_buffer_reset(struct tty_buffer *p, size_t size) { -- cgit v0.10.2 From 2ead3911e0910f19bf0d9c623ed73f70db8e0c24 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 22 Nov 2013 13:06:09 -0500 Subject: staging/fwserial: Up the tty buffer limit to 128K The firewire driver can overrun the tty buffers before the line discipline can throttle the input; up the buffer limit. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index ed7806b..656fdcb 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -2225,6 +2225,7 @@ static int fwserial_create(struct fw_unit *unit) port->index = FWTTY_INVALID_INDEX; port->port.ops = &fwtty_port_ops; port->serial = serial; + tty_buffer_set_limit(&port->port, 128 * 1024); spin_lock_init(&port->lock); INIT_DELAYED_WORK(&port->drain, fwtty_drain_tx); -- cgit v0.10.2 From 49bb8405b4a8d3d357237770f965e7c6be4377c5 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 22 Nov 2013 13:06:10 -0500 Subject: staging/fwserial: Rename data profiling functions Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index 656fdcb..8af136e 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -136,14 +136,14 @@ static struct fwtty_peer *__fwserial_peer_by_node_id(struct fw_card *card, #ifdef FWTTY_PROFILING -static void profile_fifo_avail(struct fwtty_port *port, unsigned *stat) +static void fwtty_profile_fifo(struct fwtty_port *port, unsigned *stat) { spin_lock_bh(&port->lock); - profile_size_distrib(stat, dma_fifo_avail(&port->tx_fifo)); + fwtty_profile_data(stat, dma_fifo_avail(&port->tx_fifo)); spin_unlock_bh(&port->lock); } -static void dump_profile(struct seq_file *m, struct stats *stats) +static void fwtty_dump_profile(struct seq_file *m, struct stats *stats) { /* for each stat, print sum of 0 to 2^k, then individually */ int k = 4; @@ -183,8 +183,8 @@ static void dump_profile(struct seq_file *m, struct stats *stats) } #else -#define profile_fifo_avail(port, stat) -#define dump_profile(m, stats) +#define fwtty_profile_fifo(port, stat) +#define fwtty_dump_profile(m, stats) #endif /* @@ -550,7 +550,7 @@ static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len) int err = 0; fwtty_dbg(port, "%d\n", n); - profile_size_distrib(port->stats.reads, n); + fwtty_profile_data(port->stats.reads, n); if (port->write_only) { n = 0; @@ -759,7 +759,7 @@ static int fwtty_tx(struct fwtty_port *port, bool drain) if (n == -EAGAIN) ++port->stats.tx_stall; else if (n == -ENODATA) - profile_size_distrib(port->stats.txns, 0); + fwtty_profile_data(port->stats.txns, 0); else { ++port->stats.fifo_errs; fwtty_err_ratelimited(port, "fifo err: %d\n", @@ -768,7 +768,7 @@ static int fwtty_tx(struct fwtty_port *port, bool drain) break; } - profile_size_distrib(port->stats.txns, txn->dma_pended.len); + fwtty_profile_data(port->stats.txns, txn->dma_pended.len); fwtty_send_txn_async(peer, txn, TCODE_WRITE_BLOCK_REQUEST, peer->fifo_addr, txn->dma_pended.data, @@ -1115,7 +1115,7 @@ static int fwtty_write(struct tty_struct *tty, const unsigned char *buf, int c) int n, len; fwtty_dbg(port, "%d\n", c); - profile_size_distrib(port->stats.writes, c); + fwtty_profile_data(port->stats.writes, c); spin_lock_bh(&port->lock); n = dma_fifo_in(&port->tx_fifo, buf, c); @@ -1193,7 +1193,7 @@ static void fwtty_unthrottle(struct tty_struct *tty) fwtty_dbg(port, "CRTSCTS: %d\n", (C_CRTSCTS(tty) != 0)); - profile_fifo_avail(port, port->stats.unthrottle); + fwtty_profile_fifo(port, port->stats.unthrottle); spin_lock_bh(&port->lock); port->mctrl &= ~OOB_RX_THROTTLE; @@ -1459,7 +1459,7 @@ static void fwtty_debugfs_show_port(struct seq_file *m, struct fwtty_port *port) (*port->fwcon_ops->proc_show)(m, port->con_data); } - dump_profile(m, &port->stats); + fwtty_dump_profile(m, &port->stats); } static void fwtty_debugfs_show_peer(struct seq_file *m, struct fwtty_peer *peer) diff --git a/drivers/staging/fwserial/fwserial.h b/drivers/staging/fwserial/fwserial.h index 149b24d..eab85b4 100644 --- a/drivers/staging/fwserial/fwserial.h +++ b/drivers/staging/fwserial/fwserial.h @@ -22,14 +22,14 @@ #ifdef FWTTY_PROFILING #define DISTRIBUTION_MAX_SIZE 8192 #define DISTRIBUTION_MAX_INDEX (ilog2(DISTRIBUTION_MAX_SIZE) + 1) -static inline void profile_size_distrib(unsigned stat[], unsigned val) +static inline void fwtty_profile_data(unsigned stat[], unsigned val) { int n = (val) ? min(ilog2(val) + 1, DISTRIBUTION_MAX_INDEX) : 0; ++stat[n]; } #else #define DISTRIBUTION_MAX_INDEX 0 -#define profile_size_distrib(st, n) +#define fwtty_profile_data(st, n) #endif /* Parameters for both VIRT_CABLE_PLUG & VIRT_CABLE_PLUG_RSP mgmt codes */ -- cgit v0.10.2 From 87a5a0371b7356c99f16363b80107602f110fcda Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 22 Nov 2013 13:06:11 -0500 Subject: staging/fwserial: Add Kconfig options for max ports Allow kernel configuration of max supported ports for TTY-over-Firewire driver. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/fwserial/Kconfig b/drivers/staging/fwserial/Kconfig index a0812d9..9c7c926 100644 --- a/drivers/staging/fwserial/Kconfig +++ b/drivers/staging/fwserial/Kconfig @@ -9,3 +9,23 @@ config FIREWIRE_SERIAL To compile this driver as a module, say M here: the module will be called firewire-serial. + +if FIREWIRE_SERIAL + +config FWTTY_MAX_TOTAL_PORTS + int "Maximum number of serial ports supported" + default "64" + help + Set this to the maximum number of serial ports you want the + firewire-serial driver to support. + +config FWTTY_MAX_CARD_PORTS + int "Maximum number of serial ports supported per adapter" + range 0 FWTTY_MAX_TOTAL_PORTS + default "32" + help + Set this to the maximum number of serial ports each firewire + adapter supports. The actual number of serial ports registered + is set with the module parameter "ttys". + +endif diff --git a/drivers/staging/fwserial/fwserial.h b/drivers/staging/fwserial/fwserial.h index eab85b4..54f7f9b 100644 --- a/drivers/staging/fwserial/fwserial.h +++ b/drivers/staging/fwserial/fwserial.h @@ -291,8 +291,8 @@ struct fwtty_port { #define FREQ_BREAKS (HZ / 50) /* Ports are allocated in blocks of num_ports for each fw_card */ -#define MAX_CARD_PORTS 32 /* max # of ports per card */ -#define MAX_TOTAL_PORTS 64 /* max # of ports total */ +#define MAX_CARD_PORTS CONFIG_FWTTY_MAX_CARD_PORTS +#define MAX_TOTAL_PORTS CONFIG_FWTTY_MAX_TOTAL_PORTS /* tuning parameters */ #define FWTTY_PORT_TXFIFO_LEN 4096 -- cgit v0.10.2 From 9cfb5e30951e484a403793d67d9ba0e2d4a188fd Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 25 Nov 2013 11:19:41 +0900 Subject: serial: pch_uart: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 0aa2b52..11c862c 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1853,7 +1853,6 @@ static void pch_uart_exit_port(struct eg20t_port *priv) debugfs_remove(priv->debugfs); #endif uart_remove_one_port(&pch_uart_driver, &priv->port); - pci_set_drvdata(priv->pdev, NULL); free_page((unsigned long)priv->rxbuf.buf); } -- cgit v0.10.2 From f166b4981472baa9af2ae29984ea08e0eb62ca6c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 25 Nov 2013 11:16:33 +0900 Subject: parport_serial: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 1b8bdb7..ff53314 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -596,13 +596,11 @@ static int parport_serial_pci_probe(struct pci_dev *dev, err = pci_enable_device (dev); if (err) { - pci_set_drvdata (dev, NULL); kfree (priv); return err; } if (parport_register (dev, id)) { - pci_set_drvdata (dev, NULL); kfree (priv); return -ENODEV; } @@ -611,7 +609,6 @@ static int parport_serial_pci_probe(struct pci_dev *dev, int i; for (i = 0; i < priv->num_par; i++) parport_pc_unregister_port (priv->port[i]); - pci_set_drvdata (dev, NULL); kfree (priv); return -ENODEV; } @@ -624,8 +621,6 @@ static void parport_serial_pci_remove(struct pci_dev *dev) struct parport_serial_private *priv = pci_get_drvdata (dev); int i; - pci_set_drvdata(dev, NULL); - // Serial ports if (priv->serial) pciserial_remove_ports(priv->serial); -- cgit v0.10.2 From dfabf7ffa30585fe491341f069df926cc28670ac Mon Sep 17 00:00:00 2001 From: Chao Bi Date: Tue, 26 Nov 2013 12:09:39 +0800 Subject: n_gsm: race between ld close and gsmtty open ttyA has ld associated to n_gsm, when ttyA is closing, it triggers to release gsmttyB's ld data dlci[B], then race would happen if gsmttyB is opening in parallel. (Note: This patch set differs from previous set in that it uses mutex instead of spin lock to avoid race, so that it avoids sleeping in automic context) Here are race cases we found recently in test: CASE #1 ==================================================================== releasing dlci[B] race with gsmtty_install(gsmttyB), then panic in gsmtty_open(gsmttyB), as below: tty_release(ttyA) tty_open(gsmttyB) | | ----- gsmtty_install(gsmttyB) | | ----- gsm_dlci_alloc(gsmttyB) => alloc dlci[B] tty_ldisc_release(ttyA) ----- | | gsm_dlci_release(dlci[B]) ----- | | gsm_dlci_free(dlci[B]) ----- | | ----- gsmtty_open(gsmttyB) gsmtty_open() { struct gsm_dlci *dlci = tty->driver_data; => here it uses dlci[B] ... } In gsmtty_open(gsmttyA), it uses dlci[B] which was release, so hit a panic. ===================================================================== CASE #2 ===================================================================== releasing dlci[0] race with gsmtty_install(gsmttyB), then panic in gsmtty_open(), as below: tty_release(ttyA) tty_open(gsmttyB) | | ----- gsmtty_install(gsmttyB) | | ----- gsm_dlci_alloc(gsmttyB) => alloc dlci[B] | | ----- gsmtty_open(gsmttyB) fail | | ----- tty_release(gsmttyB) | | ----- gsmtty_close(gsmttyB) | | ----- gsmtty_detach_dlci(dlci[B]) | | ----- dlci_put(dlci[B]) | | tty_ldisc_release(ttyA) ----- | | gsm_dlci_release(dlci[0]) ----- | | gsm_dlci_free(dlci[0]) ----- | | ----- dlci_put(dlci[0]) In gsmtty_detach_dlci(dlci[B]), it tries to use dlci[0] which was released, then hit panic. ===================================================================== IMHO, n_gsm tty operations would refer released ldisc, as long as gsm_dlci_release() has chance to release ldisc data when some gsmtty operations are ongoing.. This patch is try to avoid it by: 1) in n_gsm driver, use a global gsm mutex lock to avoid gsm_dlci_release() run in parallel with gsmtty_install(); 2) Increase dlci's ref count in gsmtty_install() instead of in gsmtty_open(), the purpose is to prevent gsm_dlci_release() releasing dlci after gsmtty_install() allocats dlci but before gsmtty_open increases dlci's ref count; 3) Decrease dlci's ref count in gsmtty_remove(), a tty framework API, this is the opposite process of step 2). Signed-off-by: Chao Bi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index c09db11..679294b 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -194,6 +194,7 @@ struct gsm_control { struct gsm_mux { struct tty_struct *tty; /* The tty our ldisc is bound to */ spinlock_t lock; + struct mutex mutex; unsigned int num; struct kref ref; @@ -2054,9 +2055,11 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) dlci->state == DLCI_CLOSED); } /* Free up any link layer users */ + mutex_lock(&gsm->mutex); for (i = 0; i < NUM_DLCI; i++) if (gsm->dlci[i]) gsm_dlci_release(gsm->dlci[i]); + mutex_unlock(&gsm->mutex); /* Now wipe the queues */ list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list) kfree(txq); @@ -2170,6 +2173,7 @@ struct gsm_mux *gsm_alloc_mux(void) return NULL; } spin_lock_init(&gsm->lock); + mutex_init(&gsm->mutex); kref_init(&gsm->ref); INIT_LIST_HEAD(&gsm->tx_list); @@ -2910,23 +2914,33 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) This is ok from a locking perspective as we don't have to worry about this if DLCI0 is lost */ - if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) + mutex_lock(&gsm->mutex); + if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) { + mutex_unlock(&gsm->mutex); return -EL2NSYNC; + } dlci = gsm->dlci[line]; if (dlci == NULL) { alloc = true; dlci = gsm_dlci_alloc(gsm, line); } - if (dlci == NULL) + if (dlci == NULL) { + mutex_unlock(&gsm->mutex); return -ENOMEM; + } ret = tty_port_install(&dlci->port, driver, tty); if (ret) { if (alloc) dlci_put(dlci); + mutex_unlock(&gsm->mutex); return ret; } + dlci_get(dlci); + dlci_get(gsm->dlci[0]); + mux_get(gsm); tty->driver_data = dlci; + mutex_unlock(&gsm->mutex); return 0; } @@ -2937,9 +2951,6 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) struct tty_port *port = &dlci->port; port->count++; - dlci_get(dlci); - dlci_get(dlci->gsm->dlci[0]); - mux_get(dlci->gsm); tty_port_tty_set(port, tty); dlci->modem_rx = 0; @@ -2966,7 +2977,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&dlci->mutex); gsm = dlci->gsm; if (tty_port_close_start(&dlci->port, tty, filp) == 0) - goto out; + return; gsm_dlci_begin_close(dlci); if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) { if (C_HUPCL(tty)) @@ -2974,10 +2985,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) } tty_port_close_end(&dlci->port, tty); tty_port_tty_set(&dlci->port, NULL); -out: - dlci_put(dlci); - dlci_put(gsm->dlci[0]); - mux_put(gsm); + return; } static void gsmtty_hangup(struct tty_struct *tty) @@ -3154,6 +3162,16 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state) return gsmtty_modem_update(dlci, encode); } +static void gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + struct gsm_mux *gsm = dlci->gsm; + + dlci_put(dlci); + dlci_put(gsm->dlci[0]); + mux_put(gsm); + driver->ttys[tty->index] = NULL; +} /* Virtual ttys for the demux */ static const struct tty_operations gsmtty_ops = { @@ -3173,6 +3191,7 @@ static const struct tty_operations gsmtty_ops = { .tiocmget = gsmtty_tiocmget, .tiocmset = gsmtty_tiocmset, .break_ctl = gsmtty_break_ctl, + .remove = gsmtty_remove, }; -- cgit v0.10.2 From d0ce850d60166e99d5cca011d4b871a7cc1f930c Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 3 Dec 2013 11:04:28 +0100 Subject: serial: pl011: Convert to modern PM ops Convert to modern PM ops and use the SIMPLE_DEV_PM_OPS macro to set up the PM callbacks. Cc: Greg Kroah-Hartman Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 742c8bf..a183ace 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2167,10 +2167,10 @@ static int pl011_remove(struct amba_device *dev) return 0; } -#ifdef CONFIG_PM -static int pl011_suspend(struct amba_device *dev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int pl011_suspend(struct device *dev) { - struct uart_amba_port *uap = amba_get_drvdata(dev); + struct uart_amba_port *uap = dev_get_drvdata(dev); if (!uap) return -EINVAL; @@ -2178,9 +2178,9 @@ static int pl011_suspend(struct amba_device *dev, pm_message_t state) return uart_suspend_port(&amba_reg, &uap->port); } -static int pl011_resume(struct amba_device *dev) +static int pl011_resume(struct device *dev) { - struct uart_amba_port *uap = amba_get_drvdata(dev); + struct uart_amba_port *uap = dev_get_drvdata(dev); if (!uap) return -EINVAL; @@ -2189,6 +2189,8 @@ static int pl011_resume(struct amba_device *dev) } #endif +static SIMPLE_DEV_PM_OPS(pl011_dev_pm_ops, pl011_suspend, pl011_resume); + static struct amba_id pl011_ids[] = { { .id = 0x00041011, @@ -2208,14 +2210,11 @@ MODULE_DEVICE_TABLE(amba, pl011_ids); static struct amba_driver pl011_driver = { .drv = { .name = "uart-pl011", + .pm = &pl011_dev_pm_ops, }, .id_table = pl011_ids, .probe = pl011_probe, .remove = pl011_remove, -#ifdef CONFIG_PM - .suspend = pl011_suspend, - .resume = pl011_resume, -#endif }; static int __init pl011_init(void) -- cgit v0.10.2 From 17438217a6f5e33d920ed3821a4b857311cc2872 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 28 Nov 2013 10:41:04 +0100 Subject: serial: pl011: use DMA RX polling by default Making DMA RX polling optional when DMA is on was just over-cautious: there is one single system in the kernel tree using this facility, Ux500 and after some testing I turned this on also for Ux500, which means it should simply be on by default if DMA is enabled. Cc: Jongsung Kim Cc: Chanho Min Cc: Russell King Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index a183ace..c8cc8f0 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -325,7 +325,7 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port * dmaengine_slave_config(chan, &rx_conf); uap->dmarx.chan = chan; - if (plat && plat->dma_rx_poll_enable) { + if (plat) { /* Set poll rate if specified. */ if (plat->dma_rx_poll_rate) { uap->dmarx.auto_poll_rate = false; diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index 0ddb5c0..0891ea0 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -203,7 +203,6 @@ struct amba_pl011_data { bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; - bool dma_rx_poll_enable; unsigned int dma_rx_poll_rate; unsigned int dma_rx_poll_timeout; void (*init) (void); -- cgit v0.10.2 From 6b471a9840eefc96abb3fbd9eb71e1eb93399daa Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 29 Nov 2013 17:29:24 +0800 Subject: serial: imx: add support for loopback mode. Add the loopback mode support for imx uart driver. Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index b2cfdb6..d799140 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -806,6 +806,9 @@ static unsigned int imx_get_mctrl(struct uart_port *port) if (readl(sport->port.membase + UCR2) & UCR2_CTS) tmp |= TIOCM_RTS; + if (readl(sport->port.membase + uts_reg(sport)) & UTS_LOOP) + tmp |= TIOCM_LOOP; + return tmp; } @@ -821,6 +824,11 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) temp |= UCR2_CTS; writel(temp, sport->port.membase + UCR2); + + temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP; + if (mctrl & TIOCM_LOOP) + temp |= UTS_LOOP; + writel(temp, sport->port.membase + uts_reg(sport)); } /* -- cgit v0.10.2 From 311df74ade503af204ac6b8ee55ce30ff2411398 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 3 Dec 2013 08:26:37 +0900 Subject: tty: remove DEFINE_PCI_DEVICE_TABLE macro Don't use DEFINE_PCI_DEVICE_TABLE macro, because this macro is not preferred. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 354564e..383c4c7 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -1744,7 +1744,7 @@ static void rp_flush_buffer(struct tty_struct *tty) #ifdef CONFIG_PCI -static DEFINE_PCI_DEVICE_TABLE(rocket_pci_ids) = { +static const struct pci_device_id rocket_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP4QUAD) }, { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8OCTA) }, { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP8OCTA) }, diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 11c862c..9cbd3ac 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1906,7 +1906,7 @@ static int pch_uart_pci_resume(struct pci_dev *pdev) #define pch_uart_pci_resume NULL #endif -static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = { +static const struct pci_device_id pch_uart_pci_id[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8811), .driver_data = pch_et20t_uart0}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8812), diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 328d6de..056f91b 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -810,7 +810,7 @@ static void rp2_remove(struct pci_dev *pdev) rp2_remove_ports(card); } -static DEFINE_PCI_DEVICE_TABLE(rp2_pci_tbl) = { +static const struct pci_device_id rp2_pci_tbl[] = { /* RocketPort INFINITY cards */ -- cgit v0.10.2 From 95468240d833314995e482bd4c75cfe660919e9a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 3 Dec 2013 11:04:27 +0100 Subject: serial: pl010: Convert to modern PM ops Convert to modern PM ops and use the SIMPLE_DEV_PM_OPS macro to set up the PM callbacks. Cc: Greg Kroah-Hartman Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 33bd860..01c9e72 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -756,9 +756,10 @@ static int pl010_remove(struct amba_device *dev) return 0; } -static int pl010_suspend(struct amba_device *dev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int pl010_suspend(struct device *dev) { - struct uart_amba_port *uap = amba_get_drvdata(dev); + struct uart_amba_port *uap = dev_get_drvdata(dev); if (uap) uart_suspend_port(&amba_reg, &uap->port); @@ -766,15 +767,18 @@ static int pl010_suspend(struct amba_device *dev, pm_message_t state) return 0; } -static int pl010_resume(struct amba_device *dev) +static int pl010_resume(struct device *dev) { - struct uart_amba_port *uap = amba_get_drvdata(dev); + struct uart_amba_port *uap = dev_get_drvdata(dev); if (uap) uart_resume_port(&amba_reg, &uap->port); return 0; } +#endif + +static SIMPLE_DEV_PM_OPS(pl010_dev_pm_ops, pl010_suspend, pl010_resume); static struct amba_id pl010_ids[] = { { @@ -789,12 +793,11 @@ MODULE_DEVICE_TABLE(amba, pl010_ids); static struct amba_driver pl010_driver = { .drv = { .name = "uart-pl010", + .pm = &pl010_dev_pm_ops, }, .id_table = pl010_ids, .probe = pl010_probe, .remove = pl010_remove, - .suspend = pl010_suspend, - .resume = pl010_resume, }; static int __init pl010_init(void) -- cgit v0.10.2 From 753023dcdd2da515f991aa0e6a65016713f0c24f Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 2 Dec 2013 16:12:02 -0500 Subject: tty: Fix stale tty_buffer_flush() comment Commit d7a68be4f265be10e24be931c257af30ca55566b, 'tty: Only perform flip buffer flush from tty_buffer_flush()', removed buffer flushing from flush_to_ldisc(). Fix function header comment which describes the former behavior. 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 6454c0a..08835a6 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -201,9 +201,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) * tty_buffer_flush - flush full tty buffers * @tty: tty to flush * - * flush all the buffers containing receive data. If the buffer is - * being processed by flush_to_ldisc then we defer the processing - * to that function + * flush all the buffers containing receive data. * * Locking: takes buffer lock to ensure single-threaded flip buffer * 'consumer' -- cgit v0.10.2 From 9bbc3dca9d2212f900396ab9642c48279657a8c0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 2 Dec 2013 01:17:58 -0200 Subject: tty: serial: mxs-auart: Check the return value from clk_prepare_enable() clk_prepare_enable() may fail, so let's check its return value and propagate it in the case of error. Signed-off-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 d8b6fee..aa97fd8 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -734,9 +734,12 @@ static void mxs_auart_reset(struct uart_port *u) static int mxs_auart_startup(struct uart_port *u) { + int ret; struct mxs_auart_port *s = to_auart_port(u); - clk_prepare_enable(s->clk); + ret = clk_prepare_enable(s->clk); + if (ret) + return ret; writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR); @@ -957,7 +960,9 @@ auart_console_setup(struct console *co, char *options) if (!s) return -ENODEV; - clk_prepare_enable(s->clk); + ret = clk_prepare_enable(s->clk); + if (ret) + return ret; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); -- cgit v0.10.2 From acc0f67f307f52f7aec1cffdc40a786c15dd21d9 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 9 Dec 2013 09:23:52 -0500 Subject: tty: Halve flip buffer GFP_ATOMIC memory consumption tty flip buffers use GFP_ATOMIC allocations for received data which is to be processed by the line discipline. For each byte received, an extra byte is used to indicate the error status of that byte. Instead, if the received data is error-free, encode the entire buffer without status bytes. 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 08835a6..3002e20 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -101,6 +101,7 @@ static void tty_buffer_reset(struct tty_buffer *p, size_t size) p->next = NULL; p->commit = 0; p->read = 0; + p->flags = 0; } /** @@ -229,31 +230,49 @@ void tty_buffer_flush(struct tty_struct *tty) * tty_buffer_request_room - grow tty buffer if needed * @tty: tty structure * @size: size desired + * @flags: buffer flags if new buffer allocated (default = 0) * * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. + * + * Will change over to a new buffer if the current buffer is encoded as + * TTY_NORMAL (so has no flags buffer) and the new buffer requires + * a flags buffer. */ -int tty_buffer_request_room(struct tty_port *port, size_t size) +static int __tty_buffer_request_room(struct tty_port *port, size_t size, + int flags) { struct tty_bufhead *buf = &port->buf; struct tty_buffer *b, *n; - int left; + int left, change; b = buf->tail; - left = b->size - b->used; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; + else + left = b->size - b->used; - if (left < size) { + change = (b->flags & TTYB_NORMAL) && (~flags & TTYB_NORMAL); + if (change || left < size) { /* This is the slow path - looking for new buffers to use */ if ((n = tty_buffer_alloc(port, size)) != NULL) { + n->flags = flags; buf->tail = n; b->commit = b->used; smp_mb(); b->next = n; - } else + } else if (change) + size = 0; + else size = left; } return size; } + +int tty_buffer_request_room(struct tty_port *port, size_t size) +{ + return __tty_buffer_request_room(port, size, 0); +} EXPORT_SYMBOL_GPL(tty_buffer_request_room); /** @@ -273,12 +292,14 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port, int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); - int space = tty_buffer_request_room(port, goal); + int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0; + int space = __tty_buffer_request_room(port, goal, flags); struct tty_buffer *tb = port->buf.tail; if (unlikely(space == 0)) break; memcpy(char_buf_ptr(tb, tb->used), chars, space); - memset(flag_buf_ptr(tb, tb->used), flag, space); + if (~tb->flags & TTYB_NORMAL) + memset(flag_buf_ptr(tb, tb->used), flag, space); tb->used += space; copied += space; chars += space; @@ -361,11 +382,12 @@ EXPORT_SYMBOL(tty_schedule_flip); int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, size_t size) { - int space = tty_buffer_request_room(port, size); + int space = __tty_buffer_request_room(port, size, TTYB_NORMAL); if (likely(space)) { struct tty_buffer *tb = port->buf.tail; *chars = char_buf_ptr(tb, tb->used); - memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space); + if (~tb->flags & TTYB_NORMAL) + memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space); tb->used += space; } return space; @@ -378,7 +400,10 @@ receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) { struct tty_ldisc *disc = tty->ldisc; unsigned char *p = char_buf_ptr(head, head->read); - char *f = flag_buf_ptr(head, head->read); + char *f = NULL; + + if (~head->flags & TTYB_NORMAL) + f = flag_buf_ptr(head, head->read); if (disc->ops->receive_buf2) count = disc->ops->receive_buf2(tty, p, f, count); diff --git a/include/linux/tty.h b/include/linux/tty.h index 38fcc05..ad98b437 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -39,10 +39,14 @@ struct tty_buffer { int size; int commit; int read; + int flags; /* Data points here */ unsigned long data[0]; }; +/* Values for .flags field of tty_buffer */ +#define TTYB_NORMAL 1 /* buffer has no flags buffer */ + static inline unsigned char *char_buf_ptr(struct tty_buffer *b, int ofs) { return ((unsigned char *)b->data) + ofs; diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 3f821e9..c28dd52 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -17,8 +17,12 @@ static inline int tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag) { struct tty_buffer *tb = port->buf.tail; - if (tb && tb->used < tb->size) { - *flag_buf_ptr(tb, tb->used) = flag; + int change; + + change = (tb->flags & TTYB_NORMAL) && (flag != TTY_NORMAL); + if (!change && tb->used < tb->size) { + if (~tb->flags & TTYB_NORMAL) + *flag_buf_ptr(tb, tb->used) = flag; *char_buf_ptr(tb, tb->used++) = ch; return 1; } -- cgit v0.10.2 From b46d0c46ccaa366a5bb8ac709fdf2bcaa76221fd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 2 Dec 2013 12:14:40 -0800 Subject: init.h: add missing initcall variants Add missing initcall variants when building for loadable modules. This fixes this build error on powerpc allmodconfig: drivers/tty/ehv_bytechan.c: error: type defaults to 'int' in declaration of 'console_initcall' [-Werror=implicit-int] Signed-off-by: Randy Dunlap Reported-by: Geert Uytterhoeven Cc: Benjamin Herrenschmidt Acked-by: Timur Tabi Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/init.h b/include/linux/init.h index 8e68a64..e168880 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -286,9 +286,11 @@ void __init parse_early_options(char *cmdline); #define arch_initcall(fn) module_init(fn) #define subsys_initcall(fn) module_init(fn) #define fs_initcall(fn) module_init(fn) +#define rootfs_initcall(fn) module_init(fn) #define device_initcall(fn) module_init(fn) #define late_initcall(fn) module_init(fn) +#define console_initcall(fn) module_init(fn) #define security_initcall(fn) module_init(fn) /* Each module must use one module_init(). */ -- cgit v0.10.2 From bc00024502edd2ca5d786a06270b1ba47e5907ef Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 11 Dec 2013 19:50:50 +0400 Subject: serial: clps711x: Driver refactor This is a complex patch for refactoring CLPS711X serial driver. Major changes: - Eliminate usage. - Devicetree support. Signed-off-by: Alexander Shiyan Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/arch/arm/mach-clps711x/devices.c b/arch/arm/mach-clps711x/devices.c index fb77d14..2001488 100644 --- a/arch/arm/mach-clps711x/devices.c +++ b/arch/arm/mach-clps711x/devices.c @@ -61,8 +61,29 @@ static void __init clps711x_add_syscon(void) &clps711x_syscon_res[i], 1); } +static const struct resource clps711x_uart1_res[] __initconst = { + DEFINE_RES_MEM(CLPS711X_PHYS_BASE + UARTDR1, SZ_128), + DEFINE_RES_IRQ(IRQ_UTXINT1), + DEFINE_RES_IRQ(IRQ_URXINT1), +}; + +static const struct resource clps711x_uart2_res[] __initconst = { + DEFINE_RES_MEM(CLPS711X_PHYS_BASE + UARTDR2, SZ_128), + DEFINE_RES_IRQ(IRQ_UTXINT2), + DEFINE_RES_IRQ(IRQ_URXINT2), +}; + +static void __init clps711x_add_uart(void) +{ + platform_device_register_simple("clps711x-uart", 0, clps711x_uart1_res, + ARRAY_SIZE(clps711x_uart1_res)); + platform_device_register_simple("clps711x-uart", 1, clps711x_uart2_res, + ARRAY_SIZE(clps711x_uart2_res)); +}; + void __init clps711x_devices_init(void) { clps711x_add_gpio(); clps711x_add_syscon(); + clps711x_add_uart(); } diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 8d0b994..4f59f1c 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -21,44 +21,66 @@ #include #include #include -#include #include +#include #include #include #include +#include #include +#include -#include +#include +#include -#define UART_CLPS711X_NAME "uart-clps711x" +#define UART_CLPS711X_DEVNAME "ttyCL" #define UART_CLPS711X_NR 2 #define UART_CLPS711X_MAJOR 204 #define UART_CLPS711X_MINOR 40 -#define UBRLCR(port) ((port)->line ? UBRLCR2 : UBRLCR1) -#define UARTDR(port) ((port)->line ? UARTDR2 : UARTDR1) -#define SYSFLG(port) ((port)->line ? SYSFLG2 : SYSFLG1) -#define SYSCON(port) ((port)->line ? SYSCON2 : SYSCON1) -#define TX_IRQ(port) ((port)->line ? IRQ_UTXINT2 : IRQ_UTXINT1) -#define RX_IRQ(port) ((port)->line ? IRQ_URXINT2 : IRQ_URXINT1) +#define UARTDR_OFFSET (0x00) +#define UBRLCR_OFFSET (0x40) + +#define UARTDR_FRMERR (1 << 8) +#define UARTDR_PARERR (1 << 9) +#define UARTDR_OVERR (1 << 10) + +#define UBRLCR_BAUD_MASK ((1 << 12) - 1) +#define UBRLCR_BREAK (1 << 12) +#define UBRLCR_PRTEN (1 << 13) +#define UBRLCR_EVENPRT (1 << 14) +#define UBRLCR_XSTOP (1 << 15) +#define UBRLCR_FIFOEN (1 << 16) +#define UBRLCR_WRDLEN5 (0 << 17) +#define UBRLCR_WRDLEN6 (1 << 17) +#define UBRLCR_WRDLEN7 (2 << 17) +#define UBRLCR_WRDLEN8 (3 << 17) +#define UBRLCR_WRDLEN_MASK (3 << 17) struct clps711x_port { - struct uart_driver uart; - struct clk *uart_clk; - struct uart_port port[UART_CLPS711X_NR]; - int tx_enabled[UART_CLPS711X_NR]; -#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE - struct console console; -#endif + struct uart_port port; + unsigned int tx_enabled; + int rx_irq; + struct regmap *syscon; + bool use_ms; +}; + +static struct uart_driver clps711x_uart = { + .owner = THIS_MODULE, + .driver_name = UART_CLPS711X_DEVNAME, + .dev_name = UART_CLPS711X_DEVNAME, + .major = UART_CLPS711X_MAJOR, + .minor = UART_CLPS711X_MINOR, + .nr = UART_CLPS711X_NR, }; static void uart_clps711x_stop_tx(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); - if (s->tx_enabled[port->line]) { - disable_irq(TX_IRQ(port)); - s->tx_enabled[port->line] = 0; + if (s->tx_enabled) { + disable_irq(port->irq); + s->tx_enabled = 0; } } @@ -66,33 +88,26 @@ static void uart_clps711x_start_tx(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); - if (!s->tx_enabled[port->line]) { - enable_irq(TX_IRQ(port)); - s->tx_enabled[port->line] = 1; + if (!s->tx_enabled) { + s->tx_enabled = 1; + enable_irq(port->irq); } } -static void uart_clps711x_stop_rx(struct uart_port *port) -{ - disable_irq(RX_IRQ(port)); -} - -static void uart_clps711x_enable_ms(struct uart_port *port) -{ - /* Do nothing */ -} - static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id) { struct uart_port *port = dev_id; - unsigned int status, ch, flg; + struct clps711x_port *s = dev_get_drvdata(port->dev); + unsigned int status, flg; + u32 sysflg; + u16 ch; for (;;) { - status = clps_readl(SYSFLG(port)); - if (status & SYSFLG_URXFE) + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + if (sysflg & SYSFLG_URXFE) break; - ch = clps_readw(UARTDR(port)); + ch = readw_relaxed(port->membase + UARTDR_OFFSET); status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR); ch &= 0xff; @@ -136,25 +151,31 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) struct uart_port *port = dev_id; struct clps711x_port *s = dev_get_drvdata(port->dev); struct circ_buf *xmit = &port->state->xmit; + u32 sysflg; if (port->x_char) { - clps_writew(port->x_char, UARTDR(port)); + writew_relaxed(port->x_char, port->membase + UARTDR_OFFSET); port->icount.tx++; port->x_char = 0; return IRQ_HANDLED; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - disable_irq_nosync(TX_IRQ(port)); - s->tx_enabled[port->line] = 0; + if (s->tx_enabled) { + disable_irq_nosync(port->irq); + s->tx_enabled = 0; + } return IRQ_HANDLED; } while (!uart_circ_empty(xmit)) { - clps_writew(xmit->buf[xmit->tail], UARTDR(port)); + writew_relaxed(xmit->buf[xmit->tail], + port->membase + UARTDR_OFFSET); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; - if (clps_readl(SYSFLG(port) & SYSFLG_UTXFF)) + + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + if (sysflg & SYSFLG_UTXFF) break; } @@ -166,20 +187,27 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) static unsigned int uart_clps711x_tx_empty(struct uart_port *port) { - return (clps_readl(SYSFLG(port) & SYSFLG_UBUSY)) ? 0 : TIOCSER_TEMT; + struct clps711x_port *s = dev_get_drvdata(port->dev); + u32 sysflg; + + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + + return (sysflg & SYSFLG_UBUSY) ? 0 : TIOCSER_TEMT; } static unsigned int uart_clps711x_get_mctrl(struct uart_port *port) { - unsigned int status, result = 0; + struct clps711x_port *s = dev_get_drvdata(port->dev); + unsigned int result = 0; + u32 sysflg; - if (port->line == 0) { - status = clps_readl(SYSFLG1); - if (status & SYSFLG1_DCD) + if (s->use_ms) { + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + if (sysflg & SYSFLG1_DCD) result |= TIOCM_CAR; - if (status & SYSFLG1_DSR) + if (sysflg & SYSFLG1_DSR) result |= TIOCM_DSR; - if (status & SYSFLG1_CTS) + if (sysflg & SYSFLG1_CTS) result |= TIOCM_CTS; } else result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; @@ -194,65 +222,43 @@ static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl) static void uart_clps711x_break_ctl(struct uart_port *port, int break_state) { - unsigned long flags; unsigned int ubrlcr; - spin_lock_irqsave(&port->lock, flags); - - ubrlcr = clps_readl(UBRLCR(port)); + ubrlcr = readl_relaxed(port->membase + UBRLCR_OFFSET); if (break_state) ubrlcr |= UBRLCR_BREAK; else ubrlcr &= ~UBRLCR_BREAK; - clps_writel(ubrlcr, UBRLCR(port)); - - spin_unlock_irqrestore(&port->lock, flags); + writel_relaxed(ubrlcr, port->membase + UBRLCR_OFFSET); } static int uart_clps711x_startup(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); - int ret; - - s->tx_enabled[port->line] = 1; - /* Allocate the IRQs */ - ret = devm_request_irq(port->dev, TX_IRQ(port), uart_clps711x_int_tx, - 0, UART_CLPS711X_NAME " TX", port); - if (ret) - return ret; - - ret = devm_request_irq(port->dev, RX_IRQ(port), uart_clps711x_int_rx, - 0, UART_CLPS711X_NAME " RX", port); - if (ret) { - devm_free_irq(port->dev, TX_IRQ(port), port); - return ret; - } /* Disable break */ - clps_writel(clps_readl(UBRLCR(port)) & ~UBRLCR_BREAK, UBRLCR(port)); + writel_relaxed(readl_relaxed(port->membase + UBRLCR_OFFSET) & + ~UBRLCR_BREAK, port->membase + UBRLCR_OFFSET); /* Enable the port */ - clps_writel(clps_readl(SYSCON(port)) | SYSCON_UARTEN, SYSCON(port)); - - return 0; + return regmap_update_bits(s->syscon, SYSCON_OFFSET, + SYSCON_UARTEN, SYSCON_UARTEN); } static void uart_clps711x_shutdown(struct uart_port *port) { - /* Free the interrupts */ - devm_free_irq(port->dev, TX_IRQ(port), port); - devm_free_irq(port->dev, RX_IRQ(port), port); + struct clps711x_port *s = dev_get_drvdata(port->dev); /* Disable the port */ - clps_writel(clps_readl(SYSCON(port)) & ~SYSCON_UARTEN, SYSCON(port)); + regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0); } static void uart_clps711x_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - unsigned int ubrlcr, baud, quot; - unsigned long flags; + u32 ubrlcr; + unsigned int baud, quot; /* Mask termios capabilities we don't support */ termios->c_cflag &= ~CMSPAR; @@ -291,8 +297,6 @@ static void uart_clps711x_set_termios(struct uart_port *port, /* Enable FIFO */ ubrlcr |= UBRLCR_FIFOEN; - spin_lock_irqsave(&port->lock, flags); - /* Set read status mask */ port->read_status_mask = UARTDR_OVERR; if (termios->c_iflag & INPCK) @@ -306,9 +310,7 @@ static void uart_clps711x_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); - clps_writel(ubrlcr | (quot - 1), UBRLCR(port)); - - spin_unlock_irqrestore(&port->lock, flags); + writel_relaxed(ubrlcr | (quot - 1), port->membase + UBRLCR_OFFSET); } static const char *uart_clps711x_type(struct uart_port *port) @@ -322,14 +324,12 @@ static void uart_clps711x_config_port(struct uart_port *port, int flags) port->type = PORT_CLPS711X; } -static void uart_clps711x_release_port(struct uart_port *port) +static void uart_clps711x_nop_void(struct uart_port *port) { - /* Do nothing */ } -static int uart_clps711x_request_port(struct uart_port *port) +static int uart_clps711x_nop_int(struct uart_port *port) { - /* Do nothing */ return 0; } @@ -339,181 +339,241 @@ static const struct uart_ops uart_clps711x_ops = { .get_mctrl = uart_clps711x_get_mctrl, .stop_tx = uart_clps711x_stop_tx, .start_tx = uart_clps711x_start_tx, - .stop_rx = uart_clps711x_stop_rx, - .enable_ms = uart_clps711x_enable_ms, + .stop_rx = uart_clps711x_nop_void, + .enable_ms = uart_clps711x_nop_void, .break_ctl = uart_clps711x_break_ctl, .startup = uart_clps711x_startup, .shutdown = uart_clps711x_shutdown, .set_termios = uart_clps711x_set_termios, .type = uart_clps711x_type, .config_port = uart_clps711x_config_port, - .release_port = uart_clps711x_release_port, - .request_port = uart_clps711x_request_port, + .release_port = uart_clps711x_nop_void, + .request_port = uart_clps711x_nop_int, }; #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE static void uart_clps711x_console_putchar(struct uart_port *port, int ch) { - while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF) - barrier(); + struct clps711x_port *s = dev_get_drvdata(port->dev); + u32 sysflg; + + do { + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + } while (sysflg & SYSFLG_UTXFF); - clps_writew(ch, UARTDR(port)); + writew_relaxed(ch, port->membase + UARTDR_OFFSET); } static void uart_clps711x_console_write(struct console *co, const char *c, unsigned n) { - struct clps711x_port *s = (struct clps711x_port *)co->data; - struct uart_port *port = &s->port[co->index]; - u32 syscon; - - /* Ensure that the port is enabled */ - syscon = clps_readl(SYSCON(port)); - clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); + struct uart_port *port = clps711x_uart.state[co->index].uart_port; + struct clps711x_port *s = dev_get_drvdata(port->dev); + u32 sysflg; uart_console_write(port, c, n, uart_clps711x_console_putchar); /* Wait for transmitter to become empty */ - while (clps_readl(SYSFLG(port)) & SYSFLG_UBUSY) - barrier(); - - /* Restore the uart state */ - clps_writel(syscon, SYSCON(port)); + do { + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + } while (sysflg & SYSFLG_UBUSY); } -static void uart_clps711x_console_get_options(struct uart_port *port, - int *baud, int *parity, - int *bits) +static int uart_clps711x_console_setup(struct console *co, char *options) { - if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { - unsigned int ubrlcr, quot; + int baud = 38400, bits = 8, parity = 'n', flow = 'n'; + int ret, index = co->index; + struct clps711x_port *s; + struct uart_port *port; + u32 ubrlcr, syscon; + unsigned int quot; - ubrlcr = clps_readl(UBRLCR(port)); + if (index < 0 || index >= UART_CLPS711X_NR) + return -EINVAL; - *parity = 'n'; - if (ubrlcr & UBRLCR_PRTEN) { - if (ubrlcr & UBRLCR_EVENPRT) - *parity = 'e'; - else - *parity = 'o'; - } + port = clps711x_uart.state[index].uart_port; + if (!port) + return -ENODEV; - if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) - *bits = 7; - else - *bits = 8; + s = dev_get_drvdata(port->dev); - quot = ubrlcr & UBRLCR_BAUD_MASK; - *baud = port->uartclk / (16 * (quot + 1)); - } -} + if (!options) { + regmap_read(s->syscon, SYSCON_OFFSET, &syscon); + if (syscon & SYSCON_UARTEN) { + ubrlcr = readl_relaxed(port->membase + UBRLCR_OFFSET); -static int uart_clps711x_console_setup(struct console *co, char *options) -{ - int baud = 38400, bits = 8, parity = 'n', flow = 'n'; - struct clps711x_port *s = (struct clps711x_port *)co->data; - struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0]; + if (ubrlcr & UBRLCR_PRTEN) { + if (ubrlcr & UBRLCR_EVENPRT) + parity = 'e'; + else + parity = 'o'; + } - if (options) + if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) + bits = 7; + + quot = ubrlcr & UBRLCR_BAUD_MASK; + baud = port->uartclk / (16 * (quot + 1)); + } + } else uart_parse_options(options, &baud, &parity, &bits, &flow); - else - uart_clps711x_console_get_options(port, &baud, &parity, &bits); - return uart_set_options(port, co, baud, parity, bits, flow); + ret = uart_set_options(port, co, baud, parity, bits, flow); + if (ret) + return ret; + + return regmap_update_bits(s->syscon, SYSCON_OFFSET, + SYSCON_UARTEN, SYSCON_UARTEN); } + +static struct console clps711x_console = { + .name = UART_CLPS711X_DEVNAME, + .device = uart_console_device, + .write = uart_clps711x_console_write, + .setup = uart_clps711x_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; #endif static int uart_clps711x_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; + int ret, index = np ? of_alias_get_id(np, "serial") : pdev->id; struct clps711x_port *s; - int ret, i; + struct resource *res; + struct clk *uart_clk; - s = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_port), GFP_KERNEL); - if (!s) { - dev_err(&pdev->dev, "Error allocating port structure\n"); + if (index < 0 || index >= UART_CLPS711X_NR) + return -EINVAL; + + s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL); + if (!s) return -ENOMEM; + + uart_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(uart_clk)) + return PTR_ERR(uart_clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + s->port.membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(s->port.membase)) + return PTR_ERR(s->port.membase); + + s->port.irq = platform_get_irq(pdev, 0); + if (IS_ERR_VALUE(s->port.irq)) + return s->port.irq; + + s->rx_irq = platform_get_irq(pdev, 1); + if (IS_ERR_VALUE(s->rx_irq)) + return s->rx_irq; + + if (!np) { + char syscon_name[9]; + + sprintf(syscon_name, "syscon.%i", index + 1); + s->syscon = syscon_regmap_lookup_by_pdevname(syscon_name); + if (IS_ERR(s->syscon)) + return PTR_ERR(s->syscon); + + s->use_ms = !index; + } else { + s->syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); + if (IS_ERR(s->syscon)) + return PTR_ERR(s->syscon); + + if (!index) { + bool use_irda; + + s->use_ms = of_property_read_bool(np, "uart-use-ms"); + use_irda = of_property_read_bool(np, "uart-use-irda"); + regmap_update_bits(s->syscon, SYSCON_OFFSET, + SYSCON1_SIREN, + use_irda ? SYSCON1_SIREN : 0); + } } + + s->port.line = index; + s->port.dev = &pdev->dev; + s->port.iotype = UPIO_MEM32; + s->port.mapbase = res->start; + s->port.type = PORT_CLPS711X; + s->port.fifosize = 16; + s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; + s->port.uartclk = clk_get_rate(uart_clk); + s->port.ops = &uart_clps711x_ops; + platform_set_drvdata(pdev, s); - s->uart_clk = devm_clk_get(&pdev->dev, "uart"); - if (IS_ERR(s->uart_clk)) { - dev_err(&pdev->dev, "Can't get UART clocks\n"); - return PTR_ERR(s->uart_clk); - } + ret = uart_add_one_port(&clps711x_uart, &s->port); + if (ret) + return ret; - s->uart.owner = THIS_MODULE; - s->uart.dev_name = "ttyCL"; - s->uart.major = UART_CLPS711X_MAJOR; - s->uart.minor = UART_CLPS711X_MINOR; - s->uart.nr = UART_CLPS711X_NR; -#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE - s->uart.cons = &s->console; - s->uart.cons->device = uart_console_device; - s->uart.cons->write = uart_clps711x_console_write; - s->uart.cons->setup = uart_clps711x_console_setup; - s->uart.cons->flags = CON_PRINTBUFFER; - s->uart.cons->index = -1; - s->uart.cons->data = s; - strcpy(s->uart.cons->name, "ttyCL"); -#endif - ret = uart_register_driver(&s->uart); + /* Disable port */ + if (!uart_console(&s->port)) + regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0); + + s->tx_enabled = 1; + + ret = devm_request_irq(&pdev->dev, s->port.irq, uart_clps711x_int_tx, 0, + dev_name(&pdev->dev), &s->port); if (ret) { - dev_err(&pdev->dev, "Registering UART driver failed\n"); + uart_remove_one_port(&clps711x_uart, &s->port); return ret; } - for (i = 0; i < UART_CLPS711X_NR; i++) { - s->port[i].line = i; - s->port[i].dev = &pdev->dev; - s->port[i].irq = TX_IRQ(&s->port[i]); - s->port[i].iobase = SYSCON(&s->port[i]); - s->port[i].type = PORT_CLPS711X; - s->port[i].fifosize = 16; - s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; - s->port[i].uartclk = clk_get_rate(s->uart_clk); - s->port[i].ops = &uart_clps711x_ops; - WARN_ON(uart_add_one_port(&s->uart, &s->port[i])); - } + ret = devm_request_irq(&pdev->dev, s->rx_irq, uart_clps711x_int_rx, 0, + dev_name(&pdev->dev), &s->port); + if (ret) + uart_remove_one_port(&clps711x_uart, &s->port); - return 0; + return ret; } static int uart_clps711x_remove(struct platform_device *pdev) { struct clps711x_port *s = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < UART_CLPS711X_NR; i++) - uart_remove_one_port(&s->uart, &s->port[i]); - - uart_unregister_driver(&s->uart); - return 0; + return uart_remove_one_port(&clps711x_uart, &s->port); } -static struct platform_driver clps711x_uart_driver = { +static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = { + { .compatible = "cirrus,clps711x-uart", }, + { } +}; +MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids); + +static struct platform_driver clps711x_uart_platform = { .driver = { - .name = UART_CLPS711X_NAME, - .owner = THIS_MODULE, + .name = "clps711x-uart", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(clps711x_uart_dt_ids), }, .probe = uart_clps711x_probe, .remove = uart_clps711x_remove, }; -module_platform_driver(clps711x_uart_driver); - -static struct platform_device clps711x_uart_device = { - .name = UART_CLPS711X_NAME, -}; static int __init uart_clps711x_init(void) { - return platform_device_register(&clps711x_uart_device); + int ret; + +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + clps711x_uart.cons = &clps711x_console; + clps711x_console.data = &clps711x_uart; +#endif + + ret = uart_register_driver(&clps711x_uart); + if (ret) + return ret; + + return platform_driver_register(&clps711x_uart_platform); } module_init(uart_clps711x_init); static void __exit uart_clps711x_exit(void) { - platform_device_unregister(&clps711x_uart_device); + platform_driver_unregister(&clps711x_uart_platform); + uart_unregister_driver(&clps711x_uart); } module_exit(uart_clps711x_exit); -- cgit v0.10.2 From 3c0f0f9f7d9b558089ce6701f72a932b63192384 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 11 Dec 2013 19:52:35 +0400 Subject: serial: clps711x: dts: Add bindings documentation for the CLPS711X UART This patch adds the devicetree documentation for the Cirrus Logic CLPS711X UART. Signed-off-by: Alexander Shiyan Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt b/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt new file mode 100644 index 0000000..8bbdd21 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt @@ -0,0 +1,29 @@ +* Cirrus Logic CLPS711X Universal Asynchronous Receiver/Transmitter (UART) + +Required properties: +- compatible: Should be "cirrus,clps711x-uart". +- reg: Address and length of the register set for the device. +- interrupts: Should contain UART TX and RX interrupt. +- clocks: Should contain UART core clock number. +- syscon: Phandle to SYSCON node, which contain UART control bits. + +Optional properties: +- uart-use-ms: Indicate the UART has modem signal (DCD, DSR, CTS). +- uart-use-irda: Indicate the UART use IRDA mode. + +Note: Each UART port should have an alias correctly numbered +in "aliases" node. + +Example: + aliases { + serial0 = &uart1; + }; + + uart1: uart@80000480 { + compatible = "cirrus,clps711x-uart"; + reg = <0x80000480 0x80>; + interrupts = <12 13>; + clocks = <&clks 11>; + syscon = <&syscon1>; + uart-use-ms; + }; -- cgit v0.10.2 From 4d0ed18277cc6f07513ee0b04475f19cd69e75ef Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 10 Dec 2013 17:12:02 -0500 Subject: n_tty: Fix buffer overruns with larger-than-4k pastes readline() inadvertently triggers an error recovery path when pastes larger than 4k overrun the line discipline buffer. The error recovery path discards input when the line discipline buffer is full and operating in canonical mode and no newline has been received. Because readline() changes the termios to non-canonical mode to read the line char-by-char, the line discipline buffer can become full, and then when readline() restores termios back to canonical mode for the caller, the now-full line discipline buffer triggers the error recovery. When changing termios from non-canon to canon mode and the read buffer contains data, simulate an EOF push _without_ the DISABLED_CHAR in the read buffer. Importantly for the readline() problem, the termios can be changed back to non-canonical mode without changes to the read buffer occurring; ie., as if the previous termios change had not happened (as long as no intervening read took place). Preserve existing userspace behavior which allows '\0's already received in non-canon mode to be read as '\0's in canon mode (rather than trigger add'l EOF pushes or an actual EOF). Patch based on original proposal and discussion here https://bugzilla.kernel.org/show_bug.cgi?id=55991 by Stas Sergeev Reported-by: Margarita Manterola Cc: Maximiliano Curia Cc: Pavel Machek Cc: Arkadiusz Miskiewicz Acked-by: Stas Sergeev 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 fdc2ecd..961e6a9 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -104,6 +104,7 @@ struct n_tty_data { /* must hold exclusive termios_rwsem to reset these */ unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; + unsigned char push:1; /* shared by producer and consumer */ char read_buf[N_TTY_BUF_SIZE]; @@ -341,6 +342,7 @@ static void reset_buffer_flags(struct n_tty_data *ldata) ldata->erasing = 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); + ldata->push = 0; } static void n_tty_packet_mode_flush(struct tty_struct *tty) @@ -1745,7 +1747,16 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) { bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); - ldata->line_start = ldata->canon_head = ldata->read_tail; + ldata->line_start = ldata->read_tail; + if (!L_ICANON(tty) || !read_cnt(ldata)) { + ldata->canon_head = ldata->read_tail; + ldata->push = 0; + } else { + set_bit((ldata->read_head - 1) & (N_TTY_BUF_SIZE - 1), + ldata->read_flags); + ldata->canon_head = ldata->read_head; + ldata->push = 1; + } ldata->erasing = 0; ldata->lnext = 0; } @@ -1951,6 +1962,12 @@ static int copy_from_read_buf(struct tty_struct *tty, * it copies one line of input up to and including the line-delimiting * character into the user-space buffer. * + * NB: When termios is changed from non-canonical to canonical mode and + * the read buffer contains data, n_tty_set_termios() simulates an EOF + * push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer. + * This causes data already processed as input to be immediately available + * as input although a newline has not been received. + * * Called under the atomic_read_lock mutex * * n_tty_read()/consumer path: @@ -1997,7 +2014,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, n += found; c = n; - if (found && read_buf(ldata, eol) == __DISABLED_CHAR) { + if (found && !ldata->push && read_buf(ldata, eol) == __DISABLED_CHAR) { n--; eof_push = !n && ldata->read_tail != ldata->line_start; } @@ -2024,7 +2041,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, ldata->read_tail += c; if (found) { - ldata->line_start = ldata->read_tail; + if (!ldata->push) + ldata->line_start = ldata->read_tail; + else + ldata->push = 0; tty_audit_push(tty); } return eof_push ? -EAGAIN : 0; -- cgit v0.10.2 From 8f898bfd048f65856d8911a89b1616ad3e677c6c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 17 Dec 2013 09:33:18 -0800 Subject: Revert "serial: pl011: use DMA RX polling by default" This reverts commit 17438217a6f5e33d920ed3821a4b857311cc2872 on request of Linus Walleij: Greg can you please drop or revert commit 17438217a6f5e33d920ed3821a4b857311cc2872 "serial: pl011: use DMA RX polling by default" from the TTY tree until this has been sorted out? Cc: Linus Walleij Cc: Guennadi Liakhovetski Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index c8cc8f0..a183ace 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -325,7 +325,7 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port * dmaengine_slave_config(chan, &rx_conf); uap->dmarx.chan = chan; - if (plat) { + if (plat && plat->dma_rx_poll_enable) { /* Set poll rate if specified. */ if (plat->dma_rx_poll_rate) { uap->dmarx.auto_poll_rate = false; diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index 0891ea0..0ddb5c0 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -203,6 +203,7 @@ struct amba_pl011_data { bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; + bool dma_rx_poll_enable; unsigned int dma_rx_poll_rate; unsigned int dma_rx_poll_timeout; void (*init) (void); -- cgit v0.10.2 From fe43390702a1b5741fdf217063b05c7612b38303 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 10 Dec 2013 10:18:58 +0000 Subject: serial: amba-pl011: use port lock to guard control register access When the pl011 is being used for a console, pl011_console_write forces the control register (CR) to enable the UART for transmission and then restores this to the original value afterwards. It does this while holding the port lock. Unfortunately, when the uart is started or shutdown - say in response to userland using the serial device for a terminal - then this updates the control register without any locking. This means we can have pl011_console_write Save CR pl011_startup Initialise CR, e.g. enable receive pl011_console_write Restore old CR with receive not enabled this result is a serial port which doesn't respond to any input. A similar race in reverse could happen when the device is shutdown. We can fix these problems by taking the port lock when updating CR. Signed-off-by: Jon Medhurst Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index a183ace..a576a5b 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1535,6 +1535,8 @@ static int pl011_startup(struct uart_port *port) /* * Provoke TX FIFO interrupt into asserting. */ + spin_lock_irq(&uap->port.lock); + cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE; writew(cr, uap->port.membase + UART011_CR); writew(0, uap->port.membase + UART011_FBRD); @@ -1559,6 +1561,8 @@ static int pl011_startup(struct uart_port *port) cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; writew(cr, uap->port.membase + UART011_CR); + spin_unlock_irq(&uap->port.lock); + /* * initialise the old status of the modem signals */ @@ -1627,11 +1631,13 @@ static void pl011_shutdown(struct uart_port *port) * it during startup(). */ uap->autorts = false; + spin_lock_irq(&uap->port.lock); cr = readw(uap->port.membase + UART011_CR); uap->old_cr = cr; cr &= UART011_CR_RTS | UART011_CR_DTR; cr |= UART01x_CR_UARTEN | UART011_CR_TXE; writew(cr, uap->port.membase + UART011_CR); + spin_unlock_irq(&uap->port.lock); /* * disable break condition and fifos -- cgit v0.10.2 From b60f2f66a58c43e8c928336ec72f64e72f1237ea Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 10 Dec 2013 10:18:59 +0000 Subject: serial: amba-pl011: factor out code for writing LCR_H register The code to cope with a split tx/rx LCR_H register is non-trivial so put it into it's own function to avoid duplication. Signed-off-by: Jon Medhurst Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index a576a5b..844bfba9 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1511,6 +1511,21 @@ static int pl011_hwinit(struct uart_port *port) return retval; } +static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h) +{ + writew(lcr_h, uap->port.membase + uap->lcrh_rx); + if (uap->lcrh_rx != uap->lcrh_tx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uap->port.membase + UART011_MIS); + writew(lcr_h, uap->port.membase + uap->lcrh_tx); + } +} + static int pl011_startup(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; @@ -1541,17 +1556,7 @@ static int pl011_startup(struct uart_port *port) writew(cr, uap->port.membase + UART011_CR); writew(0, uap->port.membase + UART011_FBRD); writew(1, uap->port.membase + UART011_IBRD); - writew(0, uap->port.membase + uap->lcrh_rx); - if (uap->lcrh_tx != uap->lcrh_rx) { - int i; - /* - * Wait 10 PCLKs before writing LCRH_TX register, - * to get this delay write read only register 10 times - */ - for (i = 0; i < 10; ++i) - writew(0xff, uap->port.membase + UART011_MIS); - writew(0, uap->port.membase + uap->lcrh_tx); - } + pl011_write_lcr_h(uap, 0); writew(0, uap->port.membase + UART01x_DR); while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) barrier(); @@ -1801,17 +1806,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, * UART011_FBRD & UART011_IBRD. * ----------^----------^----------^----------^----- */ - writew(lcr_h, port->membase + uap->lcrh_rx); - if (uap->lcrh_rx != uap->lcrh_tx) { - int i; - /* - * Wait 10 PCLKs before writing LCRH_TX register, - * to get this delay write read only register 10 times - */ - for (i = 0; i < 10; ++i) - writew(0xff, uap->port.membase + UART011_MIS); - writew(lcr_h, port->membase + uap->lcrh_tx); - } + pl011_write_lcr_h(uap, lcr_h); writew(old_cr, port->membase + UART011_CR); spin_unlock_irqrestore(&port->lock, flags); -- cgit v0.10.2 From 570d291048aecc02271884f6a5f50ad555f271a5 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 10 Dec 2013 10:19:00 +0000 Subject: serial: amba-pl011: preseserve hardware settings during initialisation During initialisation, a UART may already be in use for a console, so take care to preserve things like baud rate and data format to avoid corrupting console output. Signed-off-by: Jon Medhurst Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 844bfba9..182a922 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1529,7 +1529,7 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h) static int pl011_startup(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int cr; + unsigned int cr, lcr_h, fbrd, ibrd; int retval; retval = pl011_hwinit(port); @@ -1548,10 +1548,16 @@ static int pl011_startup(struct uart_port *port) writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); /* - * Provoke TX FIFO interrupt into asserting. + * Provoke TX FIFO interrupt into asserting. Taking care to preserve + * baud rate and data format specified by FBRD, IBRD and LCRH as the + * UART may already be in use as a console. */ spin_lock_irq(&uap->port.lock); + fbrd = readw(uap->port.membase + UART011_FBRD); + ibrd = readw(uap->port.membase + UART011_IBRD); + lcr_h = readw(uap->port.membase + uap->lcrh_rx); + cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE; writew(cr, uap->port.membase + UART011_CR); writew(0, uap->port.membase + UART011_FBRD); @@ -1561,6 +1567,10 @@ static int pl011_startup(struct uart_port *port) while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) barrier(); + writew(fbrd, uap->port.membase + UART011_FBRD); + writew(ibrd, uap->port.membase + UART011_IBRD); + pl011_write_lcr_h(uap, lcr_h); + /* restore RTS and DTR */ cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR); cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; -- cgit v0.10.2 From 30ae5859186f564d40cee04579e901e41c3826e2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 10 Dec 2013 14:54:42 +0100 Subject: serial: pl011: (cosmetic) remove superfluous register write In pl011_rx_chars() if pl011_dma_rx_trigger_dma() succeeds it will disable the receive interrupt, no need to do this again. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 182a922..ea6c2a3 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1214,8 +1214,8 @@ __acquires(&uap->port.lock) dev_dbg(uap->port.dev, "could not trigger RX DMA job " "fall back to interrupt mode again\n"); uap->im |= UART011_RXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); } else { - uap->im &= ~UART011_RXIM; #ifdef CONFIG_DMA_ENGINE /* Start Rx DMA poll */ if (uap->dmarx.poll_rate) { @@ -1227,8 +1227,6 @@ __acquires(&uap->port.lock) } #endif } - - writew(uap->im, uap->port.membase + UART011_IMSC); } spin_lock(&uap->port.lock); } -- cgit v0.10.2 From c25a1ad7077ab9914bad1374df8b9cdb9bf5f0ad Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 10 Dec 2013 14:54:47 +0100 Subject: serial: pl011: fix fall back from DMA to interrupt mode When falling back from DMA to interrupt mode the receive interrupt has to be re-enabled to catch new incoming data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index ea6c2a3..d58783d 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -967,6 +967,8 @@ static void pl011_dma_rx_poll(unsigned long args) spin_lock_irqsave(&uap->port.lock, flags); pl011_dma_rx_stop(uap); + uap->im |= UART011_RXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); spin_unlock_irqrestore(&uap->port.lock, flags); uap->dmarx.running = false; -- cgit v0.10.2 From 54af58363e43b557cf17374c12a825f5209ef464 Mon Sep 17 00:00:00 2001 From: Rashika Kheria Date: Mon, 16 Dec 2013 16:28:24 +0530 Subject: drivers: tty: Mark the functions as static in n_gsm.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Marks the functions gsm_cleanup_mux(), gsm_activate_mux(), gsm_free_mux(), gsm_alloc_mux() and gsm_change_mtu() as static in n_gsm.c because they are not used outside this file. Also, drop the EXPORT_SYMBOL_GPL for the above mentioned functions because nothing else in the kernel calls them. This eliminates the following warnings in n_gsm.c: drivers/tty/n_gsm.c:2022:6: warning: no previous prototype for ‘gsm_cleanup_mux’ [-Wmissing-prototypes] drivers/tty/n_gsm.c:2076:5: warning: no previous prototype for ‘gsm_activate_mux’ [-Wmissing-prototypes] drivers/tty/n_gsm.c:2120:6: warning: no previous prototype for ‘gsm_free_mux’ [-Wmissing-prototypes] drivers/tty/n_gsm.c:2156:17: warning: no previous prototype for ‘gsm_alloc_mux’ [-Wmissing-prototypes] drivers/tty/n_gsm.c:2714:5: warning: no previous prototype for ‘gsm_change_mtu’ [-Wmissing-prototypes] Signed-off-by: Rashika Kheria Reviewed-by: Josh Triplett Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 679294b..b94737b 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2020,7 +2020,7 @@ static void gsm_error(struct gsm_mux *gsm, * and then shut down each device hanging up the channels as we go. */ -void gsm_cleanup_mux(struct gsm_mux *gsm) +static void gsm_cleanup_mux(struct gsm_mux *gsm) { int i; struct gsm_dlci *dlci = gsm->dlci[0]; @@ -2065,7 +2065,6 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) kfree(txq); INIT_LIST_HEAD(&gsm->tx_list); } -EXPORT_SYMBOL_GPL(gsm_cleanup_mux); /** * gsm_activate_mux - generic GSM setup @@ -2076,7 +2075,7 @@ EXPORT_SYMBOL_GPL(gsm_cleanup_mux); * finally kick off connecting to DLCI 0 on the modem. */ -int gsm_activate_mux(struct gsm_mux *gsm) +static int gsm_activate_mux(struct gsm_mux *gsm) { struct gsm_dlci *dlci; int i = 0; @@ -2112,7 +2111,6 @@ int gsm_activate_mux(struct gsm_mux *gsm) gsm->dead = 0; /* Tty opens are now permissible */ return 0; } -EXPORT_SYMBOL_GPL(gsm_activate_mux); /** * gsm_free_mux - free up a mux @@ -2120,13 +2118,12 @@ EXPORT_SYMBOL_GPL(gsm_activate_mux); * * Dispose of allocated resources for a dead mux */ -void gsm_free_mux(struct gsm_mux *gsm) +static void gsm_free_mux(struct gsm_mux *gsm) { kfree(gsm->txframe); kfree(gsm->buf); kfree(gsm); } -EXPORT_SYMBOL_GPL(gsm_free_mux); /** * gsm_free_muxr - free up a mux @@ -2156,7 +2153,7 @@ static inline void mux_put(struct gsm_mux *gsm) * Creates a new mux ready for activation. */ -struct gsm_mux *gsm_alloc_mux(void) +static struct gsm_mux *gsm_alloc_mux(void) { struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL); if (gsm == NULL) @@ -2189,7 +2186,6 @@ struct gsm_mux *gsm_alloc_mux(void) return gsm; } -EXPORT_SYMBOL_GPL(gsm_alloc_mux); /** * gsmld_output - write to link @@ -2716,7 +2712,7 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, return; } -int gsm_change_mtu(struct net_device *net, int new_mtu) +static int gsm_change_mtu(struct net_device *net, int new_mtu) { struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); if ((new_mtu < 8) || (new_mtu > mux_net->dlci->gsm->mtu)) -- cgit v0.10.2 From 5855b2104b01e70e9db4352fce82689b214a6af2 Mon Sep 17 00:00:00 2001 From: Rashika Kheria Date: Mon, 16 Dec 2013 16:31:28 +0530 Subject: drivers: tty: Mark the function hvc_poll_init() as static in hvc_console.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark the function hvc_poll_init() as static in hvc/hvc_console.c because it is not used outside this file. This eliminates the following warning in hvc/hvc_console.c: drivers/tty/hvc/hvc_console.c:791:5: warning: no previous prototype for ‘hvc_poll_init’ [-Wmissing-prototypes] Signed-off-by: Rashika Kheria Reviewed-by: Josh Triplett Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 9eba119..50b4688 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -788,7 +788,7 @@ static int hvc_tiocmset(struct tty_struct *tty, } #ifdef CONFIG_CONSOLE_POLL -int hvc_poll_init(struct tty_driver *driver, int line, char *options) +static int hvc_poll_init(struct tty_driver *driver, int line, char *options) { return 0; } -- cgit v0.10.2 From 8be3da65d9222e1e63b7f9d001e872947588946f Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 17 Dec 2013 17:22:20 +0100 Subject: tty/serial: at91: document clock properties Document the clock properties required by the at91 usart driver. Signed-off-by: Boris BREZILLON Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/atmel-usart.txt b/Documentation/devicetree/bindings/serial/atmel-usart.txt index 2191dcb..9c5d19a 100644 --- a/Documentation/devicetree/bindings/serial/atmel-usart.txt +++ b/Documentation/devicetree/bindings/serial/atmel-usart.txt @@ -6,6 +6,9 @@ Required properties: additional mode or an USART new feature. - reg: Should contain registers location and length - interrupts: Should contain interrupt +- clock-names: tuple listing input clock names. + Required elements: "usart" +- clocks: phandles to input clocks. Optional properties: - atmel,use-dma-rx: use of PDC or DMA for receiving data @@ -26,6 +29,8 @@ Example: compatible = "atmel,at91sam9260-usart"; reg = <0xfff8c000 0x4000>; interrupts = <7>; + clocks = <&usart0_clk>; + clock-names = "usart"; atmel,use-dma-rx; atmel,use-dma-tx; }; @@ -35,6 +40,8 @@ Example: compatible = "atmel,at91sam9260-usart"; reg = <0xf001c000 0x100>; interrupts = <12 4 5>; + clocks = <&usart0_clk>; + clock-names = "usart"; atmel,use-dma-rx; atmel,use-dma-tx; dmas = <&dma0 2 0x3>, -- cgit v0.10.2 From be7065725590dd8fbf5d6a614bcfaec4a860b998 Mon Sep 17 00:00:00 2001 From: Chuansheng Liu Date: Wed, 18 Dec 2013 13:30:11 +0800 Subject: TTY/n_gsm: Removing the wrong tty_unlock/lock() in gsm_dlci_release() Commit 4d9b109060f690f5c835(tty: Prevent deadlock in n_gsm driver) tried to close all the virtual ports synchronously before closing the phycial ports, so the tty_vhangup() is used. But the tty_unlock/lock() is wrong: tty_release tty_ldisc_release tty_lock_pair(tty, o_tty) < == Here the tty is for physical port tty_ldisc_kill gsmld_close gsm_cleanup_mux gsm_dlci_release tty = tty_port_tty_get(&dlci->port) < == Here the tty(s) are for virtual port They are different ttys, so before tty_vhangup(virtual tty), do not need to call the tty_unlock(virtual tty) at all which causes unbalanced unlock warning. When enabling mutex debugging option, we will hit the below warning also: [ 99.276903] ===================================== [ 99.282172] [ BUG: bad unlock balance detected! ] [ 99.287442] 3.10.20-261976-gaec5ba0 #44 Tainted: G O [ 99.293972] ------------------------------------- [ 99.299240] mmgr/152 is trying to release lock (&tty->legacy_mutex) at: [ 99.306693] [] mutex_unlock+0xd/0x10 [ 99.311669] but there are no more locks to release! [ 99.317131] [ 99.317131] other info that might help us debug this: [ 99.324440] 3 locks held by mmgr/152: [ 99.328542] #0: (&tty->legacy_mutex/1){......}, at: [] tty_lock_nested+0x40/0x90 [ 99.338116] #1: (&tty->ldisc_mutex){......}, at: [] tty_ldisc_kill+0x22/0xd0 [ 99.347284] #2: (&gsm->mutex){......}, at: [] gsm_cleanup_mux+0x73/0x170 [ 99.356060] [ 99.356060] stack backtrace: [ 99.360932] CPU: 0 PID: 152 Comm: mmgr Tainted: G O 3.10.20-261976-gaec5ba0 #44 [ 99.370086] ef4a4de0 ef4a4de0 ef4c1d98 c1b27b91 ef4c1db8 c1292655 c1dd10f5 c1b2dcad [ 99.378921] c1b2dcad ef4a4de0 ef4a528c ffffffff ef4c1dfc c12930dd 00000246 00000000 [ 99.387754] 00000000 00000000 c15e1926 00000000 00000001 ddfa7530 00000003 c1b2dcad [ 99.396588] Call Trace: [ 99.399326] [] dump_stack+0x16/0x18 [ 99.404307] [] print_unlock_imbalance_bug+0xe5/0xf0 [ 99.410840] [] ? mutex_unlock+0xd/0x10 [ 99.416110] [] ? mutex_unlock+0xd/0x10 [ 99.421382] [] lock_release_non_nested+0x1cd/0x210 [ 99.427818] [] ? gsm_destroy_network+0x36/0x130 [ 99.433964] [] ? mutex_unlock+0xd/0x10 [ 99.439235] [] lock_release+0x82/0x1c0 [ 99.444505] [] ? mutex_unlock+0xd/0x10 [ 99.449776] [] ? mutex_unlock+0xd/0x10 [ 99.455047] [] __mutex_unlock_slowpath+0x5f/0xd0 [ 99.461288] [] mutex_unlock+0xd/0x10 [ 99.466365] [] tty_unlock+0x21/0x50 [ 99.471345] [] gsm_cleanup_mux+0xc1/0x170 [ 99.476906] [] gsmld_close+0x52/0x90 [ 99.481983] [] tty_ldisc_close.isra.1+0x35/0x50 [ 99.488127] [] tty_ldisc_kill+0x2c/0xd0 [ 99.493494] [] tty_ldisc_release+0x2f/0x50 [ 99.499152] [] tty_release+0x37c/0x4b0 [ 99.504424] [] ? mutex_unlock+0xd/0x10 [ 99.509695] [] ? mutex_unlock+0xd/0x10 [ 99.514967] [] ? eventpoll_release_file+0x7e/0x90 [ 99.521307] [] __fput+0xd9/0x200 [ 99.525996] [] ____fput+0xd/0x10 [ 99.530685] [] task_work_run+0x81/0xb0 [ 99.535957] [] do_notify_resume+0x49/0x70 [ 99.541520] [] work_notifysig+0x29/0x31 [ 99.546897] ------------[ cut here ]------------ So here we can call tty_vhangup() directly which is for virtual port. Reviewed-by: Chao Bi Signed-off-by: Liu, Chuansheng Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index b94737b..f34461c 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1705,11 +1705,8 @@ static void gsm_dlci_release(struct gsm_dlci *dlci) gsm_destroy_network(dlci); mutex_unlock(&dlci->mutex); - /* tty_vhangup needs the tty_lock, so unlock and - relock after doing the hangup. */ - tty_unlock(tty); tty_vhangup(tty); - tty_lock(tty); + tty_port_tty_set(&dlci->port, NULL); tty_kref_put(tty); } -- cgit v0.10.2 From e12348c6d0f720f0f8d665847057b5752e563982 Mon Sep 17 00:00:00 2001 From: Chuansheng Liu Date: Wed, 18 Dec 2013 15:48:23 +0800 Subject: tty: Removing the deprecated function tty_vhangup_locked() The function tty_vhangup_locked() was deprecated, removed it from the tty.h also. Signed-off-by: Liu, Chuansheng Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/tty.h b/include/linux/tty.h index ad98b437..978d0f0 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -427,7 +427,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_vhangup_locked(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); -- cgit v0.10.2 From 9c5320f8d7d9a2cf623e65d50e1113f34d9b9eb1 Mon Sep 17 00:00:00 2001 From: Jonathan Woithe Date: Mon, 9 Dec 2013 16:33:08 +1030 Subject: serial: 8250: Fix initialisation of Quatech cards with the AMCC PCI chip Fix the initialisation of older Quatech serial cards which are fitted with the AMCC PCI Matchmaker interface chip. Signed-off-by: Jonathan Woithe (jwoithe@just42.net) Cc: stable 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 4697a51..6063d15 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1259,10 +1259,10 @@ static int pci_quatech_init(struct pci_dev *dev) unsigned long base = pci_resource_start(dev, 0); if (base) { u32 tmp; - outl(inl(base + 0x38), base + 0x38); + outl(inl(base + 0x38) | 0x00002000, base + 0x38); tmp = inl(base + 0x3c); outl(tmp | 0x01000000, base + 0x3c); - outl(tmp, base + 0x3c); + outl(tmp &= ~0x01000000, base + 0x3c); } } return 0; -- cgit v0.10.2 From 48c0247d7b7bf58abb85a39021099529df365c4d Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Mon, 9 Dec 2013 12:11:15 +0100 Subject: serial: add support for 200 v3 series Titan card Signed-off-by: Yegor Yefremov Cc: stable 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 6063d15..77d0aca 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1744,6 +1744,7 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_DEVICE_ID_TITAN_800E 0xA014 #define PCI_DEVICE_ID_TITAN_200EI 0xA016 #define PCI_DEVICE_ID_TITAN_200EISI 0xA017 +#define PCI_DEVICE_ID_TITAN_200V3 0xA306 #define PCI_DEVICE_ID_TITAN_400V3 0xA310 #define PCI_DEVICE_ID_TITAN_410V3 0xA312 #define PCI_DEVICE_ID_TITAN_800V3 0xA314 @@ -4427,6 +4428,9 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_4_921600 }, -- cgit v0.10.2 From 4903713cac8bd41b576e4e3eff3d54bd199a7737 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Fri, 20 Dec 2013 00:02:50 +0400 Subject: serial: Remove old SC26XX driver New driver for NXP (Philips) UART ICs was introduced in September 2012. Old driver no longer used anywhere, this patch removes it. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index a3817ab..7fbbbad 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1146,31 +1146,13 @@ config SERIAL_QE This driver supports the QE serial ports on Freescale embedded PowerPC that contain a QUICC Engine. -config SERIAL_SC26XX - tristate "SC2681/SC2692 serial port support" - depends on SNI_RM - select SERIAL_CORE - help - This is a driver for the onboard serial ports of - older RM400 machines. - -config SERIAL_SC26XX_CONSOLE - bool "Console on SC2681/SC2692 serial port" - depends on SERIAL_SC26XX=y - select SERIAL_CORE_CONSOLE - help - Support for Console on SC2681/SC2692 serial ports. - config SERIAL_SCCNXP tristate "SCCNXP serial port support" - depends on !SERIAL_SC26XX select SERIAL_CORE - default n help This selects support for an advanced UART from NXP (Philips). Supported ICs are SCC2681, SCC2691, SCC2692, SC28L91, SC28L92, SC28L202, SCC68681 and SCC68692. - Positioned as a replacement for the driver SC26XX. config SERIAL_SCCNXP_CONSOLE bool "Console on SCCNXP serial port" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 3068c77..3680854 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -47,7 +47,6 @@ obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o -obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c deleted file mode 100644 index 887b4f7..0000000 --- a/drivers/tty/serial/sc26xx.c +++ /dev/null @@ -1,749 +0,0 @@ -/* - * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices. - * - * Copyright (C) 2006,2007 Thomas Bogendörfer (tsbogend@alpha.franken.de) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#warning "Please try migrate to use new driver SCCNXP and report the status" \ - "in the linux-serial mailing list." - -#if defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - -#define SC26XX_MAJOR 204 -#define SC26XX_MINOR_START 205 -#define SC26XX_NR 2 - -struct uart_sc26xx_port { - struct uart_port port[2]; - u8 dsr_mask[2]; - u8 cts_mask[2]; - u8 dcd_mask[2]; - u8 ri_mask[2]; - u8 dtr_mask[2]; - u8 rts_mask[2]; - u8 imr; -}; - -/* register common to both ports */ -#define RD_ISR 0x14 -#define RD_IPR 0x34 - -#define WR_ACR 0x10 -#define WR_IMR 0x14 -#define WR_OPCR 0x34 -#define WR_OPR_SET 0x38 -#define WR_OPR_CLR 0x3C - -/* access common register */ -#define READ_SC(p, r) readb((p)->membase + RD_##r) -#define WRITE_SC(p, r, v) writeb((v), (p)->membase + WR_##r) - -/* register per port */ -#define RD_PORT_MRx 0x00 -#define RD_PORT_SR 0x04 -#define RD_PORT_RHR 0x0c - -#define WR_PORT_MRx 0x00 -#define WR_PORT_CSR 0x04 -#define WR_PORT_CR 0x08 -#define WR_PORT_THR 0x0c - -/* SR bits */ -#define SR_BREAK (1 << 7) -#define SR_FRAME (1 << 6) -#define SR_PARITY (1 << 5) -#define SR_OVERRUN (1 << 4) -#define SR_TXRDY (1 << 2) -#define SR_RXRDY (1 << 0) - -#define CR_RES_MR (1 << 4) -#define CR_RES_RX (2 << 4) -#define CR_RES_TX (3 << 4) -#define CR_STRT_BRK (6 << 4) -#define CR_STOP_BRK (7 << 4) -#define CR_DIS_TX (1 << 3) -#define CR_ENA_TX (1 << 2) -#define CR_DIS_RX (1 << 1) -#define CR_ENA_RX (1 << 0) - -/* ISR bits */ -#define ISR_RXRDYB (1 << 5) -#define ISR_TXRDYB (1 << 4) -#define ISR_RXRDYA (1 << 1) -#define ISR_TXRDYA (1 << 0) - -/* IMR bits */ -#define IMR_RXRDY (1 << 1) -#define IMR_TXRDY (1 << 0) - -/* access port register */ -static inline u8 read_sc_port(struct uart_port *p, u8 reg) -{ - return readb(p->membase + p->line * 0x20 + reg); -} - -static inline void write_sc_port(struct uart_port *p, u8 reg, u8 val) -{ - writeb(val, p->membase + p->line * 0x20 + reg); -} - -#define READ_SC_PORT(p, r) read_sc_port(p, RD_PORT_##r) -#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v) - -static void sc26xx_enable_irq(struct uart_port *port, int mask) -{ - struct uart_sc26xx_port *up; - int line = port->line; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - - up->imr |= mask << (line * 4); - WRITE_SC(port, IMR, up->imr); -} - -static void sc26xx_disable_irq(struct uart_port *port, int mask) -{ - struct uart_sc26xx_port *up; - int line = port->line; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - - up->imr &= ~(mask << (line * 4)); - WRITE_SC(port, IMR, up->imr); -} - -static bool receive_chars(struct uart_port *port) -{ - struct tty_port *tport = NULL; - int limit = 10000; - unsigned char ch; - char flag; - u8 status; - - /* FIXME what is this trying to achieve? */ - if (port->state != NULL) /* Unopened serial console */ - tport = &port->state->port; - - while (limit-- > 0) { - status = READ_SC_PORT(port, SR); - if (!(status & SR_RXRDY)) - break; - ch = READ_SC_PORT(port, RHR); - - flag = TTY_NORMAL; - port->icount.rx++; - - if (unlikely(status & (SR_BREAK | SR_FRAME | - SR_PARITY | SR_OVERRUN))) { - if (status & SR_BREAK) { - status &= ~(SR_PARITY | SR_FRAME); - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (status & SR_PARITY) - port->icount.parity++; - else if (status & SR_FRAME) - port->icount.frame++; - if (status & SR_OVERRUN) - port->icount.overrun++; - - status &= port->read_status_mask; - if (status & SR_BREAK) - flag = TTY_BREAK; - else if (status & SR_PARITY) - flag = TTY_PARITY; - else if (status & SR_FRAME) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(port, ch)) - continue; - - if (status & port->ignore_status_mask) - continue; - - tty_insert_flip_char(tport, ch, flag); - } - return !!tport; -} - -static void transmit_chars(struct uart_port *port) -{ - struct circ_buf *xmit; - - if (!port->state) - return; - - xmit = &port->state->xmit; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - sc26xx_disable_irq(port, IMR_TXRDY); - return; - } - while (!uart_circ_empty(xmit)) { - if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) - break; - - WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); -} - -static irqreturn_t sc26xx_interrupt(int irq, void *dev_id) -{ - struct uart_sc26xx_port *up = dev_id; - unsigned long flags; - bool push; - u8 isr; - - spin_lock_irqsave(&up->port[0].lock, flags); - - push = false; - isr = READ_SC(&up->port[0], ISR); - if (isr & ISR_TXRDYA) - transmit_chars(&up->port[0]); - if (isr & ISR_RXRDYA) - push = receive_chars(&up->port[0]); - - spin_unlock(&up->port[0].lock); - - if (push) - tty_flip_buffer_push(&up->port[0].state->port); - - spin_lock(&up->port[1].lock); - - push = false; - if (isr & ISR_TXRDYB) - transmit_chars(&up->port[1]); - if (isr & ISR_RXRDYB) - push = receive_chars(&up->port[1]); - - spin_unlock_irqrestore(&up->port[1].lock, flags); - - if (push) - tty_flip_buffer_push(&up->port[1].state->port); - - return IRQ_HANDLED; -} - -/* port->lock is not held. */ -static unsigned int sc26xx_tx_empty(struct uart_port *port) -{ - return (READ_SC_PORT(port, SR) & SR_TXRDY) ? TIOCSER_TEMT : 0; -} - -/* port->lock held by caller. */ -static void sc26xx_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_sc26xx_port *up; - int line = port->line; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - - if (up->dtr_mask[line]) { - if (mctrl & TIOCM_DTR) - WRITE_SC(port, OPR_SET, up->dtr_mask[line]); - else - WRITE_SC(port, OPR_CLR, up->dtr_mask[line]); - } - if (up->rts_mask[line]) { - if (mctrl & TIOCM_RTS) - WRITE_SC(port, OPR_SET, up->rts_mask[line]); - else - WRITE_SC(port, OPR_CLR, up->rts_mask[line]); - } -} - -/* port->lock is held by caller and interrupts are disabled. */ -static unsigned int sc26xx_get_mctrl(struct uart_port *port) -{ - struct uart_sc26xx_port *up; - int line = port->line; - unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; - u8 ipr; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - ipr = READ_SC(port, IPR) ^ 0xff; - - if (up->dsr_mask[line]) { - mctrl &= ~TIOCM_DSR; - mctrl |= ipr & up->dsr_mask[line] ? TIOCM_DSR : 0; - } - if (up->cts_mask[line]) { - mctrl &= ~TIOCM_CTS; - mctrl |= ipr & up->cts_mask[line] ? TIOCM_CTS : 0; - } - if (up->dcd_mask[line]) { - mctrl &= ~TIOCM_CAR; - mctrl |= ipr & up->dcd_mask[line] ? TIOCM_CAR : 0; - } - if (up->ri_mask[line]) { - mctrl &= ~TIOCM_RNG; - mctrl |= ipr & up->ri_mask[line] ? TIOCM_RNG : 0; - } - return mctrl; -} - -/* port->lock held by caller. */ -static void sc26xx_stop_tx(struct uart_port *port) -{ - return; -} - -/* port->lock held by caller. */ -static void sc26xx_start_tx(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - - while (!uart_circ_empty(xmit)) { - if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) { - sc26xx_enable_irq(port, IMR_TXRDY); - break; - } - WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } -} - -/* port->lock held by caller. */ -static void sc26xx_stop_rx(struct uart_port *port) -{ -} - -/* port->lock held by caller. */ -static void sc26xx_enable_ms(struct uart_port *port) -{ -} - -/* port->lock is not held. */ -static void sc26xx_break_ctl(struct uart_port *port, int break_state) -{ - if (break_state == -1) - WRITE_SC_PORT(port, CR, CR_STRT_BRK); - else - WRITE_SC_PORT(port, CR, CR_STOP_BRK); -} - -/* port->lock is not held. */ -static int sc26xx_startup(struct uart_port *port) -{ - sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY); - WRITE_SC(port, OPCR, 0); - - /* reset tx and rx */ - WRITE_SC_PORT(port, CR, CR_RES_RX); - WRITE_SC_PORT(port, CR, CR_RES_TX); - - /* start rx/tx */ - WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX); - - /* enable irqs */ - sc26xx_enable_irq(port, IMR_RXRDY); - return 0; -} - -/* port->lock is not held. */ -static void sc26xx_shutdown(struct uart_port *port) -{ - /* disable interrupst */ - sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY); - - /* stop tx/rx */ - WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX); -} - -/* port->lock is not held. */ -static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); - unsigned int quot = uart_get_divisor(port, baud); - unsigned int iflag, cflag; - unsigned long flags; - u8 mr1, mr2, csr; - - spin_lock_irqsave(&port->lock, flags); - - while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc) - udelay(2); - - WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX); - - iflag = termios->c_iflag; - cflag = termios->c_cflag; - - port->read_status_mask = SR_OVERRUN; - if (iflag & INPCK) - port->read_status_mask |= SR_PARITY | SR_FRAME; - if (iflag & (BRKINT | PARMRK)) - port->read_status_mask |= SR_BREAK; - - port->ignore_status_mask = 0; - if (iflag & IGNBRK) - port->ignore_status_mask |= SR_BREAK; - if ((cflag & CREAD) == 0) - port->ignore_status_mask |= SR_BREAK | SR_FRAME | - SR_PARITY | SR_OVERRUN; - - switch (cflag & CSIZE) { - case CS5: - mr1 = 0x00; - break; - case CS6: - mr1 = 0x01; - break; - case CS7: - mr1 = 0x02; - break; - default: - case CS8: - mr1 = 0x03; - break; - } - mr2 = 0x07; - if (cflag & CSTOPB) - mr2 = 0x0f; - if (cflag & PARENB) { - if (cflag & PARODD) - mr1 |= (1 << 2); - } else - mr1 |= (2 << 3); - - switch (baud) { - case 50: - csr = 0x00; - break; - case 110: - csr = 0x11; - break; - case 134: - csr = 0x22; - break; - case 200: - csr = 0x33; - break; - case 300: - csr = 0x44; - break; - case 600: - csr = 0x55; - break; - case 1200: - csr = 0x66; - break; - case 2400: - csr = 0x88; - break; - case 4800: - csr = 0x99; - break; - default: - case 9600: - csr = 0xbb; - break; - case 19200: - csr = 0xcc; - break; - } - - WRITE_SC_PORT(port, CR, CR_RES_MR); - WRITE_SC_PORT(port, MRx, mr1); - WRITE_SC_PORT(port, MRx, mr2); - - WRITE_SC(port, ACR, 0x80); - WRITE_SC_PORT(port, CSR, csr); - - /* reset tx and rx */ - WRITE_SC_PORT(port, CR, CR_RES_RX); - WRITE_SC_PORT(port, CR, CR_RES_TX); - - WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX); - while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc) - udelay(2); - - /* XXX */ - uart_update_timeout(port, cflag, - (port->uartclk / (16 * quot))); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *sc26xx_type(struct uart_port *port) -{ - return "SC26XX"; -} - -static void sc26xx_release_port(struct uart_port *port) -{ -} - -static int sc26xx_request_port(struct uart_port *port) -{ - return 0; -} - -static void sc26xx_config_port(struct uart_port *port, int flags) -{ -} - -static int sc26xx_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return -EINVAL; -} - -static struct uart_ops sc26xx_ops = { - .tx_empty = sc26xx_tx_empty, - .set_mctrl = sc26xx_set_mctrl, - .get_mctrl = sc26xx_get_mctrl, - .stop_tx = sc26xx_stop_tx, - .start_tx = sc26xx_start_tx, - .stop_rx = sc26xx_stop_rx, - .enable_ms = sc26xx_enable_ms, - .break_ctl = sc26xx_break_ctl, - .startup = sc26xx_startup, - .shutdown = sc26xx_shutdown, - .set_termios = sc26xx_set_termios, - .type = sc26xx_type, - .release_port = sc26xx_release_port, - .request_port = sc26xx_request_port, - .config_port = sc26xx_config_port, - .verify_port = sc26xx_verify_port, -}; - -static struct uart_port *sc26xx_port; - -#ifdef CONFIG_SERIAL_SC26XX_CONSOLE -static void sc26xx_console_putchar(struct uart_port *port, char c) -{ - unsigned long flags; - int limit = 1000000; - - spin_lock_irqsave(&port->lock, flags); - - while (limit-- > 0) { - if (READ_SC_PORT(port, SR) & SR_TXRDY) { - WRITE_SC_PORT(port, THR, c); - break; - } - udelay(2); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void sc26xx_console_write(struct console *con, const char *s, unsigned n) -{ - struct uart_port *port = sc26xx_port; - int i; - - for (i = 0; i < n; i++) { - if (*s == '\n') - sc26xx_console_putchar(port, '\r'); - sc26xx_console_putchar(port, *s++); - } -} - -static int __init sc26xx_console_setup(struct console *con, char *options) -{ - struct uart_port *port = sc26xx_port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (port->type != PORT_SC26XX) - return -1; - - printk(KERN_INFO "Console: ttySC%d (SC26XX)\n", con->index); - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, con, baud, parity, bits, flow); -} - -static struct uart_driver sc26xx_reg; -static struct console sc26xx_console = { - .name = "ttySC", - .write = sc26xx_console_write, - .device = uart_console_device, - .setup = sc26xx_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &sc26xx_reg, -}; -#define SC26XX_CONSOLE &sc26xx_console -#else -#define SC26XX_CONSOLE NULL -#endif - -static struct uart_driver sc26xx_reg = { - .owner = THIS_MODULE, - .driver_name = "SC26xx", - .dev_name = "ttySC", - .major = SC26XX_MAJOR, - .minor = SC26XX_MINOR_START, - .nr = SC26XX_NR, - .cons = SC26XX_CONSOLE, -}; - -static u8 sc26xx_flags2mask(unsigned int flags, unsigned int bitpos) -{ - unsigned int bit = (flags >> bitpos) & 15; - - return bit ? (1 << (bit - 1)) : 0; -} - -static void sc26xx_init_masks(struct uart_sc26xx_port *up, - int line, unsigned int data) -{ - up->dtr_mask[line] = sc26xx_flags2mask(data, 0); - up->rts_mask[line] = sc26xx_flags2mask(data, 4); - up->dsr_mask[line] = sc26xx_flags2mask(data, 8); - up->cts_mask[line] = sc26xx_flags2mask(data, 12); - up->dcd_mask[line] = sc26xx_flags2mask(data, 16); - up->ri_mask[line] = sc26xx_flags2mask(data, 20); -} - -static int sc26xx_probe(struct platform_device *dev) -{ - struct resource *res; - struct uart_sc26xx_port *up; - unsigned int *sc26xx_data = dev_get_platdata(&dev->dev); - int err; - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - up = kzalloc(sizeof *up, GFP_KERNEL); - if (unlikely(!up)) - return -ENOMEM; - - up->port[0].line = 0; - up->port[0].ops = &sc26xx_ops; - up->port[0].type = PORT_SC26XX; - up->port[0].uartclk = (29491200 / 16); /* arbitrary */ - - up->port[0].mapbase = res->start; - up->port[0].membase = ioremap_nocache(up->port[0].mapbase, 0x40); - up->port[0].iotype = UPIO_MEM; - up->port[0].irq = platform_get_irq(dev, 0); - - up->port[0].dev = &dev->dev; - - sc26xx_init_masks(up, 0, sc26xx_data[0]); - - sc26xx_port = &up->port[0]; - - up->port[1].line = 1; - up->port[1].ops = &sc26xx_ops; - up->port[1].type = PORT_SC26XX; - up->port[1].uartclk = (29491200 / 16); /* arbitrary */ - - up->port[1].mapbase = up->port[0].mapbase; - up->port[1].membase = up->port[0].membase; - up->port[1].iotype = UPIO_MEM; - up->port[1].irq = up->port[0].irq; - - up->port[1].dev = &dev->dev; - - sc26xx_init_masks(up, 1, sc26xx_data[1]); - - err = uart_register_driver(&sc26xx_reg); - if (err) - goto out_free_port; - - sc26xx_reg.tty_driver->name_base = sc26xx_reg.minor; - - err = uart_add_one_port(&sc26xx_reg, &up->port[0]); - if (err) - goto out_unregister_driver; - - err = uart_add_one_port(&sc26xx_reg, &up->port[1]); - if (err) - goto out_remove_port0; - - err = request_irq(up->port[0].irq, sc26xx_interrupt, 0, "sc26xx", up); - if (err) - goto out_remove_ports; - - platform_set_drvdata(dev, up); - return 0; - -out_remove_ports: - uart_remove_one_port(&sc26xx_reg, &up->port[1]); -out_remove_port0: - uart_remove_one_port(&sc26xx_reg, &up->port[0]); - -out_unregister_driver: - uart_unregister_driver(&sc26xx_reg); - -out_free_port: - kfree(up); - sc26xx_port = NULL; - return err; -} - - -static int __exit sc26xx_driver_remove(struct platform_device *dev) -{ - struct uart_sc26xx_port *up = platform_get_drvdata(dev); - - free_irq(up->port[0].irq, up); - - uart_remove_one_port(&sc26xx_reg, &up->port[0]); - uart_remove_one_port(&sc26xx_reg, &up->port[1]); - - uart_unregister_driver(&sc26xx_reg); - - kfree(up); - sc26xx_port = NULL; - - return 0; -} - -static struct platform_driver sc26xx_driver = { - .probe = sc26xx_probe, - .remove = sc26xx_driver_remove, - .driver = { - .name = "SC26xx", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(sc26xx_driver); - -MODULE_AUTHOR("Thomas Bogendörfer"); -MODULE_DESCRIPTION("SC681/SC2692 serial driver"); -MODULE_VERSION("1.0"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:SC26xx"); -- cgit v0.10.2 From 351d6204bfc814a1aee300296d2f54460ffff172 Mon Sep 17 00:00:00 2001 From: Qixue Xiao Date: Fri, 20 Dec 2013 17:51:12 +0800 Subject: tty: an overflow of multiplication in drivers/tty/cyclades.c there is an overflow in the code : cyz_polling_cycle = (arg * HZ) / 1000, the multiplicator arg comes from user, so it may be an overflow if arg is a big number. And the value of cyc_polling_cycle will be wrong when it is used next time. Reported-by: Qixue Xiao Suggested-by: Yongjian Xu Suggested-by: Yu Chen Signed-off-by: Qixue Xiao Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 33f83fe..a57bb5a 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -2709,6 +2709,8 @@ cy_ioctl(struct tty_struct *tty, break; #ifndef CONFIG_CYZ_INTR case CYZSETPOLLCYCLE: + if (arg > LONG_MAX / HZ) + return -ENODEV; cyz_polling_cycle = (arg * HZ) / 1000; break; case CYZGETPOLLCYCLE: -- cgit v0.10.2 From f8e87cb4a19aa5f5a1ce22e130da0f4a7fa2d5f3 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 2 Jan 2014 19:23:47 -0500 Subject: tty: delete non-required instances of include None of these files are actually using any __init type directives and hence don't need to include . Most are just a left over from __devinit and __cpuinit removal, or simply due to code getting copied from one driver to the next. Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index f17d2e4..75dc9d2 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -14,7 +14,6 @@ */ #include -#include #include #include #include diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index 347050e..7ae6c29 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c index 8fd72ff..ebd5bff 100644 --- a/drivers/tty/ipwireless/tty.c +++ b/drivers/tty/ipwireless/tty.c @@ -15,7 +15,6 @@ * Copyright (C) 2007 David Sterba */ -#include #include #include #include diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index aa30271..faa64e6 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -14,7 +14,6 @@ * raised, the LCR needs to be rewritten and the uart status register read. */ #include -#include #include #include #include diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index d1a9078..56c8723 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -18,7 +18,6 @@ */ #include -#include #include #include #include diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 77d0aca..50228ee 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -11,7 +11,6 @@ */ #undef DEBUG #include -#include #include #include #include diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 35d9ab9..682a2fb 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -12,7 +12,6 @@ * the Free Software Foundation; either version 2 of the License. */ #include -#include #include #include #include diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index 1b74b88..4d180c9 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c index 527a969..6d3b22e 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c index a4927e6..f46d2ca 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index 5dafcf1..5f673b7 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 2caf9c6..9924660 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 diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 3002e20..765125d 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 6458e11..2d822aa 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index c94d234..3f746c8 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include -- cgit v0.10.2 From 591cee0a35632031cd925392a1bce507dcfe9ea8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 2 Jan 2014 13:07:38 +0100 Subject: tty/amiserial: avoid interruptible_sleep_on interruptible_sleep_on is generally problematic and we want to get rid of it. In case of TIOCMIWAIT, that race is actually in user space and does not get fixed since we can only detect changes after entering the ioctl handler, but it removes one more caller. This instance can not be trivially replaced with wait_event, so I chose to open-code the wait loop using prepare_to_wait/finish_wait. Signed-off-by: Arnd Bergmann Cc: Geert Uytterhoeven Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 71630a2..979e7c3 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1248,6 +1248,8 @@ static int rs_ioctl(struct tty_struct *tty, struct async_icount cprev, cnow; /* kernel counter temps */ void __user *argp = (void __user *)arg; unsigned long flags; + DEFINE_WAIT(wait); + int ret; if (serial_paranoia_check(info, tty->name, "rs_ioctl")) return -ENODEV; @@ -1288,25 +1290,33 @@ static int rs_ioctl(struct tty_struct *tty, cprev = info->icount; local_irq_restore(flags); while (1) { - interruptible_sleep_on(&info->tport.delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; + prepare_to_wait(&info->tport.delta_msr_wait, + &wait, TASK_INTERRUPTIBLE); local_irq_save(flags); cnow = info->icount; /* atomic copy */ local_irq_restore(flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + ret = -EIO; /* no change => error */ + break; + } if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { - return 0; + ret = 0; + break; + } + schedule(); + /* see if a signal did it */ + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; } cprev = cnow; } - /* NOTREACHED */ + finish_wait(&info->tport.delta_msr_wait, &wait); + return ret; case TIOCSERGWILD: case TIOCSERSWILD: -- cgit v0.10.2 From b8c98ae49e8d53344b1d62417eea05ebc3cdbd78 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 2 Jan 2014 13:07:40 +0100 Subject: tty: synclink: avoid sleep_on race The four variants of the synclink driver use the same code in their open() callback to wait for a port in process of being closed, using interruptible_sleep_on, which is racy and going away soon. Making things worse, these functions hold the BTM while doing so, which means that if we ever enter this code path, we cannot actually continue since the other thread that is in process of closing the port can no longer get the BTM. This addresses both issues by using wait_event_interruptible_tty() instead. Signed-off-by: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index d39cca6..8320abd 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2511,8 +2511,8 @@ static int mgslpc_open(struct tty_struct *tty, struct file * filp) /* If port is closing, signal caller to try again */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING){ - if (port->flags & ASYNC_CLOSING) - interruptible_sleep_on(&port->close_wait); + wait_event_interruptible_tty(tty, port->close_wait, + !(port->flags & ASYNC_CLOSING)); retval = ((port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); goto cleanup; diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index e1ce141..5ae14b4 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -3404,8 +3404,8 @@ static int mgsl_open(struct tty_struct *tty, struct file * filp) /* If port is closing, signal caller to try again */ if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ - if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->port.close_wait); + wait_event_interruptible_tty(tty, info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); goto cleanup; diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 1abf946..c359a91 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -674,8 +674,8 @@ static int open(struct tty_struct *tty, struct file *filp) /* If port is closing, signal caller to try again */ if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ - if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->port.close_wait); + wait_event_interruptible_tty(tty, info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); goto cleanup; diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index dc6e969..144202e 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -754,8 +754,8 @@ static int open(struct tty_struct *tty, struct file *filp) /* If port is closing, signal caller to try again */ if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ - if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->port.close_wait); + wait_event_interruptible_tty(tty, info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); goto cleanup; -- cgit v0.10.2 From 71b9e8c6694f5cfe6cd37d53d6c24a33f1f59abd Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 31 Dec 2013 20:49:41 +0400 Subject: serial: clps711x: Add support for N_IRDA line discipline This patch replace custom handling of IrDA feature with N_IRDA line discipline, so IrDA mode can be used with irtty driver. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt b/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt index 8bbdd21..12f3cf8 100644 --- a/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt +++ b/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt @@ -9,7 +9,6 @@ Required properties: Optional properties: - uart-use-ms: Indicate the UART has modem signal (DCD, DSR, CTS). -- uart-use-irda: Indicate the UART use IRDA mode. Note: Each UART port should have an alias correctly numbered in "aliases" node. diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 4f59f1c..5a931f9 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -232,6 +232,16 @@ static void uart_clps711x_break_ctl(struct uart_port *port, int break_state) writel_relaxed(ubrlcr, port->membase + UBRLCR_OFFSET); } +static void uart_clps711x_set_ldisc(struct uart_port *port, int ld) +{ + 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); + } +} + static int uart_clps711x_startup(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); @@ -342,6 +352,7 @@ static const struct uart_ops uart_clps711x_ops = { .stop_rx = uart_clps711x_nop_void, .enable_ms = uart_clps711x_nop_void, .break_ctl = uart_clps711x_break_ctl, + .set_ldisc = uart_clps711x_set_ldisc, .startup = uart_clps711x_startup, .shutdown = uart_clps711x_shutdown, .set_termios = uart_clps711x_set_termios, @@ -482,15 +493,8 @@ static int uart_clps711x_probe(struct platform_device *pdev) if (IS_ERR(s->syscon)) return PTR_ERR(s->syscon); - if (!index) { - bool use_irda; - + if (!index) s->use_ms = of_property_read_bool(np, "uart-use-ms"); - use_irda = of_property_read_bool(np, "uart-use-irda"); - regmap_update_bits(s->syscon, SYSCON_OFFSET, - SYSCON1_SIREN, - use_irda ? SYSCON1_SIREN : 0); - } } s->port.line = index; -- cgit v0.10.2 From 093a9e2a20ebc414f8ca0d31709dab1040fa2886 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 31 Dec 2013 20:49:42 +0400 Subject: serial: clps711x: Enable driver compilation with COMPILE_TEST This helps increasing build testing coverage. To do this, read{write}_relaxed() functions was be replaced with simple read{write}() variants. Potential "uninitialized variable" warnings was be fixed if driver compiled without MFD_SYSCON. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 7fbbbad..441ada4 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -181,9 +181,8 @@ config SERIAL_KS8695_CONSOLE config SERIAL_CLPS711X tristate "CLPS711X serial port support" - depends on ARCH_CLPS711X + depends on ARCH_CLPS711X || COMPILE_TEST select SERIAL_CORE - default y help This enables the driver for the on-chip UARTs of the Cirrus Logic EP711x/EP721x/EP731x processors. diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 5a931f9..b0eacb8 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -99,15 +99,16 @@ static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id) struct uart_port *port = dev_id; struct clps711x_port *s = dev_get_drvdata(port->dev); unsigned int status, flg; - u32 sysflg; u16 ch; for (;;) { + u32 sysflg = 0; + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); if (sysflg & SYSFLG_URXFE) break; - ch = readw_relaxed(port->membase + UARTDR_OFFSET); + ch = readw(port->membase + UARTDR_OFFSET); status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR); ch &= 0xff; @@ -151,10 +152,9 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) struct uart_port *port = dev_id; struct clps711x_port *s = dev_get_drvdata(port->dev); struct circ_buf *xmit = &port->state->xmit; - u32 sysflg; if (port->x_char) { - writew_relaxed(port->x_char, port->membase + UARTDR_OFFSET); + writew(port->x_char, port->membase + UARTDR_OFFSET); port->icount.tx++; port->x_char = 0; return IRQ_HANDLED; @@ -169,8 +169,9 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) } while (!uart_circ_empty(xmit)) { - writew_relaxed(xmit->buf[xmit->tail], - port->membase + UARTDR_OFFSET); + u32 sysflg = 0; + + writew(xmit->buf[xmit->tail], port->membase + UARTDR_OFFSET); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; @@ -188,7 +189,7 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) static unsigned int uart_clps711x_tx_empty(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); - u32 sysflg; + u32 sysflg = 0; regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); @@ -199,9 +200,10 @@ static unsigned int uart_clps711x_get_mctrl(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); unsigned int result = 0; - u32 sysflg; if (s->use_ms) { + u32 sysflg = 0; + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); if (sysflg & SYSFLG1_DCD) result |= TIOCM_CAR; @@ -224,12 +226,12 @@ static void uart_clps711x_break_ctl(struct uart_port *port, int break_state) { unsigned int ubrlcr; - ubrlcr = readl_relaxed(port->membase + UBRLCR_OFFSET); + ubrlcr = readl(port->membase + UBRLCR_OFFSET); if (break_state) ubrlcr |= UBRLCR_BREAK; else ubrlcr &= ~UBRLCR_BREAK; - writel_relaxed(ubrlcr, port->membase + UBRLCR_OFFSET); + writel(ubrlcr, port->membase + UBRLCR_OFFSET); } static void uart_clps711x_set_ldisc(struct uart_port *port, int ld) @@ -247,8 +249,8 @@ static int uart_clps711x_startup(struct uart_port *port) struct clps711x_port *s = dev_get_drvdata(port->dev); /* Disable break */ - writel_relaxed(readl_relaxed(port->membase + UBRLCR_OFFSET) & - ~UBRLCR_BREAK, port->membase + UBRLCR_OFFSET); + writel(readl(port->membase + UBRLCR_OFFSET) & ~UBRLCR_BREAK, + port->membase + UBRLCR_OFFSET); /* Enable the port */ return regmap_update_bits(s->syscon, SYSCON_OFFSET, @@ -320,7 +322,7 @@ static void uart_clps711x_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); - writel_relaxed(ubrlcr | (quot - 1), port->membase + UBRLCR_OFFSET); + writel(ubrlcr | (quot - 1), port->membase + UBRLCR_OFFSET); } static const char *uart_clps711x_type(struct uart_port *port) @@ -366,13 +368,13 @@ static const struct uart_ops uart_clps711x_ops = { static void uart_clps711x_console_putchar(struct uart_port *port, int ch) { struct clps711x_port *s = dev_get_drvdata(port->dev); - u32 sysflg; + u32 sysflg = 0; do { regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); } while (sysflg & SYSFLG_UTXFF); - writew_relaxed(ch, port->membase + UARTDR_OFFSET); + writew(ch, port->membase + UARTDR_OFFSET); } static void uart_clps711x_console_write(struct console *co, const char *c, @@ -380,7 +382,7 @@ static void uart_clps711x_console_write(struct console *co, const char *c, { struct uart_port *port = clps711x_uart.state[co->index].uart_port; struct clps711x_port *s = dev_get_drvdata(port->dev); - u32 sysflg; + u32 sysflg = 0; uart_console_write(port, c, n, uart_clps711x_console_putchar); @@ -396,8 +398,8 @@ static int uart_clps711x_console_setup(struct console *co, char *options) int ret, index = co->index; struct clps711x_port *s; struct uart_port *port; - u32 ubrlcr, syscon; unsigned int quot; + u32 ubrlcr; if (index < 0 || index >= UART_CLPS711X_NR) return -EINVAL; @@ -409,9 +411,11 @@ static int uart_clps711x_console_setup(struct console *co, char *options) s = dev_get_drvdata(port->dev); if (!options) { + u32 syscon = 0; + regmap_read(s->syscon, SYSCON_OFFSET, &syscon); if (syscon & SYSCON_UARTEN) { - ubrlcr = readl_relaxed(port->membase + UBRLCR_OFFSET); + ubrlcr = readl(port->membase + UBRLCR_OFFSET); if (ubrlcr & UBRLCR_PRTEN) { if (ubrlcr & UBRLCR_EVENPRT) -- cgit v0.10.2 From 99e626f50477e3b14c9be43892c33ab75b2e5bf9 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Fri, 3 Jan 2014 15:44:06 +0800 Subject: serial: sirf: use PM macro initialize PM functions use SET_SYSTEM_SLEEP_PM_OPS to initialize suspend/resume functions instead of legacy suspend and resume entries of platform_driver. this will add hibernation support automatically as suspend to disk entries are also set. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index f186a8f..6fea79b 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1518,32 +1518,37 @@ static int sirfsoc_uart_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP static int -sirfsoc_uart_suspend(struct platform_device *pdev, pm_message_t state) +sirfsoc_uart_suspend(struct device *pdev) { - struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); + struct sirfsoc_uart_port *sirfport = dev_get_drvdata(pdev); struct uart_port *port = &sirfport->port; uart_suspend_port(&sirfsoc_uart_drv, port); return 0; } -static int sirfsoc_uart_resume(struct platform_device *pdev) +static int sirfsoc_uart_resume(struct device *pdev) { - struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); + struct sirfsoc_uart_port *sirfport = dev_get_drvdata(pdev); struct uart_port *port = &sirfport->port; uart_resume_port(&sirfsoc_uart_drv, port); return 0; } +#endif + +static const struct dev_pm_ops sirfsoc_uart_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sirfsoc_uart_suspend, sirfsoc_uart_resume) +}; static struct platform_driver sirfsoc_uart_driver = { .probe = sirfsoc_uart_probe, .remove = sirfsoc_uart_remove, - .suspend = sirfsoc_uart_suspend, - .resume = sirfsoc_uart_resume, .driver = { .name = SIRFUART_PORT_NAME, .owner = THIS_MODULE, .of_match_table = sirfsoc_uart_ids, + .pm = &sirfsoc_uart_pm_ops, }, }; -- cgit v0.10.2 From 388faf9ffdaf92c81243514a2dd4c6ce04d28874 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Fri, 3 Jan 2014 15:44:07 +0800 Subject: serial: sirf: provide pm entries of uart_ops this patch provides PM entry of uart_ops, then drop clk enable and disable because serial core will do it. the patch also fixes the issue that uart hang in resume caused by not-enabled clock. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 6fea79b..a6c38ab 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1033,6 +1033,16 @@ static void sirfsoc_uart_set_termios(struct uart_port *port, spin_unlock_irqrestore(&port->lock, flags); } +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) + clk_prepare_enable(sirfport->clk); + else + clk_disable_unprepare(sirfport->clk); +} + static unsigned int sirfsoc_uart_init_tx_dma(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); @@ -1264,6 +1274,7 @@ static struct uart_ops sirfsoc_uart_ops = { .startup = sirfsoc_uart_startup, .shutdown = sirfsoc_uart_shutdown, .set_termios = sirfsoc_uart_set_termios, + .pm = sirfsoc_uart_pm, .type = sirfsoc_uart_type, .release_port = sirfsoc_uart_release_port, .request_port = sirfsoc_uart_request_port, @@ -1486,7 +1497,6 @@ usp_no_flow_control: ret = PTR_ERR(sirfport->clk); goto err; } - clk_prepare_enable(sirfport->clk); port->uartclk = clk_get_rate(sirfport->clk); port->ops = &sirfsoc_uart_ops; @@ -1502,7 +1512,6 @@ usp_no_flow_control: return 0; port_err: - clk_disable_unprepare(sirfport->clk); clk_put(sirfport->clk); err: return ret; @@ -1512,7 +1521,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_disable_unprepare(sirfport->clk); clk_put(sirfport->clk); uart_remove_one_port(&sirfsoc_uart_drv, port); return 0; -- cgit v0.10.2 From df8d4aa0d84995bf7fb8d8a978a0d67fff6ca53a Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Fri, 3 Jan 2014 15:44:08 +0800 Subject: serial: sirf: correct condition for fetching dma buffer into tty In rx dma-callback it calls tasklet_schedule, if the tasklet be scheduled after all the dma-callback in the rx dma channel, current check condition in the tasklet will not do fetch dma buffer into tty because tx_issued is equal with tx_completed, so as timeout tasklet does. so we check whether we should fetch the whole dma buffer into tty according to the status of transactions in rx dma channel. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index a6c38ab..49a2ffd 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -524,9 +524,11 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param) struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; unsigned int count; unsigned long flags; + struct dma_tx_state tx_state; spin_lock_irqsave(&sirfport->rx_lock, flags); - while (sirfport->rx_completed != sirfport->rx_issued) { + while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan, + sirfport->rx_dma_items[sirfport->rx_completed].cookie, &tx_state)) { sirfsoc_uart_insert_rx_buf_to_tty(sirfport, SIRFSOC_RX_DMA_BUF_SIZE); sirfport->rx_completed++; @@ -709,8 +711,10 @@ static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param) struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; unsigned long flags; + struct dma_tx_state tx_state; spin_lock_irqsave(&sirfport->rx_lock, flags); - while (sirfport->rx_completed != sirfport->rx_issued) { + while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan, + sirfport->rx_dma_items[sirfport->rx_completed].cookie, &tx_state)) { sirfsoc_uart_insert_rx_buf_to_tty(sirfport, SIRFSOC_RX_DMA_BUF_SIZE); if (rd_regl(port, ureg->sirfsoc_int_en_reg) & -- cgit v0.10.2 From 0cc7c6c7916b1b6f34350ff1473b80b9f7e459c0 Mon Sep 17 00:00:00 2001 From: Marek Roszko Date: Tue, 7 Jan 2014 11:45:06 +0100 Subject: tty/serial: at91: Handle shutdown more safely Interrupts were being cleaned up late in the shutdown handler, it is possible that an interrupt can occur and schedule a tasklet that runs after the port is cleaned up. There is a null dereference due to this race condition with the following stacktrace: [] (atmel_tasklet_func+0x514/0x814) from [] (tasklet_action+0x70/0xa8) [] (tasklet_action+0x70/0xa8) from [] (__do_softirq+0x90/0x144) [] (__do_softirq+0x90/0x144) from [] (irq_exit+0x40/0x4c) [] (irq_exit+0x40/0x4c) from [] (handle_IRQ+0x64/0x84) [] (handle_IRQ+0x64/0x84) from [] (__irq_svc+0x40/0x50) [] (__irq_svc+0x40/0x50) from [] (atmel_rx_dma_release+0x88/0xb8) [] (atmel_rx_dma_release+0x88/0xb8) from [] (atmel_shutdown+0x104/0x160) [] (atmel_shutdown+0x104/0x160) from [] (uart_port_shutdown+0x2c/0x38) Signed-off-by: Marek Roszko Acked-by: Leilei Zhao Cc: # v3.12 Signed-off-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 c7d99af..48ea47a 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1650,12 +1650,24 @@ static int atmel_startup(struct uart_port *port) static void atmel_shutdown(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + /* - * Ensure everything is stopped. + * Clear out any scheduled tasklets before + * we destroy the buffers + */ + tasklet_kill(&atmel_port->tasklet); + + /* + * Ensure everything is stopped and + * disable all interrupts, port and break condition. */ atmel_stop_rx(port); atmel_stop_tx(port); + UART_PUT_CR(port, ATMEL_US_RSTSTA); + UART_PUT_IDR(port, -1); + + /* * Shut-down the DMA. */ @@ -1665,12 +1677,6 @@ static void atmel_shutdown(struct uart_port *port) atmel_port->release_tx(port); /* - * Disable all interrupts, port and break condition. - */ - UART_PUT_CR(port, ATMEL_US_RSTSTA); - UART_PUT_IDR(port, -1); - - /* * Free the interrupt */ free_irq(port->irq, port); -- cgit v0.10.2 From f50c995f9ebf064cea1368bf361c4e29679415b4 Mon Sep 17 00:00:00 2001 From: Marek Roszko Date: Tue, 7 Jan 2014 11:45:07 +0100 Subject: tty/serial: at91: fix race condition in atmel_serial_remove The _remove callback could be called when a tasklet is scheduled. tasklet_kill was called inside the function in order to free up any scheduled tasklets. However it was called after uart_remove_one_port which destroys tty references needed in the port for atmel_tasklet_func. Simply putting the tasklet_kill at the start of the function will prevent this conflict. Signed-off-by: Marek Roszko Acked-by: Leilei Zhao Cc: # v3.12 Signed-off-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 48ea47a..c421d11 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2447,11 +2447,12 @@ static int atmel_serial_remove(struct platform_device *pdev) struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); int ret = 0; + tasklet_kill(&atmel_port->tasklet); + device_init_wakeup(&pdev->dev, 0); ret = uart_remove_one_port(&atmel_uart, port); - tasklet_kill(&atmel_port->tasklet); kfree(atmel_port->rx_ring.buf); /* "port" is allocated statically, so we shouldn't free it */ -- cgit v0.10.2 From bb7e73c598fb226c75f7625088a8f6a45a0fc892 Mon Sep 17 00:00:00 2001 From: Mark Deneen Date: Tue, 7 Jan 2014 11:45:09 +0100 Subject: tty/serial: at91: reset rx_ring when port is shutdown When using RX DMA, the driver won't pass any data to the uart layer until the buffer is flipped. When the port is shutdown, the dma buffers are unmapped, but the head and tail of the ring buffer are not reseted. Since the serial console will keep the port open, this will only present itself when the uart is not shared. To reproduce the issue, with an unpatched driver, run a getty on /dev/ttyS0 with no serial console and exit. Getty will exit, and when the new one returns you will be unable to log in. If you hold down a key long enough to fill the DMA buffer and flip it, you can then log in. Signed-off-by: Mark Deneen Acked-by: Leilei Zhao [nicolas.ferre@atmel.com: adapt to mainline kernel, handle !DMA case] Cc: # v3.12 Signed-off-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 c421d11..2b6ac1b 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1677,6 +1677,12 @@ static void atmel_shutdown(struct uart_port *port) atmel_port->release_tx(port); /* + * Reset ring buffer pointers + */ + atmel_port->rx_ring.head = 0; + atmel_port->rx_ring.tail = 0; + + /* * Free the interrupt */ free_irq(port->irq, port); -- cgit v0.10.2 From 3685f19e07802ec4207b52465c408f185b66490e Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 7 Jan 2014 15:00:12 -0700 Subject: serial: 8250: enable UART_BUG_NOMSR for Tegra Tegra chips have 4 or 5 identical UART modules embedded. UARTs C..E have their MODEM-control signals tied off to a static state. However UARTs A and B can optionally route those signals to/from package pins, depending on the exact pinmux configuration. When these signals are not routed to package pins, false interrupts may trigger either temporarily, or permanently, all while not showing up in the IIR; it will read as NO_INT. This will eventually lead to the UART IRQ being disabled due to unhandled interrupts. When this happens, the kernel may print e.g.: irq 68: nobody cared (try booting with the "irqpoll" option) In order to prevent this, enable UART_BUG_NOMSR. This prevents UART_IER_MSI from being enabled, which prevents the false interrupts from triggering. In practice, this is not needed under any of the following conditions: * On Tegra chips after Tegra30, since the HW bug has apparently been fixed. * On UARTs C..E since their MODEM control signals are tied to the correct static state which doesn't trigger the issue. * On UARTs A..B if the MODEM control signals are routed out to package pins, since they will then carry valid signals. However, we ignore these exceptions for now, since they are only relevant if a board actually hooks up more than a 4-wire UART, and no currently supported board does this. If we ever support a board that does, we can refine the algorithm that enables UART_BUG_NOMSR to take those exceptions into account, and/or read a flag from DT/... that indicates that the board has hooked up and pinmux'd more than a 4-wire UART. Reported-by: Olof Johansson # autotester Cc: Signed-off-by: Stephen Warren 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 e33d38c..61ecd70 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2670,6 +2670,10 @@ static void serial8250_config_port(struct uart_port *port, int flags) if (port->type == PORT_16550A && port->iotype == UPIO_AU) up->bugs |= UART_BUG_NOMSR; + /* HW bugs may trigger IRQ while IIR == NO_INT */ + if (port->type == PORT_TEGRA) + up->bugs |= UART_BUG_NOMSR; + if (port->type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(up); -- cgit v0.10.2 From 8bc661bfc0c2d221e209f4205bdaaf574d50100c Mon Sep 17 00:00:00 2001 From: Marek Roszko Date: Fri, 10 Jan 2014 10:33:11 +0100 Subject: tty/serial: at91: disable uart timer at start of shutdown The uart timer will schedule a tasklet when it fires. It is possible that it can fire inside _shutdown before it is killed in the dma and pdc cleanup routines. This causes a tasklet that exists after the port is shutdown, so when the kernel finally executes it, it panics as the tty port is NULL. This is a somewhat rare condition but its possible if a program keeps on opening/closing the port. It has been observed in particular with systemd boot messages that were causing a kernel panic because of this behavior. Moving the timer deletion to the beginning of the function stops a tasklet from being scheduled unexpectedly. Signed-off-by: Marek Roszko Cc: stable # v3.12 [nicolas.ferre@atmel.com: modify commit message, call setup_timer() in any case] Signed-off-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 2b6ac1b..a49f10d 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -825,9 +825,6 @@ static void atmel_release_rx_dma(struct uart_port *port) atmel_port->desc_rx = NULL; atmel_port->chan_rx = NULL; atmel_port->cookie_rx = -EINVAL; - - if (!atmel_port->is_usart) - del_timer_sync(&atmel_port->uart_timer); } static void atmel_rx_from_dma(struct uart_port *port) @@ -1229,9 +1226,6 @@ static void atmel_release_rx_pdc(struct uart_port *port) DMA_FROM_DEVICE); kfree(pdc->buf); } - - if (!atmel_port->is_usart) - del_timer_sync(&atmel_port->uart_timer); } static void atmel_rx_from_pdc(struct uart_port *port) @@ -1604,12 +1598,13 @@ static int atmel_startup(struct uart_port *port) /* enable xmit & rcvr */ UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); + setup_timer(&atmel_port->uart_timer, + atmel_uart_timer_callback, + (unsigned long)port); + if (atmel_use_pdc_rx(port)) { /* set UART timeout */ if (!atmel_port->is_usart) { - setup_timer(&atmel_port->uart_timer, - atmel_uart_timer_callback, - (unsigned long)port); mod_timer(&atmel_port->uart_timer, jiffies + uart_poll_timeout(port)); /* set USART timeout */ @@ -1624,9 +1619,6 @@ static int atmel_startup(struct uart_port *port) } else if (atmel_use_dma_rx(port)) { /* set UART timeout */ if (!atmel_port->is_usart) { - setup_timer(&atmel_port->uart_timer, - atmel_uart_timer_callback, - (unsigned long)port); mod_timer(&atmel_port->uart_timer, jiffies + uart_poll_timeout(port)); /* set USART timeout */ @@ -1652,6 +1644,12 @@ static void atmel_shutdown(struct uart_port *port) struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); /* + * Prevent any tasklets being scheduled during + * cleanup + */ + del_timer_sync(&atmel_port->uart_timer); + + /* * Clear out any scheduled tasklets before * we destroy the buffers */ -- cgit v0.10.2