From f28c1d0a78a8c6a217ac5362cb719efa169ae2a7 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Mon, 27 Apr 2015 16:49:04 +0100 Subject: [PATCH 001/148] Revert "serial/amba-pl011: Leave the TX IRQ alone when the UART is not open" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f2ee6dfa0e8597eea8b98d240b0033994e20d215. Jakub Kiciński observed that this patch can cause the pl011 driver to hang if if the only process with a pl011 port open is killed by a signal, pl011_shutdown() can get called with an arbitrary amount of data still in the FIFO. Calling _shutdown() with the TX FIFO non-empty is questionable behaviour and my itself be a bug. Since the affected patch was speculative anyway, and brings limited benefit, the simplest course is to remove the assumption that TXIS will always be left asserted after the port is shut down. Signed-off-by: Dave Martin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 5a4e9d579585..6f5a0720a8c8 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1639,6 +1639,9 @@ static int pl011_startup(struct uart_port *port) writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); + /* Assume that TX IRQ doesn't work until we see one: */ + uap->tx_irq_seen = 0; + spin_lock_irq(&uap->port.lock); /* restore RTS and DTR */ @@ -1702,7 +1705,7 @@ static void pl011_shutdown(struct uart_port *port) spin_lock_irq(&uap->port.lock); uap->im = 0; writew(uap->im, uap->port.membase + UART011_IMSC); - writew(0xffff & ~UART011_TXIS, uap->port.membase + UART011_ICR); + writew(0xffff, uap->port.membase + UART011_ICR); spin_unlock_irq(&uap->port.lock); pl011_dma_shutdown(uap); From 1e84d22322ceed4767db1e5342c830dd60c8210f Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Mon, 27 Apr 2015 16:49:05 +0100 Subject: [PATCH 002/148] serial/amba-pl011: Refactor and simplify TX FIFO handling Commit 734745c serial/amba-pl011: Activate TX IRQ passively adds some complexity and overhead in the form of a softirq mechanism for transmitting in the absence of interrupts. This patch simplifies the code flow to reduce the reliance on subtle behaviour and avoid fragility under future maintenance. To this end, the TX softirq mechanism is removed and instead pl011_start_tx() will now simply stuff the FIFO until full (guaranteeing future TX IRQs), or until there are no more chars to write (in which case we don't care whether an IRQ happens). Signed-off-by: Dave Martin Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 119 +++++++------------------------- 1 file changed, 26 insertions(+), 93 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 6f5a0720a8c8..f5bd8426fd75 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -58,7 +58,6 @@ #include #include #include -#include #define UART_NR 14 @@ -157,9 +156,7 @@ struct uart_amba_port { unsigned int lcrh_tx; /* vendor-specific */ unsigned int lcrh_rx; /* vendor-specific */ unsigned int old_cr; /* state during shutdown */ - struct delayed_work tx_softirq_work; bool autorts; - unsigned int tx_irq_seen; /* 0=none, 1=1, 2=2 or more */ char type[12]; #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ @@ -1172,15 +1169,14 @@ static void pl011_stop_tx(struct uart_port *port) pl011_dma_tx_stop(uap); } -static bool pl011_tx_chars(struct uart_amba_port *uap); +static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq); /* Start TX with programmed I/O only (no DMA) */ static void pl011_start_tx_pio(struct uart_amba_port *uap) { uap->im |= UART011_TXIM; writew(uap->im, uap->port.membase + UART011_IMSC); - if (!uap->tx_irq_seen) - pl011_tx_chars(uap); + pl011_tx_chars(uap, false); } static void pl011_start_tx(struct uart_port *port) @@ -1247,87 +1243,54 @@ __acquires(&uap->port.lock) spin_lock(&uap->port.lock); } -/* - * Transmit a character - * There must be at least one free entry in the TX FIFO to accept the char. - * - * Returns true if the FIFO might have space in it afterwards; - * returns false if the FIFO definitely became full. - */ -static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c) +static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, + bool from_irq) { + if (unlikely(!from_irq) && + readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) + return false; /* unable to transmit character */ + writew(c, uap->port.membase + UART01x_DR); uap->port.icount.tx++; - if (likely(uap->tx_irq_seen > 1)) - return true; - - return !(readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF); + return true; } -static bool pl011_tx_chars(struct uart_amba_port *uap) +static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) { struct circ_buf *xmit = &uap->port.state->xmit; - int count; - - if (unlikely(uap->tx_irq_seen < 2)) - /* - * Initial FIFO fill level unknown: we must check TXFF - * after each write, so just try to fill up the FIFO. - */ - count = uap->fifosize; - else /* tx_irq_seen >= 2 */ - /* - * FIFO initially at least half-empty, so we can simply - * write half the FIFO without polling TXFF. - - * Note: the *first* TX IRQ can still race with - * pl011_start_tx_pio(), which can result in the FIFO - * being fuller than expected in that case. - */ - count = uap->fifosize >> 1; - - /* - * If the FIFO is full we're guaranteed a TX IRQ at some later point, - * and can't transmit immediately in any case: - */ - if (unlikely(uap->tx_irq_seen < 2 && - readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)) - return false; + int count = uap->fifosize >> 1; if (uap->port.x_char) { - pl011_tx_char(uap, uap->port.x_char); + if (!pl011_tx_char(uap, uap->port.x_char, from_irq)) + return; uap->port.x_char = 0; --count; } if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { pl011_stop_tx(&uap->port); - goto done; + return; } /* If we are using DMA mode, try to send some characters. */ if (pl011_dma_tx_irq(uap)) - goto done; + return; - while (count-- > 0 && pl011_tx_char(uap, xmit->buf[xmit->tail])) { - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - if (uart_circ_empty(xmit)) + do { + if (likely(from_irq) && count-- == 0) break; - } + + if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq)) + break; + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } while (!uart_circ_empty(xmit)); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); - if (uart_circ_empty(xmit)) { + if (uart_circ_empty(xmit)) pl011_stop_tx(&uap->port); - goto done; - } - - if (unlikely(!uap->tx_irq_seen)) - schedule_delayed_work(&uap->tx_softirq_work, uap->port.timeout); - -done: - return false; } static void pl011_modem_status(struct uart_amba_port *uap) @@ -1354,28 +1317,6 @@ static void pl011_modem_status(struct uart_amba_port *uap) wake_up_interruptible(&uap->port.state->port.delta_msr_wait); } -static void pl011_tx_softirq(struct work_struct *work) -{ - struct delayed_work *dwork = to_delayed_work(work); - struct uart_amba_port *uap = - container_of(dwork, struct uart_amba_port, tx_softirq_work); - - spin_lock(&uap->port.lock); - while (pl011_tx_chars(uap)) ; - spin_unlock(&uap->port.lock); -} - -static void pl011_tx_irq_seen(struct uart_amba_port *uap) -{ - if (likely(uap->tx_irq_seen > 1)) - return; - - uap->tx_irq_seen++; - if (uap->tx_irq_seen < 2) - /* first TX IRQ */ - cancel_delayed_work(&uap->tx_softirq_work); -} - static irqreturn_t pl011_int(int irq, void *dev_id) { struct uart_amba_port *uap = dev_id; @@ -1414,10 +1355,8 @@ static irqreturn_t pl011_int(int irq, void *dev_id) if (status & (UART011_DSRMIS|UART011_DCDMIS| UART011_CTSMIS|UART011_RIMIS)) pl011_modem_status(uap); - if (status & UART011_TXIS) { - pl011_tx_irq_seen(uap); - pl011_tx_chars(uap); - } + if (status & UART011_TXIS) + pl011_tx_chars(uap, true); if (pass_counter-- == 0) break; @@ -1639,9 +1578,6 @@ static int pl011_startup(struct uart_port *port) writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); - /* Assume that TX IRQ doesn't work until we see one: */ - uap->tx_irq_seen = 0; - spin_lock_irq(&uap->port.lock); /* restore RTS and DTR */ @@ -1697,8 +1633,6 @@ static void pl011_shutdown(struct uart_port *port) container_of(port, struct uart_amba_port, port); unsigned int cr; - cancel_delayed_work_sync(&uap->tx_softirq_work); - /* * disable all interrupts */ @@ -2245,7 +2179,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->port.ops = &amba_pl011_pops; uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = i; - INIT_DELAYED_WORK(&uap->tx_softirq_work, pl011_tx_softirq); /* Ensure interrupts from this UART are masked and cleared */ writew(0, uap->port.membase + UART011_IMSC); From 53794183225f25830ab40e1c83ae885f11501784 Mon Sep 17 00:00:00 2001 From: Jiada Wang Date: Mon, 13 Apr 2015 18:31:43 +0900 Subject: [PATCH 003/148] serial: imx: protect Soft Reset of port with lock Previously Soft Reset (clear of SRST bit in UCR2 register) of UART in startup is not protected by lock, which may have race with console_write, as console_write may occur at anytime even when UART port is shutdown. To avoid this race, protect Soft reset of UART port with spin_lock. Signed-off-by: Jiada Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index c8cfa0637128..38717c7bb46f 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1114,6 +1114,12 @@ static int imx_startup(struct uart_port *port) writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); + /* Can we enable the DMA support? */ + if (is_imx6q_uart(sport) && !uart_console(port) && + !sport->dma_is_inited) + imx_uart_dma_init(sport); + + spin_lock_irqsave(&sport->port.lock, flags); /* Reset fifo's and state machines */ i = 100; @@ -1124,13 +1130,6 @@ static int imx_startup(struct uart_port *port) while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0)) udelay(1); - /* Can we enable the DMA support? */ - if (is_imx6q_uart(sport) && !uart_console(port) && - !sport->dma_is_inited) - imx_uart_dma_init(sport); - - spin_lock_irqsave(&sport->port.lock, flags); - /* * Finally, clear and enable interrupts */ From caec172d57fde299ee4f4b05c851a059771fd5b7 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 9 Apr 2015 23:22:43 -0300 Subject: [PATCH 004/148] serial: imx: Remove return value from imx_setup_ufcr() The return value from imx_setup_ufcr() is always 0 and its value is never checked, so better to remove the return value. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 38717c7bb46f..8aff0b4d8ddf 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -853,7 +853,7 @@ static void imx_break_ctl(struct uart_port *port, int break_state) #define TXTL 2 /* reset default */ #define RXTL 1 /* reset default */ -static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) +static void imx_setup_ufcr(struct imx_port *sport, unsigned int mode) { unsigned int val; @@ -861,7 +861,6 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE); val |= TXTL << UFCR_TXTL_SHF | RXTL; writel(val, sport->port.membase + UFCR); - return 0; } #define RX_BUF_SIZE (PAGE_SIZE) From 1d6b98774cff82860a3f044610e956bcbff556c1 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 31 Mar 2015 15:55:57 +0200 Subject: [PATCH 005/148] tty: constify return type of tty_name All users of tty_name pass the result directly to a printf-like function. This means we can actually let tty_name return the literal "NULL tty" or tty->name directly, avoiding the strcpy and a lot of medium-sized stack buffers. In preparation for that, make the return type const char*. While at it, we can also constify the tty parameter. Signed-off-by: Rasmus Villemoes Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 2 +- include/linux/tty.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e5695467598f..5a49a4a80ebd 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -243,7 +243,7 @@ static void tty_del_file(struct file *file) * Locking: none */ -char *tty_name(struct tty_struct *tty, char *buf) +const char *tty_name(const struct tty_struct *tty, char *buf) { if (!tty) /* Hmm. NULL pointer. That's fun. */ strcpy(buf, "NULL tty"); diff --git a/include/linux/tty.h b/include/linux/tty.h index fe5623c9af71..4cbecfc7b3c9 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -421,7 +421,7 @@ static inline struct tty_struct *tty_kref_get(struct tty_struct *tty) extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, const char *routine); -extern char *tty_name(struct tty_struct *tty, char *buf); +extern const char *tty_name(const struct tty_struct *tty, char *buf); extern void tty_wait_until_sent(struct tty_struct *tty, long timeout); extern int tty_check_change(struct tty_struct *tty); extern void __stop_tty(struct tty_struct *tty); From 917162c936936a37c51265b3add3885a15e66a82 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 31 Mar 2015 15:55:58 +0200 Subject: [PATCH 006/148] tty: return tty->name directly from tty_name All users of tty_name pass the return value (the provided buffer) to some printf-like function. We can thus avoid the strcpy and, more importantly, later remove the buf parameter completely, eliminating the need for some 64 byte stack buffers. Signed-off-by: Rasmus Villemoes Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 5a49a4a80ebd..1eaf0fbd99e4 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -235,7 +235,7 @@ static void tty_del_file(struct file *file) /** * tty_name - return tty naming * @tty: tty structure - * @buf: buffer for output + * @buf: unused * * Convert a tty structure into a name. The name reflects the kernel * naming policy and if udev is in use may not reflect user space @@ -246,10 +246,8 @@ static void tty_del_file(struct file *file) const char *tty_name(const struct tty_struct *tty, char *buf) { if (!tty) /* Hmm. NULL pointer. That's fun. */ - strcpy(buf, "NULL tty"); - else - strcpy(buf, tty->name); - return buf; + return "NULL tty"; + return tty->name; } EXPORT_SYMBOL(tty_name); From 429b474990cb4e5e8cfe2352daf649d0599cccb6 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 31 Mar 2015 15:55:59 +0200 Subject: [PATCH 007/148] tty: remove buf parameter from tty_name() tty_name no longer uses the buf parameter, so remove it along with all the 64 byte stack buffers that used to be passed in. Mostly generated by the coccinelle script @depends on patch@ identifier buf; constant C; expression tty; @@ - char buf[C]; <+... - tty_name(tty, buf) + tty_name(tty) ...+> allmodconfig compiles, so I'm fairly confident the stack buffers weren't used for other purposes as well. Signed-off-by: Rasmus Villemoes Reviewed-by: Peter Hurley Acked-by: Jesper Nilsson Acked-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/serio/serport.c | 5 ++--- drivers/tty/amiserial.c | 8 ++------ drivers/tty/cyclades.c | 8 ++------ drivers/tty/n_gsm.c | 3 +-- drivers/tty/n_tty.c | 7 ++----- drivers/tty/serial/crisv10.c | 8 ++------ drivers/tty/serial/serial_core.c | 4 +--- drivers/tty/tty_io.c | 28 +++++++++++----------------- drivers/tty/tty_ioctl.c | 4 +--- drivers/tty/tty_ldisc.c | 8 +++----- include/linux/tty.h | 2 +- 11 files changed, 28 insertions(+), 57 deletions(-) diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 69175b825346..9c927d35c1f5 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -167,7 +167,6 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u { struct serport *serport = (struct serport*) tty->disc_data; struct serio *serio; - char name[64]; if (test_and_set_bit(SERPORT_BUSY, &serport->flags)) return -EBUSY; @@ -177,7 +176,7 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u return -ENOMEM; strlcpy(serio->name, "Serial port", sizeof(serio->name)); - snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name)); + snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty)); serio->id = serport->id; serio->id.type = SERIO_RS232; serio->write = serport_serio_write; @@ -187,7 +186,7 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u serio->dev.parent = tty->dev; serio_register_port(serport->serio); - printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name)); + printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty)); wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags)); serio_unregister_port(serport->serio); diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index b2d760055952..894d3a84e285 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -966,9 +966,7 @@ static void rs_throttle(struct tty_struct * tty) struct serial_state *info = tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", tty_name(tty, buf), + printk("throttle %s: %d....\n", tty_name(tty), tty->ldisc.chars_in_buffer(tty)); #endif @@ -991,9 +989,7 @@ static void rs_unthrottle(struct tty_struct * tty) struct serial_state *info = tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("unthrottle %s: %d....\n", tty_name(tty, buf), + printk("unthrottle %s: %d....\n", tty_name(tty), tty->ldisc.chars_in_buffer(tty)); #endif diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index fd66f57390d0..87f6578c6f4a 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -2861,9 +2861,7 @@ static void cy_throttle(struct tty_struct *tty) unsigned long flags; #ifdef CY_DEBUG_THROTTLE - char buf[64]; - - printk(KERN_DEBUG "cyc:throttle %s: %ld...ttyC%d\n", tty_name(tty, buf), + printk(KERN_DEBUG "cyc:throttle %s: %ld...ttyC%d\n", tty_name(tty), tty->ldisc.chars_in_buffer(tty), info->line); #endif @@ -2902,10 +2900,8 @@ static void cy_unthrottle(struct tty_struct *tty) unsigned long flags; #ifdef CY_DEBUG_THROTTLE - char buf[64]; - printk(KERN_DEBUG "cyc:unthrottle %s: %ld...ttyC%d\n", - tty_name(tty, buf), tty_chars_in_buffer(tty), info->line); + tty_name(tty), tty_chars_in_buffer(tty), info->line); #endif if (serial_paranoia_check(info, tty->name, "cy_unthrottle")) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 91abc00aa833..7e039669fc82 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2274,7 +2274,6 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, const unsigned char *dp; char *f; int i; - char buf[64]; char flags = TTY_NORMAL; if (debug & 4) @@ -2296,7 +2295,7 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, break; default: WARN_ONCE(1, "%s: unknown flag %d\n", - tty_name(tty, buf), flags); + tty_name(tty), flags); break; } } diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index cf6e0f2e1331..54da8f49394d 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1179,13 +1179,12 @@ static void n_tty_receive_break(struct tty_struct *tty) static void n_tty_receive_overrun(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - char buf[64]; ldata->num_overrun++; if (time_after(jiffies, ldata->overrun_time + HZ) || time_after(ldata->overrun_time, jiffies)) { printk(KERN_WARNING "%s: %d input overrun(s)\n", - tty_name(tty, buf), + tty_name(tty), ldata->num_overrun); ldata->overrun_time = jiffies; ldata->num_overrun = 0; @@ -1460,8 +1459,6 @@ static void n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c) static void n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag) { - char buf[64]; - switch (flag) { case TTY_BREAK: n_tty_receive_break(tty); @@ -1475,7 +1472,7 @@ n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag) break; default: printk(KERN_ERR "%s: unknown flag %d\n", - tty_name(tty, buf), flag); + tty_name(tty), flag); break; } } diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 0c1825b0b41d..568ea0d2d699 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3216,9 +3216,7 @@ rs_throttle(struct tty_struct * tty) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %lu....\n", tty_name(tty, buf), + printk("throttle %s: %lu....\n", tty_name(tty), (unsigned long)tty->ldisc.chars_in_buffer(tty)); #endif DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty))); @@ -3238,9 +3236,7 @@ rs_unthrottle(struct tty_struct * tty) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("unthrottle %s: %lu....\n", tty_name(tty, buf), + printk("unthrottle %s: %lu....\n", tty_name(tty), (unsigned long)tty->ldisc.chars_in_buffer(tty)); #endif DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty))); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0b7bb12dfc68..eec067d8eedb 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -894,12 +894,10 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, * need to rate-limit; it's CAP_SYS_ADMIN only. */ if (uport->flags & UPF_SPD_MASK) { - char buf[64]; - dev_notice(uport->dev, "%s sets custom speed on %s. This is deprecated.\n", current->comm, - tty_name(port->tty, buf)); + tty_name(port->tty)); } uart_change_speed(tty, state, NULL); } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 1eaf0fbd99e4..57fc6ee12332 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -235,7 +235,6 @@ static void tty_del_file(struct file *file) /** * tty_name - return tty naming * @tty: tty structure - * @buf: unused * * Convert a tty structure into a name. The name reflects the kernel * naming policy and if udev is in use may not reflect user space @@ -243,7 +242,7 @@ static void tty_del_file(struct file *file) * Locking: none */ -const char *tty_name(const struct tty_struct *tty, char *buf) +const char *tty_name(const struct tty_struct *tty) { if (!tty) /* Hmm. NULL pointer. That's fun. */ return "NULL tty"; @@ -768,8 +767,7 @@ static void do_tty_hangup(struct work_struct *work) void tty_hangup(struct tty_struct *tty) { #ifdef TTY_DEBUG_HANGUP - char buf[64]; - printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf)); + printk(KERN_DEBUG "%s hangup...\n", tty_name(tty)); #endif schedule_work(&tty->hangup_work); } @@ -788,9 +786,7 @@ EXPORT_SYMBOL(tty_hangup); void tty_vhangup(struct tty_struct *tty) { #ifdef TTY_DEBUG_HANGUP - char buf[64]; - - printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf)); + printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty)); #endif __tty_hangup(tty, 0); } @@ -829,9 +825,7 @@ void tty_vhangup_self(void) static void tty_vhangup_session(struct tty_struct *tty) { #ifdef TTY_DEBUG_HANGUP - char buf[64]; - - printk(KERN_DEBUG "%s vhangup session...\n", tty_name(tty, buf)); + printk(KERN_DEBUG "%s vhangup session...\n", tty_name(tty)); #endif __tty_hangup(tty, 1); } @@ -1767,7 +1761,6 @@ int tty_release(struct inode *inode, struct file *filp) struct tty_struct *o_tty = NULL; int do_sleep, final; int idx; - char buf[64]; long timeout = 0; int once = 1; @@ -1791,7 +1784,7 @@ int tty_release(struct inode *inode, struct file *filp) #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "%s: %s (tty count=%d)...\n", __func__, - tty_name(tty, buf), tty->count); + tty_name(tty), tty->count); #endif if (tty->ops->close) @@ -1842,7 +1835,7 @@ int tty_release(struct inode *inode, struct file *filp) if (once) { once = 0; printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", - __func__, tty_name(tty, buf)); + __func__, tty_name(tty)); } schedule_timeout_killable(timeout); if (timeout < 120 * HZ) @@ -1854,13 +1847,13 @@ int tty_release(struct inode *inode, struct file *filp) if (o_tty) { if (--o_tty->count < 0) { printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n", - __func__, o_tty->count, tty_name(o_tty, buf)); + __func__, o_tty->count, tty_name(o_tty)); o_tty->count = 0; } } if (--tty->count < 0) { printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n", - __func__, tty->count, tty_name(tty, buf)); + __func__, tty->count, tty_name(tty)); tty->count = 0; } @@ -1903,7 +1896,7 @@ int tty_release(struct inode *inode, struct file *filp) return 0; #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: %s: final close\n", __func__, tty_name(tty, buf)); + printk(KERN_DEBUG "%s: %s: final close\n", __func__, tty_name(tty)); #endif /* * Ask the line discipline code to release its structures @@ -1914,7 +1907,8 @@ int tty_release(struct inode *inode, struct file *filp) tty_flush_works(tty); #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, tty_name(tty, buf)); + printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, + tty_name(tty)); #endif /* * The release_tty function takes care of the details of clearing diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 8e53fe469664..5232fb60b0b1 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -211,9 +211,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) void tty_wait_until_sent(struct tty_struct *tty, long timeout) { #ifdef TTY_DEBUG_WAIT_UNTIL_SENT - char buf[64]; - - printk(KERN_DEBUG "%s wait until sent...\n", tty_name(tty, buf)); + printk(KERN_DEBUG "%s wait until sent...\n", tty_name(tty)); #endif if (!timeout) timeout = MAX_SCHEDULE_TIMEOUT; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 3737f55272d2..c07fb5d9bcf9 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -22,9 +22,8 @@ #undef LDISC_DEBUG_HANGUP #ifdef LDISC_DEBUG_HANGUP -#define tty_ldisc_debug(tty, f, args...) ({ \ - char __b[64]; \ - printk(KERN_DEBUG "%s: %s: " f, __func__, tty_name(tty, __b), ##args); \ +#define tty_ldisc_debug(tty, f, args...) ({ \ + printk(KERN_DEBUG "%s: %s: " f, __func__, tty_name(tty), ##args); \ }) #else #define tty_ldisc_debug(tty, f, args...) @@ -483,7 +482,6 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) { - char buf[64]; struct tty_ldisc *new_ldisc; int r; @@ -504,7 +502,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) if (r < 0) panic("Couldn't open N_TTY ldisc for " "%s --- error %d.", - tty_name(tty, buf), r); + tty_name(tty), r); } } diff --git a/include/linux/tty.h b/include/linux/tty.h index 4cbecfc7b3c9..9a72c9144d8a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -421,7 +421,7 @@ static inline struct tty_struct *tty_kref_get(struct tty_struct *tty) extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, const char *routine); -extern const char *tty_name(const struct tty_struct *tty, char *buf); +extern const char *tty_name(const struct tty_struct *tty); extern void tty_wait_until_sent(struct tty_struct *tty, long timeout); extern int tty_check_change(struct tty_struct *tty); extern void __stop_tty(struct tty_struct *tty); From cbc7f6bd8f824bdc1a4350e69130ba0419a3589d Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Mar 2015 10:43:23 -0700 Subject: [PATCH 008/148] serial: kgdb_nmi: Use bool function return values of true/false not 1/0 Use the normal return values for bool functions Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/kgdb_nmi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index 129dc5be6028..117df151627d 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -173,18 +173,18 @@ static int kgdb_nmi_poll_one_knock(void) bool kgdb_nmi_poll_knock(void) { if (kgdb_nmi_knock < 0) - return 1; + return true; while (1) { int ret; ret = kgdb_nmi_poll_one_knock(); if (ret == NO_POLL_CHAR) - return 0; + return false; else if (ret == 1) break; } - return 1; + return true; } /* From bb5f633ab8c6cd14411ce57273533d26c35e8f85 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sat, 11 Apr 2015 12:26:19 +0200 Subject: [PATCH 009/148] drivers/tty/serial/sh-sci.h: remove dead reference on ARCH_SH7372 The Kconfig option ARCH_SH7372 has been removed by commit 59b89af1d555 ("ARM: shmobile: sh7372: Remove Legacy C SoC code"). This patch removes the last reference on this Kconfig option. Signed-off-by: Valentin Rothberg Acked-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index d5db81a0a430..350717404507 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -15,7 +15,6 @@ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7372) || \ defined(CONFIG_ARCH_R8A7740) # define SCxSR_RDxF_CLEAR(port) (serial_port_in(port, SCxSR) & 0xfffc) From 0656b1a9a5228f27af3a010b75c2c587213ee4f1 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sat, 11 Apr 2015 12:21:30 +0200 Subject: [PATCH 010/148] drivers/tty/serial: altera: fix typos in #endif comments Correct reference on CONFIG_SERIAL_ALTERA_{JTAG}UART_CONSOLE in C-comment after #endif. Signed-off-by: Valentin Rothberg Acked-by: Tobias Klauser Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/altera_jtaguart.c | 2 +- drivers/tty/serial/altera_uart.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index 0fefdd8931a2..32df2a0cb060 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -387,7 +387,7 @@ console_initcall(altera_jtaguart_console_init); #define ALTERA_JTAGUART_CONSOLE NULL -#endif /* CONFIG_ALTERA_JTAGUART_CONSOLE */ +#endif /* CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE */ static struct uart_driver altera_jtaguart_driver = { .owner = THIS_MODULE, diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index b2859fe07e14..fd87a6f574e3 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -493,7 +493,7 @@ console_initcall(altera_uart_console_init); #define ALTERA_UART_CONSOLE NULL -#endif /* CONFIG_ALTERA_UART_CONSOLE */ +#endif /* CONFIG_SERIAL_ALTERA_UART_CONSOLE */ /* * Define the altera_uart UART driver structure. From 5a56abc77f328881dfe9ff2a751c9934382a392d Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sat, 11 Apr 2015 15:56:00 +0200 Subject: [PATCH 011/148] drivers/tty/serial/crisv10.c: remove dead #ifdef block ETRAX_EXTERN_PB6CLK_ENABLED is not defined in Kconfig. The affected #ifdef block has not been compiled for years, so remove it entirely. Signed-off-by: Valentin Rothberg Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 568ea0d2d699..b6ce1a8b3ad8 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -2867,19 +2867,6 @@ change_speed(struct e100_serial *info) *R_SERIAL_PRESCALE = divisor; info->baud = SERIAL_PRESCALE_BASE/divisor; } -#ifdef CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED - else if ((info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8 && - info->custom_divisor == 1) || - (info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ && - info->custom_divisor == 8)) { - /* ext_clk selected */ - alt_source = - IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, extern) | - IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, extern); - DBAUD(printk("using external baudrate: %lu\n", CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8)); - info->baud = CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8; - } -#endif else { /* Bad baudbase, we don't support using timer0 From cc020ea404cd73d8eb579d592211c7fd2972ef0f Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sat, 11 Apr 2015 15:56:01 +0200 Subject: [PATCH 012/148] drivers/tty/serial/crisv10.c: remove dead #ifdef blocks ETRAX_RS485_{ON_PORT_G, LTC1387} are not defined in Kconfig. The affected #ifdef block have not been compiled for years, so remove them entirely. Signed-off-by: Valentin Rothberg Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index b6ce1a8b3ad8..d46b4140ac20 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -56,10 +56,6 @@ static char *serial_version = "$Revision: 1.25 $"; #error "RX_TIMEOUT_TICKS == 0 not allowed, use 1" #endif -#if defined(CONFIG_ETRAX_RS485_ON_PA) && defined(CONFIG_ETRAX_RS485_ON_PORT_G) -#error "Disable either CONFIG_ETRAX_RS485_ON_PA or CONFIG_ETRAX_RS485_ON_PORT_G" -#endif - /* * All of the compatibilty code so we can compile serial.c against * older kernels is hidden in serial_compat.h @@ -487,9 +483,6 @@ static struct fast_timer fast_timers_rs485[NR_PORTS]; #if defined(CONFIG_ETRAX_RS485_ON_PA) static int rs485_pa_bit = CONFIG_ETRAX_RS485_ON_PA_BIT; #endif -#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) -static int rs485_port_g_bit = CONFIG_ETRAX_RS485_ON_PORT_G_BIT; -#endif #endif /* Info and macros needed for each ports extra control/status signals. */ @@ -1367,16 +1360,6 @@ e100_enable_rs485(struct tty_struct *tty, struct serial_rs485 *r) #if defined(CONFIG_ETRAX_RS485_ON_PA) *R_PORT_PA_DATA = port_pa_data_shadow |= (1 << rs485_pa_bit); #endif -#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - rs485_port_g_bit, 1); -#endif -#if defined(CONFIG_ETRAX_RS485_LTC1387) - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - CONFIG_ETRAX_RS485_LTC1387_DXEN_PORT_G_BIT, 1); - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 1); -#endif info->rs485 = *r; @@ -3707,16 +3690,6 @@ rs_close(struct tty_struct *tty, struct file * filp) info->rs485.flags &= ~(SER_RS485_ENABLED); #if defined(CONFIG_ETRAX_RS485_ON_PA) *R_PORT_PA_DATA = port_pa_data_shadow &= ~(1 << rs485_pa_bit); -#endif -#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - rs485_port_g_bit, 0); -#endif -#if defined(CONFIG_ETRAX_RS485_LTC1387) - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - CONFIG_ETRAX_RS485_LTC1387_DXEN_PORT_G_BIT, 0); - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 0); #endif } #endif @@ -4246,15 +4219,6 @@ static int __init rs_init(void) return -EBUSY; } #endif -#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) - if (cris_io_interface_allocate_pins(if_serial_0, 'g', rs485_pa_bit, - rs485_port_g_bit)) { - printk(KERN_ERR "ETRAX100LX serial: Could not allocate " - "RS485 pin\n"); - put_tty_driver(driver); - return -EBUSY; - } -#endif #endif /* Initialize the tty_driver structure */ From f52e17e4a73e806867328cfade0f768188708878 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sat, 11 Apr 2015 15:56:02 +0200 Subject: [PATCH 013/148] drivers/tty/serial/crisv10.c: remove dead #ifdef blocks ETRAX_SERIAL_PROC_ENTRY is not defined in Kconfig. The affected #ifdef block has not been compiled for years, and the embedded macro, PROCSTAT, ended up as a NOOP. Hence, remove the block and all calls to PROCSTAT. Signed-off-by: Valentin Rothberg Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index d46b4140ac20..08886e619f45 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -451,30 +451,6 @@ static struct e100_serial rs_table[] = { static struct fast_timer fast_timers[NR_PORTS]; #endif -#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY -#define PROCSTAT(x) x -struct ser_statistics_type { - int overrun_cnt; - int early_errors_cnt; - int ser_ints_ok_cnt; - int errors_cnt; - unsigned long int processing_flip; - unsigned long processing_flip_still_room; - unsigned long int timeout_flush_cnt; - int rx_dma_ints; - int tx_dma_ints; - int rx_tot; - int tx_tot; -}; - -static struct ser_statistics_type ser_stat[NR_PORTS]; - -#else - -#define PROCSTAT(x) - -#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */ - /* RS-485 */ #if defined(CONFIG_ETRAX_RS485) #ifdef CONFIG_ETRAX_FAST_TIMER @@ -1824,7 +1800,6 @@ static void receive_chars_dma(struct e100_serial *info) */ unsigned char data = info->ioport[REG_DATA]; - PROCSTAT(ser_stat[info->line].errors_cnt++); DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n", ((rstat & SER_ERROR_MASK) << 8) | data); @@ -1926,7 +1901,6 @@ tr_interrupt(int irq, void *dev_id) /* Read jiffies_usec first, * we want this time to be as late as possible */ - PROCSTAT(ser_stat[info->line].tx_dma_ints++); info->last_tx_active_usec = GET_JIFFIES_USEC(); info->last_tx_active = jiffies; transmit_chars_dma(info); @@ -2005,7 +1979,6 @@ static int force_eop_if_needed(struct e100_serial *info) */ if (!info->forced_eop) { info->forced_eop = 1; - PROCSTAT(ser_stat[info->line].timeout_flush_cnt++); TIMERD(DEBUG_LOG(info->line, "timeout EOP %i\n", info->line)); FORCE_EOP(info); } @@ -2357,7 +2330,6 @@ static void handle_ser_rx_interrupt(struct e100_serial *info) DEBUG_LOG(info->line, "#iERR s d %04X\n", ((rstat & SER_ERROR_MASK) << 8) | data); } - PROCSTAT(ser_stat[info->line].early_errors_cnt++); } else { /* It was a valid byte, now let the DMA do the rest */ unsigned long curr_time_u = GET_JIFFIES_USEC(); unsigned long curr_time = jiffies; @@ -2390,7 +2362,6 @@ static void handle_ser_rx_interrupt(struct e100_serial *info) DINTR2(DEBUG_LOG(info->line, "ser_rx OK %d\n", info->line)); info->break_detected_cnt = 0; - PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++); } /* Restarting the DMA never hurts */ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); From 74a76089d2916ff7f87c37a482da6497b17cdb90 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sat, 11 Apr 2015 15:56:03 +0200 Subject: [PATCH 014/148] drivers/tty/serial/crisv10.c: rename CPP identifier CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED The 'CONFIG_' prefix is reserved for Kconfig options in Make and CPP syntax only. Remove this prefix from the self-defined CPP identifier to apply to this convention and make static analysis tools happy. Signed-off-by: Valentin Rothberg Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 08886e619f45..7dced3a3f96b 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -708,10 +708,10 @@ static unsigned char dummy_ser[NR_PORTS] = {0xFF, 0xFF, 0xFF,0xFF}; defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED) || \ defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED) || \ defined(CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED) -#define CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED +#define ETRAX_SERX_DTR_RI_DSR_CD_MIXED #endif -#ifdef CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED +#ifdef ETRAX_SERX_DTR_RI_DSR_CD_MIXED /* The pins can be mixed on PA and PB */ #define CONTROL_PINS_PORT_NOT_USED(line) \ &dummy_ser[line], &dummy_ser[line], \ @@ -804,7 +804,7 @@ static const struct control_pins e100_modem_pins[NR_PORTS] = #endif } }; -#else /* CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED */ +#else /* ETRAX_SERX_DTR_RI_DSR_CD_MIXED */ /* All pins are on either PA or PB for each serial port */ #define CONTROL_PINS_PORT_NOT_USED(line) \ @@ -886,7 +886,7 @@ static const struct control_pins e100_modem_pins[NR_PORTS] = #endif } }; -#endif /* !CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED */ +#endif /* !ETRAX_SERX_DTR_RI_DSR_CD_MIXED */ #define E100_RTS_MASK 0x20 #define E100_CTS_MASK 0x40 From d033e82db9a5e9bc93224c8a058b9c03e4e3f8ad Mon Sep 17 00:00:00 2001 From: Leilei Zhao Date: Thu, 9 Apr 2015 10:48:15 +0800 Subject: [PATCH 015/148] tty/serial: at91: handle IRQ status more safely Handle the changed flag of IRQ status in interruption instead of handling it in tasklet due to the tasklet may be scheduled more than once in one interruption. Otherwise, the changed status may be processed more than once which will lead to unexpected result. And seriously, kernel will crash. Signed-off-by: Leilei Zhao Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 27dade29646b..b1fcddd6652a 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -165,6 +165,7 @@ struct atmel_uart_port { struct tasklet_struct tasklet; unsigned int irq_status; unsigned int irq_status_prev; + unsigned int status_change; struct circ_buf rx_ring; @@ -1177,6 +1178,9 @@ atmel_handle_status(struct uart_port *port, unsigned int pending, if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC)) { atmel_port->irq_status = status; + atmel_port->status_change = atmel_port->irq_status ^ + atmel_port->irq_status_prev; + atmel_port->irq_status_prev = status; tasklet_schedule(&atmel_port->tasklet); } } @@ -1523,17 +1527,14 @@ static void atmel_tasklet_func(unsigned long data) { struct uart_port *port = (struct uart_port *)data; struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - unsigned int status; - unsigned int status_change; + unsigned int status = atmel_port->irq_status; + unsigned int status_change = atmel_port->status_change; /* The interrupt handler does not take the lock */ spin_lock(&port->lock); atmel_port->schedule_tx(port); - status = atmel_port->irq_status; - status_change = status ^ atmel_port->irq_status_prev; - if (status_change & (ATMEL_US_RI | ATMEL_US_DSR | ATMEL_US_DCD | ATMEL_US_CTS)) { /* TODO: All reads to CSR will clear these interrupts! */ @@ -1548,7 +1549,7 @@ static void atmel_tasklet_func(unsigned long data) wake_up_interruptible(&port->state->port.delta_msr_wait); - atmel_port->irq_status_prev = status; + atmel_port->status_change = 0; } atmel_port->schedule_rx(port); From 47eb16f68bcce84a2b23cb86d24d6c4066c5d24a Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sun, 12 Apr 2015 17:03:22 +0200 Subject: [PATCH 016/148] drivers/tty/serial/8250/8250_core.c: remove CONFIG_HUB6 This file local CPP identifier is not referenced anywhere else, so we can safely remove it. Signed-off-by: Valentin Rothberg Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 4506e405c8f3..cde54b985aef 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -92,11 +92,6 @@ static unsigned int skip_txen_test; /* force skip of txen test at init time */ #define CONFIG_SERIAL_MANY_PORTS 1 #endif -/* - * HUB6 is always on. This will be removed once the header - * files have been cleaned. - */ -#define CONFIG_HUB6 1 #include /* From 5b84c967ccf0573ee4a0b2a53720d5e1e4a8c0f8 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sun, 12 Apr 2015 17:14:45 +0200 Subject: [PATCH 017/148] drivers/tty/serial/mpc52xx_uart.c: fix typo in C comment Fix reference on PPC_MPC52xx in C comment after #endif. Signed-off-by: Valentin Rothberg Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mpc52xx_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 1589f17c1fca..6fc07eb9d74e 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -405,7 +405,7 @@ static struct psc_ops mpc5200b_psc_ops = { .get_mr1 = mpc52xx_psc_get_mr1, }; -#endif /* CONFIG_MPC52xx */ +#endif /* CONFIG_PPC_MPC52xx */ #ifdef CONFIG_PPC_MPC512x #define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1)) From 60d0da5168349041b167973c7464ba0815d5fd67 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sun, 12 Apr 2015 17:54:35 +0200 Subject: [PATCH 018/148] serial: bfin: ctsrts: enfore Kconfig naming convention The CONFIG_ prefix is reserved for Kconfig options in Make and CPP syntax; static analysis tools rely on this convention. This patch enforces this behavior for SERIAL_BFIN_{HARD_}CTSRTS. Signed-off-by: Valentin Rothberg Acked-by: Sonic Zhang Signed-off-by: Greg Kroah-Hartman --- arch/blackfin/include/asm/bfin_serial.h | 8 ++++---- drivers/tty/serial/bfin_uart.c | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/arch/blackfin/include/asm/bfin_serial.h b/arch/blackfin/include/asm/bfin_serial.h index d00d732784b1..b550ada7321b 100644 --- a/arch/blackfin/include/asm/bfin_serial.h +++ b/arch/blackfin/include/asm/bfin_serial.h @@ -22,9 +22,9 @@ defined(CONFIG_BFIN_UART2_CTSRTS) || \ defined(CONFIG_BFIN_UART3_CTSRTS) # if defined(BFIN_UART_BF54X_STYLE) || defined(BFIN_UART_BF60X_STYLE) -# define CONFIG_SERIAL_BFIN_HARD_CTSRTS +# define SERIAL_BFIN_HARD_CTSRTS # else -# define CONFIG_SERIAL_BFIN_CTSRTS +# define SERIAL_BFIN_CTSRTS # endif #endif @@ -50,8 +50,8 @@ struct bfin_serial_port { #elif ANOMALY_05000363 unsigned int anomaly_threshold; #endif -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) +#if defined(SERIAL_BFIN_CTSRTS) || \ + defined(SERIAL_BFIN_HARD_CTSRTS) int cts_pin; int rts_pin; #endif diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 155781ece050..ae3cf94b146b 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -74,8 +74,8 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart); static void bfin_serial_reset_irda(struct uart_port *port); -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) +#if defined(SERIAL_BFIN_CTSRTS) || \ + defined(SERIAL_BFIN_HARD_CTSRTS) static unsigned int bfin_serial_get_mctrl(struct uart_port *port) { struct bfin_serial_port *uart = (struct bfin_serial_port *)port; @@ -110,7 +110,7 @@ static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id) struct bfin_serial_port *uart = dev_id; struct uart_port *uport = &uart->port; unsigned int status = bfin_serial_get_mctrl(uport); -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS +#ifdef SERIAL_BFIN_HARD_CTSRTS UART_CLEAR_SCTS(uart); if (uport->hw_stopped) { @@ -700,7 +700,7 @@ static int bfin_serial_startup(struct uart_port *port) # endif #endif -#ifdef CONFIG_SERIAL_BFIN_CTSRTS +#ifdef SERIAL_BFIN_CTSRTS if (uart->cts_pin >= 0) { if (request_irq(gpio_to_irq(uart->cts_pin), bfin_serial_mctrl_cts_int, @@ -718,7 +718,7 @@ static int bfin_serial_startup(struct uart_port *port) gpio_direction_output(uart->rts_pin, 0); } #endif -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS +#ifdef SERIAL_BFIN_HARD_CTSRTS if (uart->cts_pin >= 0) { if (request_irq(uart->status_irq, bfin_serial_mctrl_cts_int, 0, "BFIN_UART_MODEM_STATUS", uart)) { @@ -766,13 +766,13 @@ static void bfin_serial_shutdown(struct uart_port *port) free_irq(uart->tx_irq, uart); #endif -#ifdef CONFIG_SERIAL_BFIN_CTSRTS +#ifdef SERIAL_BFIN_CTSRTS if (uart->cts_pin >= 0) free_irq(gpio_to_irq(uart->cts_pin), uart); if (uart->rts_pin >= 0) gpio_free(uart->rts_pin); #endif -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS +#ifdef SERIAL_BFIN_HARD_CTSRTS if (uart->cts_pin >= 0) free_irq(uart->status_irq, uart); #endif @@ -788,7 +788,7 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, unsigned int ier, lcr = 0; unsigned long timeout; -#ifdef CONFIG_SERIAL_BFIN_CTSRTS +#ifdef SERIAL_BFIN_CTSRTS if (old == NULL && uart->cts_pin != -1) termios->c_cflag |= CRTSCTS; else if (uart->cts_pin == -1) @@ -1110,8 +1110,8 @@ bfin_serial_console_setup(struct console *co, char *options) int baud = 57600; int bits = 8; int parity = 'n'; -# if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) +# if defined(SERIAL_BFIN_CTSRTS) || \ + defined(SERIAL_BFIN_HARD_CTSRTS) int flow = 'r'; # else int flow = 'n'; @@ -1322,8 +1322,8 @@ static int bfin_serial_probe(struct platform_device *pdev) init_timer(&(uart->rx_dma_timer)); #endif -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) +#if defined(SERIAL_BFIN_CTSRTS) || \ + defined(SERIAL_BFIN_HARD_CTSRTS) res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (res == NULL) uart->cts_pin = -1; From bbdfe620b97443e1a127570ca856b000904d42f3 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sun, 12 Apr 2015 18:18:23 +0200 Subject: [PATCH 019/148] drivers/tty/serial/mcf.c: fix typo on SERIAL_MCF_CONSOLE Correct reference in C-comment after #endif. Signed-off-by: Valentin Rothberg Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mcf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index a9b0ab38a68c..02eb32217685 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -597,7 +597,7 @@ console_initcall(mcf_console_init); #define MCF_CONSOLE NULL /****************************************************************************/ -#endif /* CONFIG_MCF_CONSOLE */ +#endif /* CONFIG_SERIAL_MCF_CONSOLE */ /****************************************************************************/ /* From 6b3cddccf4eec0883feb065aea28dd9770bb17d0 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 11 Apr 2015 11:02:36 -0400 Subject: [PATCH 020/148] serial: core: Fix unused variable warnings from uart_console() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_SERIAL_CORE_CONSOLE=n, build warnings are generated by uart_console() macro expansion: drivers/tty/serial/of_serial.c: In function ‘of_serial_suspend_8250’: drivers/tty/serial/of_serial.c:262:20: warning: unused variable ‘port’ [-Wunused-variable] struct uart_port *port = &port8250->port; ^ drivers/tty/serial/of_serial.c: In function ‘of_serial_resume_8250’: drivers/tty/serial/of_serial.c:272:20: warning: unused variable ‘port’ [-Wunused-variable] struct uart_port *port = &port8250->port; Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 025dad9dcde4..297d4fa1cfe5 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -35,7 +35,7 @@ #define uart_console(port) \ ((port)->cons && (port)->cons->index == (port)->line) #else -#define uart_console(port) (0) +#define uart_console(port) ({ (void)port; 0; }) #endif struct uart_port; From 507224aa88fc2c11f252705ad286bca155e0df02 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sun, 12 Apr 2015 17:37:24 +0200 Subject: [PATCH 021/148] serial: 8250: remove Kconfig indirection Remove CONFIG_SERIAL_DETECT_IRQ and CONFIG_SERIAL_MANY_PORTS, and substitute all references to the proper 8250 Kconfig options. Now, the actual Kconfig dependencies are not hidden when reading the code and static analyzers are less confused. Signed-off-by: Valentin Rothberg Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- arch/alpha/include/asm/serial.h | 2 +- arch/m68k/include/asm/serial.h | 2 +- arch/mn10300/include/asm/serial.h | 4 ++-- arch/x86/include/asm/serial.h | 2 +- drivers/tty/serial/8250/8250_core.c | 8 -------- 5 files changed, 5 insertions(+), 13 deletions(-) diff --git a/arch/alpha/include/asm/serial.h b/arch/alpha/include/asm/serial.h index 9d263e8d8ccc..22909b83f473 100644 --- a/arch/alpha/include/asm/serial.h +++ b/arch/alpha/include/asm/serial.h @@ -13,7 +13,7 @@ #define BASE_BAUD ( 1843200 / 16 ) /* Standard COM flags (except for COM4, because of the 8514 problem) */ -#ifdef CONFIG_SERIAL_DETECT_IRQ +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) #define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) #else diff --git a/arch/m68k/include/asm/serial.h b/arch/m68k/include/asm/serial.h index 7267536adbcc..06d0cb19b4e1 100644 --- a/arch/m68k/include/asm/serial.h +++ b/arch/m68k/include/asm/serial.h @@ -17,7 +17,7 @@ #define BASE_BAUD ( 1843200 / 16 ) /* Standard COM flags (except for COM4, because of the 8514 problem) */ -#ifdef CONFIG_SERIAL_DETECT_IRQ +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) #define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) #else diff --git a/arch/mn10300/include/asm/serial.h b/arch/mn10300/include/asm/serial.h index 23a799293599..c1990218f18c 100644 --- a/arch/mn10300/include/asm/serial.h +++ b/arch/mn10300/include/asm/serial.h @@ -13,7 +13,7 @@ #define _ASM_SERIAL_H /* Standard COM flags (except for COM4, because of the 8514 problem) */ -#ifdef CONFIG_SERIAL_DETECT_IRQ +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) #define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) #else @@ -21,7 +21,7 @@ #define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF #endif -#ifdef CONFIG_SERIAL_MANY_PORTS +#ifdef CONFIG_SERIAL_8250_MANY_PORTS #define FOURPORT_FLAGS ASYNC_FOURPORT #define ACCENT_FLAGS 0 #define BOCA_FLAGS 0 diff --git a/arch/x86/include/asm/serial.h b/arch/x86/include/asm/serial.h index 8378b8c9109c..bb658211edad 100644 --- a/arch/x86/include/asm/serial.h +++ b/arch/x86/include/asm/serial.h @@ -11,7 +11,7 @@ #define BASE_BAUD (1843200/16) /* Standard COM flags (except for COM4, because of the 8514 problem) */ -#ifdef CONFIG_SERIAL_DETECT_IRQ +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ # define STD_COMX_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ) # define STD_COM4_FLAGS (UPF_BOOT_AUTOCONF | 0 | UPF_AUTO_IRQ) #else diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index cde54b985aef..01cecd52b909 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -85,14 +85,6 @@ static unsigned int skip_txen_test; /* force skip of txen test at init time */ #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) -#ifdef CONFIG_SERIAL_8250_DETECT_IRQ -#define CONFIG_SERIAL_DETECT_IRQ 1 -#endif -#ifdef CONFIG_SERIAL_8250_MANY_PORTS -#define CONFIG_SERIAL_MANY_PORTS 1 -#endif - - #include /* * SERIAL_PORT_DFNS tells us about built-in ports that have no From 4b8038dca0c0ccf5e4689cc4fbbbf4f3728304be Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Mon, 20 Apr 2015 08:10:22 +0000 Subject: [PATCH 022/148] Revert "serial: sirf: add a new uart type support" This reverts commit 52bec4ed4e("serial: sirf: add a new uart type support"). we misunderstood the clock dependency in atlas7. Actually involved several clocks are in a tree structure. we still only need to take the leaf clock node for BT uarts. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/serial/sirf-uart.txt | 15 +------- drivers/tty/serial/sirfsoc_uart.c | 34 ++++--------------- drivers/tty/serial/sirfsoc_uart.h | 4 --- 3 files changed, 8 insertions(+), 45 deletions(-) diff --git a/Documentation/devicetree/bindings/serial/sirf-uart.txt b/Documentation/devicetree/bindings/serial/sirf-uart.txt index f0c39261c5d4..67e2a0aeb042 100644 --- a/Documentation/devicetree/bindings/serial/sirf-uart.txt +++ b/Documentation/devicetree/bindings/serial/sirf-uart.txt @@ -2,8 +2,7 @@ Required properties: - compatible : Should be "sirf,prima2-uart", "sirf, prima2-usp-uart", - "sirf,atlas7-uart" or "sirf,atlas7-bt-uart" which means - uart located in BT module and used for BT. + "sirf,atlas7-uart" or "sirf,atlas7-usp-uart". - reg : Offset and length of the register set for the device - interrupts : Should contain uart interrupt - fifosize : Should define hardware rx/tx fifo size @@ -33,15 +32,3 @@ usp@b0090000 { rts-gpios = <&gpio 15 0>; cts-gpios = <&gpio 46 0>; }; - -for uart use in BT module, -uart6: uart@11000000 { - cell-index = <6>; - compatible = "sirf,atlas7-bt-uart", "sirf,atlas7-uart"; - reg = <0x11000000 0x1000>; - interrupts = <0 100 0>; - clocks = <&clks 138>, <&clks 140>, <&clks 141>; - clock-names = "uart", "general", "noc"; - fifosize = <128>; - status = "disabled"; -} diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 9de3eabe5737..8081cdba8778 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1032,19 +1032,10 @@ static void sirfsoc_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - if (!state) { - if (sirfport->is_bt_uart) { - clk_prepare_enable(sirfport->clk_noc); - clk_prepare_enable(sirfport->clk_general); - } + if (!state) clk_prepare_enable(sirfport->clk); - } else { + else clk_disable_unprepare(sirfport->clk); - if (sirfport->is_bt_uart) { - clk_disable_unprepare(sirfport->clk_general); - clk_disable_unprepare(sirfport->clk_noc); - } - } } static int sirfsoc_uart_startup(struct uart_port *port) @@ -1387,26 +1378,12 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) } port->irq = res->start; - sirfport->clk = devm_clk_get(&pdev->dev, NULL); + sirfport->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(sirfport->clk)) { ret = PTR_ERR(sirfport->clk); goto err; } port->uartclk = clk_get_rate(sirfport->clk); - if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-bt-uart")) { - sirfport->clk_general = devm_clk_get(&pdev->dev, "general"); - if (IS_ERR(sirfport->clk_general)) { - ret = PTR_ERR(sirfport->clk_general); - goto err; - } - sirfport->clk_noc = devm_clk_get(&pdev->dev, "noc"); - if (IS_ERR(sirfport->clk_noc)) { - ret = PTR_ERR(sirfport->clk_noc); - goto err; - } - sirfport->is_bt_uart = true; - } else - sirfport->is_bt_uart = false; port->ops = &sirfsoc_uart_ops; spin_lock_init(&port->lock); @@ -1415,7 +1392,7 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) ret = uart_add_one_port(&sirfsoc_uart_drv, port); if (ret != 0) { dev_err(&pdev->dev, "Cannot add UART port(%d).\n", pdev->id); - goto err; + goto port_err; } sirfport->rx_dma_chan = dma_request_slave_channel(port->dev, "rx"); @@ -1444,6 +1421,8 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) sirfport->rx_dma_items[j].xmit.buf, sirfport->rx_dma_items[j].dma_addr); dma_release_channel(sirfport->rx_dma_chan); +port_err: + clk_put(sirfport->clk); err: return ret; } @@ -1452,6 +1431,7 @@ static int sirfsoc_uart_remove(struct platform_device *pdev) { struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); struct uart_port *port = &sirfport->port; + clk_put(sirfport->clk); uart_remove_one_port(&sirfsoc_uart_drv, port); if (sirfport->rx_dma_chan) { int i; diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index 727eb6b88fff..11fd6814f76d 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -417,10 +417,6 @@ struct sirfsoc_uart_port { struct uart_port port; struct clk *clk; - /* UART6 for BT usage in A7DA platform need multi-clock source */ - bool is_bt_uart; - struct clk *clk_general; - struct clk *clk_noc; /* for SiRFatlas7, there are SET/CLR for UART_INT_EN */ bool is_atlas7; struct sirfsoc_uart_register *uart_reg; From adeede7319cb57e0709ffaebb761932915e46d9d Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Mon, 20 Apr 2015 08:10:23 +0000 Subject: [PATCH 023/148] serial: sirf: move from clk_get to devm_clk_get Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 8081cdba8778..247faed40e37 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1378,7 +1378,7 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) } port->irq = res->start; - sirfport->clk = clk_get(&pdev->dev, NULL); + sirfport->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sirfport->clk)) { ret = PTR_ERR(sirfport->clk); goto err; @@ -1392,7 +1392,7 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) ret = uart_add_one_port(&sirfsoc_uart_drv, port); if (ret != 0) { dev_err(&pdev->dev, "Cannot add UART port(%d).\n", pdev->id); - goto port_err; + goto err; } sirfport->rx_dma_chan = dma_request_slave_channel(port->dev, "rx"); @@ -1421,8 +1421,6 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) sirfport->rx_dma_items[j].xmit.buf, sirfport->rx_dma_items[j].dma_addr); dma_release_channel(sirfport->rx_dma_chan); -port_err: - clk_put(sirfport->clk); err: return ret; } @@ -1431,7 +1429,6 @@ static int sirfsoc_uart_remove(struct platform_device *pdev) { struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); struct uart_port *port = &sirfport->port; - clk_put(sirfport->clk); uart_remove_one_port(&sirfsoc_uart_drv, port); if (sirfport->rx_dma_chan) { int i; From 9a499db0325b8a8e2368f21fef66705b120f38ba Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 23 Apr 2015 16:15:39 +0200 Subject: [PATCH 024/148] serial: ifx6x60: Remove dangerous spi_driver casts Casting spi_driver pointers to "void *" when calling spi_{,un}register_driver() bypasses all type checking. Remove the superfluous casts to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 590390970996..7d858a74ee38 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -1381,7 +1381,7 @@ static void __exit ifx_spi_exit(void) /* unregister */ tty_unregister_driver(tty_drv); put_tty_driver(tty_drv); - spi_unregister_driver((void *)&ifx_spi_driver); + spi_unregister_driver(&ifx_spi_driver); unregister_reboot_notifier(&ifx_modem_reboot_notifier_block); } @@ -1420,7 +1420,7 @@ static int __init ifx_spi_init(void) goto err_free_tty; } - result = spi_register_driver((void *)&ifx_spi_driver); + result = spi_register_driver(&ifx_spi_driver); if (result) { pr_err("%s: spi_register_driver failed(%d)", DRVNAME, result); @@ -1436,7 +1436,7 @@ static int __init ifx_spi_init(void) return 0; err_unreg_spi: - spi_unregister_driver((void *)&ifx_spi_driver); + spi_unregister_driver(&ifx_spi_driver); err_unreg_tty: tty_unregister_driver(tty_drv); err_free_tty: From cbbcd1f3b8b05e3e4434ab1eb183091aeb6189eb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 23 Apr 2015 16:15:40 +0200 Subject: [PATCH 025/148] serial: ifx6x60: Remove superfluous casts when calling request_irq() There's no need to cast the last parameter of {request,free}_irq() to "void *", as any pointer type is accepted. Remove the superfluous casts to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 7d858a74ee38..536a33b99be9 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -1175,7 +1175,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi) ret = request_irq(gpio_to_irq(ifx_dev->gpio.reset_out), ifx_spi_reset_interrupt, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DRVNAME, - (void *)ifx_dev); + ifx_dev); if (ret) { dev_err(&spi->dev, "Unable to get irq %x\n", gpio_to_irq(ifx_dev->gpio.reset_out)); @@ -1185,9 +1185,8 @@ static int ifx_spi_spi_probe(struct spi_device *spi) ret = ifx_spi_reset(ifx_dev); ret = request_irq(gpio_to_irq(ifx_dev->gpio.srdy), - ifx_spi_srdy_interrupt, - IRQF_TRIGGER_RISING, DRVNAME, - (void *)ifx_dev); + ifx_spi_srdy_interrupt, IRQF_TRIGGER_RISING, DRVNAME, + ifx_dev); if (ret) { dev_err(&spi->dev, "Unable to get irq %x", gpio_to_irq(ifx_dev->gpio.srdy)); @@ -1212,7 +1211,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi) return 0; error_ret7: - free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), (void *)ifx_dev); + free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), ifx_dev); error_ret6: gpio_free(ifx_dev->gpio.srdy); error_ret5: @@ -1243,8 +1242,8 @@ static int ifx_spi_spi_remove(struct spi_device *spi) /* stop activity */ tasklet_kill(&ifx_dev->io_work_tasklet); /* free irq */ - free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), (void *)ifx_dev); - free_irq(gpio_to_irq(ifx_dev->gpio.srdy), (void *)ifx_dev); + free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), ifx_dev); + free_irq(gpio_to_irq(ifx_dev->gpio.srdy), ifx_dev); gpio_free(ifx_dev->gpio.srdy); gpio_free(ifx_dev->gpio.mrdy); From 35a0f950aadcb65e43fc1cbc5790f313737d9148 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 23 Apr 2015 16:15:41 +0200 Subject: [PATCH 026/148] serial: SERIAL_IFX6X60 should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `ifx_spi_free_device': ifx6x60.c:(.text+0x96d9a): undefined reference to `dma_free_coherent' drivers/built-in.o: In function `ifx_spi_spi_probe': ifx6x60.c:(.text+0x978a2): undefined reference to `dma_alloc_coherent' While DMA is optional in this driver, and is used only if ifx_modem_platform_data.use_dma is set, there are currently no in-tree users of ifx_modem_platform_data (and thus of this driver), so just make it depend on HAS_DMA. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index f8120c1bde14..daee18338636 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1349,7 +1349,7 @@ config SERIAL_ALTERA_UART_CONSOLE config SERIAL_IFX6X60 tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)" - depends on GPIOLIB && SPI + depends on GPIOLIB && SPI && HAS_DMA help Support for the IFX6x60 modem devices on Intel MID platforms. From c2d4bb9d9387231f5e265d59600aeb6409be8e7a Mon Sep 17 00:00:00 2001 From: John Ogness Date: Mon, 27 Apr 2015 13:52:15 +0200 Subject: [PATCH 027/148] ARM: common: edma: clear completion interrupts on stop When stopping a DMA transfer with interrupts disabled it is possible that the DMA transfer completes before the events are cleared. In this case the completion interrupt will be pending, causing a completion callback after the transfer was stopped. By clearing the completion interrupt for the stopping channel it is ensured that no completion event will be generated after the stop. Signed-off-by: John Ogness Acked-by: Peter Ujfalusi Signed-off-by: Greg Kroah-Hartman --- arch/arm/common/edma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 5662a872689b..873dbfcc7dc9 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1350,6 +1350,9 @@ void edma_stop(unsigned channel) edma_shadow0_write_array(ctlr, SH_SECR, j, mask); edma_write_array(ctlr, EDMA_EMCR, j, mask); + /* clear possibly pending completion interrupt */ + edma_shadow0_write_array(ctlr, SH_ICR, j, mask); + pr_debug("EDMA: EER%d %08x\n", j, edma_shadow0_read_array(ctlr, SH_EER, j)); From 02ec6041a8dd17d9bd7dd12eb6280a6b112f83e5 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Mon, 27 Apr 2015 13:52:25 +0200 Subject: [PATCH 028/148] dmaenegine: edma: allow pause/resume for non-cyclic mode The 8250_omap serial driver relies on dmaengine_pause() actually pausing the DMA transfer. Before this patch dmaengine_pause() is a NOP for non-cylic DMA transfers. This allowed the 8250_omap driver to read DMA buffers while the DMA was still active, resulting in lost serial data. Signed-off-by: John Ogness Acked-by: Peter Ujfalusi Signed-off-by: Greg Kroah-Hartman --- drivers/dma/edma.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index bf09db7ca9ee..88853af69489 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -300,8 +300,7 @@ static int edma_dma_pause(struct dma_chan *chan) { struct edma_chan *echan = to_edma_chan(chan); - /* Pause/Resume only allowed with cyclic mode */ - if (!echan->edesc || !echan->edesc->cyclic) + if (!echan->edesc) return -EINVAL; edma_pause(echan->ch_num); @@ -312,10 +311,6 @@ static int edma_dma_resume(struct dma_chan *chan) { struct edma_chan *echan = to_edma_chan(chan); - /* Pause/Resume only allowed with cyclic mode */ - if (!echan->edesc->cyclic) - return -EINVAL; - edma_resume(echan->ch_num); return 0; } From eda0cd3546ab2c69796ea0bfdc04723c74372f1d Mon Sep 17 00:00:00 2001 From: John Ogness Date: Mon, 27 Apr 2015 13:52:33 +0200 Subject: [PATCH 029/148] tty: serial: 8250: omap: synchronize rx_running The rx_running flag should show if DMA is currently active. However there is a window between when the flag is set/cleared and when the DMA is started/stopped. Because the flag is queried from both hard and soft irq contexts, the driver can make incorrect decisions and do things like start a DMA transfer using a buffer that is already setup to be used for a DMA transfer. This patch adds a spinlock to synchronize the rx_running flag and close the above mentioned window. Signed-off-by: John Ogness Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 65 ++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 9289999cb7c6..77c9f5aa8369 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -98,6 +98,7 @@ struct omap8250_priv { struct pm_qos_request pm_qos_request; struct work_struct qos_work; struct uart_8250_dma omap8250_dma; + spinlock_t rx_dma_lock; }; static u32 uart_read(struct uart_8250_port *up, u32 reg) @@ -669,14 +670,21 @@ static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir); static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) { + struct omap8250_priv *priv = p->port.private_data; struct uart_8250_dma *dma = p->dma; struct tty_port *tty_port = &p->port.state->port; struct dma_tx_state state; int count; + unsigned long flags; dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, dma->rx_size, DMA_FROM_DEVICE); + spin_lock_irqsave(&priv->rx_dma_lock, flags); + + if (!dma->rx_running) + goto unlock; + dma->rx_running = 0; dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); dmaengine_terminate_all(dma->rxchan); @@ -685,6 +693,9 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) tty_insert_flip_string(tty_port, dma->rx_buf, count); p->port.icount.rx += count; +unlock: + spin_unlock_irqrestore(&priv->rx_dma_lock, flags); + if (!error) omap_8250_rx_dma(p, 0); @@ -696,28 +707,45 @@ static void __dma_rx_complete(void *param) __dma_rx_do_complete(param, false); } +static void omap_8250_rx_dma_flush(struct uart_8250_port *p) +{ + struct omap8250_priv *priv = p->port.private_data; + struct uart_8250_dma *dma = p->dma; + unsigned long flags; + + spin_lock_irqsave(&priv->rx_dma_lock, flags); + + if (!dma->rx_running) { + spin_unlock_irqrestore(&priv->rx_dma_lock, flags); + return; + } + + dmaengine_pause(dma->rxchan); + + spin_unlock_irqrestore(&priv->rx_dma_lock, flags); + + __dma_rx_do_complete(p, true); +} + static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) { + struct omap8250_priv *priv = p->port.private_data; struct uart_8250_dma *dma = p->dma; + int err = 0; struct dma_async_tx_descriptor *desc; + unsigned long flags; switch (iir & 0x3f) { case UART_IIR_RLSI: /* 8250_core handles errors and break interrupts */ - if (dma->rx_running) { - dmaengine_pause(dma->rxchan); - __dma_rx_do_complete(p, true); - } + omap_8250_rx_dma_flush(p); return -EIO; case UART_IIR_RX_TIMEOUT: /* * If RCVR FIFO trigger level was not reached, complete the * transfer and let 8250_core copy the remaining data. */ - if (dma->rx_running) { - dmaengine_pause(dma->rxchan); - __dma_rx_do_complete(p, true); - } + omap_8250_rx_dma_flush(p); return -ETIMEDOUT; case UART_IIR_RDI: /* @@ -729,24 +757,25 @@ static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) * the DMA won't do anything soon so we have to cancel the DMA * transfer and purge the FIFO manually. */ - if (dma->rx_running) { - dmaengine_pause(dma->rxchan); - __dma_rx_do_complete(p, true); - } + omap_8250_rx_dma_flush(p); return -ETIMEDOUT; default: break; } + spin_lock_irqsave(&priv->rx_dma_lock, flags); + if (dma->rx_running) - return 0; + goto out; desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, dma->rx_size, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EBUSY; + if (!desc) { + err = -EBUSY; + goto out; + } dma->rx_running = 1; desc->callback = __dma_rx_complete; @@ -758,7 +787,9 @@ static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) dma->rx_size, DMA_FROM_DEVICE); dma_async_issue_pending(dma->rxchan); - return 0; +out: + spin_unlock_irqrestore(&priv->rx_dma_lock, flags); + return err; } static int omap_8250_tx_dma(struct uart_8250_port *p); @@ -1065,6 +1096,8 @@ static int omap8250_probe(struct platform_device *pdev) priv->latency); INIT_WORK(&priv->qos_work, omap8250_uart_qos_work); + spin_lock_init(&priv->rx_dma_lock); + device_init_wakeup(&pdev->dev, true); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, -1); From c547630f6b5cfebf211b313d7c1f54794ac013f0 Mon Sep 17 00:00:00 2001 From: Firo Yang Date: Sun, 26 Apr 2015 18:46:06 +0800 Subject: [PATCH 030/148] ARM: meson: serial: convert iounmap to devm_iounmap The function meson_uart_release_port() inappropriately try to iounmap() a resource managed by devm_ioremap_nocache(). The function meson_uart_release_port() maybe called by uart_ioctl() that means meson_uart_release_port() is not called from within a probe or remove function, for safety, I convert iounmap() to devm_iounmap(). Signed-off-by: Firo Yang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/meson_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 67c036702629..0fc83c962d10 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -370,7 +370,7 @@ static int meson_uart_verify_port(struct uart_port *port, static void meson_uart_release_port(struct uart_port *port) { if (port->flags & UPF_IOREMAP) { - iounmap(port->membase); + devm_iounmap(port->dev, port->membase); port->membase = NULL; } } From 59f89f21b5f34b50584a218734d2b9cc1efd8c0c Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 27 Apr 2015 08:49:54 +0200 Subject: [PATCH 031/148] tty: serial: 8250_mtk: remove unnecessary test When the driver has probed successfully the clk pointer is always valid, so no need to test for it. Signed-off-by: Sascha Hauer Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 7a11fac775c4..1297827ca7dd 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -214,10 +214,8 @@ static int mtk8250_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); serial8250_unregister_port(data->line); - if (!IS_ERR(data->uart_clk)) { - clk_disable_unprepare(data->uart_clk); - clk_put(data->uart_clk); - } + clk_disable_unprepare(data->uart_clk); + clk_put(data->uart_clk); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); @@ -249,8 +247,7 @@ static int mtk8250_runtime_suspend(struct device *dev) { struct mtk8250_data *data = dev_get_drvdata(dev); - if (!IS_ERR(data->uart_clk)) - clk_disable_unprepare(data->uart_clk); + clk_disable_unprepare(data->uart_clk); return 0; } @@ -259,8 +256,7 @@ static int mtk8250_runtime_resume(struct device *dev) { struct mtk8250_data *data = dev_get_drvdata(dev); - if (!IS_ERR(data->uart_clk)) - clk_prepare_enable(data->uart_clk); + clk_prepare_enable(data->uart_clk); return 0; } From a5fd844599d635baa7e527549dc10896944aad77 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 27 Apr 2015 08:49:55 +0200 Subject: [PATCH 032/148] tty: serial: 8250_mtk: Use devm_clk_get When a struct device * is present clk_get should be used rather than of_clk_get. Use the devm variant of this function to be able to drop the clk_put in the error and remove pathes. While at it fix a wrong error message. Signed-off-by: Sascha Hauer Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 1297827ca7dd..bcfaa8dcddd5 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -131,18 +131,16 @@ static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p, struct mtk8250_data *data) { int err; - struct device_node *np = pdev->dev.of_node; - data->uart_clk = of_clk_get(np, 0); + data->uart_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(data->uart_clk)) { - dev_warn(&pdev->dev, "Can't get timer clock\n"); + dev_warn(&pdev->dev, "Can't get uart clock\n"); return PTR_ERR(data->uart_clk); } err = clk_prepare_enable(data->uart_clk); if (err) { dev_warn(&pdev->dev, "Can't prepare clock\n"); - clk_put(data->uart_clk); return err; } p->uartclk = clk_get_rate(data->uart_clk); @@ -215,7 +213,6 @@ static int mtk8250_remove(struct platform_device *pdev) serial8250_unregister_port(data->line); clk_disable_unprepare(data->uart_clk); - clk_put(data->uart_clk); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); From 68e5fc4a255a7f453b8d156b89d1e37a0ad61465 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 27 Apr 2015 08:49:56 +0200 Subject: [PATCH 033/148] tty: serial: 8250_mtk: use pm_runtime callbacks for enabling The pm_runtime callbacks already enable and disable the device. Use them in probe() and remove() instead of duplicating the code. This allows us to concentrate more code for enabling/disabling the UART in a single place. Signed-off-by: Sascha Hauer Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 69 ++++++++++++++++-------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index bcfaa8dcddd5..2f28bd08b69f 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -115,6 +115,29 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, tty_termios_encode_baud_rate(termios, baud, baud); } +static int mtk8250_runtime_suspend(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + + clk_disable_unprepare(data->uart_clk); + + return 0; +} + +static int mtk8250_runtime_resume(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(data->uart_clk); + if (err) { + dev_warn(dev, "Can't enable clock\n"); + return err; + } + + return 0; +} + static void mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) { @@ -130,19 +153,12 @@ mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p, struct mtk8250_data *data) { - int err; - data->uart_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(data->uart_clk)) { dev_warn(&pdev->dev, "Can't get uart clock\n"); return PTR_ERR(data->uart_clk); } - err = clk_prepare_enable(data->uart_clk); - if (err) { - dev_warn(&pdev->dev, "Can't prepare clock\n"); - return err; - } p->uartclk = clk_get_rate(data->uart_clk); return 0; @@ -193,15 +209,19 @@ static int mtk8250_probe(struct platform_device *pdev) writel(0x0, uart.port.membase + (MTK_UART_RATE_FIX << uart.port.regshift)); + platform_set_drvdata(pdev, data); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + err = mtk8250_runtime_resume(&pdev->dev); + if (err) + return err; + } + data->line = serial8250_register_8250_port(&uart); if (data->line < 0) return data->line; - platform_set_drvdata(pdev, data); - - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - return 0; } @@ -212,10 +232,13 @@ static int mtk8250_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); serial8250_unregister_port(data->line); - clk_disable_unprepare(data->uart_clk); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); + + if (!pm_runtime_status_suspended(&pdev->dev)) + mtk8250_runtime_suspend(&pdev->dev); + return 0; } @@ -239,26 +262,6 @@ static int mtk8250_resume(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM -static int mtk8250_runtime_suspend(struct device *dev) -{ - struct mtk8250_data *data = dev_get_drvdata(dev); - - clk_disable_unprepare(data->uart_clk); - - return 0; -} - -static int mtk8250_runtime_resume(struct device *dev) -{ - struct mtk8250_data *data = dev_get_drvdata(dev); - - clk_prepare_enable(data->uart_clk); - - return 0; -} -#endif - static const struct dev_pm_ops mtk8250_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(mtk8250_suspend, mtk8250_resume) SET_RUNTIME_PM_OPS(mtk8250_runtime_suspend, mtk8250_runtime_resume, From c1c325d703d347d967242efb8fef6a1c91a31aac Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 27 Apr 2015 08:49:57 +0200 Subject: [PATCH 034/148] tty: serial: 8250_mtk: Add support for bus clock The mtk 8250 needs two clocks, one for providing the baudrate and one that needs to be enabled for register accesses. The latter has not been supported, this patch adds support for it. It is optional for now since not all SoCs provide a bus clock. Signed-off-by: Sascha Hauer Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/serial/mtk-uart.txt | 12 ++++++-- drivers/tty/serial/8250/8250_mtk.c | 28 ++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/serial/mtk-uart.txt b/Documentation/devicetree/bindings/serial/mtk-uart.txt index 44152261e5c5..8d63f1da07aa 100644 --- a/Documentation/devicetree/bindings/serial/mtk-uart.txt +++ b/Documentation/devicetree/bindings/serial/mtk-uart.txt @@ -14,7 +14,14 @@ Required properties: - interrupts: A single interrupt specifier. -- clocks: Clock driving the hardware. +- clocks : Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. +- clock-names: + - "baud": The clock the baudrate is derived from + - "bus": The bus clock for register accesses (optional) + +For compatibility with older device trees an unnamed clock is used for the +baud clock if the baudclk does not exist. Do not use this for new designs. Example: @@ -22,5 +29,6 @@ Example: compatible = "mediatek,mt6589-uart", "mediatek,mt6577-uart"; reg = <0x11006000 0x400>; interrupts = ; - clocks = <&uart_clk>; + clocks = <&uart_clk>, <&bus_clk>; + clock-names = "baud", "bus"; }; diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 2f28bd08b69f..8eb3876563f1 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -34,6 +34,7 @@ struct mtk8250_data { int line; struct clk *uart_clk; + struct clk *bus_clk; }; static void @@ -120,6 +121,7 @@ static int mtk8250_runtime_suspend(struct device *dev) struct mtk8250_data *data = dev_get_drvdata(dev); clk_disable_unprepare(data->uart_clk); + clk_disable_unprepare(data->bus_clk); return 0; } @@ -135,6 +137,12 @@ static int mtk8250_runtime_resume(struct device *dev) return err; } + err = clk_prepare_enable(data->bus_clk); + if (err) { + dev_warn(dev, "Can't enable bus clock\n"); + return err; + } + return 0; } @@ -153,13 +161,24 @@ mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p, struct mtk8250_data *data) { - data->uart_clk = devm_clk_get(&pdev->dev, NULL); + data->uart_clk = devm_clk_get(&pdev->dev, "baud"); if (IS_ERR(data->uart_clk)) { - dev_warn(&pdev->dev, "Can't get uart clock\n"); - return PTR_ERR(data->uart_clk); + /* + * For compatibility with older device trees try unnamed + * clk when no baud clk can be found. + */ + data->uart_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(data->uart_clk)) { + dev_warn(&pdev->dev, "Can't get uart clock\n"); + return PTR_ERR(data->uart_clk); + } + + return 0; } - p->uartclk = clk_get_rate(data->uart_clk); + data->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(data->bus_clk)) + return PTR_ERR(data->bus_clk); return 0; } @@ -204,6 +223,7 @@ static int mtk8250_probe(struct platform_device *pdev) uart.port.regshift = 2; uart.port.private_data = data; uart.port.set_termios = mtk8250_set_termios; + uart.port.uartclk = clk_get_rate(data->uart_clk); /* Disable Rate Fix function */ writel(0x0, uart.port.membase + From 1c5841e832e2d7563c31de4946118e78baf573a3 Mon Sep 17 00:00:00 2001 From: Eddie Huang Date: Tue, 28 Apr 2015 21:40:32 +0800 Subject: [PATCH 035/148] tty: serial: 8250: export early_serial8250_setup function 8250-like uart driver may call early_serial8250_setup to reuse 8250_early.c character output function. Signed-off-by: Eddie Huang Tested-by: Sascha Hauer Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_early.c | 2 +- include/linux/serial_8250.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index 6c0fd8b9d1c3..771dda29a0f8 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -131,7 +131,7 @@ static void __init init_port(struct earlycon_device *device) serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); } -static int __init early_serial8250_setup(struct earlycon_device *device, +int __init early_serial8250_setup(struct earlycon_device *device, const char *options) { if (!(device->port.membase || device->port.iobase)) diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 78097e7a330a..f0c68d88b6f4 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -137,6 +137,8 @@ extern int early_serial_setup(struct uart_port *port); extern unsigned int serial8250_early_in(struct uart_port *port, int offset); extern void serial8250_early_out(struct uart_port *port, int offset, int value); +extern int early_serial8250_setup(struct earlycon_device *device, + const char *options); extern void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old); extern int serial8250_do_startup(struct uart_port *port); From 2c40b57dc82de8e1337666341efe41a0fe9dd741 Mon Sep 17 00:00:00 2001 From: Eddie Huang Date: Tue, 28 Apr 2015 21:40:33 +0800 Subject: [PATCH 036/148] tty: serial: 8250_mtk: Add earlycon Add 8250 MTK UART driver to support earlycon device tree. Earlycon take effect by add "earlycon" in kernel boot argument add "linux,sdtout-path" property in device tree file Signed-off-by: Eddie Huang Tested-by: Sascha Hauer Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 8eb3876563f1..4488c2bdb7ba 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -305,6 +305,19 @@ static struct platform_driver mtk8250_platform_driver = { }; module_platform_driver(mtk8250_platform_driver); +static int __init early_mtk8250_setup(struct earlycon_device *device, + const char *options) +{ + if (!device->port.membase) + return -ENODEV; + + device->port.iotype = UPIO_MEM32; + + return early_serial8250_setup(device, NULL); +} + +OF_EARLYCON_DECLARE(mtk8250, "mediatek,mt6577-uart", early_mtk8250_setup); + MODULE_AUTHOR("Matthias Brugger"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Mediatek 8250 serial port driver"); From a6ffe8966acbb66bbff03bb9273dfe88b04585c2 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Wed, 29 Apr 2015 06:45:08 +0000 Subject: [PATCH 037/148] serial: sirf: use dynamic method allocate uart structure In different platform of SiRF SoCs, there is no same uart and usp-uart numbers, it is not convenient to use hard-coded ports array and port lines. here we drop the hard-coded ports table , and drop "cell-index". then move to use alias id to get line. for example: aliases { serial0 = &uart0; serial1 = &uart1; serial2 = &uart2; serial3 = &uart3; serial4 = &uart4; serial5 = &uart5; serial6 = &uart6; serial9 = &usp2; }; at the same, enlarge the max port number according to the chip with the most UART. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 84 ++++++++----------------------- drivers/tty/serial/sirfsoc_uart.h | 8 +-- 2 files changed, 23 insertions(+), 69 deletions(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 247faed40e37..e033b93f5e1b 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -59,50 +59,7 @@ static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = { {9600, 1114979}, }; -static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = { - [0] = { - .port = { - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF, - .line = 0, - }, - }, - [1] = { - .port = { - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF, - .line = 1, - }, - }, - [2] = { - .port = { - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF, - .line = 2, - }, - }, - [3] = { - .port = { - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF, - .line = 3, - }, - }, - [4] = { - .port = { - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF, - .line = 4, - }, - }, - [5] = { - .port = { - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF, - .line = 5, - }, - }, -}; +static struct sirfsoc_uart_port *sirf_ports[SIRFSOC_UART_NR]; static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port) { @@ -1187,27 +1144,29 @@ sirfsoc_uart_console_setup(struct console *co, char *options) unsigned int bits = 8; unsigned int parity = 'n'; unsigned int flow = 'n'; - struct uart_port *port = &sirfsoc_uart_ports[co->index].port; - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_uart_port *sirfport; + struct sirfsoc_register *ureg; if (co->index < 0 || co->index >= SIRFSOC_UART_NR) return -EINVAL; - - if (!port->mapbase) + sirfport = sirf_ports[co->index]; + if (!sirfport) + return -ENODEV; + ureg = &sirfport->uart_reg->uart_reg; + if (!sirfport->port.mapbase) return -ENODEV; /* enable usp in mode1 register */ if (sirfport->uart_reg->uart_type == SIRF_USP_UART) - wr_regl(port, ureg->sirfsoc_mode1, SIRFSOC_USP_EN | + wr_regl(&sirfport->port, ureg->sirfsoc_mode1, SIRFSOC_USP_EN | SIRFSOC_USP_ENDIAN_CTRL_LSBF); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - port->cons = co; + sirfport->port.cons = co; /* default console tx/rx transfer using io mode */ sirfport->rx_dma_chan = NULL; sirfport->tx_dma_chan = NULL; - return uart_set_options(port, co, baud, parity, bits, flow); + return uart_set_options(&sirfport->port, co, baud, parity, bits, flow); } static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch) @@ -1224,8 +1183,10 @@ static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch) static void sirfsoc_uart_console_write(struct console *co, const char *s, unsigned int count) { - struct uart_port *port = &sirfsoc_uart_ports[co->index].port; - uart_console_write(port, s, count, sirfsoc_uart_console_putchar); + struct sirfsoc_uart_port *sirfport = sirf_ports[co->index]; + + uart_console_write(&sirfport->port, s, count, + sirfsoc_uart_console_putchar); } static struct console sirfsoc_uart_console = { @@ -1284,16 +1245,15 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) const struct of_device_id *match; match = of_match_node(sirfsoc_uart_ids, pdev->dev.of_node); - if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) { - dev_err(&pdev->dev, - "Unable to find cell-index in uart node.\n"); - ret = -EFAULT; + sirfport = devm_kzalloc(&pdev->dev, sizeof(*sirfport), GFP_KERNEL); + if (!sirfport) { + ret = -ENOMEM; goto err; } - if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart")) - pdev->id += ((struct sirfsoc_uart_register *) - match->data)->uart_param.register_uart_nr; - sirfport = &sirfsoc_uart_ports[pdev->id]; + sirfport->port.line = of_alias_get_id(pdev->dev.of_node, "serial"); + sirf_ports[sirfport->port.line] = sirfport; + sirfport->port.iotype = UPIO_MEM; + sirfport->port.flags = UPF_BOOT_AUTOCONF; port = &sirfport->port; port->dev = &pdev->dev; port->private_data = sirfport; diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index 11fd6814f76d..cd30ba94bec8 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -9,8 +9,6 @@ struct sirfsoc_uart_param { const char *uart_name; const char *port_name; - u32 uart_nr; - u32 register_uart_nr; }; struct sirfsoc_register { @@ -183,8 +181,6 @@ struct sirfsoc_uart_register sirfsoc_usp = { .uart_param = { .uart_name = "ttySiRF", .port_name = "sirfsoc-uart", - .uart_nr = 2, - .register_uart_nr = 3, }, }; @@ -255,8 +251,6 @@ struct sirfsoc_uart_register sirfsoc_uart = { .uart_param = { .uart_name = "ttySiRF", .port_name = "sirfsoc_uart", - .uart_nr = 3, - .register_uart_nr = 0, }, }; /* uart io ctrl */ @@ -365,7 +359,7 @@ struct sirfsoc_uart_register sirfsoc_uart = { #define SIRFSOC_UART_MINOR 0 #define SIRFUART_PORT_NAME "sirfsoc-uart" #define SIRFUART_MAP_SIZE 0x200 -#define SIRFSOC_UART_NR 6 +#define SIRFSOC_UART_NR 11 #define SIRFSOC_PORT_TYPE 0xa5 /* Uart Common Use Macro*/ From cb4595a2158371f8180b226fce42a47086585d5c Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Wed, 29 Apr 2015 06:45:09 +0000 Subject: [PATCH 038/148] serial: sirf: use uart_port's fifosize for fifo related operation In SiRF platform, there are different fifo size of uart and usp, with the fifosize configuration changes in different chips, we can not use port line to decide how to check FIFO full,empty and level. There is a direct mapping between FIFO HW register layout with fifo size, so move to use fifosize as the input to check fifo status. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 24 ++++++-------- drivers/tty/serial/sirfsoc_uart.h | 53 ++++++++++++++----------------- 2 files changed, 33 insertions(+), 44 deletions(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index e033b93f5e1b..7214abe0d913 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -73,8 +73,7 @@ static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port) struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; reg = rd_regl(port, ureg->sirfsoc_tx_fifo_status); - - return (reg & ufifo_st->ff_empty(port->line)) ? TIOCSER_TEMT : 0; + return (reg & ufifo_st->ff_empty(port)) ? TIOCSER_TEMT : 0; } static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port) @@ -247,8 +246,7 @@ static void sirfsoc_uart_start_tx(struct uart_port *port) if (sirfport->tx_dma_chan) sirfsoc_uart_tx_with_dma(sirfport); else { - sirfsoc_uart_pio_tx_chars(sirfport, - SIRFSOC_UART_IO_TX_REASONABLE_CNT); + sirfsoc_uart_pio_tx_chars(sirfport, port->fifosize); wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); if (!sirfport->is_atlas7) wr_regl(port, ureg->sirfsoc_int_en_reg, @@ -374,7 +372,7 @@ sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count) if (!tty) return -ENODEV; while (!(rd_regl(port, ureg->sirfsoc_rx_fifo_status) & - ufifo_st->ff_empty(port->line))) { + ufifo_st->ff_empty(port))) { ch = rd_regl(port, ureg->sirfsoc_rx_fifo_data) | SIRFUART_DUMMY_READ; if (unlikely(uart_handle_sysrq_char(port, ch))) @@ -401,7 +399,7 @@ sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count) unsigned int num_tx = 0; while (!uart_circ_empty(xmit) && !(rd_regl(port, ureg->sirfsoc_tx_fifo_status) & - ufifo_st->ff_full(port->line)) && + ufifo_st->ff_full(port)) && count--) { wr_regl(port, ureg->sirfsoc_tx_fifo_data, xmit->buf[xmit->tail]); @@ -626,8 +624,7 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) sirfsoc_uart_handle_rx_done(sirfport); } else { if (intr_status & SIRFUART_RX_IO_INT_ST(uint_st)) - sirfsoc_uart_pio_rx_chars(port, - SIRFSOC_UART_IO_RX_MAX_CNT); + sirfsoc_uart_pio_rx_chars(port, port->fifosize); } spin_unlock(&port->lock); tty_flip_buffer_push(&state->port); @@ -641,10 +638,10 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) return IRQ_HANDLED; } else { sirfsoc_uart_pio_tx_chars(sirfport, - SIRFSOC_UART_IO_TX_REASONABLE_CNT); + port->fifosize); if ((uart_circ_empty(xmit)) && (rd_regl(port, ureg->sirfsoc_tx_fifo_status) & - ufifo_st->ff_empty(port->line))) + ufifo_st->ff_empty(port))) sirfsoc_uart_stop_tx(port); } } @@ -746,7 +743,7 @@ sirfsoc_usp_calc_sample_div(unsigned long set_rate, unsigned long ioclk_div = 0; unsigned long temp_delta; - for (sample_div = SIRF_MIN_SAMPLE_DIV; + for (sample_div = SIRF_USP_MIN_SAMPLE_DIV; sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) { temp_delta = ioclk_rate - (ioclk_rate + (set_rate * sample_div) / 2) @@ -1012,7 +1009,6 @@ static int sirfsoc_uart_startup(struct uart_port *port) index, port->irq); goto irq_err; } - /* initial hardware settings */ wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl) | @@ -1174,8 +1170,8 @@ static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch) struct sirfsoc_uart_port *sirfport = to_sirfport(port); struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; - while (rd_regl(port, - ureg->sirfsoc_tx_fifo_status) & ufifo_st->ff_full(port->line)) + while (rd_regl(port, ureg->sirfsoc_tx_fifo_status) & + ufifo_st->ff_full(port)) cpu_relax(); wr_regl(port, ureg->sirfsoc_tx_fifo_data, ch); } diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index cd30ba94bec8..af2f1878330a 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -6,6 +6,7 @@ * Licensed under GPLv2 or later. */ #include +#include struct sirfsoc_uart_param { const char *uart_name; const char *port_name; @@ -43,8 +44,8 @@ struct sirfsoc_register { u32 sirfsoc_async_param_reg; }; -typedef u32 (*fifo_full_mask)(int line); -typedef u32 (*fifo_empty_mask)(int line); +typedef u32 (*fifo_full_mask)(struct uart_port *port); +typedef u32 (*fifo_empty_mask)(struct uart_port *port); struct sirfsoc_fifo_status { fifo_full_mask ff_full; @@ -103,21 +104,20 @@ struct sirfsoc_uart_register { enum sirfsoc_uart_type uart_type; }; -u32 usp_ff_full(int line) +u32 uart_usp_ff_full_mask(struct uart_port *port) { - return 0x80; + u32 full_bit; + + full_bit = ilog2(port->fifosize); + return (1 << full_bit); } -u32 usp_ff_empty(int line) + +u32 uart_usp_ff_empty_mask(struct uart_port *port) { - return 0x100; -} -u32 uart_ff_full(int line) -{ - return (line == 1) ? (0x20) : (0x80); -} -u32 uart_ff_empty(int line) -{ - return (line == 1) ? (0x40) : (0x100); + u32 empty_bit; + + empty_bit = ilog2(port->fifosize); + return (1 << empty_bit); } struct sirfsoc_uart_register sirfsoc_usp = { .uart_reg = { @@ -175,8 +175,8 @@ struct sirfsoc_uart_register sirfsoc_usp = { .sirfsoc_rxd_brk = BIT(15), }, .fifo_status = { - .ff_full = usp_ff_full, - .ff_empty = usp_ff_empty, + .ff_full = uart_usp_ff_full_mask, + .ff_empty = uart_usp_ff_empty_mask, }, .uart_param = { .uart_name = "ttySiRF", @@ -245,8 +245,8 @@ struct sirfsoc_uart_register sirfsoc_uart = { .sirfsoc_rts = BIT(15), }, .fifo_status = { - .ff_full = uart_ff_full, - .ff_empty = uart_ff_empty, + .ff_full = uart_usp_ff_full_mask, + .ff_empty = uart_usp_ff_empty_mask, }, .uart_param = { .uart_name = "ttySiRF", @@ -294,6 +294,7 @@ struct sirfsoc_uart_register sirfsoc_uart = { /* Macro Specific*/ #define SIRFUART_INT_EN_CLR 0x0060 /* Baud Rate Calculation */ +#define SIRF_USP_MIN_SAMPLE_DIV 0x1 #define SIRF_MIN_SAMPLE_DIV 0xf #define SIRF_MAX_SAMPLE_DIV 0x3f #define SIRF_IOCLK_DIV_MAX 0xffff @@ -328,7 +329,7 @@ struct sirfsoc_uart_register sirfsoc_uart = { #define SIRFUART_RECV_TIMEOUT(port, x) \ (((port)->line > 2) ? (x & 0xFFFF) : ((x) & 0xFFFF) << 16) -#define SIRFUART_FIFO_THD(port) ((port->line) == 1 ? 16 : 64) +#define SIRFUART_FIFO_THD(port) (port->fifosize >> 1) #define SIRFUART_ERR_INT_STAT(port, unit_st) \ (uint_st->sirfsoc_rx_oflow | \ uint_st->sirfsoc_frm_err | \ @@ -365,10 +366,6 @@ struct sirfsoc_uart_register sirfsoc_uart = { /* Uart Common Use Macro*/ #define SIRFSOC_RX_DMA_BUF_SIZE 256 #define BYTES_TO_ALIGN(dma_addr) ((unsigned long)(dma_addr) & 0x3) -#define LOOP_DMA_BUFA_FILL 1 -#define LOOP_DMA_BUFB_FILL 2 -#define TX_TRAN_PIO 1 -#define TX_TRAN_DMA 2 /* Uart Fifo Level Chk */ #define SIRFUART_TX_FIFO_SC_OFFSET 0 #define SIRFUART_TX_FIFO_LC_OFFSET 10 @@ -437,10 +434,6 @@ struct sirfsoc_uart_port { #define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) /* UART Port Mask */ -#define SIRFUART_FIFOLEVEL_MASK(port) ((port->line == 1) ? (0x1f) : (0x7f)) -#define SIRFUART_FIFOFULL_MASK(port) ((port->line == 1) ? (0x20) : (0x80)) -#define SIRFUART_FIFOEMPTY_MASK(port) ((port->line == 1) ? (0x40) : (0x100)) - -/* I/O Mode */ -#define SIRFSOC_UART_IO_RX_MAX_CNT 256 -#define SIRFSOC_UART_IO_TX_REASONABLE_CNT 256 +#define SIRFUART_FIFOLEVEL_MASK(port) ((port->fifosize - 1) & 0xFFF) +#define SIRFUART_FIFOFULL_MASK(port) (port->fifosize & 0xFFF) +#define SIRFUART_FIFOEMPTY_MASK(port) ((port->fifosize & 0xFFF) << 1) From ed334c0e3b4def138a27c84d7331c304f87a4701 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:04 +0200 Subject: [PATCH 039/148] tty: 68328serial.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/68328serial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index 5dc9c4bfa66e..748c18f8c8cd 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -508,7 +508,8 @@ static void change_speed(struct m68k_serial *info, struct tty_struct *tty) int i; cflag = tty->termios.c_cflag; - if (!(port = info->port)) + port = info->port; + if (!port) return; ustcnt = uart->ustcnt; From 2eeaf0bbca79412f15d00a08b930cbd9235fc3a2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:05 +0200 Subject: [PATCH 040/148] tty: amiserial.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 894d3a84e285..e53d9a512c6d 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1782,7 +1782,8 @@ static int __exit amiga_serial_remove(struct platform_device *pdev) struct serial_state *state = platform_get_drvdata(pdev); /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ - if ((error = tty_unregister_driver(serial_driver))) + error = tty_unregister_driver(serial_driver); + if (error) printk("SERIAL: failed to unregister serial driver (%d)\n", error); put_tty_driver(serial_driver); From 6d4e751e108b3a2333412b603911f8243e60a86e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:06 +0200 Subject: [PATCH 041/148] tty: consolemap.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby CC: Takashi Iwai CC: Imre Deak Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/consolemap.c | 60 +++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 59b25e039968..c8c91f0476a2 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -261,19 +261,22 @@ u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) int m; if (glyph < 0 || glyph >= MAX_GLYPH) return 0; - else if (!(p = *conp->vc_uni_pagedir_loc)) - return glyph; - else if (use_unicode) { - if (!p->inverse_trans_unicode) + else { + p = *conp->vc_uni_pagedir_loc; + if (!p) return glyph; - else - return p->inverse_trans_unicode[glyph]; - } else { - m = inv_translate[conp->vc_num]; - if (!p->inverse_translations[m]) - return glyph; - else - return p->inverse_translations[m][glyph]; + else if (use_unicode) { + if (!p->inverse_trans_unicode) + return glyph; + else + return p->inverse_trans_unicode[glyph]; + } else { + m = inv_translate[conp->vc_num]; + if (!p->inverse_translations[m]) + return glyph; + else + return p->inverse_translations[m][glyph]; + } } } EXPORT_SYMBOL_GPL(inverse_translate); @@ -397,7 +400,8 @@ static void con_release_unimap(struct uni_pagedir *p) if (p == dflt) dflt = NULL; for (i = 0; i < 32; i++) { - if ((p1 = p->uni_pgdir[i]) != NULL) { + p1 = p->uni_pgdir[i]; + if (p1 != NULL) { for (j = 0; j < 32; j++) kfree(p1[j]); kfree(p1); @@ -473,14 +477,16 @@ con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos) int i, n; u16 **p1, *p2; - if (!(p1 = p->uni_pgdir[n = unicode >> 11])) { + p1 = p->uni_pgdir[n = unicode >> 11]; + if (!p1) { p1 = p->uni_pgdir[n] = kmalloc(32*sizeof(u16 *), GFP_KERNEL); if (!p1) return -ENOMEM; for (i = 0; i < 32; i++) p1[i] = NULL; } - if (!(p2 = p1[n = (unicode >> 6) & 0x1f])) { + p2 = p1[n = (unicode >> 6) & 0x1f]; + if (!p2) { p2 = p1[n] = kmalloc(64*sizeof(u16), GFP_KERNEL); if (!p2) return -ENOMEM; memset(p2, 0xff, 64*sizeof(u16)); /* No glyphs for the characters (yet) */ @@ -569,10 +575,12 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) * entries from "p" (old) to "q" (new). */ l = 0; /* unicode value */ - for (i = 0; i < 32; i++) - if ((p1 = p->uni_pgdir[i])) - for (j = 0; j < 32; j++) - if ((p2 = p1[j])) { + for (i = 0; i < 32; i++) { + p1 = p->uni_pgdir[i]; + if (p1) + for (j = 0; j < 32; j++) { + p2 = p1[j]; + if (p2) { for (k = 0; k < 64; k++, l++) if (p2[k] != 0xffff) { /* @@ -593,9 +601,11 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) /* Account for row of 64 empty entries */ l += 64; } + } else /* Account for empty table */ l += 32 * 64; + } /* * Finished copying font table, set vc_uni_pagedir to new table @@ -735,10 +745,12 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni ect = 0; if (*vc->vc_uni_pagedir_loc) { p = *vc->vc_uni_pagedir_loc; - for (i = 0; i < 32; i++) - if ((p1 = p->uni_pgdir[i])) - for (j = 0; j < 32; j++) - if ((p2 = *(p1++))) + for (i = 0; i < 32; i++) { + p1 = p->uni_pgdir[i]; + if (p1) + for (j = 0; j < 32; j++) { + p2 = *(p1++); + if (p2) for (k = 0; k < 64; k++) { if (*p2 < MAX_GLYPH && ect++ < ct) { __put_user((u_short)((i<<11)+(j<<6)+k), @@ -749,6 +761,8 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni } p2++; } + } + } } __put_user(ect, uct); console_unlock(); From 9ff46047b6ee6821895b34359dd46dc8111706b8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:07 +0200 Subject: [PATCH 042/148] tty: crisv10.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Mikael Starvik CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman Acked-by: Jesper Nilsson --- drivers/tty/serial/crisv10.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 7dced3a3f96b..3e4470af5c50 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -1635,7 +1635,8 @@ alloc_recv_buffer(unsigned int size) { struct etrax_recv_buffer *buffer; - if (!(buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC))) + buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC); + if (!buffer) return NULL; buffer->next = NULL; @@ -1671,7 +1672,8 @@ add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char fl { struct etrax_recv_buffer *buffer; if (info->uses_dma_in) { - if (!(buffer = alloc_recv_buffer(4))) + buffer = alloc_recv_buffer(4); + if (!buffer) return 0; buffer->length = 1; @@ -1709,7 +1711,8 @@ static unsigned int handle_descr_data(struct e100_serial *info, append_recv_buffer(info, buffer); - if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) + buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE); + if (!buffer) panic("%s: Failed to allocate memory for receive buffer!\n", __func__); descr->buf = virt_to_phys(buffer->buffer); @@ -1825,7 +1828,8 @@ static int start_recv_dma(struct e100_serial *info) /* Set up the receiving descriptors */ for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) { - if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) + buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE); + if (!buffer) panic("%s: Failed to allocate memory for receive buffer!\n", __func__); descr[i].ctrl = d_int; From b8dccc1b2e178c4d9702c30fadbadff0c093e3d7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:08 +0200 Subject: [PATCH 043/148] tty: hvc_console.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby CC: Tomoki Sekiyama CC: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvc_console.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 4fcec1d793a7..4e9c4cc9e1b5 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -319,7 +319,8 @@ static int hvc_install(struct tty_driver *driver, struct tty_struct *tty) int rc; /* Auto increments kref reference if found. */ - if (!(hp = hvc_get_by_index(tty->index))) + hp = hvc_get_by_index(tty->index); + if (!hp) return -ENODEV; tty->driver_data = hp; From 873b4f189289660f7f35c1e65f52e7f437bf20c8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:09 +0200 Subject: [PATCH 044/148] tty: hvcs.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby CC: Masanari Iida Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvcs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 81ff7e1bfb1a..f7ff97c0ad34 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -1044,8 +1044,8 @@ static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, * It is possible that the vty-server was removed between the time that * the conn was registered and now. */ - if (!(rc = request_irq(irq, &hvcs_handle_interrupt, - 0, "ibmhvcs", hvcsd))) { + rc = request_irq(irq, &hvcs_handle_interrupt, 0, "ibmhvcs", hvcsd); + if (!rc) { /* * It is possible the vty-server was removed after the irq was * requested but before we have time to enable interrupts. From 01e51df54697c0ad6dbc5c7bc81b3af3f5329dc0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:10 +0200 Subject: [PATCH 045/148] tty: icom.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/icom.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index 45fc323b95e6..ffc7cb2585a6 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -1504,7 +1504,8 @@ static int icom_probe(struct pci_dev *dev, return retval; } - if ( (retval = pci_request_regions(dev, "icom"))) { + retval = pci_request_regions(dev, "icom"); + if (retval) { dev_err(&dev->dev, "pci_request_regions FAILED\n"); pci_disable_device(dev); return retval; @@ -1512,7 +1513,8 @@ static int icom_probe(struct pci_dev *dev, pci_set_master(dev); - if ( (retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg))) { + retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg); + if (retval) { dev_err(&dev->dev, "PCI Config read FAILED\n"); return retval; } @@ -1556,9 +1558,8 @@ static int icom_probe(struct pci_dev *dev, } /* save off irq and request irq line */ - if ( (retval = request_irq(dev->irq, icom_interrupt, - IRQF_SHARED, ICOM_DRIVER_NAME, - (void *) icom_adapter))) { + retval = request_irq(dev->irq, icom_interrupt, IRQF_SHARED, ICOM_DRIVER_NAME, (void *)icom_adapter); + if (retval) { goto probe_exit2; } From a8d087d3dc7594ce295680eb7b9d2b332a0e4b9c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:11 +0200 Subject: [PATCH 046/148] tty: ioc3_serial.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Pat Gefre CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ioc3_serial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c index abd7ea26ed9a..27b5fefac171 100644 --- a/drivers/tty/serial/ioc3_serial.c +++ b/drivers/tty/serial/ioc3_serial.c @@ -2137,7 +2137,8 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) /* register port with the serial core */ - if ((ret = ioc3_serial_core_attach(is, idd))) + ret = ioc3_serial_core_attach(is, idd); + if (ret) goto out4; Num_of_ioc3_cards++; From 9a4115ba10d9488e390ae93e43ef801961f537e8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:12 +0200 Subject: [PATCH 047/148] tty: ioc4_serial.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Pat Gefre CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ioc4_serial.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index aa28209f44c1..e5c42fef69d2 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -1011,7 +1011,8 @@ static irqreturn_t ioc4_intr(int irq, void *arg) */ for (xx = 0; xx < num_intrs; xx++) { intr_info = &soft->is_intr_type[intr_type].is_intr_info[xx]; - if ((this_mir = this_ir & intr_info->sd_bits)) { + this_mir = this_ir & intr_info->sd_bits; + if (this_mir) { /* Disable owned interrupts, call handler */ handled++; write_ireg(soft, intr_info->sd_bits, IOC4_W_IEC, @@ -2865,10 +2866,12 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd) /* register port with the serial core - 1 rs232, 1 rs422 */ - if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS232))) + ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS232); + if (ret) goto out4; - if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS422))) + ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS422); + if (ret) goto out5; Num_of_ioc4_cards++; From f2908f70c6ecb2993efb1594924f6e9962c00547 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:13 +0200 Subject: [PATCH 048/148] tty: mpsc.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mpsc.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index 856fd5a5fa3c..82bb6d1fe23b 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -913,7 +913,8 @@ static int mpsc_make_ready(struct mpsc_port_info *pi) if (!pi->ready) { mpsc_init_hw(pi); - if ((rc = mpsc_alloc_ring_mem(pi))) + rc = mpsc_alloc_ring_mem(pi); + if (rc) return rc; mpsc_init_rings(pi); pi->ready = 1; @@ -1895,7 +1896,8 @@ static int mpsc_shared_drv_probe(struct platform_device *dev) int rc = -ENODEV; if (dev->id == 0) { - if (!(rc = mpsc_shared_map_regs(dev))) { + rc = mpsc_shared_map_regs(dev); + if (!rc) { pdata = (struct mpsc_shared_pdata *) dev_get_platdata(&dev->dev); @@ -2081,14 +2083,16 @@ static int mpsc_drv_probe(struct platform_device *dev) if (dev->id < MPSC_NUM_CTLRS) { pi = &mpsc_ports[dev->id]; - if (!(rc = mpsc_drv_map_regs(pi, dev))) { + rc = mpsc_drv_map_regs(pi, dev); + if (!rc) { mpsc_drv_get_platform_data(pi, dev, dev->id); pi->port.dev = &dev->dev; - if (!(rc = mpsc_make_ready(pi))) { + rc = mpsc_make_ready(pi); + if (!rc) { spin_lock_init(&pi->tx_lock); - if (!(rc = uart_add_one_port(&mpsc_reg, - &pi->port))) { + rc = uart_add_one_port(&mpsc_reg, &pi->port); + if (!rc) { rc = 0; } else { mpsc_release_port((struct uart_port *) @@ -2136,9 +2140,12 @@ static int __init mpsc_drv_init(void) memset(mpsc_ports, 0, sizeof(mpsc_ports)); memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs)); - if (!(rc = uart_register_driver(&mpsc_reg))) { - if (!(rc = platform_driver_register(&mpsc_shared_driver))) { - if ((rc = platform_driver_register(&mpsc_driver))) { + rc = uart_register_driver(&mpsc_reg); + if (!rc) { + rc = platform_driver_register(&mpsc_shared_driver); + if (!rc) { + rc = platform_driver_register(&mpsc_driver); + if (rc) { platform_driver_unregister(&mpsc_shared_driver); uart_unregister_driver(&mpsc_reg); } From a271ca37ac85efac89533799d53dc32325480db1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:14 +0200 Subject: [PATCH 049/148] tty: synclink.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/synclink.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index b7991707ffc0..2fac7123b274 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -4410,7 +4410,8 @@ static void synclink_cleanup(void) printk("Unloading %s: %s\n", driver_name, driver_version); if (serial_driver) { - if ((rc = tty_unregister_driver(serial_driver))) + rc = tty_unregister_driver(serial_driver); + if (rc) printk("%s(%d) failed to unregister tty driver err=%d\n", __FILE__,__LINE__,rc); put_tty_driver(serial_driver); @@ -7751,7 +7752,8 @@ static int hdlcdev_open(struct net_device *dev) printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name); /* generic HDLC layer open processing */ - if ((rc = hdlc_open(dev))) + rc = hdlc_open(dev); + if (rc) return rc; /* arbitrate between network and tty opens */ @@ -8018,7 +8020,8 @@ static int hdlcdev_init(struct mgsl_struct *info) /* allocate and initialize network and HDLC layer objects */ - if (!(dev = alloc_hdlcdev(info))) { + dev = alloc_hdlcdev(info); + if (!dev) { printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__); return -ENOMEM; } @@ -8039,7 +8042,8 @@ static int hdlcdev_init(struct mgsl_struct *info) hdlc->xmit = hdlcdev_xmit; /* register objects with HDLC layer */ - if ((rc = register_hdlc_device(dev))) { + rc = register_hdlc_device(dev); + if (rc) { printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); free_netdev(dev); return rc; @@ -8075,7 +8079,8 @@ static int synclink_init_one (struct pci_dev *dev, return -EIO; } - if (!(info = mgsl_allocate_device())) { + info = mgsl_allocate_device(); + if (!info) { printk("can't allocate device instance data.\n"); return -EIO; } From 3236133efedb39e9e6bac2bf242c181b58dc0928 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:15 +0200 Subject: [PATCH 050/148] tty: synclink_gt.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/synclink_gt.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 0e8c39b6ccd4..0ea8eee00178 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -1539,7 +1539,8 @@ static int hdlcdev_open(struct net_device *dev) DBGINFO(("%s hdlcdev_open\n", dev->name)); /* generic HDLC layer open processing */ - if ((rc = hdlc_open(dev))) + rc = hdlc_open(dev); + if (rc) return rc; /* arbitrate between network and tty opens */ @@ -1803,7 +1804,8 @@ static int hdlcdev_init(struct slgt_info *info) /* allocate and initialize network and HDLC layer objects */ - if (!(dev = alloc_hdlcdev(info))) { + dev = alloc_hdlcdev(info); + if (!dev) { printk(KERN_ERR "%s hdlc device alloc failure\n", info->device_name); return -ENOMEM; } @@ -1824,7 +1826,8 @@ static int hdlcdev_init(struct slgt_info *info) hdlc->xmit = hdlcdev_xmit; /* register objects with HDLC layer */ - if ((rc = register_hdlc_device(dev))) { + rc = register_hdlc_device(dev); + if (rc) { printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); free_netdev(dev); return rc; @@ -1879,7 +1882,8 @@ static void rx_async(struct slgt_info *info) stat = 0; - if ((status = *(p+1) & (BIT1 + BIT0))) { + status = *(p + 1) & (BIT1 + BIT0); + if (status) { if (status & BIT1) icount->parity++; else if (status & BIT0) @@ -3755,7 +3759,8 @@ static void slgt_cleanup(void) if (serial_driver) { for (info=slgt_device_list ; info != NULL ; info=info->next_device) tty_unregister_device(serial_driver, info->line); - if ((rc = tty_unregister_driver(serial_driver))) + rc = tty_unregister_driver(serial_driver); + if (rc) DBGERR(("tty_unregister_driver error=%d\n", rc)); put_tty_driver(serial_driver); } From 485e148d11829025b9195cc8baf0ab6cc6a19558 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:16 +0200 Subject: [PATCH 051/148] tty: synclinkmp.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/synclinkmp.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index c3f90910fed9..08633a8139ff 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -1655,7 +1655,8 @@ static int hdlcdev_open(struct net_device *dev) printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name); /* generic HDLC layer open processing */ - if ((rc = hdlc_open(dev))) + rc = hdlc_open(dev); + if (rc) return rc; /* arbitrate between network and tty opens */ @@ -1922,7 +1923,8 @@ static int hdlcdev_init(SLMP_INFO *info) /* allocate and initialize network and HDLC layer objects */ - if (!(dev = alloc_hdlcdev(info))) { + dev = alloc_hdlcdev(info); + if (!dev) { printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__); return -ENOMEM; } @@ -1943,7 +1945,8 @@ static int hdlcdev_init(SLMP_INFO *info) hdlc->xmit = hdlcdev_xmit; /* register objects with HDLC layer */ - if ((rc = register_hdlc_device(dev))) { + rc = register_hdlc_device(dev); + if (rc) { printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); free_netdev(dev); return rc; @@ -3920,7 +3923,8 @@ static void synclinkmp_cleanup(void) printk("Unloading %s %s\n", driver_name, driver_version); if (serial_driver) { - if ((rc = tty_unregister_driver(serial_driver))) + rc = tty_unregister_driver(serial_driver); + if (rc) printk("%s(%d) failed to unregister tty driver err=%d\n", __FILE__,__LINE__,rc); put_tty_driver(serial_driver); From e16cb0a72fc291847adf10ec26b7879c69f12327 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:17 +0200 Subject: [PATCH 052/148] tty: tty_buffer.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 75661641f5fe..5b2b0ffdde28 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -262,7 +262,8 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t 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 = tty_buffer_alloc(port, size); + if (n != NULL) { n->flags = flags; buf->tail = n; b->commit = b->used; From f9ce5ccfd97a61ed318eb52cbb358f1aa826d82f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:18 +0200 Subject: [PATCH 053/148] tty: tty_ldsem.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldsem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c index 0ffb0cbe2823..ad7eba5ca380 100644 --- a/drivers/tty/tty_ldsem.c +++ b/drivers/tty/tty_ldsem.c @@ -299,7 +299,8 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout) timeout = schedule_timeout(timeout); raw_spin_lock_irq(&sem->wait_lock); set_task_state(tsk, TASK_UNINTERRUPTIBLE); - if ((locked = writer_trylock(sem))) + locked = writer_trylock(sem); + if (locked) break; } From c27ffc1080179c3f3b85e1e194fa61f1c9923b62 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2015 18:21:25 +0200 Subject: [PATCH 054/148] serial: sh-sci: Move private definitions to private header file Move private register definitions and enums from the public header file to the driver private "sh-sci.h" header file. The common Serial Control Register definitions are left in the public header file, as they're needed to fill in plat_sci_port.scscr on legacy systems not using DT. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.h | 83 +++++++++++++++++++++++++++++++++---- include/linux/serial_sci.h | 65 ----------------------------- 2 files changed, 76 insertions(+), 72 deletions(-) diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 350717404507..238ad0c5584b 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -2,6 +2,82 @@ #include #include +#define SCI_MAJOR 204 +#define SCI_MINOR_START 8 + + +/* + * SCI register subset common for all port types. + * Not all registers will exist on all parts. + */ +enum { + SCSMR, /* Serial Mode Register */ + SCBRR, /* Bit Rate Register */ + SCSCR, /* Serial Control Register */ + SCxSR, /* Serial Status Register */ + SCFCR, /* FIFO Control Register */ + SCFDR, /* FIFO Data Count Register */ + SCxTDR, /* Transmit (FIFO) Data Register */ + SCxRDR, /* Receive (FIFO) Data Register */ + SCLSR, /* Line Status Register */ + SCTFDR, /* Transmit FIFO Data Count Register */ + SCRFDR, /* Receive FIFO Data Count Register */ + SCSPTR, /* Serial Port Register */ + HSSRR, /* Sampling Rate Register */ + + SCIx_NR_REGS, +}; + + +/* SCSMR (Serial Mode Register) */ +#define SCSMR_CHR (1 << 6) /* 7-bit Character Length */ +#define SCSMR_PE (1 << 5) /* Parity Enable */ +#define SCSMR_ODD (1 << 4) /* Odd Parity */ +#define SCSMR_STOP (1 << 3) /* Stop Bit Length */ +#define SCSMR_CKS 0x0003 /* Clock Select */ + +/* Serial Control Register, SCIFA/SCIFB only bits */ +#define SCSCR_TDRQE (1 << 15) /* Tx Data Transfer Request Enable */ +#define SCSCR_RDRQE (1 << 14) /* Rx Data Transfer Request Enable */ + +/* SCxSR (Serial Status Register) on SCI */ +#define SCI_TDRE 0x80 /* Transmit Data Register Empty */ +#define SCI_RDRF 0x40 /* Receive Data Register Full */ +#define SCI_ORER 0x20 /* Overrun Error */ +#define SCI_FER 0x10 /* Framing Error */ +#define SCI_PER 0x08 /* Parity Error */ +#define SCI_TEND 0x04 /* Transmit End */ + +#define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER) + +/* SCxSR (Serial Status Register) on SCIF, HSCIF */ +#define SCIF_ER 0x0080 /* Receive Error */ +#define SCIF_TEND 0x0040 /* Transmission End */ +#define SCIF_TDFE 0x0020 /* Transmit FIFO Data Empty */ +#define SCIF_BRK 0x0010 /* Break Detect */ +#define SCIF_FER 0x0008 /* Framing Error */ +#define SCIF_PER 0x0004 /* Parity Error */ +#define SCIF_RDF 0x0002 /* Receive FIFO Data Full */ +#define SCIF_DR 0x0001 /* Receive Data Ready */ + +#define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) + +/* SCFCR (FIFO Control Register) */ +#define SCFCR_MCE 0x0008 +#define SCFCR_TFRST 0x0004 +#define SCFCR_RFRST 0x0002 +#define SCFCR_LOOP (1 << 0) /* Loopback Test */ + +/* SCSPTR (Serial Port Register), optional */ +#define SCSPTR_RTSIO (1 << 7) /* Serial Port RTS Pin Input/Output */ +#define SCSPTR_CTSIO (1 << 5) /* Serial Port CTS Pin Input/Output */ +#define SCSPTR_SPB2IO (1 << 1) /* Serial Port Break Input/Output */ +#define SCSPTR_SPB2DT (1 << 0) /* Serial Port Break Data */ + +/* HSSRR HSCIF */ +#define HSCIF_SRE 0x8000 /* Sampling Rate Register Enable */ + + #define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) #define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) #define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) @@ -28,10 +104,3 @@ # define SCxSR_BREAK_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x00e3) #endif -/* SCFCR */ -#define SCFCR_RFRST 0x0002 -#define SCFCR_TFRST 0x0004 -#define SCFCR_MCE 0x0008 - -#define SCI_MAJOR 204 -#define SCI_MINOR_START 8 diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 6c5e3bb282b0..395fceb8c060 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -10,13 +10,6 @@ #define SCIx_NOT_SUPPORTED (-1) -/* SCSMR (Serial Mode Register) */ -#define SCSMR_CHR (1 << 6) /* 7-bit Character Length */ -#define SCSMR_PE (1 << 5) /* Parity Enable */ -#define SCSMR_ODD (1 << 4) /* Odd Parity */ -#define SCSMR_STOP (1 << 3) /* Stop Bit Length */ -#define SCSMR_CKS 0x0003 /* Clock Select */ - /* Serial Control Register (@ = not supported by all parts) */ #define SCSCR_TIE (1 << 7) /* Transmit Interrupt Enable */ #define SCSCR_RIE (1 << 6) /* Receive Interrupt Enable */ @@ -26,43 +19,7 @@ #define SCSCR_TOIE (1 << 2) /* Timeout Interrupt Enable @ */ #define SCSCR_CKE1 (1 << 1) /* Clock Enable 1 */ #define SCSCR_CKE0 (1 << 0) /* Clock Enable 0 */ -/* SCIFA/SCIFB only */ -#define SCSCR_TDRQE (1 << 15) /* Tx Data Transfer Request Enable */ -#define SCSCR_RDRQE (1 << 14) /* Rx Data Transfer Request Enable */ -/* SCxSR (Serial Status Register) on SCI */ -#define SCI_TDRE 0x80 /* Transmit Data Register Empty */ -#define SCI_RDRF 0x40 /* Receive Data Register Full */ -#define SCI_ORER 0x20 /* Overrun Error */ -#define SCI_FER 0x10 /* Framing Error */ -#define SCI_PER 0x08 /* Parity Error */ -#define SCI_TEND 0x04 /* Transmit End */ - -#define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER) - -/* SCxSR (Serial Status Register) on SCIF, HSCIF */ -#define SCIF_ER 0x0080 /* Receive Error */ -#define SCIF_TEND 0x0040 /* Transmission End */ -#define SCIF_TDFE 0x0020 /* Transmit FIFO Data Empty */ -#define SCIF_BRK 0x0010 /* Break Detect */ -#define SCIF_FER 0x0008 /* Framing Error */ -#define SCIF_PER 0x0004 /* Parity Error */ -#define SCIF_RDF 0x0002 /* Receive FIFO Data Full */ -#define SCIF_DR 0x0001 /* Receive Data Ready */ - -#define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) - -/* SCFCR (FIFO Control Register) */ -#define SCFCR_LOOP (1 << 0) /* Loopback Test */ - -/* SCSPTR (Serial Port Register), optional */ -#define SCSPTR_RTSIO (1 << 7) /* Serial Port RTS Pin Input/Output */ -#define SCSPTR_CTSIO (1 << 5) /* Serial Port CTS Pin Input/Output */ -#define SCSPTR_SPB2IO (1 << 1) /* Serial Port Break Input/Output */ -#define SCSPTR_SPB2DT (1 << 0) /* Serial Port Break Data */ - -/* HSSRR HSCIF */ -#define HSCIF_SRE 0x8000 /* Sampling Rate Register Enable */ enum { SCIx_PROBE_REGTYPE, @@ -82,28 +39,6 @@ enum { SCIx_NR_REGTYPES, }; -/* - * SCI register subset common for all port types. - * Not all registers will exist on all parts. - */ -enum { - SCSMR, /* Serial Mode Register */ - SCBRR, /* Bit Rate Register */ - SCSCR, /* Serial Control Register */ - SCxSR, /* Serial Status Register */ - SCFCR, /* FIFO Control Register */ - SCFDR, /* FIFO Data Count Register */ - SCxTDR, /* Transmit (FIFO) Data Register */ - SCxRDR, /* Receive (FIFO) Data Register */ - SCLSR, /* Line Status Register */ - SCTFDR, /* Transmit FIFO Data Count Register */ - SCRFDR, /* Receive FIFO Data Count Register */ - SCSPTR, /* Serial Port Register */ - HSSRR, /* Sampling Rate Register */ - - SCIx_NR_REGS, -}; - struct device; struct plat_sci_port_ops { From 76735e9d558bb81b8c4c5246572b0fca27bfff4d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2015 18:21:26 +0200 Subject: [PATCH 055/148] serial: sh-sci: Add (H)SCIF RTS/CTS pin data register bit definitions Add the missing register bit definitions to set the RTS pin and read the CTS pin on (H)SCIF. Extracted from patches by Magnus Damm . Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 238ad0c5584b..8b4447e66708 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -70,7 +70,9 @@ enum { /* SCSPTR (Serial Port Register), optional */ #define SCSPTR_RTSIO (1 << 7) /* Serial Port RTS Pin Input/Output */ +#define SCSPTR_RTSDT (1 << 6) /* Serial Port RTS Pin Data */ #define SCSPTR_CTSIO (1 << 5) /* Serial Port CTS Pin Input/Output */ +#define SCSPTR_CTSDT (1 << 4) /* Serial Port CTS Pin Data */ #define SCSPTR_SPB2IO (1 << 1) /* Serial Port Break Input/Output */ #define SCSPTR_SPB2DT (1 << 0) /* Serial Port Break Data */ From c097abc33f70f0d59618b779cbca3df358c88a57 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2015 18:21:27 +0200 Subject: [PATCH 056/148] serial: sh-sci: Add SCIFA/B SCPCR register definitions Add the register definitions for the Serial Port Control and Data Registers on SCIFA/SCIFB, which are needed for RTS/CTS pin control. Extracted from patches by Magnus Damm . Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 22 ++++++++++++++++++++++ drivers/tty/serial/sh-sci.h | 10 ++++++++++ 2 files changed, 32 insertions(+) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index e7d6566fafaf..7ee0f686bdf1 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -168,6 +168,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, [HSSRR] = sci_reg_invalid, + [SCPCR] = sci_reg_invalid, + [SCPDR] = sci_reg_invalid, }, /* @@ -188,6 +190,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, [HSSRR] = sci_reg_invalid, + [SCPCR] = sci_reg_invalid, + [SCPDR] = sci_reg_invalid, }, /* @@ -207,6 +211,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, [HSSRR] = sci_reg_invalid, + [SCPCR] = { 0x30, 16 }, + [SCPDR] = { 0x34, 16 }, }, /* @@ -226,6 +232,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, [HSSRR] = sci_reg_invalid, + [SCPCR] = { 0x30, 16 }, + [SCPDR] = { 0x34, 16 }, }, /* @@ -246,6 +254,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = { 0x20, 16 }, [SCLSR] = { 0x24, 16 }, [HSSRR] = sci_reg_invalid, + [SCPCR] = sci_reg_invalid, + [SCPDR] = sci_reg_invalid, }, /* @@ -265,6 +275,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, [HSSRR] = sci_reg_invalid, + [SCPCR] = sci_reg_invalid, + [SCPDR] = sci_reg_invalid, }, /* @@ -284,6 +296,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = { 0x20, 16 }, [SCLSR] = { 0x24, 16 }, [HSSRR] = sci_reg_invalid, + [SCPCR] = sci_reg_invalid, + [SCPDR] = sci_reg_invalid, }, /* @@ -303,6 +317,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = { 0x20, 16 }, [SCLSR] = { 0x24, 16 }, [HSSRR] = { 0x40, 16 }, + [SCPCR] = sci_reg_invalid, + [SCPDR] = sci_reg_invalid, }, /* @@ -323,6 +339,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = sci_reg_invalid, [SCLSR] = { 0x24, 16 }, [HSSRR] = sci_reg_invalid, + [SCPCR] = sci_reg_invalid, + [SCPDR] = sci_reg_invalid, }, /* @@ -343,6 +361,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = { 0x24, 16 }, [SCLSR] = { 0x28, 16 }, [HSSRR] = sci_reg_invalid, + [SCPCR] = sci_reg_invalid, + [SCPDR] = sci_reg_invalid, }, /* @@ -363,6 +383,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, [HSSRR] = sci_reg_invalid, + [SCPCR] = sci_reg_invalid, + [SCPDR] = sci_reg_invalid, }, }; diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 8b4447e66708..2bae842fb0fc 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -24,6 +24,8 @@ enum { SCRFDR, /* Receive FIFO Data Count Register */ SCSPTR, /* Serial Port Register */ HSSRR, /* Sampling Rate Register */ + SCPCR, /* Serial Port Control Register */ + SCPDR, /* Serial Port Data Register */ SCIx_NR_REGS, }; @@ -79,6 +81,14 @@ enum { /* HSSRR HSCIF */ #define HSCIF_SRE 0x8000 /* Sampling Rate Register Enable */ +/* SCPCR (Serial Port Control Register), SCIFA/SCIFB only */ +#define SCPCR_RTSC (1 << 4) /* Serial Port RTS Pin / Output Pin */ +#define SCPCR_CTSC (1 << 3) /* Serial Port CTS Pin / Input Pin */ + +/* SCPDR (Serial Port Data Register), SCIFA/SCIFB only */ +#define SCPDR_RTSD (1 << 4) /* Serial Port RTS Output Pin Data */ +#define SCPDR_CTSD (1 << 3) /* Serial Port CTS Input Pin Data */ + #define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) #define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) From 31f90796c66876ea1894ea93e394b264e69dfdfc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2015 18:21:28 +0200 Subject: [PATCH 057/148] serial: sh-sci: Document remaining FIFO Control Register bits Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 2bae842fb0fc..1586d68470b6 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -65,9 +65,9 @@ enum { #define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) /* SCFCR (FIFO Control Register) */ -#define SCFCR_MCE 0x0008 -#define SCFCR_TFRST 0x0004 -#define SCFCR_RFRST 0x0002 +#define SCFCR_MCE 0x0008 /* Modem Control Enable */ +#define SCFCR_TFRST 0x0004 /* Transmit FIFO Data Register Reset */ +#define SCFCR_RFRST 0x0002 /* Receive FIFO Data Register Reset */ #define SCFCR_LOOP (1 << 0) /* Loopback Test */ /* SCSPTR (Serial Port Register), optional */ From d94a0a3857987c76c37a8095977fe554799ab69d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2015 18:21:29 +0200 Subject: [PATCH 058/148] serial: sh-sci: Standardize on using the BIT() macro to define register bits Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.h | 73 +++++++++++++++++++------------------ include/linux/serial_sci.h | 19 +++++----- 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 1586d68470b6..5282738375ae 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -1,3 +1,4 @@ +#include #include #include #include @@ -32,62 +33,62 @@ enum { /* SCSMR (Serial Mode Register) */ -#define SCSMR_CHR (1 << 6) /* 7-bit Character Length */ -#define SCSMR_PE (1 << 5) /* Parity Enable */ -#define SCSMR_ODD (1 << 4) /* Odd Parity */ -#define SCSMR_STOP (1 << 3) /* Stop Bit Length */ -#define SCSMR_CKS 0x0003 /* Clock Select */ +#define SCSMR_CHR BIT(6) /* 7-bit Character Length */ +#define SCSMR_PE BIT(5) /* Parity Enable */ +#define SCSMR_ODD BIT(4) /* Odd Parity */ +#define SCSMR_STOP BIT(3) /* Stop Bit Length */ +#define SCSMR_CKS 0x0003 /* Clock Select */ /* Serial Control Register, SCIFA/SCIFB only bits */ -#define SCSCR_TDRQE (1 << 15) /* Tx Data Transfer Request Enable */ -#define SCSCR_RDRQE (1 << 14) /* Rx Data Transfer Request Enable */ +#define SCSCR_TDRQE BIT(15) /* Tx Data Transfer Request Enable */ +#define SCSCR_RDRQE BIT(14) /* Rx Data Transfer Request Enable */ /* SCxSR (Serial Status Register) on SCI */ -#define SCI_TDRE 0x80 /* Transmit Data Register Empty */ -#define SCI_RDRF 0x40 /* Receive Data Register Full */ -#define SCI_ORER 0x20 /* Overrun Error */ -#define SCI_FER 0x10 /* Framing Error */ -#define SCI_PER 0x08 /* Parity Error */ -#define SCI_TEND 0x04 /* Transmit End */ +#define SCI_TDRE BIT(7) /* Transmit Data Register Empty */ +#define SCI_RDRF BIT(6) /* Receive Data Register Full */ +#define SCI_ORER BIT(5) /* Overrun Error */ +#define SCI_FER BIT(4) /* Framing Error */ +#define SCI_PER BIT(3) /* Parity Error */ +#define SCI_TEND BIT(2) /* Transmit End */ #define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER) /* SCxSR (Serial Status Register) on SCIF, HSCIF */ -#define SCIF_ER 0x0080 /* Receive Error */ -#define SCIF_TEND 0x0040 /* Transmission End */ -#define SCIF_TDFE 0x0020 /* Transmit FIFO Data Empty */ -#define SCIF_BRK 0x0010 /* Break Detect */ -#define SCIF_FER 0x0008 /* Framing Error */ -#define SCIF_PER 0x0004 /* Parity Error */ -#define SCIF_RDF 0x0002 /* Receive FIFO Data Full */ -#define SCIF_DR 0x0001 /* Receive Data Ready */ +#define SCIF_ER BIT(7) /* Receive Error */ +#define SCIF_TEND BIT(6) /* Transmission End */ +#define SCIF_TDFE BIT(5) /* Transmit FIFO Data Empty */ +#define SCIF_BRK BIT(4) /* Break Detect */ +#define SCIF_FER BIT(3) /* Framing Error */ +#define SCIF_PER BIT(2) /* Parity Error */ +#define SCIF_RDF BIT(1) /* Receive FIFO Data Full */ +#define SCIF_DR BIT(0) /* Receive Data Ready */ #define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) /* SCFCR (FIFO Control Register) */ -#define SCFCR_MCE 0x0008 /* Modem Control Enable */ -#define SCFCR_TFRST 0x0004 /* Transmit FIFO Data Register Reset */ -#define SCFCR_RFRST 0x0002 /* Receive FIFO Data Register Reset */ -#define SCFCR_LOOP (1 << 0) /* Loopback Test */ +#define SCFCR_MCE BIT(3) /* Modem Control Enable */ +#define SCFCR_TFRST BIT(2) /* Transmit FIFO Data Register Reset */ +#define SCFCR_RFRST BIT(1) /* Receive FIFO Data Register Reset */ +#define SCFCR_LOOP BIT(0) /* Loopback Test */ /* SCSPTR (Serial Port Register), optional */ -#define SCSPTR_RTSIO (1 << 7) /* Serial Port RTS Pin Input/Output */ -#define SCSPTR_RTSDT (1 << 6) /* Serial Port RTS Pin Data */ -#define SCSPTR_CTSIO (1 << 5) /* Serial Port CTS Pin Input/Output */ -#define SCSPTR_CTSDT (1 << 4) /* Serial Port CTS Pin Data */ -#define SCSPTR_SPB2IO (1 << 1) /* Serial Port Break Input/Output */ -#define SCSPTR_SPB2DT (1 << 0) /* Serial Port Break Data */ +#define SCSPTR_RTSIO BIT(7) /* Serial Port RTS Pin Input/Output */ +#define SCSPTR_RTSDT BIT(6) /* Serial Port RTS Pin Data */ +#define SCSPTR_CTSIO BIT(5) /* Serial Port CTS Pin Input/Output */ +#define SCSPTR_CTSDT BIT(4) /* Serial Port CTS Pin Data */ +#define SCSPTR_SPB2IO BIT(1) /* Serial Port Break Input/Output */ +#define SCSPTR_SPB2DT BIT(0) /* Serial Port Break Data */ /* HSSRR HSCIF */ -#define HSCIF_SRE 0x8000 /* Sampling Rate Register Enable */ +#define HSCIF_SRE BIT(15) /* Sampling Rate Register Enable */ /* SCPCR (Serial Port Control Register), SCIFA/SCIFB only */ -#define SCPCR_RTSC (1 << 4) /* Serial Port RTS Pin / Output Pin */ -#define SCPCR_CTSC (1 << 3) /* Serial Port CTS Pin / Input Pin */ +#define SCPCR_RTSC BIT(4) /* Serial Port RTS Pin / Output Pin */ +#define SCPCR_CTSC BIT(3) /* Serial Port CTS Pin / Input Pin */ /* SCPDR (Serial Port Data Register), SCIFA/SCIFB only */ -#define SCPDR_RTSD (1 << 4) /* Serial Port RTS Output Pin Data */ -#define SCPDR_CTSD (1 << 3) /* Serial Port CTS Input Pin Data */ +#define SCPDR_RTSD BIT(4) /* Serial Port RTS Output Pin Data */ +#define SCPDR_CTSD BIT(3) /* Serial Port CTS Input Pin Data */ #define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 395fceb8c060..7c536ac5be05 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -1,6 +1,7 @@ #ifndef __LINUX_SERIAL_SCI_H #define __LINUX_SERIAL_SCI_H +#include #include #include @@ -11,14 +12,14 @@ #define SCIx_NOT_SUPPORTED (-1) /* Serial Control Register (@ = not supported by all parts) */ -#define SCSCR_TIE (1 << 7) /* Transmit Interrupt Enable */ -#define SCSCR_RIE (1 << 6) /* Receive Interrupt Enable */ -#define SCSCR_TE (1 << 5) /* Transmit Enable */ -#define SCSCR_RE (1 << 4) /* Receive Enable */ -#define SCSCR_REIE (1 << 3) /* Receive Error Interrupt Enable @ */ -#define SCSCR_TOIE (1 << 2) /* Timeout Interrupt Enable @ */ -#define SCSCR_CKE1 (1 << 1) /* Clock Enable 1 */ -#define SCSCR_CKE0 (1 << 0) /* Clock Enable 0 */ +#define SCSCR_TIE BIT(7) /* Transmit Interrupt Enable */ +#define SCSCR_RIE BIT(6) /* Receive Interrupt Enable */ +#define SCSCR_TE BIT(5) /* Transmit Enable */ +#define SCSCR_RE BIT(4) /* Receive Enable */ +#define SCSCR_REIE BIT(3) /* Receive Error Interrupt Enable @ */ +#define SCSCR_TOIE BIT(2) /* Timeout Interrupt Enable @ */ +#define SCSCR_CKE1 BIT(1) /* Clock Enable 1 */ +#define SCSCR_CKE0 BIT(0) /* Clock Enable 0 */ enum { @@ -48,7 +49,7 @@ struct plat_sci_port_ops { /* * Port-specific capabilities */ -#define SCIx_HAVE_RTSCTS (1 << 0) +#define SCIx_HAVE_RTSCTS BIT(0) /* * Platform device specific platform_data struct From 2922598cd913dc1a3fc0d2e77463075b80ea769f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2015 18:21:30 +0200 Subject: [PATCH 059/148] serial: sh-sci: Replace hardcoded values in SCxSR_*_CLEAR macros Add the missing overrun error bit in SCxSR on SCIFA/SCIFB and SCIF on SH7705/SH7720/SH7721. Document what the corresponding bit(s) on plain SCIF are used for. Sort the components of SCIF_DEFAULT_ERROR_MASK by reverse definition order. Replace the hardcoded values in the SCxSR_*_CLEAR macros by proper defines. Use bit masks (negations of sets of bits) to make it more obvious which bits are being cleared. Assembler output (on sh) was compared before and after this commit: - For the first branch of the big "#if defined(...) || ..." construct, the code has changed slightly, as 32-bit bitmasks can be loaded in a single instruction, unlike the old large 16-bit constants (the SCxSR register is 16 bit, so we don't care about the top 16 bits), - For the second branch, the generated code is identical. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.h | 44 ++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 5282738375ae..3939513b0454 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -50,10 +50,16 @@ enum { #define SCI_FER BIT(4) /* Framing Error */ #define SCI_PER BIT(3) /* Parity Error */ #define SCI_TEND BIT(2) /* Transmit End */ +#define SCI_RESERVED 0x03 /* All reserved bits */ #define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER) -/* SCxSR (Serial Status Register) on SCIF, HSCIF */ +#define SCI_RDxF_CLEAR ~(SCI_RESERVED | SCI_RDRF) +#define SCI_ERROR_CLEAR ~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER) +#define SCI_TDxE_CLEAR ~(SCI_RESERVED | SCI_TEND | SCI_TDRE) +#define SCI_BREAK_CLEAR ~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER) + +/* SCxSR (Serial Status Register) on SCIF, SCIFA, SCIFB, HSCIF */ #define SCIF_ER BIT(7) /* Receive Error */ #define SCIF_TEND BIT(6) /* Transmission End */ #define SCIF_TDFE BIT(5) /* Transmit FIFO Data Empty */ @@ -62,8 +68,18 @@ enum { #define SCIF_PER BIT(2) /* Parity Error */ #define SCIF_RDF BIT(1) /* Receive FIFO Data Full */ #define SCIF_DR BIT(0) /* Receive Data Ready */ +/* SCIF only (optional) */ +#define SCIF_PERC 0xf000 /* Number of Parity Errors */ +#define SCIF_FERC 0x0f00 /* Number of Framing Errors */ +/*SCIFA/SCIFB and SCIF on SH7705/SH7720/SH7721 only */ +#define SCIFA_ORER BIT(9) /* Overrun Error */ -#define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) +#define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_BRK | SCIF_ER) + +#define SCIF_RDxF_CLEAR ~(SCIF_DR | SCIF_RDF) +#define SCIF_ERROR_CLEAR ~(SCIFA_ORER | SCIF_PER | SCIF_FER | SCIF_ER) +#define SCIF_TDxE_CLEAR ~(SCIF_TDFE) +#define SCIF_BREAK_CLEAR ~(SCIF_PER | SCIF_FER | SCIF_BRK) /* SCFCR (FIFO Control Register) */ #define SCFCR_MCE BIT(3) /* Modem Control Enable */ @@ -106,14 +122,22 @@ enum { defined(CONFIG_ARCH_SH73A0) || \ defined(CONFIG_ARCH_R8A7740) -# define SCxSR_RDxF_CLEAR(port) (serial_port_in(port, SCxSR) & 0xfffc) -# define SCxSR_ERROR_CLEAR(port) (serial_port_in(port, SCxSR) & 0xfd73) -# define SCxSR_TDxE_CLEAR(port) (serial_port_in(port, SCxSR) & 0xffdf) -# define SCxSR_BREAK_CLEAR(port) (serial_port_in(port, SCxSR) & 0xffe3) +# define SCxSR_RDxF_CLEAR(port) \ + (serial_port_in(port, SCxSR) & SCIF_RDxF_CLEAR) +# define SCxSR_ERROR_CLEAR(port) \ + (serial_port_in(port, SCxSR) & SCIF_ERROR_CLEAR) +# define SCxSR_TDxE_CLEAR(port) \ + (serial_port_in(port, SCxSR) & SCIF_TDxE_CLEAR) +# define SCxSR_BREAK_CLEAR(port) \ + (serial_port_in(port, SCxSR) & SCIF_BREAK_CLEAR) #else -# define SCxSR_RDxF_CLEAR(port) (((port)->type == PORT_SCI) ? 0xbc : 0x00fc) -# define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073) -# define SCxSR_TDxE_CLEAR(port) (((port)->type == PORT_SCI) ? 0x78 : 0x00df) -# define SCxSR_BREAK_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x00e3) +# define SCxSR_RDxF_CLEAR(port) \ + ((((port)->type == PORT_SCI) ? SCI_RDxF_CLEAR : SCIF_RDxF_CLEAR) & 0xff) +# define SCxSR_ERROR_CLEAR(port) \ + ((((port)->type == PORT_SCI) ? SCI_ERROR_CLEAR : SCIF_ERROR_CLEAR) & 0xff) +# define SCxSR_TDxE_CLEAR(port) \ + ((((port)->type == PORT_SCI) ? SCI_TDxE_CLEAR : SCIF_TDxE_CLEAR) & 0xff) +# define SCxSR_BREAK_CLEAR(port) \ + ((((port)->type == PORT_SCI) ? SCI_BREAK_CLEAR : SCIF_BREAK_CLEAR) & 0xff) #endif From 75c249fd7cb9097c58e44bc82f61b1f72ef79b3a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2015 18:21:31 +0200 Subject: [PATCH 060/148] serial: sh-sci: Replace hardcoded overrun bit values Add the missing overrun bit definition for (H)SCIF. Replace overrun_bit by overrun_mask, so we can use the existing defines instead of hardcoded values. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 28 +++++++++++++--------------- drivers/tty/serial/sh-sci.h | 3 +++ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 7ee0f686bdf1..83c46e158159 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -81,7 +81,7 @@ struct sci_port { /* Platform configuration */ struct plat_sci_port *cfg; - int overrun_bit; + unsigned int overrun_mask; unsigned int error_mask; unsigned int sampling_rate; @@ -803,7 +803,7 @@ static int sci_handle_errors(struct uart_port *port) struct sci_port *s = to_sci_port(port); /* Handle overruns */ - if (status & (1 << s->overrun_bit)) { + if (status & s->overrun_mask) { port->icount.overrun++; /* overrun error */ @@ -867,7 +867,7 @@ static int sci_handle_fifo_overrun(struct uart_port *port) struct sci_port *s = to_sci_port(port); struct plat_sci_reg *reg; int copied = 0, offset; - u16 status, bit; + u16 status; switch (port->type) { case PORT_SCIF: @@ -887,10 +887,8 @@ static int sci_handle_fifo_overrun(struct uart_port *port) return 0; status = serial_port_in(port, offset); - bit = 1 << s->overrun_bit; - - if (status & bit) { - status &= ~bit; + if (status & s->overrun_mask) { + status &= ~s->overrun_mask; serial_port_out(port, offset, status); port->icount.overrun++; @@ -1081,7 +1079,7 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) ret = sci_br_interrupt(irq, ptr); /* Overrun Interrupt */ - if (orer_status & (1 << s->overrun_bit)) + if (orer_status & s->overrun_mask) sci_handle_fifo_overrun(port); return ret; @@ -2256,32 +2254,32 @@ static int sci_init_single(struct platform_device *dev, switch (p->type) { case PORT_SCIFB: port->fifosize = 256; - sci_port->overrun_bit = 9; + sci_port->overrun_mask = SCIFA_ORER; sampling_rate = 16; break; case PORT_HSCIF: port->fifosize = 128; sampling_rate = 0; - sci_port->overrun_bit = 0; + sci_port->overrun_mask = SCLSR_ORER; break; case PORT_SCIFA: port->fifosize = 64; - sci_port->overrun_bit = 9; + sci_port->overrun_mask = SCIFA_ORER; sampling_rate = 16; break; case PORT_SCIF: port->fifosize = 16; if (p->regtype == SCIx_SH7705_SCIF_REGTYPE) { - sci_port->overrun_bit = 9; + sci_port->overrun_mask = SCIFA_ORER; sampling_rate = 16; } else { - sci_port->overrun_bit = 0; + sci_port->overrun_mask = SCLSR_ORER; sampling_rate = 32; } break; default: port->fifosize = 1; - sci_port->overrun_bit = 5; + sci_port->overrun_mask = SCI_ORER; sampling_rate = 32; break; } @@ -2335,7 +2333,7 @@ static int sci_init_single(struct platform_device *dev, * Make the error mask inclusive of overrun detection, if * supported. */ - sci_port->error_mask |= 1 << sci_port->overrun_bit; + sci_port->error_mask |= sci_port->overrun_mask; port->type = p->type; port->flags = UPF_FIXED_PORT | p->flags; diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 3939513b0454..3393f67b4e84 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -87,6 +87,9 @@ enum { #define SCFCR_RFRST BIT(1) /* Receive FIFO Data Register Reset */ #define SCFCR_LOOP BIT(0) /* Loopback Test */ +/* SCLSR (Line Status Register) on (H)SCIF */ +#define SCLSR_ORER BIT(0) /* Overrun Error */ + /* SCSPTR (Serial Port Register), optional */ #define SCSPTR_RTSIO BIT(7) /* Serial Port RTS Pin Input/Output */ #define SCSPTR_RTSDT BIT(6) /* Serial Port RTS Pin Data */ From 2e0842a154f63ae222ececd6b6b1db4c75426516 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2015 18:21:32 +0200 Subject: [PATCH 061/148] serial: sh-sci: Use the correct register for overrun checks The various SCI implementations use 3 different methods to signal overrun errors: - Bit SCI_ORER in register SCxSR on SCI, - Bit SCIFA_ORER in register SCxSR on SCIFA and SCIFB, and SCIF on SH7705/SH7720/SH7721, - Bit SCLSR_ORER in (optional!) register SCLSR on (H)SCIF. However: 1. sci_handle_fifo_overrun() a. handles (H)SCIF and SCIFA/SCIFB only, b. treats SCIF on SH7705/SH7720/SH7721 incorrectly, 2. sci_mpxed_interrupt() a. treats SCIF on SH7705/SH7720/SH7721 incorrectly, b. ignores that not all SCIFs have the SCLSR register, causing "Invalid register access" WARN()ings. To fix the above: 1. Determine and store the correct register enum during initialization, 2. Replace the duplicated buggy switch statements by using the stored register enum, 3. Add the missing existence check to sci_mpxed_interrupt(). Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 40 ++++++++++++++----------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 83c46e158159..b636c53a1e5b 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -81,6 +81,7 @@ struct sci_port { /* Platform configuration */ struct plat_sci_port *cfg; + unsigned int overrun_reg; unsigned int overrun_mask; unsigned int error_mask; unsigned int sampling_rate; @@ -866,30 +867,17 @@ static int sci_handle_fifo_overrun(struct uart_port *port) struct tty_port *tport = &port->state->port; struct sci_port *s = to_sci_port(port); struct plat_sci_reg *reg; - int copied = 0, offset; + int copied = 0; u16 status; - switch (port->type) { - case PORT_SCIF: - case PORT_HSCIF: - offset = SCLSR; - break; - case PORT_SCIFA: - case PORT_SCIFB: - offset = SCxSR; - break; - default: - return 0; - } - - reg = sci_getreg(port, offset); + reg = sci_getreg(port, s->overrun_reg); if (!reg->size) return 0; - status = serial_port_in(port, offset); + status = serial_port_in(port, s->overrun_reg); if (status & s->overrun_mask) { status &= ~s->overrun_mask; - serial_port_out(port, offset, status); + serial_port_out(port, s->overrun_reg, status); port->icount.overrun++; @@ -1041,15 +1029,11 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) ssr_status = serial_port_in(port, SCxSR); scr_status = serial_port_in(port, SCSCR); - switch (port->type) { - case PORT_SCIF: - case PORT_HSCIF: - orer_status = serial_port_in(port, SCLSR); - break; - case PORT_SCIFA: - case PORT_SCIFB: + if (s->overrun_reg == SCxSR) orer_status = ssr_status; - break; + else { + if (sci_getreg(port, s->overrun_reg)->size) + orer_status = serial_port_in(port, s->overrun_reg); } err_enabled = scr_status & port_rx_irq_mask(port); @@ -2254,31 +2238,37 @@ static int sci_init_single(struct platform_device *dev, switch (p->type) { case PORT_SCIFB: port->fifosize = 256; + sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCIFA_ORER; sampling_rate = 16; break; case PORT_HSCIF: port->fifosize = 128; sampling_rate = 0; + sci_port->overrun_reg = SCLSR; sci_port->overrun_mask = SCLSR_ORER; break; case PORT_SCIFA: port->fifosize = 64; + sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCIFA_ORER; sampling_rate = 16; break; case PORT_SCIF: port->fifosize = 16; if (p->regtype == SCIx_SH7705_SCIF_REGTYPE) { + sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCIFA_ORER; sampling_rate = 16; } else { + sci_port->overrun_reg = SCLSR; sci_port->overrun_mask = SCLSR_ORER; sampling_rate = 32; } break; default: port->fifosize = 1; + sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCI_ORER; sampling_rate = 32; break; From afd66db6138989b54c1f7d3c688551b70482133a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2015 18:21:33 +0200 Subject: [PATCH 062/148] serial: sh-sci: Don't set SCLSR bits in the SCxSR error mask error_mask is the union of all error indicating bits in the SCxSR register, while overrun_mask may apply to a different register (SCLSR), depending on the SCI variant. Hence overrun_mask should only be ORed into error_mask if it applies to the SCxSR register. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index b636c53a1e5b..c3e884dd925f 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2323,7 +2323,8 @@ static int sci_init_single(struct platform_device *dev, * Make the error mask inclusive of overrun detection, if * supported. */ - sci_port->error_mask |= sci_port->overrun_mask; + if (sci_port->overrun_reg == SCxSR) + sci_port->error_mask |= sci_port->overrun_mask; port->type = p->type; port->flags = UPF_FIXED_PORT | p->flags; From 99fb215eea10b9c7e8ab172e5932d296e63c3f73 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2015 18:21:34 +0200 Subject: [PATCH 063/148] serial: sh-sci: Remove obsolete comment about overrun detection The code it refers to was removed in commit b545e4f40613be70 ("serial: sh-sci: Compute overrun_bit without using baud rate algo"). Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index c3e884dd925f..b74a644e4b04 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2314,11 +2314,6 @@ static int sci_init_single(struct platform_device *dev, sci_port->error_mask = (p->type == PORT_SCI) ? SCI_DEFAULT_ERROR_MASK : SCIF_DEFAULT_ERROR_MASK; - /* - * Establish sensible defaults for the overrun detection, unless - * the part has explicitly disabled support for it. - */ - /* * Make the error mask inclusive of overrun detection, if * supported. From 31ada047681834d0d6f04adfb305ff26b52d2823 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:40:02 +0900 Subject: [PATCH 064/148] serial: imx: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 8aff0b4d8ddf..91e36bcfde74 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -239,7 +239,7 @@ static struct imx_uart_data imx_uart_devdata[] = { }, }; -static struct platform_device_id imx_uart_devtype[] = { +static const struct platform_device_id imx_uart_devtype[] = { { .name = "imx1-uart", .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART], From 0cd4521fb6fdaf54273235cb12b094618ac6c30f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:40:03 +0900 Subject: [PATCH 065/148] serial: mxs: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index f7e5825b55ab..13cf7738fbdc 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -169,7 +169,7 @@ struct mxs_auart_port { bool ms_irq_enabled; }; -static struct platform_device_id mxs_auart_devtype[] = { +static const struct platform_device_id mxs_auart_devtype[] = { { .name = "mxs-auart-imx23", .driver_data = IMX23_AUART }, { .name = "mxs-auart-imx28", .driver_data = IMX28_AUART }, { /* sentinel */ } From 75781979f8d33ea02f51c0e447abf4db67497080 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:40:04 +0900 Subject: [PATCH 066/148] serial: samsung: Staticize local symbol Staticize symbols not exported and not used outside of file. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index a0ae942d9562..19ce68e421a7 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -348,7 +348,7 @@ static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport) s3c24xx_serial_start_tx_dma(ourport, count); } -void s3c24xx_serial_start_tx(struct uart_port *port) +static void s3c24xx_serial_start_tx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); struct circ_buf *xmit = &port->state->xmit; From 24ee4df12216a0b1c9a0c22f83fbf366eb216d33 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:40:05 +0900 Subject: [PATCH 067/148] serial: samsung: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 19ce68e421a7..67d0c213b1c7 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -2337,7 +2337,7 @@ static struct s3c24xx_serial_drv_data exynos5433_serial_drv_data = { #define EXYNOS5433_SERIAL_DRV_DATA (kernel_ulong_t)NULL #endif -static struct platform_device_id s3c24xx_serial_driver_ids[] = { +static const struct platform_device_id s3c24xx_serial_driver_ids[] = { { .name = "s3c2410-uart", .driver_data = S3C2410_SERIAL_DRV_DATA, From 59cfc45f17d6d1dda2990e6f5a94df24a18330b8 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 5 May 2015 08:26:27 +0200 Subject: [PATCH 068/148] serial: 8250: Do nothing if nr_uarts=0 When nr_uarts was set to 0 (via config or 8250_core.nr_uarts), we crash early on x86 because serial8250_isa_init_ports dereferences base_ops which remains NULL. In fact, there is nothing to do for all the callers of serial8250_isa_init_ports if there are no uarts. Based on suggestions by Peter Hurley. Signed-off-by: Jan Kiszka Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 01cecd52b909..5521ff83303f 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3535,6 +3535,9 @@ static struct console univ8250_console = { static int __init univ8250_console_init(void) { + if (nr_uarts == 0) + return -ENODEV; + serial8250_isa_init_ports(); register_console(&univ8250_console); return 0; @@ -3565,7 +3568,7 @@ int __init early_serial_setup(struct uart_port *port) { struct uart_port *p; - if (port->line >= ARRAY_SIZE(serial8250_ports)) + if (port->line >= ARRAY_SIZE(serial8250_ports) || nr_uarts == 0) return -ENODEV; serial8250_isa_init_ports(); @@ -3932,6 +3935,9 @@ static int __init serial8250_init(void) { int ret; + if (nr_uarts == 0) + return -ENODEV; + serial8250_isa_init_ports(); printk(KERN_INFO "Serial: 8250/16550 driver, " From 54585ba0362a121f4e3b2fcddf62f28dbd1cf1f5 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 7 May 2015 18:55:40 +0900 Subject: [PATCH 069/148] serial: xuartps: add __init to earlycon write method Early console functions are only used during the early boot stage. This change just saves a small amount of memory footprint. Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 3ddbac767db3..009e0dbc12d2 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1075,7 +1075,8 @@ static void cdns_uart_console_putchar(struct uart_port *port, int ch) writel(ch, port->membase + CDNS_UART_FIFO_OFFSET); } -static void cdns_early_write(struct console *con, const char *s, unsigned n) +static void __init cdns_early_write(struct console *con, const char *s, + unsigned n) { struct earlycon_device *dev = con->data; From 1ed7b84b28c1a808d70e0b189febca5bc7e58fd7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 7 May 2015 19:04:46 +0900 Subject: [PATCH 070/148] serial: 8250: do not copy port.fifosize member twice The port.fifosize member has already been copied at 8 lines above. Maybe the compiler optimization can clean it away, but just in case. Signed-off-by: Masahiro Yamada Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 5521ff83303f..242e796480de 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3840,7 +3840,6 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->port.mapbase = up->port.mapbase; uart->port.mapsize = up->port.mapsize; uart->port.private_data = up->port.private_data; - uart->port.fifosize = up->port.fifosize; uart->tx_loadsz = up->tx_loadsz; uart->capabilities = up->capabilities; uart->port.throttle = up->port.throttle; From 7a7a7e6d9b8f78e73b45bdc436c5fbdbb7220c74 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 8 May 2015 13:03:13 +0900 Subject: [PATCH 071/148] serial: of_serial: do not set port.type twice The port.type has already been set by of_platform_serial_setup() called from a few lines above. Setting it to the same value is redundant. Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/of_serial.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 137381e649e5..28b9b4789bbf 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -188,7 +188,6 @@ static int of_platform_serial_probe(struct platform_device *ofdev) { struct uart_8250_port port8250; memset(&port8250, 0, sizeof(port8250)); - port.type = port_type; port8250.port = port; if (port.fifosize) From 245c0278ab2a2e3d0360296710b4c285291469b5 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 5 May 2015 15:17:52 +0100 Subject: [PATCH 072/148] serial: tegra: Correct delay after TX flush For all tegra devices (up to t210), there is a hardware issue that requires software to wait for 32 UART clock periods for the flush to propagate otherwise TX data could be post. Add a helper function to wait for N UART clock periods and update delay following FIFO flush to be 32 UART clock cycles. Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 1d5ea3964ee5..9e08d3f07509 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -234,6 +234,22 @@ static void tegra_uart_break_ctl(struct uart_port *u, int break_ctl) tup->lcr_shadow = lcr; } +/** + * tegra_uart_wait_cycle_time: Wait for N UART clock periods + * + * @tup: Tegra serial port data structure. + * @cycles: Number of clock periods to wait. + * + * Tegra UARTs are clocked at 16X the baud/bit rate and hence the UART + * clock speed is 16X the current baud rate. + */ +static void tegra_uart_wait_cycle_time(struct tegra_uart_port *tup, + unsigned int cycles) +{ + if (tup->current_baud) + udelay(DIV_ROUND_UP(cycles * 1000000, tup->current_baud * 16)); +} + /* Wait for a symbol-time. */ static void tegra_uart_wait_sym_time(struct tegra_uart_port *tup, unsigned int syms) @@ -263,8 +279,12 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits) /* Dummy read to ensure the write is posted */ tegra_uart_read(tup, UART_SCR); - /* Wait for the flush to propagate. */ - tegra_uart_wait_sym_time(tup, 1); + /* + * For all tegra devices (up to t210), there is a hardware issue that + * requires software to wait for 32 UART clock periods for the flush + * to propagate, otherwise data could be lost. + */ + tegra_uart_wait_cycle_time(tup, 32); } static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) From 11e71007a5652dce2528a5d2451fe2697c6a370a Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 5 May 2015 15:17:53 +0100 Subject: [PATCH 073/148] serial: tegra: Add delay after enabling FIFO mode For all tegra devices (up to t210), there is a hardware issue that requires software to wait for 3 UART clock periods after enabling the TX fifo, otherwise data could be lost. Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 9e08d3f07509..0d9d7ceb1dbb 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -885,6 +885,16 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) tup->fcr_shadow |= TEGRA_UART_TX_TRIG_16B; tegra_uart_write(tup, tup->fcr_shadow, UART_FCR); + /* Dummy read to ensure the write is posted */ + tegra_uart_read(tup, UART_SCR); + + /* + * For all tegra devices (up to t210), there is a hardware issue that + * requires software to wait for 3 UART clock periods after enabling + * the TX fifo, otherwise data could be lost. + */ + tegra_uart_wait_cycle_time(tup, 3); + /* * Initialize the UART with default configuration * (115200, N, 8, 1) so that the receive DMA buffer may be From db8e78474efd5006ece9ed15d804b78ec6e70702 Mon Sep 17 00:00:00 2001 From: Shardar Shariff Md Date: Tue, 5 May 2015 15:17:54 +0100 Subject: [PATCH 074/148] serial: tegra: check the count and read if any from dma It is only necessary to read data from the dma buffer when the count value is non-zero and hence, tegra_uart_copy_rx_to_tty() so only be called when this is the case. Although, this was being tested for in two places, there is a third place where this was not tested. However, instead of adding another if-statement prior to calling tegra_uart_copy_rx_to_tty(), move the test inside the function. Signed-off-by: Shardar Shariff Md [jonathanh@nvidia.com: Re-worked patch to move the check for the count value inside the function tegra_uart_copy_rx_to_tty(). Updated changelog with more commentary.] Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 0d9d7ceb1dbb..a53899c47e60 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -554,6 +554,10 @@ static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup, { int copied; + /* If count is zero, then there is no data to be copied */ + if (!count) + return; + tup->uport.icount.rx += count; if (!tty) { dev_err(tup->uport.dev, "No tty port\n"); @@ -588,8 +592,7 @@ static void tegra_uart_rx_dma_complete(void *args) set_rts(tup, false); /* If we are here, DMA is stopped */ - if (count) - tegra_uart_copy_rx_to_tty(tup, port, count); + tegra_uart_copy_rx_to_tty(tup, port, count); tegra_uart_handle_rx_pio(tup, port); if (tty) { @@ -626,8 +629,7 @@ static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup, count = tup->rx_bytes_requested - state.residue; /* If we are here, DMA is stopped */ - if (count) - tegra_uart_copy_rx_to_tty(tup, port, count); + tegra_uart_copy_rx_to_tty(tup, port, count); tegra_uart_handle_rx_pio(tup, port); if (tty) { From 853a699739fede12a5465be685f0f366276cb507 Mon Sep 17 00:00:00 2001 From: Shardar Shariff Md Date: Tue, 5 May 2015 15:17:55 +0100 Subject: [PATCH 075/148] serial: tegra: handle race condition on uart rx side The tegra serial driver has two paths through which receive data is copied up to the tty layer. These are: 1. DMA completion callback 2. UART RX interrupt A UART RX interrupt occurs for either RX_TIMEOUT (data has been sitting in the Rx FIFO for more than 4 character times without being read because there is not enough data to reach the trigger level), End of Receive Data event (receiver detects that data stops coming in for more than 4 character times) or a receive error. In the RX interrupt path, the following happens ... - All RX DMA transfers are stopped - Any data in the DMA buffer and RX FIFO are copied up to the tty layer. - DMA is restarted/primed for the RX path In the DMA completion callback, the DMA buffer is copied up to the tty layer but there is no check to see if the RX interrupt could have occurred between the DMA interrupt firing the the DMA callback running. Hence, if a RX interrupt was to occur shortly after the DMA completion interrupt, it is possible that the RX interrupt path has already copied the DMA buffer before the DMA callback has been called. Therefore, when the DMA callback is called, if the DMA is already in-progress, then this indicates that the UART RX interrupt has already occurred and there is nothing to do in the DMA callback. This race condition can cause duplicated data to be received. Signed-off-by: Shardar Shariff Md [jonathanh@nvidia.com: Moved async_tx_ack() call to after check to see if DMA has completed because if the DMA is in progress we do not need to ACK yet. Changed the print from dev_info to dev_debug. Updated changelog to add more commentary on the race condition based upon feedback from author.] Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index a53899c47e60..17d8a08b047b 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -583,9 +583,19 @@ static void tegra_uart_rx_dma_complete(void *args) struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); struct tty_port *port = &u->state->port; unsigned long flags; + struct dma_tx_state state; + enum dma_status status; + + spin_lock_irqsave(&u->lock, flags); + + status = dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); + + if (status == DMA_IN_PROGRESS) { + dev_dbg(tup->uport.dev, "RX DMA is in progress\n"); + goto done; + } async_tx_ack(tup->rx_dma_desc); - spin_lock_irqsave(&u->lock, flags); /* Deactivate flow control to stop sender */ if (tup->rts_active) @@ -607,6 +617,7 @@ static void tegra_uart_rx_dma_complete(void *args) if (tup->rts_active) set_rts(tup, true); +done: spin_unlock_irqrestore(&u->lock, flags); } From 0b0c1bdf7a197e7b58d0f8b1121c23b7b81ef84d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 5 May 2015 15:17:56 +0100 Subject: [PATCH 076/148] serial: tegra: Use unsigned types for RX and TX byte counts The function tty_insert_flip_string() takes an argument "size" which is of type size_t. This is an unsigned type. Update the count, rx_bytes_requested and tx_bytes_requested in the tegra serial driver to be unsigned integers so that an unsigned type is passed to tty_insert_flip_string(). Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 17d8a08b047b..a5312e4b6393 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -131,8 +131,8 @@ struct tegra_uart_port { struct dma_async_tx_descriptor *rx_dma_desc; dma_cookie_t tx_cookie; dma_cookie_t rx_cookie; - int tx_bytes_requested; - int rx_bytes_requested; + unsigned int tx_bytes_requested; + unsigned int rx_bytes_requested; }; static void tegra_uart_start_next_tx(struct tegra_uart_port *tup); @@ -408,7 +408,7 @@ static void tegra_uart_tx_dma_complete(void *args) struct circ_buf *xmit = &tup->uport.state->xmit; struct dma_tx_state state; unsigned long flags; - int count; + unsigned int count; dmaengine_tx_status(tup->tx_dma_chan, tup->rx_cookie, &state); count = tup->tx_bytes_requested - state.residue; @@ -500,7 +500,7 @@ static void tegra_uart_stop_tx(struct uart_port *u) struct tegra_uart_port *tup = to_tegra_uport(u); struct circ_buf *xmit = &tup->uport.state->xmit; struct dma_tx_state state; - int count; + unsigned int count; if (tup->tx_in_progress != TEGRA_UART_TX_DMA) return; @@ -550,7 +550,8 @@ static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup, } static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup, - struct tty_port *tty, int count) + struct tty_port *tty, + unsigned int count) { int copied; @@ -579,7 +580,7 @@ static void tegra_uart_rx_dma_complete(void *args) { struct tegra_uart_port *tup = args; struct uart_port *u = &tup->uport; - int count = tup->rx_bytes_requested; + unsigned int count = tup->rx_bytes_requested; struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); struct tty_port *port = &u->state->port; unsigned long flags; @@ -628,7 +629,7 @@ static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup, struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); struct tty_port *port = &tup->uport.state->port; struct uart_port *u = &tup->uport; - int count; + unsigned int count; /* Deactivate flow control to stop sender */ if (tup->rts_active) From 49433c802cb3405e62d3394b9c663f6c9ad329e5 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 5 May 2015 15:17:57 +0100 Subject: [PATCH 077/148] serial: tegra: Fix cookie used by TX channel The DMA cookie for the RX channel is being used by the TX channel. Therefore, fix driver to use the correct DMA cookie for the TX channel. Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index a5312e4b6393..987c05665e62 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -410,7 +410,7 @@ static void tegra_uart_tx_dma_complete(void *args) unsigned long flags; unsigned int count; - dmaengine_tx_status(tup->tx_dma_chan, tup->rx_cookie, &state); + dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state); count = tup->tx_bytes_requested - state.residue; async_tx_ack(tup->tx_dma_desc); spin_lock_irqsave(&tup->uport.lock, flags); From d92aca3a76662d2da8fe843ab124c45b47ea127d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 5 May 2015 15:17:58 +0100 Subject: [PATCH 078/148] serial: tegra: Correct shutdown of UARTs There are two issues in the shutdown path of the UARTs which are: 1. The function tegra_uart_shutdown() calls tegra_uart_flush_buffer() to stop DMA TX transfers. However, tegra_uart_flush_buffer() is called after the DMA channels have already been freed and so actually does nothing. 2. The function that frees the DMA channels (tegra_uart_dma_channel_free()), unmaps the dma buffer before freeing the DMA channel and does not ensure the DMA has been stopped. Resolve this by fixing the code in tegra_uart_dma_channel_free() to ensure the DMA is stopped, free the DMA channel and then unmap the DMA buffer. Finally, remove the unnecessary call to tegra_uart_flush_buffer(). Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 987c05665e62..96378da9aefc 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -1020,24 +1020,23 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup, bool dma_to_memory) { - struct dma_chan *dma_chan; - if (dma_to_memory) { + dmaengine_terminate_all(tup->rx_dma_chan); + dma_release_channel(tup->rx_dma_chan); dma_free_coherent(tup->uport.dev, TEGRA_UART_RX_DMA_BUFFER_SIZE, tup->rx_dma_buf_virt, tup->rx_dma_buf_phys); - dma_chan = tup->rx_dma_chan; tup->rx_dma_chan = NULL; tup->rx_dma_buf_phys = 0; tup->rx_dma_buf_virt = NULL; } else { + dmaengine_terminate_all(tup->tx_dma_chan); + dma_release_channel(tup->tx_dma_chan); dma_unmap_single(tup->uport.dev, tup->tx_dma_buf_phys, UART_XMIT_SIZE, DMA_TO_DEVICE); - dma_chan = tup->tx_dma_chan; tup->tx_dma_chan = NULL; tup->tx_dma_buf_phys = 0; tup->tx_dma_buf_virt = NULL; } - dma_release_channel(dma_chan); } static int tegra_uart_startup(struct uart_port *u) @@ -1104,8 +1103,6 @@ static void tegra_uart_shutdown(struct uart_port *u) tegra_uart_dma_channel_free(tup, true); tegra_uart_dma_channel_free(tup, false); free_irq(u->irq, tup); - - tegra_uart_flush_buffer(u); } static void tegra_uart_enable_ms(struct uart_port *u) From ad909b3f8b162a61ce9e32726dadb380e51f8949 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 5 May 2015 15:17:59 +0100 Subject: [PATCH 079/148] serial: tegra: Correct error handling on DMA setup Function tegra_uart_dma_channel_allocate() does not check that dma_map_single() mapped the DMA buffer correctly. Add a check for this and appropriate error handling. Furthermore, if dmaengine_slave_config() (called by tegra_uart_dma_channel_allocate()) fails, then memory allocated/mapped is not freed/unmapped. Therefore, call tegra_uart_dma_channel_free() instead of just dma_release_channel() if dmaengine_slave_config() fails. Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 51 +++++++++++++++++-------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 96378da9aefc..3b63f103f0c9 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -949,6 +949,28 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) return 0; } +static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup, + bool dma_to_memory) +{ + if (dma_to_memory) { + dmaengine_terminate_all(tup->rx_dma_chan); + dma_release_channel(tup->rx_dma_chan); + dma_free_coherent(tup->uport.dev, TEGRA_UART_RX_DMA_BUFFER_SIZE, + tup->rx_dma_buf_virt, tup->rx_dma_buf_phys); + tup->rx_dma_chan = NULL; + tup->rx_dma_buf_phys = 0; + tup->rx_dma_buf_virt = NULL; + } else { + dmaengine_terminate_all(tup->tx_dma_chan); + dma_release_channel(tup->tx_dma_chan); + dma_unmap_single(tup->uport.dev, tup->tx_dma_buf_phys, + UART_XMIT_SIZE, DMA_TO_DEVICE); + tup->tx_dma_chan = NULL; + tup->tx_dma_buf_phys = 0; + tup->tx_dma_buf_virt = NULL; + } +} + static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, bool dma_to_memory) { @@ -981,6 +1003,11 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, dma_phys = dma_map_single(tup->uport.dev, tup->uport.state->xmit.buf, UART_XMIT_SIZE, DMA_TO_DEVICE); + if (dma_mapping_error(tup->uport.dev, dma_phys)) { + dev_err(tup->uport.dev, "dma_map_single tx failed\n"); + dma_release_channel(dma_chan); + return -ENOMEM; + } dma_buf = tup->uport.state->xmit.buf; } @@ -1013,32 +1040,10 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, return 0; scrub: - dma_release_channel(dma_chan); + tegra_uart_dma_channel_free(tup, dma_to_memory); return ret; } -static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup, - bool dma_to_memory) -{ - if (dma_to_memory) { - dmaengine_terminate_all(tup->rx_dma_chan); - dma_release_channel(tup->rx_dma_chan); - dma_free_coherent(tup->uport.dev, TEGRA_UART_RX_DMA_BUFFER_SIZE, - tup->rx_dma_buf_virt, tup->rx_dma_buf_phys); - tup->rx_dma_chan = NULL; - tup->rx_dma_buf_phys = 0; - tup->rx_dma_buf_virt = NULL; - } else { - dmaengine_terminate_all(tup->tx_dma_chan); - dma_release_channel(tup->tx_dma_chan); - dma_unmap_single(tup->uport.dev, tup->tx_dma_buf_phys, - UART_XMIT_SIZE, DMA_TO_DEVICE); - tup->tx_dma_chan = NULL; - tup->tx_dma_buf_phys = 0; - tup->tx_dma_buf_virt = NULL; - } -} - static int tegra_uart_startup(struct uart_port *u) { struct tegra_uart_port *tup = to_tegra_uport(u); From bd63364caa8df38bad2b25b11b2a1b849475cce5 Mon Sep 17 00:00:00 2001 From: Scot Doyle Date: Thu, 26 Mar 2015 13:54:39 +0000 Subject: [PATCH 080/148] vt: add cursor blink interval escape sequence Add an escape sequence to specify the current console's cursor blink interval. The interval is specified as a number of milliseconds until the next cursor display state toggle, from 50 to 65535. /proc/loadavg did not show a difference with a one msec interval, but the lower bound is set to 50 msecs since slower hardware wasn't tested. Store the interval in the vc_data structure for later access by fbcon, initializing the value to fbcon's current hardcoded value of 200 msecs. Signed-off-by: Scot Doyle Acked-by: Pavel Machek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 9 +++++++++ include/linux/console_struct.h | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 4a24eb2b0ede..b075489f314e 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -135,6 +135,7 @@ const struct consw *conswitchp; */ #define DEFAULT_BELL_PITCH 750 #define DEFAULT_BELL_DURATION (HZ/8) +#define DEFAULT_CURSOR_BLINK_MS 200 struct vc vc_cons [MAX_NR_CONSOLES]; @@ -1590,6 +1591,13 @@ static void setterm_command(struct vc_data *vc) case 15: /* activate the previous console */ set_console(last_console); break; + case 16: /* set cursor blink duration in msec */ + if (vc->vc_npar >= 1 && vc->vc_par[1] >= 50 && + vc->vc_par[1] <= USHRT_MAX) + vc->vc_cur_blink_ms = vc->vc_par[1]; + else + vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS; + break; } } @@ -1717,6 +1725,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear) vc->vc_bell_pitch = DEFAULT_BELL_PITCH; vc->vc_bell_duration = DEFAULT_BELL_DURATION; + vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS; gotoxy(vc, 0, 0); save_cur(vc); diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h index e859c98d1767..e329ee2667e1 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -104,6 +104,7 @@ struct vc_data { unsigned int vc_resize_user; /* resize request from user */ unsigned int vc_bell_pitch; /* Console bell pitch */ unsigned int vc_bell_duration; /* Console bell duration */ + unsigned short vc_cur_blink_ms; /* Cursor blink duration */ struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */ struct uni_pagedir *vc_uni_pagedir; struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */ From 27a4c827c34ac4256a190cc9d24607f953c1c459 Mon Sep 17 00:00:00 2001 From: Scot Doyle Date: Thu, 26 Mar 2015 13:56:38 +0000 Subject: [PATCH 081/148] fbcon: use the cursor blink interval provided by vt vt now provides a cursor blink interval via vc_data. Use this interval instead of the currently hardcoded 200 msecs. Store it in fbcon_ops to avoid locking the console in cursor_timer_handler(). Signed-off-by: Scot Doyle Acked-by: Pavel Machek Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/fbcon.c | 10 +++++----- drivers/video/console/fbcon.h | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index b97210671a81..05b1d1a71ef9 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -402,7 +402,7 @@ static void cursor_timer_handler(unsigned long dev_addr) struct fbcon_ops *ops = info->fbcon_par; queue_work(system_power_efficient_wq, &info->queue); - mod_timer(&ops->cursor_timer, jiffies + HZ/5); + mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies); } static void fbcon_add_cursor_timer(struct fb_info *info) @@ -417,7 +417,7 @@ static void fbcon_add_cursor_timer(struct fb_info *info) init_timer(&ops->cursor_timer); ops->cursor_timer.function = cursor_timer_handler; - ops->cursor_timer.expires = jiffies + HZ / 5; + ops->cursor_timer.expires = jiffies + ops->cur_blink_jiffies; ops->cursor_timer.data = (unsigned long ) info; add_timer(&ops->cursor_timer); ops->flags |= FBCON_FLAGS_CURSOR_TIMER; @@ -1309,9 +1309,9 @@ static void fbcon_cursor(struct vc_data *vc, int mode) if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1) return; - if (vc->vc_cursor_type & 0x10) - fbcon_del_cursor_timer(info); - else + ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); + fbcon_del_cursor_timer(info); + if (!(vc->vc_cursor_type & 0x10)) fbcon_add_cursor_timer(info); ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h index 6bd2e0c7f209..7aaa4eabbba0 100644 --- a/drivers/video/console/fbcon.h +++ b/drivers/video/console/fbcon.h @@ -70,6 +70,7 @@ struct fbcon_ops { struct fb_cursor cursor_state; struct display *p; int currcon; /* Current VC. */ + int cur_blink_jiffies; int cursor_flash; int cursor_reset; int blank_state; From 360ee94b47a80b0e986c9e890f1862d29edf2c45 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 10 Apr 2015 16:11:05 -0700 Subject: [PATCH 082/148] TTY: msm_smd_tty: Remove unused driver This code is no longer used now that mach-msm has been removed. Delete it. Cc: David Brown Cc: Bryan Huntsman Cc: Daniel Walker Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 8 -- drivers/tty/serial/Makefile | 1 - drivers/tty/serial/msm_smd_tty.c | 232 ------------------------------- 3 files changed, 241 deletions(-) delete mode 100644 drivers/tty/serial/msm_smd_tty.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index daee18338636..59a8ad6e7c3e 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1378,14 +1378,6 @@ config SERIAL_PCH_UART_CONSOLE (the system console is the device which receives all kernel messages and warnings and which allows logins in single user mode). -config SERIAL_MSM_SMD - bool "Enable tty device interface for some SMD ports" - default n - depends on MSM_SMD - help - Enables userspace clients to read and write to some streaming SMD - ports via tty device interface for MSM chipset. - config SERIAL_MXS_AUART depends on ARCH_MXS tristate "MXS AUART support" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index c3ac3d930b33..432cb6465227 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -79,7 +79,6 @@ obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o -obj-$(CONFIG_SERIAL_MSM_SMD) += msm_smd_tty.o obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o diff --git a/drivers/tty/serial/msm_smd_tty.c b/drivers/tty/serial/msm_smd_tty.c deleted file mode 100644 index 1238ac370bff..000000000000 --- a/drivers/tty/serial/msm_smd_tty.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2011, Code Aurora Forum. All rights reserved. - * Author: Brian Swetland - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#define MAX_SMD_TTYS 32 - -struct smd_tty_info { - struct tty_port port; - smd_channel_t *ch; -}; - -struct smd_tty_channel_desc { - int id; - const char *name; -}; - -static struct smd_tty_info smd_tty[MAX_SMD_TTYS]; - -static const struct smd_tty_channel_desc smd_default_tty_channels[] = { - { .id = 0, .name = "SMD_DS" }, - { .id = 27, .name = "SMD_GPSNMEA" }, -}; - -static const struct smd_tty_channel_desc *smd_tty_channels = - smd_default_tty_channels; -static int smd_tty_channels_len = ARRAY_SIZE(smd_default_tty_channels); - -static void smd_tty_notify(void *priv, unsigned event) -{ - unsigned char *ptr; - int avail; - struct smd_tty_info *info = priv; - struct tty_struct *tty; - - if (event != SMD_EVENT_DATA) - return; - - tty = tty_port_tty_get(&info->port); - if (!tty) - return; - - for (;;) { - if (test_bit(TTY_THROTTLED, &tty->flags)) - break; - avail = smd_read_avail(info->ch); - if (avail == 0) - break; - - avail = tty_prepare_flip_string(&info->port, &ptr, avail); - - if (smd_read(info->ch, ptr, avail) != avail) { - /* shouldn't be possible since we're in interrupt - ** context here and nobody else could 'steal' our - ** characters. - */ - pr_err("OOPS - smd_tty_buffer mismatch?!"); - } - - tty_flip_buffer_push(&info->port); - } - - /* XXX only when writable and necessary */ - tty_wakeup(tty); - tty_kref_put(tty); -} - -static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty) -{ - struct smd_tty_info *info = container_of(tport, struct smd_tty_info, - port); - int i, res = 0; - const char *name = NULL; - - for (i = 0; i < smd_tty_channels_len; i++) { - if (smd_tty_channels[i].id == tty->index) { - name = smd_tty_channels[i].name; - break; - } - } - if (!name) - return -ENODEV; - - if (info->ch) - smd_kick(info->ch); - else - res = smd_open(name, &info->ch, info, smd_tty_notify); - - if (!res) - tty->driver_data = info; - - return res; -} - -static void smd_tty_port_shutdown(struct tty_port *tport) -{ - struct smd_tty_info *info = container_of(tport, struct smd_tty_info, - port); - - if (info->ch) { - smd_close(info->ch); - info->ch = 0; - } -} - -static int smd_tty_open(struct tty_struct *tty, struct file *f) -{ - struct smd_tty_info *info = smd_tty + tty->index; - - return tty_port_open(&info->port, tty, f); -} - -static void smd_tty_close(struct tty_struct *tty, struct file *f) -{ - struct smd_tty_info *info = tty->driver_data; - - tty_port_close(&info->port, tty, f); -} - -static int smd_tty_write(struct tty_struct *tty, - const unsigned char *buf, int len) -{ - struct smd_tty_info *info = tty->driver_data; - int avail; - - /* if we're writing to a packet channel we will - ** never be able to write more data than there - ** is currently space for - */ - avail = smd_write_avail(info->ch); - if (len > avail) - len = avail; - - return smd_write(info->ch, buf, len); -} - -static int smd_tty_write_room(struct tty_struct *tty) -{ - struct smd_tty_info *info = tty->driver_data; - return smd_write_avail(info->ch); -} - -static int smd_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct smd_tty_info *info = tty->driver_data; - return smd_read_avail(info->ch); -} - -static void smd_tty_unthrottle(struct tty_struct *tty) -{ - struct smd_tty_info *info = tty->driver_data; - smd_kick(info->ch); -} - -static const struct tty_port_operations smd_tty_port_ops = { - .shutdown = smd_tty_port_shutdown, - .activate = smd_tty_port_activate, -}; - -static const struct tty_operations smd_tty_ops = { - .open = smd_tty_open, - .close = smd_tty_close, - .write = smd_tty_write, - .write_room = smd_tty_write_room, - .chars_in_buffer = smd_tty_chars_in_buffer, - .unthrottle = smd_tty_unthrottle, -}; - -static struct tty_driver *smd_tty_driver; - -static int __init smd_tty_init(void) -{ - int ret, i; - - smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS); - if (smd_tty_driver == 0) - return -ENOMEM; - - smd_tty_driver->driver_name = "smd_tty_driver"; - smd_tty_driver->name = "smd"; - smd_tty_driver->major = 0; - smd_tty_driver->minor_start = 0; - smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - smd_tty_driver->subtype = SERIAL_TYPE_NORMAL; - smd_tty_driver->init_termios = tty_std_termios; - smd_tty_driver->init_termios.c_iflag = 0; - smd_tty_driver->init_termios.c_oflag = 0; - smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; - smd_tty_driver->init_termios.c_lflag = 0; - smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | - TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - tty_set_operations(smd_tty_driver, &smd_tty_ops); - - ret = tty_register_driver(smd_tty_driver); - if (ret) - return ret; - - for (i = 0; i < smd_tty_channels_len; i++) { - struct tty_port *port = &smd_tty[smd_tty_channels[i].id].port; - tty_port_init(port); - port->ops = &smd_tty_port_ops; - tty_port_register_device(port, smd_tty_driver, - smd_tty_channels[i].id, NULL); - } - - return 0; -} - -module_init(smd_tty_init); From 03ac6b3499c8a0b1fbb5c33ad6234a9e860f2614 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sun, 12 Apr 2015 18:35:18 +0200 Subject: [PATCH 083/148] drivers/tty/nozomi.c: rename CONFIG_MAGIC The CONFIG_ prefix is reserved for Kconfig options in Make and CPP syntax. CONFIG_MAGIC is a file local CPP identifier so change the prefix to apply to Kconfig's naming convention. Signed-off-by: Valentin Rothberg Signed-off-by: Greg Kroah-Hartman --- drivers/tty/nozomi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 74885af8c7bd..80f9de907563 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -140,8 +140,8 @@ static int debug; #define R_FCR 0x0000 /* Flow Control Register */ #define R_IER 0x0004 /* Interrupt Enable Register */ -#define CONFIG_MAGIC 0xEFEFFEFE -#define TOGGLE_VALID 0x0000 +#define NOZOMI_CONFIG_MAGIC 0xEFEFFEFE +#define TOGGLE_VALID 0x0000 /* Definition of interrupt tokens */ #define MDM_DL1 0x0001 @@ -660,9 +660,9 @@ static int nozomi_read_config_table(struct nozomi *dc) read_mem32((u32 *) &dc->config_table, dc->base_addr + 0, sizeof(struct config_table)); - if (dc->config_table.signature != CONFIG_MAGIC) { + if (dc->config_table.signature != NOZOMI_CONFIG_MAGIC) { dev_err(&dc->pdev->dev, "ConfigTable Bad! 0x%08X != 0x%08X\n", - dc->config_table.signature, CONFIG_MAGIC); + dc->config_table.signature, NOZOMI_CONFIG_MAGIC); return 0; } From 80463501fe3a6cfdfb0aca92b1f2abbda61092a1 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 14 Apr 2015 15:28:46 +1000 Subject: [PATCH 084/148] tty/hvc: remove celleb-only beat driver The beat hvc driver is only used by celleb. celleb has been dropped [1], so drop the drivers. [1] http://patchwork.ozlabs.org/patch/451730/ CC: Greg Kroah-Hartman CC: Jiri Slaby CC: Valentin Rothberg CC: mpe@ellerman.id.au CC: linuxppc-dev@lists.ozlabs.org Signed-off-by: Daniel Axtens Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/Kconfig | 7 -- drivers/tty/hvc/Makefile | 1 - drivers/tty/hvc/hvc_beat.c | 134 ------------------------------------- 3 files changed, 142 deletions(-) delete mode 100644 drivers/tty/hvc/hvc_beat.c diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 8902f9b4df71..2509d057b99c 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -42,13 +42,6 @@ config HVC_RTAS help IBM Console device driver which makes use of RTAS -config HVC_BEAT - bool "Toshiba's Beat Hypervisor Console support" - depends on PPC_CELLEB - select HVC_DRIVER - help - Toshiba's Cell Reference Set Beat Console device driver - config HVC_IUCV bool "z/VM IUCV Hypervisor console support (VM only)" depends on S390 diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile index 4ca3723b0a3a..6a2702be76d1 100644 --- a/drivers/tty/hvc/Makefile +++ b/drivers/tty/hvc/Makefile @@ -4,7 +4,6 @@ obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o obj-$(CONFIG_HVC_TILE) += hvc_tile.o obj-$(CONFIG_HVC_DCC) += hvc_dcc.o -obj-$(CONFIG_HVC_BEAT) += hvc_beat.o obj-$(CONFIG_HVC_DRIVER) += hvc_console.o obj-$(CONFIG_HVC_IRQ) += hvc_irq.o obj-$(CONFIG_HVC_XEN) += hvc_xen.o diff --git a/drivers/tty/hvc/hvc_beat.c b/drivers/tty/hvc/hvc_beat.c deleted file mode 100644 index 1560d235449e..000000000000 --- a/drivers/tty/hvc/hvc_beat.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Beat hypervisor console driver - * - * (C) Copyright 2006 TOSHIBA CORPORATION - * - * This code is based on drivers/char/hvc_rtas.c: - * (C) Copyright IBM Corporation 2001-2005 - * (C) Copyright Red Hat, Inc. 2005 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hvc_console.h" - -extern int64_t beat_get_term_char(uint64_t, uint64_t *, uint64_t *, uint64_t *); -extern int64_t beat_put_term_char(uint64_t, uint64_t, uint64_t, uint64_t); - -struct hvc_struct *hvc_beat_dev = NULL; - -/* bug: only one queue is available regardless of vtermno */ -static int hvc_beat_get_chars(uint32_t vtermno, char *buf, int cnt) -{ - static unsigned char q[sizeof(unsigned long) * 2] - __attribute__((aligned(sizeof(unsigned long)))); - static int qlen = 0; - u64 got; - -again: - if (qlen) { - if (qlen > cnt) { - memcpy(buf, q, cnt); - qlen -= cnt; - memmove(q + cnt, q, qlen); - return cnt; - } else { /* qlen <= cnt */ - int r; - - memcpy(buf, q, qlen); - r = qlen; - qlen = 0; - return r; - } - } - if (beat_get_term_char(vtermno, &got, - ((u64 *)q), ((u64 *)q) + 1) == 0) { - qlen = got; - goto again; - } - return 0; -} - -static int hvc_beat_put_chars(uint32_t vtermno, const char *buf, int cnt) -{ - unsigned long kb[2]; - int rest, nlen; - - for (rest = cnt; rest > 0; rest -= nlen) { - nlen = (rest > 16) ? 16 : rest; - memcpy(kb, buf, nlen); - beat_put_term_char(vtermno, nlen, kb[0], kb[1]); - buf += nlen; - } - return cnt; -} - -static const struct hv_ops hvc_beat_get_put_ops = { - .get_chars = hvc_beat_get_chars, - .put_chars = hvc_beat_put_chars, -}; - -static int hvc_beat_useit = 1; - -static int hvc_beat_config(char *p) -{ - hvc_beat_useit = simple_strtoul(p, NULL, 0); - return 0; -} - -static int __init hvc_beat_console_init(void) -{ - if (hvc_beat_useit && of_machine_is_compatible("Beat")) { - hvc_instantiate(0, 0, &hvc_beat_get_put_ops); - } - return 0; -} - -/* temp */ -static int __init hvc_beat_init(void) -{ - struct hvc_struct *hp; - - if (!firmware_has_feature(FW_FEATURE_BEAT)) - return -ENODEV; - - hp = hvc_alloc(0, 0, &hvc_beat_get_put_ops, 16); - if (IS_ERR(hp)) - return PTR_ERR(hp); - hvc_beat_dev = hp; - return 0; -} - -static void __exit hvc_beat_exit(void) -{ - if (hvc_beat_dev) - hvc_remove(hvc_beat_dev); -} - -module_init(hvc_beat_init); -module_exit(hvc_beat_exit); - -__setup("hvc_beat=", hvc_beat_config); - -console_initcall(hvc_beat_console_init); From d364b5c3e446bbf9892edbb000bf4c358f86bd8b Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 1 Apr 2015 21:06:16 +0300 Subject: [PATCH 085/148] vt: fix console lock vs. kernfs s_active lock order Currently there is a lock order problem between the console lock and the kernfs s_active lock of the console driver's bind sysfs entry. When writing to the sysfs entry the lock order is first s_active then console lock, when unregistering the console driver via do_unregister_con_driver() the order is the opposite. See the below bugzilla reference for one instance of a lockdep backtrace. Fix this by unregistering the console driver from a deferred work, where we can safely drop the console lock while unregistering the device and corresponding sysfs entries (which in turn acquire s_active). Note that we have to keep the console driver slot in the registered_con_driver array reserved for the driver that's being unregistered until it's fully removed. Otherwise a concurrent call to do_register_con_driver could try to reuse the same slot and fail when registering the corresponding device with a minor index that's still in use. Note that the referenced bug report contains two dmesg logs with two distinct lockdep reports: [1] is about a locking scenario involving s_active, console_lock and the fb_notifier list lock, while [2] is about a locking scenario involving only s_active and console_lock. In [1] locking fb_notifier triggers the lockdep warning only because of its dependence on console_lock, otherwise case [1] is the same s_active<->console_lock dependency problem fixed by this patch. Before this change we have the following locking scenarios involving the 3 locks: a) via do_unregister_framebuffer()->...->do_unregister_con_driver(): 1. console lock 2. fb_notifier lock 3. s_active lock b) for example via give_up_console()->do_unregister_con_driver(): 1. console lock 2. s_active lock c) via vt_bind()/vt_unbind(): 1. s_active lock 2. console lock Since c) is the console bind sysfs entry's write code path we can't change the locking order there. We can only fix this issue by removing s_active's dependence on the other two locks in a) and b). We can do this only in the vt code which owns the corresponding sysfs entry, so that after the change we have: a) 1. console lock 2. fb_notifier lock b) 1. console lock c) 1. s_active lock 2. console lock d) in the new con_driver_unregister_callback(): 1. s_active lock [1] https://bugs.freedesktop.org/attachment.cgi?id=87716 [2] https://bugs.freedesktop.org/attachment.cgi?id=107602 v2: - get console_lock earlier in con_driver_unregister_callback(), so we protect the following console driver field assignments there - add code coment explaining the reason for deferring the sysfs entry removal - add a third paragraph to the commit message explaining why there are two distinct lockdep reports and that this issue is independent of fb/fbcon. (Peter Hurley) v3: - clarify further the third paragraph v4: - rebased on v4 of patch 1/3 Signed-off-by: Imre Deak Reviewed-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 61 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index b075489f314e..08e303c4208c 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -108,6 +108,7 @@ #define CON_DRIVER_FLAG_MODULE 1 #define CON_DRIVER_FLAG_INIT 2 #define CON_DRIVER_FLAG_ATTR 4 +#define CON_DRIVER_FLAG_ZOMBIE 8 struct con_driver { const struct consw *con; @@ -154,6 +155,7 @@ static int set_vesa_blanking(char __user *p); static void set_cursor(struct vc_data *vc); static void hide_cursor(struct vc_data *vc); static void console_callback(struct work_struct *ignored); +static void con_driver_unregister_callback(struct work_struct *ignored); static void blank_screen_t(unsigned long dummy); static void set_palette(struct vc_data *vc); @@ -183,6 +185,7 @@ static int blankinterval = 10*60; core_param(consoleblank, blankinterval, int, 0444); static DECLARE_WORK(console_work, console_callback); +static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback); /* * fg_console is the current virtual console, @@ -3605,7 +3608,8 @@ static int do_register_con_driver(const struct consw *csw, int first, int last) for (i = 0; i < MAX_NR_CON_DRIVER; i++) { con_driver = ®istered_con_driver[i]; - if (con_driver->con == NULL) { + if (con_driver->con == NULL && + !(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE)) { con_driver->con = csw; con_driver->desc = desc; con_driver->node = i; @@ -3667,16 +3671,20 @@ int do_unregister_con_driver(const struct consw *csw) struct con_driver *con_driver = ®istered_con_driver[i]; if (con_driver->con == csw) { - vtconsole_deinit_device(con_driver); - device_destroy(vtconsole_class, - MKDEV(0, con_driver->node)); + /* + * Defer the removal of the sysfs entries since that + * will acquire the kernfs s_active lock and we can't + * acquire this lock while holding the console lock: + * the unbind sysfs entry imposes already the opposite + * order. Reset con already here to prevent any later + * lookup to succeed and mark this slot as zombie, so + * it won't get reused until we complete the removal + * in the deferred work. + */ con_driver->con = NULL; - con_driver->desc = NULL; - con_driver->dev = NULL; - con_driver->node = 0; - con_driver->flag = 0; - con_driver->first = 0; - con_driver->last = 0; + con_driver->flag = CON_DRIVER_FLAG_ZOMBIE; + schedule_work(&con_driver_unregister_work); + return 0; } } @@ -3685,6 +3693,39 @@ int do_unregister_con_driver(const struct consw *csw) } EXPORT_SYMBOL_GPL(do_unregister_con_driver); +static void con_driver_unregister_callback(struct work_struct *ignored) +{ + int i; + + console_lock(); + + for (i = 0; i < MAX_NR_CON_DRIVER; i++) { + struct con_driver *con_driver = ®istered_con_driver[i]; + + if (!(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE)) + continue; + + console_unlock(); + + vtconsole_deinit_device(con_driver); + device_destroy(vtconsole_class, MKDEV(0, con_driver->node)); + + console_lock(); + + if (WARN_ON_ONCE(con_driver->con)) + con_driver->con = NULL; + con_driver->desc = NULL; + con_driver->dev = NULL; + con_driver->node = 0; + WARN_ON_ONCE(con_driver->flag != CON_DRIVER_FLAG_ZOMBIE); + con_driver->flag = 0; + con_driver->first = 0; + con_driver->last = 0; + } + + console_unlock(); +} + /* * If we support more console drivers, this function is used * when a driver wants to take over some existing consoles From 77232f791e0605521a978cbe7f79a93df24df374 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 13 Apr 2015 11:16:21 +0200 Subject: [PATCH 086/148] vt: Don't check KD_GRAPHICS when binding/unbinding This was introduced in commit 6db4063c5b72b46e9793b0f141a7a3984ac6facf Author: Antonino A. Daplas Date: Mon Jun 26 00:27:12 2006 -0700 [PATCH] VT binding: Add sysfs control to the VT layer with the justification "In addition, if any of the consoles are in KD_GRAPHICS mode, binding and unbinding will not succeed. KD_GRAPHICS mode usually indicates that the underlying console hardware is used for other purposes other than displaying text (ie X). This feature should prevent binding/unbinding from interfering with a graphics application using the VT." I think we should lift this artificial restriction though: - KD_GRAPHICS doesn't get cleaned up automatically, which means it's easy to have terminals stuck in KD_GRAPHICS when hacking around on X. - X doesn't really care, especially with drm where kms already blocks fbdev (and hence fbcon) when there's an active compositor. - This is a root-only interface with a separate .config option and it's possible to hang your machine already anyway if you unload/reload drivers and don't know what you're doing. With this patch i915.ko module reloading works again reliably, something in the recent fedora upgrades broke things. Cc: Antonino A. Daplas Cc: Peter Hurley Cc: Imre Deak Signed-off-by: Daniel Vetter Acked-by: David Herrmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 08e303c4208c..8fe52989b380 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3204,22 +3204,6 @@ static int do_bind_con_driver(const struct consw *csw, int first, int last, #ifdef CONFIG_VT_HW_CONSOLE_BINDING -static int con_is_graphics(const struct consw *csw, int first, int last) -{ - int i, retval = 0; - - for (i = first; i <= last; i++) { - struct vc_data *vc = vc_cons[i].d; - - if (vc && vc->vc_mode == KD_GRAPHICS) { - retval = 1; - break; - } - } - - return retval; -} - /* unlocked version of unbind_con_driver() */ int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt) { @@ -3305,8 +3289,7 @@ static int vt_bind(struct con_driver *con) const struct consw *defcsw = NULL, *csw = NULL; int i, more = 1, first = -1, last = -1, deflt = 0; - if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || - con_is_graphics(con->con, con->first, con->last)) + if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE)) goto err; csw = con->con; @@ -3357,8 +3340,7 @@ static int vt_unbind(struct con_driver *con) int i, more = 1, first = -1, last = -1, deflt = 0; int ret; - if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || - con_is_graphics(con->con, con->first, con->last)) + if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE)) goto err; csw = con->con; From f6a2fbb903d51b1e328d6e358048426ca97932ad Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 17 Apr 2015 01:14:15 +0100 Subject: [PATCH 087/148] PM / runtime: add note about re-calling in during device probe() The sh_eth driver has come up with an issue where the runtime_pm code suspends it during the probe() method due to the network device registration re-calling into the driver. Add a note about this into the documentation. Signed-off-by: Ben Dooks Signed-off-by: Ben Hutchings Signed-off-by: Rafael J. Wysocki --- Documentation/power/runtime_pm.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 44fe1d28a163..e76dc0ad4d2b 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt @@ -556,6 +556,12 @@ helper functions described in Section 4. In that case, pm_runtime_resume() should be used. Of course, for this purpose the device's runtime PM has to be enabled earlier by calling pm_runtime_enable(). +Note, if the device may execute pm_runtime calls during the probe (such as +if it is registers with a subsystem that may call back in) then the +pm_runtime_get_sync() call paired with a pm_runtime_put() call will be +appropriate to ensure that the device is not put back to sleep during the +probe. This can happen with systems such as the network device layer. + It may be desirable to suspend the device once ->probe() has finished. Therefore the driver core uses the asyncronous pm_request_idle() to submit a request to execute the subsystem-level idle callback for the device at that From fa8cd0f61fe17a8b202a23b68b2d0393c872372b Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 19 May 2015 11:51:14 +0100 Subject: [PATCH 088/148] serial/amba-pl011: Fix mismerge between v4.1-rc4 and tty-next In commit 02730d3c053a9af1d402e1c8dc8bbbc5a1340406 (Merge 4.1-rc4 into tty-next), git mismerged some lines, reintroducing a reference to the removed field uart_amba_port.tx_irq_seen. This causes a build failure. This patch removes the mismerged lines, restoring the code to what was in tty-next (which was the intention). Signed-off-by: Dave Martin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 6fabc059efed..f5bd8426fd75 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1578,9 +1578,6 @@ static int pl011_startup(struct uart_port *port) writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); - /* Assume that TX IRQ doesn't work until we see one: */ - uap->tx_irq_seen = 0; - spin_lock_irq(&uap->port.lock); /* restore RTS and DTR */ From 56f487c78015936097474fd89b2ccb229d500d0f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 13 May 2015 16:36:32 -0700 Subject: [PATCH 089/148] PM / Runtime: Update last_busy in rpm_resume If we don't update last_busy in rpm_resume, devices can go back to sleep immediately after resume. This happens at least in cases where the device has been powered off and does not have any interrupt pending until there's something in the FIFO. Signed-off-by: Tony Lindgren Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 5070c4fe8542..4ffe4a2add76 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -741,6 +741,7 @@ static int rpm_resume(struct device *dev, int rpmflags) } else { no_callback: __update_runtime_status(dev, RPM_ACTIVE); + pm_runtime_mark_last_busy(dev); if (parent) atomic_inc(&parent->power.child_count); } From 4990d4fe327b9d9a7a3be7103a82699406fdde69 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 18 May 2015 15:40:29 -0700 Subject: [PATCH 090/148] PM / Wakeirq: Add automated device wake IRQ handling Turns out we can automate the handling for the device_may_wakeup() quite a bit by using the kernel wakeup source list as suggested by Rafael J. Wysocki . And as some hardware has separate dedicated wake-up interrupt in addition to the IO interrupt, we can automate the handling by adding a generic threaded interrupt handler that just calls the device PM runtime to wake up the device. This allows dropping code from device drivers as we currently are doing it in multiple ways, and often wrong. For most drivers, we should be able to drop the following boilerplate code from runtime_suspend and runtime_resume functions: ... device_init_wakeup(dev, true); ... if (device_may_wakeup(dev)) enable_irq_wake(irq); ... if (device_may_wakeup(dev)) disable_irq_wake(irq); ... device_init_wakeup(dev, false); ... We can replace it with just the following init and exit time code: ... device_init_wakeup(dev, true); dev_pm_set_wake_irq(dev, irq); ... dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); ... And for hardware with dedicated wake-up interrupts: ... device_init_wakeup(dev, true); dev_pm_set_dedicated_wake_irq(dev, irq); ... dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); ... Signed-off-by: Tony Lindgren Signed-off-by: Rafael J. Wysocki --- drivers/base/power/Makefile | 2 +- drivers/base/power/main.c | 3 + drivers/base/power/power.h | 48 ++++++ drivers/base/power/runtime.c | 5 + drivers/base/power/wakeirq.c | 273 +++++++++++++++++++++++++++++++++++ drivers/base/power/wakeup.c | 92 ++++++++++++ include/linux/pm.h | 2 + include/linux/pm_wakeirq.h | 52 +++++++ include/linux/pm_wakeup.h | 9 ++ 9 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 drivers/base/power/wakeirq.c create mode 100644 include/linux/pm_wakeirq.h diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 1cb8544598d5..f94a6ccfe787 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o +obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp.o diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 3d874eca7104..6f2515c571f1 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -587,6 +588,7 @@ void dpm_resume_noirq(pm_message_t state) async_synchronize_full(); dpm_show_time(starttime, state, "noirq"); resume_device_irqs(); + device_wakeup_disarm_wake_irqs(); cpuidle_resume(); trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false); } @@ -1104,6 +1106,7 @@ int dpm_suspend_noirq(pm_message_t state) trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true); cpuidle_pause(); + device_wakeup_arm_wake_irqs(); suspend_device_irqs(); mutex_lock(&dpm_list_mtx); pm_transition = state; diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index b6b8a273c5da..f1a5d95e7b20 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -20,6 +20,46 @@ static inline void pm_runtime_early_init(struct device *dev) extern void pm_runtime_init(struct device *dev); extern void pm_runtime_remove(struct device *dev); +struct wake_irq { + struct device *dev; + int irq; + bool dedicated_irq:1; +}; + +extern void dev_pm_arm_wake_irq(struct wake_irq *wirq); +extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq); + +#ifdef CONFIG_PM_SLEEP + +extern int device_wakeup_attach_irq(struct device *dev, + struct wake_irq *wakeirq); +extern void device_wakeup_detach_irq(struct device *dev); +extern void device_wakeup_arm_wake_irqs(void); +extern void device_wakeup_disarm_wake_irqs(void); + +#else + +static inline int +device_wakeup_attach_irq(struct device *dev, + struct wake_irq *wakeirq) +{ + return 0; +} + +static inline void device_wakeup_detach_irq(struct device *dev) +{ +} + +static inline void device_wakeup_arm_wake_irqs(void) +{ +} + +static inline void device_wakeup_disarm_wake_irqs(void) +{ +} + +#endif /* CONFIG_PM_SLEEP */ + /* * sysfs.c */ @@ -52,6 +92,14 @@ static inline void wakeup_sysfs_remove(struct device *dev) {} static inline int pm_qos_sysfs_add(struct device *dev) { return 0; } static inline void pm_qos_sysfs_remove(struct device *dev) {} +static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq) +{ +} + +static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq) +{ +} + #endif #ifdef CONFIG_PM_SLEEP diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 4ffe4a2add76..e1a10a03df8e 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "power.h" @@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) callback = RPM_GET_CALLBACK(dev, runtime_suspend); + dev_pm_enable_wake_irq(dev); retval = rpm_callback(callback, dev); if (retval) goto fail; @@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) return retval; fail: + dev_pm_disable_wake_irq(dev); __update_runtime_status(dev, RPM_ACTIVE); dev->power.deferred_resume = false; wake_up_all(&dev->power.wait_queue); @@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags) callback = RPM_GET_CALLBACK(dev, runtime_resume); + dev_pm_disable_wake_irq(dev); retval = rpm_callback(callback, dev); if (retval) { __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_cancel_pending(dev); + dev_pm_enable_wake_irq(dev); } else { no_callback: __update_runtime_status(dev, RPM_ACTIVE); diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c new file mode 100644 index 000000000000..7470004ca810 --- /dev/null +++ b/drivers/base/power/wakeirq.c @@ -0,0 +1,273 @@ +/* + * wakeirq.c - Device wakeirq helper functions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "power.h" + +/** + * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ + * @dev: Device entry + * @irq: Device wake-up capable interrupt + * @wirq: Wake irq specific data + * + * Internal function to attach either a device IO interrupt or a + * dedicated wake-up interrupt as a wake IRQ. + */ +static int dev_pm_attach_wake_irq(struct device *dev, int irq, + struct wake_irq *wirq) +{ + unsigned long flags; + int err; + + if (!dev || !wirq) + return -EINVAL; + + spin_lock_irqsave(&dev->power.lock, flags); + if (dev_WARN_ONCE(dev, dev->power.wakeirq, + "wake irq already initialized\n")) { + spin_unlock_irqrestore(&dev->power.lock, flags); + return -EEXIST; + } + + dev->power.wakeirq = wirq; + spin_unlock_irqrestore(&dev->power.lock, flags); + + err = device_wakeup_attach_irq(dev, wirq); + if (err) + return err; + + return 0; +} + +/** + * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ + * @dev: Device entry + * @irq: Device IO interrupt + * + * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets + * automatically configured for wake-up from suspend based + * on the device specific sysfs wakeup entry. Typically called + * during driver probe after calling device_init_wakeup(). + */ +int dev_pm_set_wake_irq(struct device *dev, int irq) +{ + struct wake_irq *wirq; + int err; + + wirq = kzalloc(sizeof(*wirq), GFP_KERNEL); + if (!wirq) + return -ENOMEM; + + wirq->dev = dev; + wirq->irq = irq; + + err = dev_pm_attach_wake_irq(dev, irq, wirq); + if (err) + kfree(wirq); + + return err; +} +EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq); + +/** + * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ + * @dev: Device entry + * + * Detach a device wake IRQ and free resources. + * + * Note that it's OK for drivers to call this without calling + * dev_pm_set_wake_irq() as all the driver instances may not have + * a wake IRQ configured. This avoid adding wake IRQ specific + * checks into the drivers. + */ +void dev_pm_clear_wake_irq(struct device *dev) +{ + struct wake_irq *wirq = dev->power.wakeirq; + unsigned long flags; + + if (!wirq) + return; + + spin_lock_irqsave(&dev->power.lock, flags); + dev->power.wakeirq = NULL; + spin_unlock_irqrestore(&dev->power.lock, flags); + + device_wakeup_detach_irq(dev); + if (wirq->dedicated_irq) + free_irq(wirq->irq, wirq); + kfree(wirq); +} +EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq); + +/** + * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts + * @irq: Device specific dedicated wake-up interrupt + * @_wirq: Wake IRQ data + * + * Some devices have a separate wake-up interrupt in addition to the + * device IO interrupt. The wake-up interrupt signals that a device + * should be woken up from it's idle state. This handler uses device + * specific pm_runtime functions to wake the device, and then it's + * up to the device to do whatever it needs to. Note that as the + * device may need to restore context and start up regulators, we + * use a threaded IRQ. + * + * Also note that we are not resending the lost device interrupts. + * We assume that the wake-up interrupt just needs to wake-up the + * device, and then device's pm_runtime_resume() can deal with the + * situation. + */ +static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq) +{ + struct wake_irq *wirq = _wirq; + int res; + + /* We don't want RPM_ASYNC or RPM_NOWAIT here */ + res = pm_runtime_resume(wirq->dev); + if (res < 0) + dev_warn(wirq->dev, + "wake IRQ with no resume: %i\n", res); + + return IRQ_HANDLED; +} + +/** + * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt + * @dev: Device entry + * @irq: Device wake-up interrupt + * + * Unless your hardware has separate wake-up interrupts in addition + * to the device IO interrupts, you don't need this. + * + * Sets up a threaded interrupt handler for a device that has + * a dedicated wake-up interrupt in addition to the device IO + * interrupt. + * + * The interrupt starts disabled, and needs to be managed for + * the device by the bus code or the device driver using + * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq() + * functions. + */ +int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) +{ + struct wake_irq *wirq; + int err; + + wirq = kzalloc(sizeof(*wirq), GFP_KERNEL); + if (!wirq) + return -ENOMEM; + + wirq->dev = dev; + wirq->irq = irq; + wirq->dedicated_irq = true; + irq_set_status_flags(irq, IRQ_NOAUTOEN); + + /* + * Consumer device may need to power up and restore state + * so we use a threaded irq. + */ + err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq, + IRQF_ONESHOT, dev_name(dev), wirq); + if (err) + goto err_free; + + err = dev_pm_attach_wake_irq(dev, irq, wirq); + if (err) + goto err_free_irq; + + return err; + +err_free_irq: + free_irq(irq, wirq); +err_free: + kfree(wirq); + + return err; +} +EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq); + +/** + * dev_pm_enable_wake_irq - Enable device wake-up interrupt + * @dev: Device + * + * Called from the bus code or the device driver for + * runtime_suspend() to enable the wake-up interrupt while + * the device is running. + * + * Note that for runtime_suspend()) the wake-up interrupts + * should be unconditionally enabled unlike for suspend() + * that is conditional. + */ +void dev_pm_enable_wake_irq(struct device *dev) +{ + struct wake_irq *wirq = dev->power.wakeirq; + + if (wirq && wirq->dedicated_irq) + enable_irq(wirq->irq); +} +EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); + +/** + * dev_pm_disable_wake_irq - Disable device wake-up interrupt + * @dev: Device + * + * Called from the bus code or the device driver for + * runtime_resume() to disable the wake-up interrupt while + * the device is running. + */ +void dev_pm_disable_wake_irq(struct device *dev) +{ + struct wake_irq *wirq = dev->power.wakeirq; + + if (wirq && wirq->dedicated_irq) + disable_irq_nosync(wirq->irq); +} +EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq); + +/** + * dev_pm_arm_wake_irq - Arm device wake-up + * @wirq: Device wake-up interrupt + * + * Sets up the wake-up event conditionally based on the + * device_may_wake(). + */ +void dev_pm_arm_wake_irq(struct wake_irq *wirq) +{ + if (!wirq) + return; + + if (device_may_wakeup(wirq->dev)) + enable_irq_wake(wirq->irq); +} + +/** + * dev_pm_disarm_wake_irq - Disarm device wake-up + * @wirq: Device wake-up interrupt + * + * Clears up the wake-up event conditionally based on the + * device_may_wake(). + */ +void dev_pm_disarm_wake_irq(struct wake_irq *wirq) +{ + if (!wirq) + return; + + if (device_may_wakeup(wirq->dev)) + disable_irq_wake(wirq->irq); +} diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 77262009f89d..7332ebc9cab0 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "power.h" @@ -238,6 +239,97 @@ int device_wakeup_enable(struct device *dev) } EXPORT_SYMBOL_GPL(device_wakeup_enable); +/** + * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source + * @dev: Device to handle + * @wakeirq: Device specific wakeirq entry + * + * Attach a device wakeirq to the wakeup source so the device + * wake IRQ can be configured automatically for suspend and + * resume. + */ +int device_wakeup_attach_irq(struct device *dev, + struct wake_irq *wakeirq) +{ + struct wakeup_source *ws; + int ret = 0; + + spin_lock_irq(&dev->power.lock); + ws = dev->power.wakeup; + if (!ws) { + dev_err(dev, "forgot to call call device_init_wakeup?\n"); + ret = -EINVAL; + goto unlock; + } + + if (ws->wakeirq) { + ret = -EEXIST; + goto unlock; + } + + ws->wakeirq = wakeirq; + +unlock: + spin_unlock_irq(&dev->power.lock); + + return ret; +} + +/** + * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source + * @dev: Device to handle + * + * Removes a device wakeirq from the wakeup source. + */ +void device_wakeup_detach_irq(struct device *dev) +{ + struct wakeup_source *ws; + + spin_lock_irq(&dev->power.lock); + ws = dev->power.wakeup; + if (!ws) + goto unlock; + + ws->wakeirq = NULL; + +unlock: + spin_unlock_irq(&dev->power.lock); +} + +/** + * device_wakeup_arm_wake_irqs(void) + * + * Itereates over the list of device wakeirqs to arm them. + */ +void device_wakeup_arm_wake_irqs(void) +{ + struct wakeup_source *ws; + + rcu_read_lock(); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) { + if (ws->wakeirq) + dev_pm_arm_wake_irq(ws->wakeirq); + } + rcu_read_unlock(); +} + +/** + * device_wakeup_disarm_wake_irqs(void) + * + * Itereates over the list of device wakeirqs to disarm them. + */ +void device_wakeup_disarm_wake_irqs(void) +{ + struct wakeup_source *ws; + + rcu_read_lock(); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) { + if (ws->wakeirq) + dev_pm_disarm_wake_irq(ws->wakeirq); + } + rcu_read_unlock(); +} + /** * device_wakeup_detach - Detach a device's wakeup source object from it. * @dev: Device to detach the wakeup source object from. diff --git a/include/linux/pm.h b/include/linux/pm.h index 2d29c64f8fb1..1c4ed0cb7907 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -529,6 +529,7 @@ enum rpm_request { }; struct wakeup_source; +struct wake_irq; struct pm_domain_data; struct pm_subsys_data { @@ -568,6 +569,7 @@ struct dev_pm_info { unsigned long timer_expires; struct work_struct work; wait_queue_head_t wait_queue; + struct wake_irq *wakeirq; atomic_t usage_count; atomic_t child_count; unsigned int disable_depth:3; diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h new file mode 100644 index 000000000000..4046fa1b7d25 --- /dev/null +++ b/include/linux/pm_wakeirq.h @@ -0,0 +1,52 @@ +/* + * pm_wakeirq.h - Device wakeirq helper functions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_PM_WAKEIRQ_H +#define _LINUX_PM_WAKEIRQ_H + +#ifdef CONFIG_PM + +extern int dev_pm_set_wake_irq(struct device *dev, int irq); +extern int dev_pm_set_dedicated_wake_irq(struct device *dev, + int irq); +extern void dev_pm_clear_wake_irq(struct device *dev); +extern void dev_pm_enable_wake_irq(struct device *dev); +extern void dev_pm_disable_wake_irq(struct device *dev); + +#else /* !CONFIG_PM */ + +static inline int dev_pm_set_wake_irq(struct device *dev, int irq) +{ + return 0; +} + +static inline int dev_pm_set_dedicated__wake_irq(struct device *dev, + int irq) +{ + return 0; +} + +static inline void dev_pm_clear_wake_irq(struct device *dev) +{ +} + +static inline void dev_pm_enable_wake_irq(struct device *dev) +{ +} + +static inline void dev_pm_disable_wake_irq(struct device *dev) +{ +} + +#endif /* CONFIG_PM */ +#endif /* _LINUX_PM_WAKEIRQ_H */ diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index a0f70808d7f4..a3447932df1f 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -28,9 +28,17 @@ #include +struct wake_irq; + /** * struct wakeup_source - Representation of wakeup sources * + * @name: Name of the wakeup source + * @entry: Wakeup source list entry + * @lock: Wakeup source lock + * @wakeirq: Optional device specific wakeirq + * @timer: Wakeup timer list + * @timer_expires: Wakeup timer expiration * @total_time: Total time this wakeup source has been active. * @max_time: Maximum time this wakeup source has been continuously active. * @last_time: Monotonic clock when the wakeup source's was touched last time. @@ -47,6 +55,7 @@ struct wakeup_source { const char *name; struct list_head entry; spinlock_t lock; + struct wake_irq *wakeirq; struct timer_list timer; unsigned long timer_expires; ktime_t total_time; From 5b83b2234be6733cfe22036c38031b2c890b3db8 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 21 May 2015 15:51:52 -0700 Subject: [PATCH 091/148] mmc: omap_hsmmc: Change wake-up interrupt to use generic wakeirq We can now use generic wakeirq handling and remove the custom handling for the wake-up interrupts. Acked-by: Ulf Hansson Signed-off-by: Tony Lindgren --- drivers/mmc/host/omap_hsmmc.c | 49 +++++------------------------------ 1 file changed, 6 insertions(+), 43 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 9df2b6801f76..b2b411da297b 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -43,6 +43,7 @@ #include #include #include +#include #include /* OMAP HSMMC Host Controller Registers */ @@ -218,7 +219,6 @@ struct omap_hsmmc_host { unsigned int flags; #define AUTO_CMD23 (1 << 0) /* Auto CMD23 support */ #define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */ -#define HSMMC_WAKE_IRQ_ENABLED (1 << 2) struct omap_hsmmc_next next_data; struct omap_hsmmc_platform_data *pdata; @@ -1117,22 +1117,6 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id) -{ - struct omap_hsmmc_host *host = dev_id; - - /* cirq is level triggered, disable to avoid infinite loop */ - spin_lock(&host->irq_lock); - if (host->flags & HSMMC_WAKE_IRQ_ENABLED) { - disable_irq_nosync(host->wake_irq); - host->flags &= ~HSMMC_WAKE_IRQ_ENABLED; - } - spin_unlock(&host->irq_lock); - pm_request_resume(host->dev); /* no use counter */ - - return IRQ_HANDLED; -} - static void set_sd_bus_power(struct omap_hsmmc_host *host) { unsigned long i; @@ -1665,7 +1649,6 @@ static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable) static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host) { - struct mmc_host *mmc = host->mmc; int ret; /* @@ -1677,11 +1660,7 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host) if (!host->dev->of_node || !host->wake_irq) return -ENODEV; - /* Prevent auto-enabling of IRQ */ - irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN); - ret = devm_request_irq(host->dev, host->wake_irq, omap_hsmmc_wake_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - mmc_hostname(mmc), host); + ret = dev_pm_set_dedicated_wake_irq(host->dev, host->wake_irq); if (ret) { dev_err(mmc_dev(host->mmc), "Unable to request wake IRQ\n"); goto err; @@ -1718,7 +1697,7 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host) return 0; err_free_irq: - devm_free_irq(host->dev, host->wake_irq, host); + dev_pm_clear_wake_irq(host->dev); err: dev_warn(host->dev, "no SDIO IRQ support, falling back to polling\n"); host->wake_irq = 0; @@ -2007,6 +1986,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) omap_hsmmc_ops.multi_io_quirk = omap_hsmmc_multi_io_quirk; } + device_init_wakeup(&pdev->dev, true); pm_runtime_enable(host->dev); pm_runtime_get_sync(host->dev); pm_runtime_set_autosuspend_delay(host->dev, MMC_AUTOSUSPEND_DELAY); @@ -2147,6 +2127,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (host->use_reg) omap_hsmmc_reg_put(host); err_irq: + device_init_wakeup(&pdev->dev, false); if (host->tx_chan) dma_release_channel(host->tx_chan); if (host->rx_chan) @@ -2178,6 +2159,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev) pm_runtime_put_sync(host->dev); pm_runtime_disable(host->dev); + device_init_wakeup(&pdev->dev, false); if (host->dbclk) clk_disable_unprepare(host->dbclk); @@ -2204,11 +2186,6 @@ static int omap_hsmmc_suspend(struct device *dev) OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); } - /* do not wake up due to sdio irq */ - if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && - !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ)) - disable_irq(host->wake_irq); - if (host->dbclk) clk_disable_unprepare(host->dbclk); @@ -2233,11 +2210,6 @@ static int omap_hsmmc_resume(struct device *dev) omap_hsmmc_conf_bus_power(host); omap_hsmmc_protect_card(host); - - if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && - !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ)) - enable_irq(host->wake_irq); - pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); return 0; @@ -2277,10 +2249,6 @@ static int omap_hsmmc_runtime_suspend(struct device *dev) } pinctrl_pm_select_idle_state(dev); - - WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED); - enable_irq(host->wake_irq); - host->flags |= HSMMC_WAKE_IRQ_ENABLED; } else { pinctrl_pm_select_idle_state(dev); } @@ -2302,11 +2270,6 @@ static int omap_hsmmc_runtime_resume(struct device *dev) spin_lock_irqsave(&host->irq_lock, flags); if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && (host->flags & HSMMC_SDIO_IRQ_ENABLED)) { - /* sdio irq flag can't change while in runtime suspend */ - if (host->flags & HSMMC_WAKE_IRQ_ENABLED) { - disable_irq_nosync(host->wake_irq); - host->flags &= ~HSMMC_WAKE_IRQ_ENABLED; - } pinctrl_pm_select_default_state(host->dev); From 8687634b7908c42eb700e0469e110e02833611d1 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Mon, 11 May 2015 13:00:31 +0200 Subject: [PATCH 092/148] tty/serial: at91: RS485 mode: 0 is valid for delay_rts_after_send MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In RS485 mode, we may want to set the delay_rts_after_send value to 0. In the datasheet, the 0 value is said to "disable" the Transmitter Timeguard but this is exactly the expected behavior if we want no delay... Moreover, if the value was set to non-zero value by device-tree or earlier ioctl command, it was impossible to change it back to zero. Reported-by: Sami Pietikäinen Signed-off-by: Nicolas Ferre Cc: stable@vger.kernel.org # 3.2+ Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index b1fcddd6652a..2a8f528153e7 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -316,8 +316,7 @@ static int atmel_config_rs485(struct uart_port *port, if (rs485conf->flags & SER_RS485_ENABLED) { dev_dbg(port->dev, "Setting UART to RS485\n"); atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; - if ((rs485conf->delay_rts_after_send) > 0) - UART_PUT_TTGR(port, rs485conf->delay_rts_after_send); + UART_PUT_TTGR(port, rs485conf->delay_rts_after_send); mode |= ATMEL_US_USMODE_RS485; } else { dev_dbg(port->dev, "Setting UART to RS232\n"); @@ -355,8 +354,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) /* override mode to RS485 if needed, otherwise keep the current mode */ if (port->rs485.flags & SER_RS485_ENABLED) { - if ((port->rs485.delay_rts_after_send) > 0) - UART_PUT_TTGR(port, port->rs485.delay_rts_after_send); + UART_PUT_TTGR(port, port->rs485.delay_rts_after_send); mode &= ~ATMEL_US_USMODE; mode |= ATMEL_US_USMODE_RS485; } @@ -2062,8 +2060,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, /* mode */ if (port->rs485.flags & SER_RS485_ENABLED) { - if ((port->rs485.delay_rts_after_send) > 0) - UART_PUT_TTGR(port, port->rs485.delay_rts_after_send); + UART_PUT_TTGR(port, port->rs485.delay_rts_after_send); mode |= ATMEL_US_USMODE_RS485; } else if (termios->c_cflag & CRTSCTS) { /* RS232 with hardware handshake (RTS/CTS) */ From 98b3f0d535d72e80235aa8b4f7727ff0c89db6b1 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 11 May 2015 10:25:58 +0900 Subject: [PATCH 093/148] tty: rocket: fix comment of ROCKET_SPD_HI This comment does not reflect the actual code. It should be 57600, not 56000. Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- drivers/tty/rocket.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/rocket.h b/drivers/tty/rocket.h index ec863f35f1a9..c11a9392f219 100644 --- a/drivers/tty/rocket.h +++ b/drivers/tty/rocket.h @@ -44,7 +44,7 @@ struct rocket_version { #define ROCKET_HUP_NOTIFY 0x00000004 #define ROCKET_SPLIT_TERMIOS 0x00000008 #define ROCKET_SPD_MASK 0x00000070 -#define ROCKET_SPD_HI 0x00000010 /* Use 56000 instead of 38400 bps */ +#define ROCKET_SPD_HI 0x00000010 /* Use 57600 instead of 38400 bps */ #define ROCKET_SPD_VHI 0x00000020 /* Use 115200 instead of 38400 bps */ #define ROCKET_SPD_SHI 0x00000030 /* Use 230400 instead of 38400 bps */ #define ROCKET_SPD_WARP 0x00000040 /* Use 460800 instead of 38400 bps */ From 113c62ee49d212ecb934147c6ba84cfa79c26121 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 11 May 2015 10:23:38 +0900 Subject: [PATCH 094/148] tty: fix comment of ASYNCB_SPD_HI This comment does not reflect the actual code. It should be 57600, not 56000. Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/tty_flags.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/tty_flags.h b/include/uapi/linux/tty_flags.h index fae4864737fa..072e41e45ee2 100644 --- a/include/uapi/linux/tty_flags.h +++ b/include/uapi/linux/tty_flags.h @@ -15,7 +15,7 @@ #define ASYNCB_FOURPORT 1 /* Set OU1, OUT2 per AST Fourport settings */ #define ASYNCB_SAK 2 /* Secure Attention Key (Orange book) */ #define ASYNCB_SPLIT_TERMIOS 3 /* [x] Separate termios for dialin/callout */ -#define ASYNCB_SPD_HI 4 /* Use 56000 instead of 38400 bps */ +#define ASYNCB_SPD_HI 4 /* Use 57600 instead of 38400 bps */ #define ASYNCB_SPD_VHI 5 /* Use 115200 instead of 38400 bps */ #define ASYNCB_SKIP_TEST 6 /* Skip UART test during autoconfiguration */ #define ASYNCB_AUTO_IRQ 7 /* Do automatic IRQ during From c1b7ac6f4dab63e14ead6a715bc64d39f4a02b2b Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Thu, 14 May 2015 06:45:21 +0000 Subject: [PATCH 095/148] serial: sirf: enable ATLAS7 USP serial support differentiate difference port types by re-defining the status MARCO or putting HW differences into private data of the related ports. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 129 ++++++++++++++++++++++-------- drivers/tty/serial/sirfsoc_uart.h | 33 ++++---- 2 files changed, 115 insertions(+), 47 deletions(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 7214abe0d913..1d0d47f2a8c2 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -138,16 +138,19 @@ static void sirfsoc_uart_stop_tx(struct uart_port *port) rd_regl(port, ureg->sirfsoc_int_en_reg) & ~uint_en->sirfsoc_txfifo_empty_en); else - wr_regl(port, SIRFUART_INT_EN_CLR, + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, uint_en->sirfsoc_txfifo_empty_en); } } else { + if (sirfport->uart_reg->uart_type == SIRF_USP_UART) + wr_regl(port, ureg->sirfsoc_tx_rx_en, rd_regl(port, + ureg->sirfsoc_tx_rx_en) & ~SIRFUART_TX_EN); if (!sirfport->is_atlas7) wr_regl(port, ureg->sirfsoc_int_en_reg, rd_regl(port, ureg->sirfsoc_int_en_reg) & ~uint_en->sirfsoc_txfifo_empty_en); else - wr_regl(port, SIRFUART_INT_EN_CLR, + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, uint_en->sirfsoc_txfifo_empty_en); } } @@ -178,7 +181,7 @@ static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport) rd_regl(port, ureg->sirfsoc_int_en_reg)& ~(uint_en->sirfsoc_txfifo_empty_en)); else - wr_regl(port, SIRFUART_INT_EN_CLR, + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, uint_en->sirfsoc_txfifo_empty_en); /* * DMA requires buffer address and buffer length are both aligned with @@ -246,6 +249,9 @@ static void sirfsoc_uart_start_tx(struct uart_port *port) if (sirfport->tx_dma_chan) sirfsoc_uart_tx_with_dma(sirfport); else { + if (sirfport->uart_reg->uart_type == SIRF_USP_UART) + wr_regl(port, ureg->sirfsoc_tx_rx_en, rd_regl(port, + ureg->sirfsoc_tx_rx_en) | SIRFUART_TX_EN); sirfsoc_uart_pio_tx_chars(sirfport, port->fifosize); wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); if (!sirfport->is_atlas7) @@ -269,21 +275,25 @@ static void sirfsoc_uart_stop_rx(struct uart_port *port) if (!sirfport->is_atlas7) wr_regl(port, ureg->sirfsoc_int_en_reg, rd_regl(port, ureg->sirfsoc_int_en_reg) & - ~(SIRFUART_RX_DMA_INT_EN(port, uint_en) | + ~(SIRFUART_RX_DMA_INT_EN(uint_en, + sirfport->uart_reg->uart_type) | uint_en->sirfsoc_rx_done_en)); else - wr_regl(port, SIRFUART_INT_EN_CLR, - SIRFUART_RX_DMA_INT_EN(port, uint_en)| - uint_en->sirfsoc_rx_done_en); + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, + SIRFUART_RX_DMA_INT_EN(uint_en, + sirfport->uart_reg->uart_type)| + uint_en->sirfsoc_rx_done_en); dmaengine_terminate_all(sirfport->rx_dma_chan); } else { if (!sirfport->is_atlas7) wr_regl(port, ureg->sirfsoc_int_en_reg, rd_regl(port, ureg->sirfsoc_int_en_reg)& - ~(SIRFUART_RX_IO_INT_EN(port, uint_en))); + ~(SIRFUART_RX_IO_INT_EN(uint_en, + sirfport->uart_reg->uart_type))); else - wr_regl(port, SIRFUART_INT_EN_CLR, - SIRFUART_RX_IO_INT_EN(port, uint_en)); + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, + SIRFUART_RX_IO_INT_EN(uint_en, + sirfport->uart_reg->uart_type)); } } @@ -304,7 +314,7 @@ static void sirfsoc_uart_disable_ms(struct uart_port *port) rd_regl(port, ureg->sirfsoc_int_en_reg)& ~uint_en->sirfsoc_cts_en); else - wr_regl(port, SIRFUART_INT_EN_CLR, + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, uint_en->sirfsoc_cts_en); } else disable_irq(gpio_to_irq(sirfport->cts_gpio)); @@ -455,7 +465,7 @@ static void sirfsoc_rx_submit_one_dma_desc(struct uart_port *port, int index) dmaengine_prep_slave_single(sirfport->rx_dma_chan, sirfport->rx_dma_items[index].dma_addr, SIRFSOC_RX_DMA_BUF_SIZE, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); - if (!sirfport->rx_dma_items[index].desc) { + if (IS_ERR_OR_NULL(sirfport->rx_dma_items[index].desc)) { dev_err(port->dev, "DMA slave single fail\n"); return; } @@ -475,12 +485,13 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param) struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; unsigned int count; - unsigned long flags; struct dma_tx_state tx_state; + unsigned long flags; spin_lock_irqsave(&port->lock, flags); while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan, - sirfport->rx_dma_items[sirfport->rx_completed].cookie, &tx_state)) { + 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++; @@ -504,7 +515,7 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param) rd_regl(port, ureg->sirfsoc_int_en_reg) & ~(uint_en->sirfsoc_rx_done_en)); else - wr_regl(port, SIRFUART_INT_EN_CLR, + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, uint_en->sirfsoc_rx_done_en); sirfsoc_uart_start_next_rx_dma(port); } else { @@ -538,7 +549,7 @@ static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport) rd_regl(port, ureg->sirfsoc_int_en_reg) & ~(uint_en->sirfsoc_rx_timeout_en)); else - wr_regl(port, SIRFUART_INT_EN_CLR, + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, uint_en->sirfsoc_rx_timeout_en); tasklet_schedule(&sirfport->rx_tmo_process_tasklet); } @@ -558,7 +569,7 @@ static void sirfsoc_uart_handle_rx_done(struct sirfsoc_uart_port *sirfport) rd_regl(port, ureg->sirfsoc_int_en_reg) & ~(uint_en->sirfsoc_rx_done_en)); else - wr_regl(port, SIRFUART_INT_EN_CLR, + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, uint_en->sirfsoc_rx_done_en); wr_regl(port, ureg->sirfsoc_int_st_reg, uint_st->sirfsoc_rx_timeout); @@ -583,7 +594,8 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) intr_status = rd_regl(port, ureg->sirfsoc_int_st_reg); wr_regl(port, ureg->sirfsoc_int_st_reg, intr_status); intr_status &= rd_regl(port, ureg->sirfsoc_int_en_reg); - if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT(port, uint_st)))) { + if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT(uint_st, + sirfport->uart_reg->uart_type)))) { if (intr_status & uint_st->sirfsoc_rxd_brk) { port->icount.brk++; if (uart_handle_break(port)) @@ -622,9 +634,50 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) sirfsoc_uart_handle_rx_tmo(sirfport); if (intr_status & uint_st->sirfsoc_rx_done) sirfsoc_uart_handle_rx_done(sirfport); - } else { - if (intr_status & SIRFUART_RX_IO_INT_ST(uint_st)) + } else if (intr_status & SIRFUART_RX_IO_INT_ST(uint_st)) { + /* + * chip will trigger continuous RX_TIMEOUT interrupt + * in RXFIFO empty and not trigger if RXFIFO recevice + * data in limit time, original method use RX_TIMEOUT + * will trigger lots of useless interrupt in RXFIFO + * empty.RXFIFO received one byte will trigger RX_DONE + * interrupt.use RX_DONE to wait for data received + * into RXFIFO, use RX_THD/RX_FULL for lots data receive + * and use RX_TIMEOUT for the last left data. + */ + if (intr_status & uint_st->sirfsoc_rx_done) { + if (!sirfport->is_atlas7) { + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) + & ~(uint_en->sirfsoc_rx_done_en)); + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) + | (uint_en->sirfsoc_rx_timeout_en)); + } else { + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, + uint_en->sirfsoc_rx_done_en); + wr_regl(port, ureg->sirfsoc_int_en_reg, + uint_en->sirfsoc_rx_timeout_en); + } + } else { + if (intr_status & uint_st->sirfsoc_rx_timeout) { + if (!sirfport->is_atlas7) { + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) + & ~(uint_en->sirfsoc_rx_timeout_en)); + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) + | (uint_en->sirfsoc_rx_done_en)); + } else { + wr_regl(port, + ureg->sirfsoc_int_en_clr_reg, + uint_en->sirfsoc_rx_timeout_en); + wr_regl(port, ureg->sirfsoc_int_en_reg, + uint_en->sirfsoc_rx_done_en); + } + } sirfsoc_uart_pio_rx_chars(port, port->fifosize); + } } spin_unlock(&port->lock); tty_flip_buffer_push(&state->port); @@ -657,11 +710,12 @@ static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param) struct uart_port *port = &sirfport->port; 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; + unsigned long flags; spin_lock_irqsave(&port->lock, flags); while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan, - sirfport->rx_dma_items[sirfport->rx_completed].cookie, &tx_state)) { + 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) & @@ -705,10 +759,12 @@ static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port) if (!sirfport->is_atlas7) wr_regl(port, ureg->sirfsoc_int_en_reg, rd_regl(port, ureg->sirfsoc_int_en_reg) | - SIRFUART_RX_DMA_INT_EN(port, uint_en)); + SIRFUART_RX_DMA_INT_EN(uint_en, + sirfport->uart_reg->uart_type)); else wr_regl(port, ureg->sirfsoc_int_en_reg, - SIRFUART_RX_DMA_INT_EN(port, uint_en)); + SIRFUART_RX_DMA_INT_EN(uint_en, + sirfport->uart_reg->uart_type)); } static void sirfsoc_uart_start_rx(struct uart_port *port) @@ -727,10 +783,12 @@ static void sirfsoc_uart_start_rx(struct uart_port *port) if (!sirfport->is_atlas7) wr_regl(port, ureg->sirfsoc_int_en_reg, rd_regl(port, ureg->sirfsoc_int_en_reg) | - SIRFUART_RX_IO_INT_EN(port, uint_en)); + SIRFUART_RX_IO_INT_EN(uint_en, + sirfport->uart_reg->uart_type)); else wr_regl(port, ureg->sirfsoc_int_en_reg, - SIRFUART_RX_IO_INT_EN(port, uint_en)); + SIRFUART_RX_IO_INT_EN(uint_en, + sirfport->uart_reg->uart_type)); } } @@ -930,7 +988,7 @@ static void sirfsoc_uart_set_termios(struct uart_port *port, wr_regl(port, ureg->sirfsoc_tx_fifo_op, (txfifo_op_reg & ~SIRFUART_FIFO_START)); if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { - config_reg |= SIRFUART_RECV_TIMEOUT(port, rx_time_out); + config_reg |= SIRFUART_UART_RECV_TIMEOUT(rx_time_out); wr_regl(port, ureg->sirfsoc_line_ctrl, config_reg); } else { /*tx frame ctrl*/ @@ -953,7 +1011,7 @@ static void sirfsoc_uart_set_termios(struct uart_port *port, wr_regl(port, ureg->sirfsoc_rx_frame_ctrl, len_val); /*async param*/ wr_regl(port, ureg->sirfsoc_async_param_reg, - (SIRFUART_RECV_TIMEOUT(port, rx_time_out)) | + (SIRFUART_USP_RECV_TIMEOUT(rx_time_out)) | (sample_div_reg & SIRFSOC_USP_ASYNC_DIV2_MASK) << SIRFSOC_USP_ASYNC_DIV2_OFFSET); } @@ -1071,7 +1129,7 @@ static void sirfsoc_uart_shutdown(struct uart_port *port) if (!sirfport->is_atlas7) wr_regl(port, ureg->sirfsoc_int_en_reg, 0); else - wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL); + wr_regl(port, ureg->sirfsoc_int_en_clr_reg, ~0UL); free_irq(port->irq, sirfport); if (sirfport->ms_enabled) @@ -1217,10 +1275,11 @@ static struct uart_driver sirfsoc_uart_drv = { #endif }; -static const struct of_device_id sirfsoc_uart_ids[] = { +static struct of_device_id sirfsoc_uart_ids[] = { { .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,}, { .compatible = "sirf,atlas7-uart", .data = &sirfsoc_uart}, { .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp}, + { .compatible = "sirf,atlas7-usp-uart", .data = &sirfsoc_usp}, {} }; MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids); @@ -1257,9 +1316,12 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) sirfport->hw_flow_ctrl = of_property_read_bool(pdev->dev.of_node, "sirf,uart-has-rtscts"); - if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-uart")) + if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-uart") || + of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-uart")) sirfport->uart_reg->uart_type = SIRF_REAL_UART; - if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart")) { + if (of_device_is_compatible(pdev->dev.of_node, + "sirf,prima2-usp-uart") || of_device_is_compatible( + pdev->dev.of_node, "sirf,atlas7-usp-uart")) { sirfport->uart_reg->uart_type = SIRF_USP_UART; if (!sirfport->hw_flow_ctrl) goto usp_no_flow_control; @@ -1297,7 +1359,8 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) gpio_direction_output(sirfport->rts_gpio, 1); } usp_no_flow_control: - if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-uart")) + if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-uart") || + of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-usp-uart")) sirfport->is_atlas7 = true; if (of_property_read_u32(pdev->dev.of_node, diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index af2f1878330a..b49c23a94ee8 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -20,6 +20,7 @@ struct sirfsoc_register { u32 sirfsoc_tx_rx_en; u32 sirfsoc_int_en_reg; u32 sirfsoc_int_st_reg; + u32 sirfsoc_int_en_clr_reg; u32 sirfsoc_tx_dma_io_ctrl; u32 sirfsoc_tx_dma_io_len; u32 sirfsoc_tx_fifo_ctrl; @@ -143,6 +144,7 @@ struct sirfsoc_uart_register sirfsoc_usp = { .sirfsoc_rx_fifo_op = 0x0130, .sirfsoc_rx_fifo_status = 0x0134, .sirfsoc_rx_fifo_data = 0x0138, + .sirfsoc_int_en_clr_reg = 0x140, }, .uart_int_en = { .sirfsoc_rx_done_en = BIT(0), @@ -191,6 +193,7 @@ struct sirfsoc_uart_register sirfsoc_uart = { .sirfsoc_divisor = 0x0050, .sirfsoc_int_en_reg = 0x0054, .sirfsoc_int_st_reg = 0x0058, + .sirfsoc_int_en_clr_reg = 0x0060, .sirfsoc_tx_dma_io_ctrl = 0x0100, .sirfsoc_tx_dma_io_len = 0x0104, .sirfsoc_tx_fifo_ctrl = 0x0108, @@ -291,8 +294,6 @@ struct sirfsoc_uart_register sirfsoc_uart = { #define SIRFUART_IO_MODE BIT(0) #define SIRFUART_DMA_MODE 0x0 -/* Macro Specific*/ -#define SIRFUART_INT_EN_CLR 0x0060 /* Baud Rate Calculation */ #define SIRF_USP_MIN_SAMPLE_DIV 0x1 #define SIRF_MIN_SAMPLE_DIV 0xf @@ -326,34 +327,38 @@ struct sirfsoc_uart_register sirfsoc_uart = { #define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000) #define SIRFUART_RECV_TIMEOUT_VALUE(x) \ (((x) > 0xFFFF) ? 0xFFFF : ((x) & 0xFFFF)) -#define SIRFUART_RECV_TIMEOUT(port, x) \ - (((port)->line > 2) ? (x & 0xFFFF) : ((x) & 0xFFFF) << 16) +#define SIRFUART_USP_RECV_TIMEOUT(x) (x & 0xFFFF) +#define SIRFUART_UART_RECV_TIMEOUT(x) ((x & 0xFFFF) << 16) #define SIRFUART_FIFO_THD(port) (port->fifosize >> 1) -#define SIRFUART_ERR_INT_STAT(port, unit_st) \ +#define SIRFUART_ERR_INT_STAT(unit_st, uart_type) \ (uint_st->sirfsoc_rx_oflow | \ uint_st->sirfsoc_frm_err | \ uint_st->sirfsoc_rxd_brk | \ - ((port->line > 2) ? 0 : uint_st->sirfsoc_parity_err)) -#define SIRFUART_RX_IO_INT_EN(port, uint_en) \ - (uint_en->sirfsoc_rx_timeout_en |\ + ((uart_type != SIRF_REAL_UART) ? \ + 0 : uint_st->sirfsoc_parity_err)) +#define SIRFUART_RX_IO_INT_EN(uint_en, uart_type) \ + (uint_en->sirfsoc_rx_done_en |\ uint_en->sirfsoc_rxfifo_thd_en |\ uint_en->sirfsoc_rxfifo_full_en |\ uint_en->sirfsoc_frm_err_en |\ uint_en->sirfsoc_rx_oflow_en |\ uint_en->sirfsoc_rxd_brk_en |\ - ((port->line > 2) ? 0 : uint_en->sirfsoc_parity_err_en)) + ((uart_type != SIRF_REAL_UART) ? \ + 0 : uint_en->sirfsoc_parity_err_en)) #define SIRFUART_RX_IO_INT_ST(uint_st) \ - (uint_st->sirfsoc_rx_timeout |\ - uint_st->sirfsoc_rxfifo_thd |\ - uint_st->sirfsoc_rxfifo_full) + (uint_st->sirfsoc_rxfifo_thd |\ + uint_st->sirfsoc_rxfifo_full|\ + uint_st->sirfsoc_rx_done |\ + uint_st->sirfsoc_rx_timeout) #define SIRFUART_CTS_INT_ST(uint_st) (uint_st->sirfsoc_cts) -#define SIRFUART_RX_DMA_INT_EN(port, uint_en) \ +#define SIRFUART_RX_DMA_INT_EN(uint_en, uart_type) \ (uint_en->sirfsoc_rx_timeout_en |\ uint_en->sirfsoc_frm_err_en |\ uint_en->sirfsoc_rx_oflow_en |\ uint_en->sirfsoc_rxd_brk_en |\ - ((port->line > 2) ? 0 : uint_en->sirfsoc_parity_err_en)) + ((uart_type != SIRF_REAL_UART) ? \ + 0 : uint_en->sirfsoc_parity_err_en)) /* Generic Definitions */ #define SIRFSOC_UART_NAME "ttySiRF" #define SIRFSOC_UART_MAJOR 0 From eab192ae56fd52416af485e3d7ba2c6982200d71 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Thu, 14 May 2015 06:45:22 +0000 Subject: [PATCH 096/148] serial: sirf: fix the issue that HW flow control doesn't work for BT >From HW spec, when rxfifo's data is less than AFC_RX_THD(RX threshhold), RTS signal is active. otherwise, RTS signal is inactive. Crrently the RX threshhold is set as zero, so RTS has no chance to be active. This patch replaces the default 0 by a positive number. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 1d0d47f2a8c2..48cceaf5338a 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -344,7 +344,8 @@ static void sirfsoc_uart_enable_ms(struct uart_port *port) if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { wr_regl(port, ureg->sirfsoc_afc_ctrl, rd_regl(port, ureg->sirfsoc_afc_ctrl) | - SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN); + SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN | + SIRFUART_AFC_CTRL_RX_THD); if (!sirfport->is_atlas7) wr_regl(port, ureg->sirfsoc_int_en_reg, rd_regl(port, ureg->sirfsoc_int_en_reg) From 36c0991089ef190ee5e3f72e1346c623b24a0e7d Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Thu, 14 May 2015 06:45:23 +0000 Subject: [PATCH 097/148] serial: sirf: fix endless loop bug in uart receive tasklet In special condition, when cpu schedule into rx_tmo_process_tl or rx_dma_complete_tl and all the receive dma tasks have done, it will go into endless loop because no dma task cookie status be changed. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 48cceaf5338a..ffeb7663bd6e 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -488,6 +488,7 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param) unsigned int count; struct dma_tx_state tx_state; unsigned long flags; + int i = 0; spin_lock_irqsave(&port->lock, flags); while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan, @@ -497,6 +498,9 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param) SIRFSOC_RX_DMA_BUF_SIZE); sirfport->rx_completed++; sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT; + i++; + if (i > SIRFSOC_RX_LOOP_BUF_CNT) + break; } count = CIRC_CNT(sirfport->rx_dma_items[sirfport->rx_issued].xmit.head, sirfport->rx_dma_items[sirfport->rx_issued].xmit.tail, @@ -713,6 +717,8 @@ static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param) struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; struct dma_tx_state tx_state; unsigned long flags; + int i = 0; + spin_lock_irqsave(&port->lock, flags); while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan, sirfport->rx_dma_items[sirfport->rx_completed].cookie, @@ -726,6 +732,9 @@ static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param) else sirfport->rx_completed++; sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT; + i++; + if (i > SIRFSOC_RX_LOOP_BUF_CNT) + break; } spin_unlock_irqrestore(&port->lock, flags); tty_flip_buffer_push(&port->state->port); From d9e8e976faef867f9be579d6a76e1271d5d30da8 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Thu, 14 May 2015 06:45:24 +0000 Subject: [PATCH 098/148] serial: sirf: add uart receive's some error counter and mark add overrun error's flag mark and parity's counter, we can show the statistic from procfs node. BTW, let the indentation of stick bits configuration look better. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index ffeb7663bd6e..a5007216338d 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -606,14 +606,18 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) if (uart_handle_break(port)) goto recv_char; } - if (intr_status & uint_st->sirfsoc_rx_oflow) + if (intr_status & uint_st->sirfsoc_rx_oflow) { port->icount.overrun++; + flag = TTY_OVERRUN; + } if (intr_status & uint_st->sirfsoc_frm_err) { port->icount.frame++; flag = TTY_FRAME; } - if (intr_status & uint_st->sirfsoc_parity_err) + if (intr_status & uint_st->sirfsoc_parity_err) { + port->icount.parity++; flag = TTY_PARITY; + } wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START); @@ -932,10 +936,11 @@ static void sirfsoc_uart_set_termios(struct uart_port *port, config_reg |= SIRFUART_STICK_BIT_MARK; else config_reg |= SIRFUART_STICK_BIT_SPACE; - } else if (termios->c_cflag & PARODD) { - config_reg |= SIRFUART_STICK_BIT_ODD; } else { - config_reg |= SIRFUART_STICK_BIT_EVEN; + if (termios->c_cflag & PARODD) + config_reg |= SIRFUART_STICK_BIT_ODD; + else + config_reg |= SIRFUART_STICK_BIT_EVEN; } } } else { From 7f60f2fe16206d5db6a228dfe1de5ea0a9e5da46 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Thu, 14 May 2015 06:45:25 +0000 Subject: [PATCH 099/148] serial: sirf: add serial loopback function support Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 20 ++++++++++++++++++++ drivers/tty/serial/sirfsoc_uart.h | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index a5007216338d..0e4379939703 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -108,6 +108,26 @@ static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0; unsigned int current_val; + if (mctrl & TIOCM_LOOP) { + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) + wr_regl(port, ureg->sirfsoc_line_ctrl, + rd_regl(port, ureg->sirfsoc_line_ctrl) | + SIRFUART_LOOP_BACK); + else + wr_regl(port, ureg->sirfsoc_mode1, + rd_regl(port, ureg->sirfsoc_mode1) | + SIRFSOC_USP_LOOP_BACK_CTRL); + } else { + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) + wr_regl(port, ureg->sirfsoc_line_ctrl, + rd_regl(port, ureg->sirfsoc_line_ctrl) & + ~SIRFUART_LOOP_BACK); + else + wr_regl(port, ureg->sirfsoc_mode1, + rd_regl(port, ureg->sirfsoc_mode1) & + ~SIRFSOC_USP_LOOP_BACK_CTRL); + } + if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled) return; if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index b49c23a94ee8..3ab3141e1aae 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -322,7 +322,7 @@ struct sirfsoc_uart_register sirfsoc_uart = { #define SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET 24 #define SIRFSOC_USP_ASYNC_DIV2_MASK 0x3f #define SIRFSOC_USP_ASYNC_DIV2_OFFSET 16 - +#define SIRFSOC_USP_LOOP_BACK_CTRL BIT(2) /* USP-UART Common */ #define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000) #define SIRFUART_RECV_TIMEOUT_VALUE(x) \ From c35b49b7166b8054ce33827d5dfd8d6041f5b4df Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Thu, 14 May 2015 06:45:26 +0000 Subject: [PATCH 100/148] serial: sirf: assign console default index if users not set a valid one it seems this is a more typical behaviour from reviewing other console drivers. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 0e4379939703..8d759629ffb0 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1236,7 +1236,7 @@ sirfsoc_uart_console_setup(struct console *co, char *options) struct sirfsoc_uart_port *sirfport; struct sirfsoc_register *ureg; if (co->index < 0 || co->index >= SIRFSOC_UART_NR) - return -EINVAL; + co->index = 1; sirfport = sirf_ports[co->index]; if (!sirfport) return -ENODEV; From 8f8e48f4d6f722a35aac9c990fa54f7bb07b4d5b Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 20 May 2015 12:21:04 +0100 Subject: [PATCH 101/148] serial: tegra: Fix memory leak on DMA setup failure If the call to dmaengine_slave_config() fails, then the DMA buffer will not be freed/unmapped. Fix this by moving the code that stores the address of the buffer in the tegra_uart_port structure to before the call to dmaengine_slave_config(). Reported-by: Alexandre Courbot Signed-off-by: Jon Hunter Reviewed-by: Alexandre Courbot Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 32 +++++++++++-------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 3b63f103f0c9..cf0133ae762d 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -999,6 +999,12 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, dma_release_channel(dma_chan); return -ENOMEM; } + dma_sconfig.src_addr = tup->uport.mapbase; + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.src_maxburst = 4; + tup->rx_dma_chan = dma_chan; + tup->rx_dma_buf_virt = dma_buf; + tup->rx_dma_buf_phys = dma_phys; } else { dma_phys = dma_map_single(tup->uport.dev, tup->uport.state->xmit.buf, UART_XMIT_SIZE, @@ -1009,39 +1015,23 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, return -ENOMEM; } dma_buf = tup->uport.state->xmit.buf; - } - - if (dma_to_memory) { - dma_sconfig.src_addr = tup->uport.mapbase; - dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - dma_sconfig.src_maxburst = 4; - } else { dma_sconfig.dst_addr = tup->uport.mapbase; dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma_sconfig.dst_maxburst = 16; + tup->tx_dma_chan = dma_chan; + tup->tx_dma_buf_virt = dma_buf; + tup->tx_dma_buf_phys = dma_phys; } ret = dmaengine_slave_config(dma_chan, &dma_sconfig); if (ret < 0) { dev_err(tup->uport.dev, "Dma slave config failed, err = %d\n", ret); - goto scrub; + tegra_uart_dma_channel_free(tup, dma_to_memory); + return ret; } - if (dma_to_memory) { - tup->rx_dma_chan = dma_chan; - tup->rx_dma_buf_virt = dma_buf; - tup->rx_dma_buf_phys = dma_phys; - } else { - tup->tx_dma_chan = dma_chan; - tup->tx_dma_buf_virt = dma_buf; - tup->tx_dma_buf_phys = dma_phys; - } return 0; - -scrub: - tegra_uart_dma_channel_free(tup, dma_to_memory); - return ret; } static int tegra_uart_startup(struct uart_port *u) From 9e91597f24234062c8bb4278ba7c6197be84e668 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 20 May 2015 22:07:35 +0200 Subject: [PATCH 102/148] serial: 8250_omap: provide complete custom startup & shutdown callbacks The currently in-use port->startup and port->shutdown are "okay". The startup part for instance does the tiny omap extra part and invokes serial8250_do_startup() for the remaining pieces. The workflow in serial8250_do_startup() is okay except for the part where UART_RX is read without a check if there is something to read. I tried to workaround it in commit 0aa525d11859 ("tty: serial: 8250_core: read only RX if there is something in the FIFO") but then reverted it later in commit ca8bb4aefb9 ("serial: 8250: Revert "tty: serial: 8250_core: read only RX if there is something in the FIFO""). This is the second attempt to get it to work on older OMAPs without breaking other chips this time Peter Hurley suggested to pull in the few needed lines from serial8250_do_startup() and drop everything else that is not required including making it simpler like using just request_irq() instead the chain handler like it is doing now. So lets try that. Fixes: ca8bb4aefb93 ("serial: 8250: Revert "tty: serial: 8250_core: read only RX if there is something in the FIFO"") Tested-by: Tony Lindgren Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 82 +++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 77c9f5aa8369..978204333c94 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -563,12 +563,36 @@ static irqreturn_t omap_wake_irq(int irq, void *dev_id) return IRQ_NONE; } +#ifdef CONFIG_SERIAL_8250_DMA +static int omap_8250_dma_handle_irq(struct uart_port *port); +#endif + +static irqreturn_t omap8250_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir; + int ret; + +#ifdef CONFIG_SERIAL_8250_DMA + if (up->dma) { + ret = omap_8250_dma_handle_irq(port); + return IRQ_RETVAL(ret); + } +#endif + + serial8250_rpm_get(up); + iir = serial_port_in(port, UART_IIR); + ret = serial8250_handle_irq(port, iir); + serial8250_rpm_put(up); + + return IRQ_RETVAL(ret); +} + static int omap_8250_startup(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); struct omap8250_priv *priv = port->private_data; - int ret; if (priv->wakeirq) { @@ -581,10 +605,31 @@ static int omap_8250_startup(struct uart_port *port) pm_runtime_get_sync(port->dev); - ret = serial8250_do_startup(port); - if (ret) + up->mcr = 0; + serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + + serial_out(up, UART_LCR, UART_LCR_WLEN8); + + up->lsr_saved_flags = 0; + up->msr_saved_flags = 0; + + if (up->dma) { + ret = serial8250_request_dma(up); + if (ret) { + dev_warn_ratelimited(port->dev, + "failed to request DMA\n"); + up->dma = NULL; + } + } + + ret = request_irq(port->irq, omap8250_irq, IRQF_SHARED, + dev_name(port->dev), port); + if (ret < 0) goto err; + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + #ifdef CONFIG_PM up->capabilities |= UART_CAP_RPM; #endif @@ -611,8 +656,7 @@ static int omap_8250_startup(struct uart_port *port) static void omap_8250_shutdown(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); struct omap8250_priv *priv = port->private_data; flush_work(&priv->qos_work); @@ -622,11 +666,24 @@ static void omap_8250_shutdown(struct uart_port *port) pm_runtime_get_sync(port->dev); serial_out(up, UART_OMAP_WER, 0); - serial8250_do_shutdown(port); + + up->ier = 0; + serial_out(up, UART_IER, 0); + + if (up->dma) + serial8250_release_dma(up); + + /* + * Disable break condition and FIFOs + */ + if (up->lcr & UART_LCR_SBC) + serial_out(up, UART_LCR, up->lcr & ~UART_LCR_SBC); + serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); + free_irq(port->irq, port); if (priv->wakeirq) free_irq(priv->wakeirq, port); } @@ -1005,6 +1062,13 @@ static inline int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) } #endif +static int omap8250_no_handle_irq(struct uart_port *port) +{ + /* IRQ has not been requested but handling irq? */ + WARN_ONCE(1, "Unexpected irq handling before port startup\n"); + return 0; +} + static int omap8250_probe(struct platform_device *pdev) { struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1108,6 +1172,7 @@ static int omap8250_probe(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); omap_serial_fill_features_erratas(&up, priv); + up.port.handle_irq = omap8250_no_handle_irq; #ifdef CONFIG_SERIAL_8250_DMA if (pdev->dev.of_node) { /* @@ -1121,7 +1186,6 @@ static int omap8250_probe(struct platform_device *pdev) ret = of_property_count_strings(pdev->dev.of_node, "dma-names"); if (ret == 2) { up.dma = &priv->omap8250_dma; - up.port.handle_irq = omap_8250_dma_handle_irq; priv->omap8250_dma.fn = the_no_dma_filter_fn; priv->omap8250_dma.tx_dma = omap_8250_tx_dma; priv->omap8250_dma.rx_dma = omap_8250_rx_dma; From 3c99121ca5c5f79959c172a6297572a273f54fb6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 20 May 2015 19:46:24 +0200 Subject: [PATCH 103/148] serial: sh-sci: Add DMA support to the DT binding documentation Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/serial/renesas,sci-serial.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index ae73bb0e9ad9..b91fcff4b7d4 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -44,6 +44,11 @@ Required properties: Note: Each enabled SCIx UART should have an alias correctly numbered in the "aliases" node. +Optional properties: + - dmas: Must contain a list of two references to DMA specifiers, one for + transmission, and one for reception. + - dma-names: Must contain a list of two DMA names, "tx" and "rx". + Example: aliases { serial0 = &scifa0; @@ -56,4 +61,6 @@ Example: interrupts = <0 144 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7790_CLK_SCIFA0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x21>, <&dmac0 0x22>; + dma-names = "tx", "rx"; }; From a1706ed92e9d28990e1a78f697b09e8996f9af71 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 20 May 2015 19:46:25 +0200 Subject: [PATCH 104/148] ARM: shmobile: r8a7790 dtsi: Describe DMA for the serial ports Add DMA properties to all SCIF, SCIFA, SCIFB, and HSCIF device nodes. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/r8a7790.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index 4bb2f4c17321..e4daa05d6cc4 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -531,6 +531,8 @@ scifa0: serial@e6c40000 { interrupts = <0 144 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7790_CLK_SCIFA0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x21>, <&dmac0 0x22>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -540,6 +542,8 @@ scifa1: serial@e6c50000 { interrupts = <0 145 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7790_CLK_SCIFA1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x25>, <&dmac0 0x26>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -549,6 +553,8 @@ scifa2: serial@e6c60000 { interrupts = <0 151 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7790_CLK_SCIFA2>; clock-names = "sci_ick"; + dmas = <&dmac0 0x27>, <&dmac0 0x28>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -558,6 +564,8 @@ scifb0: serial@e6c20000 { interrupts = <0 148 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7790_CLK_SCIFB0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x3d>, <&dmac0 0x3e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -567,6 +575,8 @@ scifb1: serial@e6c30000 { interrupts = <0 149 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7790_CLK_SCIFB1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x19>, <&dmac0 0x1a>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -576,6 +586,8 @@ scifb2: serial@e6ce0000 { interrupts = <0 150 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7790_CLK_SCIFB2>; clock-names = "sci_ick"; + dmas = <&dmac0 0x1d>, <&dmac0 0x1e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -585,6 +597,8 @@ scif0: serial@e6e60000 { interrupts = <0 152 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7790_CLK_SCIF0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x29>, <&dmac0 0x2a>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -594,6 +608,8 @@ scif1: serial@e6e68000 { interrupts = <0 153 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7790_CLK_SCIF1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x2d>, <&dmac0 0x2e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -603,6 +619,8 @@ hscif0: serial@e62c0000 { interrupts = <0 154 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7790_CLK_HSCIF0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x39>, <&dmac0 0x3a>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -612,6 +630,8 @@ hscif1: serial@e62c8000 { interrupts = <0 155 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7790_CLK_HSCIF1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x4d>, <&dmac0 0x4e>; + dma-names = "tx", "rx"; status = "disabled"; }; From 0c35004808c25da508b6efade365e78da5991983 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 20 May 2015 19:46:26 +0200 Subject: [PATCH 105/148] ARM: shmobile: r8a7791 dtsi: Describe DMA for the serial ports Add DMA properties to all SCIF, SCIFA, SCIFB, and HSCIF device nodes. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/r8a7791.dtsi | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index 4696062f6dde..e8af960e2208 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -509,6 +509,8 @@ scifa0: serial@e6c40000 { interrupts = <0 144 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7791_CLK_SCIFA0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x21>, <&dmac0 0x22>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -518,6 +520,8 @@ scifa1: serial@e6c50000 { interrupts = <0 145 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7791_CLK_SCIFA1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x25>, <&dmac0 0x26>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -527,6 +531,8 @@ scifa2: serial@e6c60000 { interrupts = <0 151 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7791_CLK_SCIFA2>; clock-names = "sci_ick"; + dmas = <&dmac0 0x27>, <&dmac0 0x28>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -536,6 +542,8 @@ scifa3: serial@e6c70000 { interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp11_clks R8A7791_CLK_SCIFA3>; clock-names = "sci_ick"; + dmas = <&dmac0 0x1b>, <&dmac0 0x1c>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -545,6 +553,8 @@ scifa4: serial@e6c78000 { interrupts = <0 30 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp11_clks R8A7791_CLK_SCIFA4>; clock-names = "sci_ick"; + dmas = <&dmac0 0x1f>, <&dmac0 0x20>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -554,6 +564,8 @@ scifa5: serial@e6c80000 { interrupts = <0 31 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp11_clks R8A7791_CLK_SCIFA5>; clock-names = "sci_ick"; + dmas = <&dmac0 0x23>, <&dmac0 0x24>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -563,6 +575,8 @@ scifb0: serial@e6c20000 { interrupts = <0 148 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7791_CLK_SCIFB0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x3d>, <&dmac0 0x3e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -572,6 +586,8 @@ scifb1: serial@e6c30000 { interrupts = <0 149 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7791_CLK_SCIFB1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x19>, <&dmac0 0x1a>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -581,6 +597,8 @@ scifb2: serial@e6ce0000 { interrupts = <0 150 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7791_CLK_SCIFB2>; clock-names = "sci_ick"; + dmas = <&dmac0 0x1d>, <&dmac0 0x1e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -590,6 +608,8 @@ scif0: serial@e6e60000 { interrupts = <0 152 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7791_CLK_SCIF0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x29>, <&dmac0 0x2a>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -599,6 +619,8 @@ scif1: serial@e6e68000 { interrupts = <0 153 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7791_CLK_SCIF1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x2d>, <&dmac0 0x2e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -608,6 +630,8 @@ scif2: serial@e6e58000 { interrupts = <0 22 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7791_CLK_SCIF2>; clock-names = "sci_ick"; + dmas = <&dmac0 0x2b>, <&dmac0 0x2c>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -617,6 +641,8 @@ scif3: serial@e6ea8000 { interrupts = <0 23 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7791_CLK_SCIF3>; clock-names = "sci_ick"; + dmas = <&dmac0 0x2f>, <&dmac0 0x30>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -626,6 +652,8 @@ scif4: serial@e6ee0000 { interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7791_CLK_SCIF4>; clock-names = "sci_ick"; + dmas = <&dmac0 0xfb>, <&dmac0 0xfc>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -635,6 +663,8 @@ scif5: serial@e6ee8000 { interrupts = <0 25 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7791_CLK_SCIF5>; clock-names = "sci_ick"; + dmas = <&dmac0 0xfd>, <&dmac0 0xfe>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -644,6 +674,8 @@ hscif0: serial@e62c0000 { interrupts = <0 154 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7791_CLK_HSCIF0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x39>, <&dmac0 0x3a>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -653,6 +685,8 @@ hscif1: serial@e62c8000 { interrupts = <0 155 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7791_CLK_HSCIF1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x4d>, <&dmac0 0x4e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -662,6 +696,8 @@ hscif2: serial@e62d0000 { interrupts = <0 21 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7791_CLK_HSCIF2>; clock-names = "sci_ick"; + dmas = <&dmac0 0x3b>, <&dmac0 0x3c>; + dma-names = "tx", "rx"; status = "disabled"; }; From fe54e93ba86686c57521ec5436e459df23610c9f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 20 May 2015 19:46:27 +0200 Subject: [PATCH 106/148] ARM: shmobile: r8a7794 dtsi: Describe DMA for the serial ports Add DMA properties to all SCIF, SCIFA, SCIFB, and HSCIF device nodes. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/r8a7794.dtsi | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi index 7a3ffa51a8bf..7a84aaf2732f 100644 --- a/arch/arm/boot/dts/r8a7794.dtsi +++ b/arch/arm/boot/dts/r8a7794.dtsi @@ -173,6 +173,8 @@ scifa0: serial@e6c40000 { interrupts = <0 144 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7794_CLK_SCIFA0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x21>, <&dmac0 0x22>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -182,6 +184,8 @@ scifa1: serial@e6c50000 { interrupts = <0 145 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7794_CLK_SCIFA1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x25>, <&dmac0 0x26>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -191,6 +195,8 @@ scifa2: serial@e6c60000 { interrupts = <0 151 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7794_CLK_SCIFA2>; clock-names = "sci_ick"; + dmas = <&dmac0 0x27>, <&dmac0 0x28>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -200,6 +206,8 @@ scifa3: serial@e6c70000 { interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp11_clks R8A7794_CLK_SCIFA3>; clock-names = "sci_ick"; + dmas = <&dmac0 0x1b>, <&dmac0 0x1c>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -209,6 +217,8 @@ scifa4: serial@e6c78000 { interrupts = <0 30 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp11_clks R8A7794_CLK_SCIFA4>; clock-names = "sci_ick"; + dmas = <&dmac0 0x1f>, <&dmac0 0x20>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -218,6 +228,8 @@ scifa5: serial@e6c80000 { interrupts = <0 31 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp11_clks R8A7794_CLK_SCIFA5>; clock-names = "sci_ick"; + dmas = <&dmac0 0x23>, <&dmac0 0x24>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -227,6 +239,8 @@ scifb0: serial@e6c20000 { interrupts = <0 148 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7794_CLK_SCIFB0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x3d>, <&dmac0 0x3e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -236,6 +250,8 @@ scifb1: serial@e6c30000 { interrupts = <0 149 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7794_CLK_SCIFB1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x19>, <&dmac0 0x1a>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -245,6 +261,8 @@ scifb2: serial@e6ce0000 { interrupts = <0 150 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp2_clks R8A7794_CLK_SCIFB2>; clock-names = "sci_ick"; + dmas = <&dmac0 0x1d>, <&dmac0 0x1e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -254,6 +272,8 @@ scif0: serial@e6e60000 { interrupts = <0 152 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7794_CLK_SCIF0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x29>, <&dmac0 0x2a>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -263,6 +283,8 @@ scif1: serial@e6e68000 { interrupts = <0 153 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7794_CLK_SCIF1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x2d>, <&dmac0 0x2e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -272,6 +294,8 @@ scif2: serial@e6e58000 { interrupts = <0 22 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7794_CLK_SCIF2>; clock-names = "sci_ick"; + dmas = <&dmac0 0x2b>, <&dmac0 0x2c>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -281,6 +305,8 @@ scif3: serial@e6ea8000 { interrupts = <0 23 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7794_CLK_SCIF3>; clock-names = "sci_ick"; + dmas = <&dmac0 0x2f>, <&dmac0 0x30>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -290,6 +316,8 @@ scif4: serial@e6ee0000 { interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7794_CLK_SCIF4>; clock-names = "sci_ick"; + dmas = <&dmac0 0xfb>, <&dmac0 0xfc>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -299,6 +327,8 @@ scif5: serial@e6ee8000 { interrupts = <0 25 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7794_CLK_SCIF5>; clock-names = "sci_ick"; + dmas = <&dmac0 0xfd>, <&dmac0 0xfe>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -308,6 +338,8 @@ hscif0: serial@e62c0000 { interrupts = <0 154 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7794_CLK_HSCIF0>; clock-names = "sci_ick"; + dmas = <&dmac0 0x39>, <&dmac0 0x3a>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -317,6 +349,8 @@ hscif1: serial@e62c8000 { interrupts = <0 155 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7794_CLK_HSCIF1>; clock-names = "sci_ick"; + dmas = <&dmac0 0x4d>, <&dmac0 0x4e>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -326,6 +360,8 @@ hscif2: serial@e62d0000 { interrupts = <0 21 IRQ_TYPE_LEVEL_HIGH>; clocks = <&mstp7_clks R8A7794_CLK_HSCIF2>; clock-names = "sci_ick"; + dmas = <&dmac0 0x3b>, <&dmac0 0x3c>; + dma-names = "tx", "rx"; status = "disabled"; }; From c33eecc7abce688208430b2bb28be9442673ebef Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 21 May 2015 14:06:11 +0200 Subject: [PATCH 107/148] tty: Spelling s/reseved/reserved/ Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index c408689a85a9..382d3fcba6cc 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -161,7 +161,7 @@ struct gsm_dlci { struct net_device *net; /* network interface, if created */ }; -/* DLCI 0, 62/63 are special or reseved see gsmtty_open */ +/* DLCI 0, 62/63 are special or reserved see gsmtty_open */ #define NUM_DLCI 64 From 445df7ff3fd1a0a914a434881c116b856e236d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 19 May 2015 21:56:29 +0200 Subject: [PATCH 108/148] serial: mctrl-gpio: drop usages of IS_ERR_OR_NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function mctrl_gpio_init returns failure if the assignment to any member of the gpio array results in an error pointer. So there is no need to check for such error values in the other functions. Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_mctrl_gpio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 0ec756c62bcf..ef8ea1f184c7 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -49,8 +49,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) unsigned int count = 0; for (i = 0; i < UART_GPIO_MAX; i++) - if (!IS_ERR_OR_NULL(gpios->gpio[i]) && - mctrl_gpios_desc[i].dir_out) { + if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) { desc_array[count] = gpios->gpio[i]; value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl); count++; @@ -118,7 +117,7 @@ void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) enum mctrl_gpio_idx i; for (i = 0; i < UART_GPIO_MAX; i++) - if (!IS_ERR_OR_NULL(gpios->gpio[i])) + if (gpios->gpio[i]) devm_gpiod_put(dev, gpios->gpio[i]); devm_kfree(dev, gpios); } From abab381feea7e61a0b5125504aa955cf339676ca Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 22 May 2015 21:59:36 +0200 Subject: [PATCH 109/148] tty: remove platform_sysrq_reset_seq The platform_sysrq_reset_seq code was intended as a way for an embedded platform to provide its own sysrq sequence at compile time. After over two years, nobody has started using it in an upstream kernel, and the platforms that were interested in it have moved on to devicetree, which can be used to configure the sequence without requiring kernel changes. The method is also incompatible with the way that most architectures build support for multiple platforms into a single kernel. Now the code is producing warnings when built with gcc-5.1: drivers/tty/sysrq.c: In function 'sysrq_init': drivers/tty/sysrq.c:959:33: warning: array subscript is above array bounds [-Warray-bounds] key = platform_sysrq_reset_seq[i]; We could fix this, but it seems unlikely that it will ever be used, so let's just remove the code instead. We still have the option to pass the sequence either in DT, using the kernel command line, or using the /sys/module/sysrq/parameters/reset_seq file. Signed-off-by: Arnd Bergmann Fixes: 154b7a489a ("Input: sysrq - allow specifying alternate reset sequence") ---- v2: moved sysrq_reset_downtime_ms variable to avoid introducing a compile warning when CONFIG_INPUT is disabled Signed-off-by: Greg Kroah-Hartman --- drivers/tty/sysrq.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 843f2cdc280b..9ffdfcf2ec6e 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -55,9 +55,6 @@ static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; static bool __read_mostly sysrq_always_enabled; -unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED }; -int sysrq_reset_downtime_ms __weak; - static bool sysrq_on(void) { return sysrq_enabled || sysrq_always_enabled; @@ -569,6 +566,7 @@ void handle_sysrq(int key) EXPORT_SYMBOL(handle_sysrq); #ifdef CONFIG_INPUT +static int sysrq_reset_downtime_ms; /* Simple translation table for the SysRq keys */ static const unsigned char sysrq_xlate[KEY_CNT] = @@ -949,23 +947,8 @@ static bool sysrq_handler_registered; static inline void sysrq_register_handler(void) { - unsigned short key; int error; - int i; - /* First check if a __weak interface was instantiated. */ - for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) { - key = platform_sysrq_reset_seq[i]; - if (key == KEY_RESERVED || key > KEY_MAX) - break; - - sysrq_reset_seq[sysrq_reset_seq_len++] = key; - } - - /* - * DT configuration takes precedence over anything that would - * have been defined via the __weak interface. - */ sysrq_of_get_keyreset_config(); error = input_register_handler(&sysrq_handler); From 357d56151976a78d90dc3dfac01777de0ef05212 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 19 May 2015 22:26:04 +0200 Subject: [PATCH 110/148] serial: samsung: only use earlycon for console A configuration that enables earlycon but not the core console code causes a link error: drivers/built-in.o: In function `setup_earlycon': drivers/tty/serial/earlycon.c:70: undefined reference to `uart_parse_earlycon' That error can be triggered by the newly added samsung earlycon support, which is missing a 'select' statement. As suggested by Peter Hurley, solves the problem by moving the 'select SERIAL_EARLYCON' statement to the samsung console driver option, as it is done by all other console drivers. Signed-off-by: Arnd Bergmann Fixes: b94ba0328d3b3 ("serial: samsung: Add support for early console") Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 59a8ad6e7c3e..f5a88e75dda6 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -241,7 +241,6 @@ config SERIAL_SAMSUNG tristate "Samsung SoC serial support" depends on PLAT_SAMSUNG || ARCH_EXYNOS select SERIAL_CORE - select SERIAL_EARLYCON help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, providing /dev/ttySAC0, 1 and 2 (note, some machines may not @@ -277,6 +276,7 @@ config SERIAL_SAMSUNG_CONSOLE bool "Support for console on Samsung SoC serial port" depends on SERIAL_SAMSUNG=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Allow selection of the S3C24XX on-board serial ports for use as an virtual console. From 7798edeebc8101b1e8b1d7fb9eee3710f0877cbc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 19 May 2015 22:11:09 +0200 Subject: [PATCH 111/148] tty: serial/8250: remove console dependency for mediatek If the mediatek serial port driver is built-in, but serial console is disabled in Kconfig (e.g. when the serial driver itself is a loadable module), we get this build error: drivers/built-in.o: In function `early_mtk8250_setup': undefined reference to `early_serial8250_setup' To avoid that problem, this patch encloses the early_mtk8250_setup function in #ifdef CONFIG_SERIAL_8250_CONSOLE, the same symbol that guards the early_serial8250_setup function. Signed-off-by: Arnd Bergmann Acked-by: Eddie Huang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 4488c2bdb7ba..78883ca64ddd 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -305,6 +305,7 @@ static struct platform_driver mtk8250_platform_driver = { }; module_platform_driver(mtk8250_platform_driver); +#ifdef CONFIG_SERIAL_8250_CONSOLE static int __init early_mtk8250_setup(struct earlycon_device *device, const char *options) { @@ -317,6 +318,7 @@ static int __init early_mtk8250_setup(struct earlycon_device *device, } OF_EARLYCON_DECLARE(mtk8250, "mediatek,mt6577-uart", early_mtk8250_setup); +#endif MODULE_AUTHOR("Matthias Brugger"); MODULE_LICENSE("GPL"); From 49bb3c862cc418e3bc9464654e4ccb8ebb5cc2ec Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 May 2015 17:26:14 +0100 Subject: [PATCH 112/148] drivers: PL011: avoid potential unregister_driver call Although we care about not unregistering the driver if there are still ports connected during the .remove callback, we do miss this check in the pl011_probe function. So if the current port allocation fails, but there are other ports already registered, we will kill those. So factor out the port removal into a separate function and use that in the probe function, too. Signed-off-by: Andre Przywara Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 36 ++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index f5bd8426fd75..b4de23cad915 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2135,6 +2135,23 @@ static int pl011_probe_dt_alias(int index, struct device *dev) return ret; } +/* unregisters the driver also if no more ports are left */ +static void pl011_unregister_port(struct uart_amba_port *uap) +{ + int i; + bool busy = false; + + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) { + if (amba_ports[i] == uap) + amba_ports[i] = NULL; + else if (amba_ports[i]) + busy = true; + } + pl011_dma_remove(uap); + if (!busy) + uart_unregister_driver(&amba_reg); +} + static int pl011_probe(struct amba_device *dev, const struct amba_id *id) { struct uart_amba_port *uap; @@ -2200,10 +2217,8 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) } ret = uart_add_one_port(&amba_reg, &uap->port); - if (ret) { - amba_ports[i] = NULL; - uart_unregister_driver(&amba_reg); - } + if (ret) + pl011_unregister_port(uap); return ret; } @@ -2211,20 +2226,9 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) static int pl011_remove(struct amba_device *dev) { struct uart_amba_port *uap = amba_get_drvdata(dev); - bool busy = false; - int i; uart_remove_one_port(&amba_reg, &uap->port); - - for (i = 0; i < ARRAY_SIZE(amba_ports); i++) - if (amba_ports[i] == uap) - amba_ports[i] = NULL; - else if (amba_ports[i]) - busy = true; - - pl011_dma_remove(uap); - if (!busy) - uart_unregister_driver(&amba_reg); + pl011_unregister_port(uap); return 0; } From 867b8e8e89f2f72882d267be3e035978fdd45a4a Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 May 2015 17:26:15 +0100 Subject: [PATCH 113/148] drivers: PL011: refactor pl011_startup() Split the pl011_startup() function into smaller chunks to allow easier reuse later when adding SBSA support. Signed-off-by: Andre Przywara Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 48 +++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index b4de23cad915..209aeb68abad 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1556,6 +1556,32 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h) } } +static int pl011_allocate_irq(struct uart_amba_port *uap) +{ + writew(uap->im, uap->port.membase + UART011_IMSC); + + return request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); +} + +/* + * Enable interrupts, only timeouts when using DMA + * if initial RX DMA job failed, start in interrupt mode + * as well. + */ +static void pl011_enable_interrupts(struct uart_amba_port *uap) +{ + spin_lock_irq(&uap->port.lock); + + /* Clear out any spuriously appearing RX interrupts */ + writew(UART011_RTIS | UART011_RXIS, + uap->port.membase + UART011_ICR); + uap->im = UART011_RTIM; + if (!pl011_dma_rx_running(uap)) + uap->im |= UART011_RXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + spin_unlock_irq(&uap->port.lock); +} + static int pl011_startup(struct uart_port *port) { struct uart_amba_port *uap = @@ -1567,12 +1593,7 @@ static int pl011_startup(struct uart_port *port) if (retval) goto clk_dis; - writew(uap->im, uap->port.membase + UART011_IMSC); - - /* - * Allocate the IRQ - */ - retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); + retval = pl011_allocate_irq(uap); if (retval) goto clk_dis; @@ -1595,20 +1616,7 @@ static int pl011_startup(struct uart_port *port) /* Startup DMA */ pl011_dma_startup(uap); - /* - * Finally, enable interrupts, only timeouts when using DMA - * if initial RX DMA job failed, start in interrupt mode - * as well. - */ - spin_lock_irq(&uap->port.lock); - /* Clear out any spuriously appearing RX interrupts */ - writew(UART011_RTIS | UART011_RXIS, - uap->port.membase + UART011_ICR); - uap->im = UART011_RTIM; - if (!pl011_dma_rx_running(uap)) - uap->im |= UART011_RXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); - spin_unlock_irq(&uap->port.lock); + pl011_enable_interrupts(uap); return 0; From 95166a3fd5dc1f06321abb28b5713afd2bcc87c4 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 May 2015 17:26:16 +0100 Subject: [PATCH 114/148] drivers: PL011: refactor pl011_shutdown() Split the pl011_shutdown() function into smaller chunks to allow easier reuse later when adding SBSA support. Signed-off-by: Andre Przywara Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 57 ++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 209aeb68abad..a43039daa6af 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1635,34 +1635,15 @@ static void pl011_shutdown_channel(struct uart_amba_port *uap, writew(val, uap->port.membase + lcrh); } -static void pl011_shutdown(struct uart_port *port) +/* + * disable the port. It should not disable RTS and DTR. + * Also RTS and DTR state should be preserved to restore + * it during startup(). + */ +static void pl011_disable_uart(struct uart_amba_port *uap) { - struct uart_amba_port *uap = - container_of(port, struct uart_amba_port, port); unsigned int cr; - /* - * disable all interrupts - */ - spin_lock_irq(&uap->port.lock); - uap->im = 0; - writew(uap->im, uap->port.membase + UART011_IMSC); - writew(0xffff, uap->port.membase + UART011_ICR); - spin_unlock_irq(&uap->port.lock); - - pl011_dma_shutdown(uap); - - /* - * Free the interrupt - */ - free_irq(uap->port.irq, uap); - - /* - * disable the port - * disable the port. It should not disable RTS and DTR. - * Also RTS and DTR state should be preserved to restore - * it during startup(). - */ uap->autorts = false; spin_lock_irq(&uap->port.lock); cr = readw(uap->port.membase + UART011_CR); @@ -1678,6 +1659,32 @@ static void pl011_shutdown(struct uart_port *port) pl011_shutdown_channel(uap, uap->lcrh_rx); if (uap->lcrh_rx != uap->lcrh_tx) pl011_shutdown_channel(uap, uap->lcrh_tx); +} + +static void pl011_disable_interrupts(struct uart_amba_port *uap) +{ + spin_lock_irq(&uap->port.lock); + + /* mask all interrupts and clear all pending ones */ + uap->im = 0; + writew(uap->im, uap->port.membase + UART011_IMSC); + writew(0xffff, uap->port.membase + UART011_ICR); + + spin_unlock_irq(&uap->port.lock); +} + +static void pl011_shutdown(struct uart_port *port) +{ + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); + + pl011_disable_interrupts(uap); + + pl011_dma_shutdown(uap); + + free_irq(uap->port.irq, uap); + + pl011_disable_uart(uap); /* * Shut down the clock producer From ef5a9358844a2e6f466facaca3247b64c72ef417 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 May 2015 17:26:17 +0100 Subject: [PATCH 115/148] drivers: PL011: refactor pl011_set_termios() Split the pl011_set_termios() function into smaller chunks to allow easier reuse later when adding SBSA support. Signed-off-by: Andre Przywara Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 60 ++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index a43039daa6af..686951a19716 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1705,6 +1705,38 @@ static void pl011_shutdown(struct uart_port *port) uap->port.ops->flush_buffer(port); } +static void +pl011_setup_status_masks(struct uart_port *port, struct ktermios *termios) +{ + port->read_status_mask = UART011_DR_OE | 255; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART011_DR_FE | UART011_DR_PE; + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + port->read_status_mask |= UART011_DR_BE; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART011_DR_FE | UART011_DR_PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= UART011_DR_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART011_DR_OE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= UART_DUMMY_DR_RX; +} + static void pl011_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) @@ -1769,33 +1801,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, */ uart_update_timeout(port, termios->c_cflag, baud); - port->read_status_mask = UART011_DR_OE | 255; - if (termios->c_iflag & INPCK) - port->read_status_mask |= UART011_DR_FE | UART011_DR_PE; - if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) - port->read_status_mask |= UART011_DR_BE; - - /* - * Characters to ignore - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= UART011_DR_FE | UART011_DR_PE; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= UART011_DR_BE; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= UART011_DR_OE; - } - - /* - * Ignore all characters if CREAD is not set. - */ - if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= UART_DUMMY_DR_RX; + pl011_setup_status_masks(port, termios); if (UART_ENABLE_MS(port, termios->c_cflag)) pl011_enable_ms(port); From 3873e2d7f63a9da3098b28c405d13f15667a01b0 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 May 2015 17:26:18 +0100 Subject: [PATCH 116/148] drivers: PL011: refactor pl011_probe() Currently the pl011_probe() function is relying on some AMBA IDs and a device tree node to initialize the driver and a port. Both features are not necessarily required for the driver: - we lack AMBA IDs in the ARM SBSA generic UART and - we lack a DT node in ACPI systems. So lets refactor the function to ease later reuse. Signed-off-by: Andre Przywara Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 96 +++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 35 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 686951a19716..38d2245d7485 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2173,65 +2173,54 @@ static void pl011_unregister_port(struct uart_amba_port *uap) uart_unregister_driver(&amba_reg); } -static int pl011_probe(struct amba_device *dev, const struct amba_id *id) +static int pl011_find_free_port(void) { - struct uart_amba_port *uap; - struct vendor_data *vendor = id->data; - void __iomem *base; - int i, ret; + int i; for (i = 0; i < ARRAY_SIZE(amba_ports); i++) if (amba_ports[i] == NULL) - break; + return i; - if (i == ARRAY_SIZE(amba_ports)) - return -EBUSY; + return -EBUSY; +} - uap = devm_kzalloc(&dev->dev, sizeof(struct uart_amba_port), - GFP_KERNEL); - if (uap == NULL) - return -ENOMEM; +static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap, + struct resource *mmiobase, int index) +{ + void __iomem *base; - i = pl011_probe_dt_alias(i, &dev->dev); - - base = devm_ioremap(&dev->dev, dev->res.start, - resource_size(&dev->res)); + base = devm_ioremap_resource(dev, mmiobase); if (!base) return -ENOMEM; - uap->clk = devm_clk_get(&dev->dev, NULL); - if (IS_ERR(uap->clk)) - return PTR_ERR(uap->clk); + index = pl011_probe_dt_alias(index, dev); - uap->vendor = vendor; - uap->lcrh_rx = vendor->lcrh_rx; - uap->lcrh_tx = vendor->lcrh_tx; uap->old_cr = 0; - uap->fifosize = vendor->get_fifosize(dev); - uap->port.dev = &dev->dev; - uap->port.mapbase = dev->res.start; + uap->port.dev = dev; + uap->port.mapbase = mmiobase->start; uap->port.membase = base; uap->port.iotype = UPIO_MEM; - uap->port.irq = dev->irq[0]; uap->port.fifosize = uap->fifosize; - uap->port.ops = &amba_pl011_pops; uap->port.flags = UPF_BOOT_AUTOCONF; - uap->port.line = i; + uap->port.line = index; + + amba_ports[index] = uap; + + return 0; +} + +static int pl011_register_port(struct uart_amba_port *uap) +{ + int ret; /* Ensure interrupts from this UART are masked and cleared */ writew(0, uap->port.membase + UART011_IMSC); writew(0xffff, uap->port.membase + UART011_ICR); - snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev)); - - amba_ports[i] = uap; - - amba_set_drvdata(dev, uap); - if (!amba_reg.state) { ret = uart_register_driver(&amba_reg); if (ret < 0) { - dev_err(&dev->dev, + dev_err(uap->port.dev, "Failed to register AMBA-PL011 driver\n"); return ret; } @@ -2244,6 +2233,43 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) return ret; } +static int pl011_probe(struct amba_device *dev, const struct amba_id *id) +{ + struct uart_amba_port *uap; + struct vendor_data *vendor = id->data; + int portnr, ret; + + portnr = pl011_find_free_port(); + if (portnr < 0) + return portnr; + + uap = devm_kzalloc(&dev->dev, sizeof(struct uart_amba_port), + GFP_KERNEL); + if (!uap) + return -ENOMEM; + + uap->clk = devm_clk_get(&dev->dev, NULL); + if (IS_ERR(uap->clk)) + return PTR_ERR(uap->clk); + + uap->vendor = vendor; + uap->lcrh_rx = vendor->lcrh_rx; + uap->lcrh_tx = vendor->lcrh_tx; + uap->fifosize = vendor->get_fifosize(dev); + uap->port.irq = dev->irq[0]; + uap->port.ops = &amba_pl011_pops; + + snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev)); + + ret = pl011_setup_port(&dev->dev, uap, &dev->res, portnr); + if (ret) + return ret; + + amba_set_drvdata(dev, uap); + + return pl011_register_port(uap); +} + static int pl011_remove(struct amba_device *dev) { struct uart_amba_port *uap = amba_get_drvdata(dev); From 075167ed71b7bd9fdeaa5c2f69179471d17e3712 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 May 2015 17:26:19 +0100 Subject: [PATCH 117/148] drivers: PL011: replace UART_MIS reading with _RIS & _IMSC The PL011 register UART_MIS is actually a bitwise AND of the UART_RIS and the UART_MISC register. Since the SBSA UART does not include the _MIS register, use the two separate registers to get the same behaviour. Since we are inside the spinlock and we read the _IMSC register only once, there should be no race issue. Signed-off-by: Andre Przywara Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 38d2245d7485..b66636bf06a0 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1322,11 +1322,13 @@ static irqreturn_t pl011_int(int irq, void *dev_id) struct uart_amba_port *uap = dev_id; unsigned long flags; unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; + u16 imsc; int handled = 0; unsigned int dummy_read; spin_lock_irqsave(&uap->port.lock, flags); - status = readw(uap->port.membase + UART011_MIS); + imsc = readw(uap->port.membase + UART011_IMSC); + status = readw(uap->port.membase + UART011_RIS) & imsc; if (status) { do { if (uap->vendor->cts_event_workaround) { @@ -1361,7 +1363,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id) if (pass_counter-- == 0) break; - status = readw(uap->port.membase + UART011_MIS); + status = readw(uap->port.membase + UART011_RIS) & imsc; } while (status != 0); handled = 1; } From 9c4ef4b0301673c6fa48b5ad138b6ce94e34c841 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 May 2015 17:26:20 +0100 Subject: [PATCH 118/148] drivers: PL011: move cts_event workaround into separate function To avoid lines with more than 80 characters and to make the pl011_int() function more readable, move the workaround out into a separate function. Signed-off-by: Andre Przywara Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index b66636bf06a0..7ecf3b380132 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1317,6 +1317,25 @@ static void pl011_modem_status(struct uart_amba_port *uap) wake_up_interruptible(&uap->port.state->port.delta_msr_wait); } +static void check_apply_cts_event_workaround(struct uart_amba_port *uap) +{ + unsigned int dummy_read; + + if (!uap->vendor->cts_event_workaround) + return; + + /* workaround to make sure that all bits are unlocked.. */ + writew(0x00, uap->port.membase + UART011_ICR); + + /* + * WA: introduce 26ns(1 uart clk) delay before W1C; + * single apb access will incur 2 pclk(133.12Mhz) delay, + * so add 2 dummy reads + */ + dummy_read = readw(uap->port.membase + UART011_ICR); + dummy_read = readw(uap->port.membase + UART011_ICR); +} + static irqreturn_t pl011_int(int irq, void *dev_id) { struct uart_amba_port *uap = dev_id; @@ -1324,25 +1343,13 @@ static irqreturn_t pl011_int(int irq, void *dev_id) unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; u16 imsc; int handled = 0; - unsigned int dummy_read; spin_lock_irqsave(&uap->port.lock, flags); imsc = readw(uap->port.membase + UART011_IMSC); status = readw(uap->port.membase + UART011_RIS) & imsc; if (status) { do { - if (uap->vendor->cts_event_workaround) { - /* workaround to make sure that all bits are unlocked.. */ - writew(0x00, uap->port.membase + UART011_ICR); - - /* - * WA: introduce 26ns(1 uart clk) delay before W1C; - * single apb access will incur 2 pclk(133.12Mhz) delay, - * so add 2 dummy reads - */ - dummy_read = readw(uap->port.membase + UART011_ICR); - dummy_read = readw(uap->port.membase + UART011_ICR); - } + check_apply_cts_event_workaround(uap); writew(status & ~(UART011_TXIS|UART011_RTIS| UART011_RXIS), From 71eec4836b834b992e0cefeefc8b85efe4cb185b Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 May 2015 17:26:21 +0100 Subject: [PATCH 119/148] drivers: PL011: allow avoiding UART enabling/disabling The SBSA UART should not be enabled or disabled (it is always on), and consequently the spec lacks the UART_CR register. Add a vendor specific property to skip disabling or enabling of the UART. This will be used later by the SBSA UART support. Signed-off-by: Andre Przywara Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 7ecf3b380132..3f6b9150e575 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -78,6 +78,7 @@ struct vendor_data { bool oversampling; bool dma_threshold; bool cts_event_workaround; + bool always_enabled; unsigned int (*get_fifosize)(struct amba_device *dev); }; @@ -94,6 +95,7 @@ static struct vendor_data vendor_arm = { .oversampling = false, .dma_threshold = false, .cts_event_workaround = false, + .always_enabled = false, .get_fifosize = get_fifosize_arm, }; @@ -109,6 +111,7 @@ static struct vendor_data vendor_st = { .oversampling = true, .dma_threshold = true, .cts_event_workaround = true, + .always_enabled = false, .get_fifosize = get_fifosize_st, }; @@ -1958,7 +1961,7 @@ static void pl011_console_write(struct console *co, const char *s, unsigned int count) { struct uart_amba_port *uap = amba_ports[co->index]; - unsigned int status, old_cr, new_cr; + unsigned int status, old_cr = 0, new_cr; unsigned long flags; int locked = 1; @@ -1975,10 +1978,12 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) /* * First save the CR then disable the interrupts */ - old_cr = readw(uap->port.membase + UART011_CR); - new_cr = old_cr & ~UART011_CR_CTSEN; - new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE; - writew(new_cr, uap->port.membase + UART011_CR); + if (!uap->vendor->always_enabled) { + old_cr = readw(uap->port.membase + UART011_CR); + new_cr = old_cr & ~UART011_CR_CTSEN; + new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE; + writew(new_cr, uap->port.membase + UART011_CR); + } uart_console_write(&uap->port, s, count, pl011_console_putchar); @@ -1989,7 +1994,8 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) do { status = readw(uap->port.membase + UART01x_FR); } while (status & UART01x_FR_BUSY); - writew(old_cr, uap->port.membase + UART011_CR); + if (!uap->vendor->always_enabled) + writew(old_cr, uap->port.membase + UART011_CR); if (locked) spin_unlock(&uap->port.lock); From cefc2d1d66f0e0f9fbe72558c27ceeb076073f38 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 May 2015 17:26:22 +0100 Subject: [PATCH 120/148] drivers: PL011: allow to supply fixed option string The SBSA UART has a fixed baud rate and flow control setting, which cannot be changed or queried by software. Add a vendor specific property to always return fixed values when trying to read the console options. Signed-off-by: Andre Przywara Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 3f6b9150e575..6558400f766f 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -79,6 +79,7 @@ struct vendor_data { bool dma_threshold; bool cts_event_workaround; bool always_enabled; + bool fixed_options; unsigned int (*get_fifosize)(struct amba_device *dev); }; @@ -96,6 +97,7 @@ static struct vendor_data vendor_arm = { .dma_threshold = false, .cts_event_workaround = false, .always_enabled = false, + .fixed_options = false, .get_fifosize = get_fifosize_arm, }; @@ -112,6 +114,7 @@ static struct vendor_data vendor_st = { .dma_threshold = true, .cts_event_workaround = true, .always_enabled = false, + .fixed_options = false, .get_fifosize = get_fifosize_st, }; @@ -160,6 +163,7 @@ struct uart_amba_port { unsigned int lcrh_rx; /* vendor-specific */ unsigned int old_cr; /* state during shutdown */ bool autorts; + unsigned int fixed_baud; /* vendor-set fixed baud rate */ char type[12]; #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ @@ -2076,10 +2080,15 @@ static int __init pl011_console_setup(struct console *co, char *options) uap->port.uartclk = clk_get_rate(uap->clk); - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - pl011_console_get_options(uap, &baud, &parity, &bits); + if (uap->vendor->fixed_options) { + baud = uap->fixed_baud; + } else { + if (options) + uart_parse_options(options, + &baud, &parity, &bits, &flow); + else + pl011_console_get_options(uap, &baud, &parity, &bits); + } return uart_set_options(&uap->port, co, baud, parity, bits, flow); } From 0dd1e247fd39aed20fd2baacc62ca44d82534798 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 May 2015 17:26:23 +0100 Subject: [PATCH 121/148] drivers: PL011: add support for the ARM SBSA generic UART The ARM Server Base System Architecture[1] document describes a generic UART which is a subset of the PL011 UART. It lacks DMA support, baud rate control and modem status line control, among other things. The idea is to move the UART initialization and setup into the firmware (which does this job today already) and let the kernel just use the UART for sending and receiving characters. We use the recent refactoring to build a new struct uart_ops variable which points to some new functions avoiding access to the missing registers. We reuse as much existing PL011 code as possible. In contrast to the PL011 the SBSA UART does not define any AMBA or PrimeCell relations, so we go with a pretty generic probe function which only uses platform device functions. A DT binding is provided with this patch, ACPI support is added in a separate one. Signed-off-by: Andre Przywara Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/arm_sbsa_uart.txt | 10 + drivers/tty/serial/amba-pl011.c | 171 ++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt diff --git a/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt b/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt new file mode 100644 index 000000000000..4163e7eb7763 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt @@ -0,0 +1,10 @@ +* ARM SBSA defined generic UART +This UART uses a subset of the PL011 registers and consequently lives +in the PL011 driver. It's baudrate and other communication parameters +cannot be adjusted at runtime, so it lacks a clock specifier here. + +Required properties: +- compatible: must be "arm,sbsa-uart" +- reg: exactly one register range +- interrupts: exactly one interrupt specifier +- current-speed: the (fixed) baud rate set by the firmware diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 6558400f766f..c0850f3cc439 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -101,6 +101,14 @@ static struct vendor_data vendor_arm = { .get_fifosize = get_fifosize_arm, }; +static struct vendor_data vendor_sbsa = { + .oversampling = false, + .dma_threshold = false, + .cts_event_workaround = false, + .always_enabled = true, + .fixed_options = true, +}; + static unsigned int get_fifosize_st(struct amba_device *dev) { return 64; @@ -1641,6 +1649,28 @@ static int pl011_startup(struct uart_port *port) return retval; } +static int sbsa_uart_startup(struct uart_port *port) +{ + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); + int retval; + + retval = pl011_hwinit(port); + if (retval) + return retval; + + retval = pl011_allocate_irq(uap); + if (retval) + return retval; + + /* The SBSA UART does not support any modem status lines. */ + uap->old_status = 0; + + pl011_enable_interrupts(uap); + + return 0; +} + static void pl011_shutdown_channel(struct uart_amba_port *uap, unsigned int lcrh) { @@ -1721,6 +1751,19 @@ static void pl011_shutdown(struct uart_port *port) uap->port.ops->flush_buffer(port); } +static void sbsa_uart_shutdown(struct uart_port *port) +{ + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); + + pl011_disable_interrupts(uap); + + free_irq(uap->port.irq, uap); + + if (uap->port.ops->flush_buffer) + uap->port.ops->flush_buffer(port); +} + static void pl011_setup_status_masks(struct uart_port *port, struct ktermios *termios) { @@ -1872,6 +1915,27 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, spin_unlock_irqrestore(&port->lock, flags); } +static void +sbsa_uart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); + unsigned long flags; + + tty_termios_encode_baud_rate(termios, uap->fixed_baud, uap->fixed_baud); + + /* The SBSA UART only supports 8n1 without hardware flow control. */ + termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD); + termios->c_cflag &= ~(CMSPAR | CRTSCTS); + termios->c_cflag |= CS8 | CLOCAL; + + spin_lock_irqsave(&port->lock, flags); + uart_update_timeout(port, CS8, uap->fixed_baud); + pl011_setup_status_masks(port, termios); + spin_unlock_irqrestore(&port->lock, flags); +} + static const char *pl011_type(struct uart_port *port) { struct uart_amba_port *uap = @@ -1947,6 +2011,37 @@ static struct uart_ops amba_pl011_pops = { #endif }; +static void sbsa_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int sbsa_uart_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static const struct uart_ops sbsa_uart_pops = { + .tx_empty = pl011_tx_empty, + .set_mctrl = sbsa_uart_set_mctrl, + .get_mctrl = sbsa_uart_get_mctrl, + .stop_tx = pl011_stop_tx, + .start_tx = pl011_start_tx, + .stop_rx = pl011_stop_rx, + .startup = sbsa_uart_startup, + .shutdown = sbsa_uart_shutdown, + .set_termios = sbsa_uart_set_termios, + .type = pl011_type, + .release_port = pl011_release_port, + .request_port = pl011_request_port, + .config_port = pl011_config_port, + .verify_port = pl011_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_init = pl011_hwinit, + .poll_get_char = pl011_get_poll_char, + .poll_put_char = pl011_put_poll_char, +#endif +}; + static struct uart_amba_port *amba_ports[UART_NR]; #ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE @@ -2327,6 +2422,79 @@ static int pl011_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pl011_dev_pm_ops, pl011_suspend, pl011_resume); +static int sbsa_uart_probe(struct platform_device *pdev) +{ + struct uart_amba_port *uap; + struct resource *r; + int portnr, ret; + int baudrate; + + /* + * Check the mandatory baud rate parameter in the DT node early + * so that we can easily exit with the error. + */ + if (pdev->dev.of_node) { + struct device_node *np = pdev->dev.of_node; + + ret = of_property_read_u32(np, "current-speed", &baudrate); + if (ret) + return ret; + } else { + baudrate = 115200; + } + + portnr = pl011_find_free_port(); + if (portnr < 0) + return portnr; + + uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port), + GFP_KERNEL); + if (!uap) + return -ENOMEM; + + uap->vendor = &vendor_sbsa; + uap->fifosize = 32; + uap->port.irq = platform_get_irq(pdev, 0); + uap->port.ops = &sbsa_uart_pops; + uap->fixed_baud = baudrate; + + snprintf(uap->type, sizeof(uap->type), "SBSA"); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + ret = pl011_setup_port(&pdev->dev, uap, r, portnr); + if (ret) + return ret; + + platform_set_drvdata(pdev, uap); + + return pl011_register_port(uap); +} + +static int sbsa_uart_remove(struct platform_device *pdev) +{ + struct uart_amba_port *uap = platform_get_drvdata(pdev); + + uart_remove_one_port(&amba_reg, &uap->port); + pl011_unregister_port(uap); + return 0; +} + +static const struct of_device_id sbsa_uart_of_match[] = { + { .compatible = "arm,sbsa-uart", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sbsa_uart_of_match); + +static struct platform_driver arm_sbsa_uart_platform_driver = { + .probe = sbsa_uart_probe, + .remove = sbsa_uart_remove, + .driver = { + .name = "sbsa-uart", + .of_match_table = of_match_ptr(sbsa_uart_of_match), + }, +}; + static struct amba_id pl011_ids[] = { { .id = 0x00041011, @@ -2357,11 +2525,14 @@ static int __init pl011_init(void) { printk(KERN_INFO "Serial: AMBA PL011 UART driver\n"); + if (platform_driver_register(&arm_sbsa_uart_platform_driver)) + pr_warn("could not register SBSA UART platform driver\n"); return amba_driver_register(&pl011_driver); } static void __exit pl011_exit(void) { + platform_driver_unregister(&arm_sbsa_uart_platform_driver); amba_driver_unregister(&pl011_driver); } From 3db9ab0b6d8a293435a78c048f877099e040f72c Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Thu, 21 May 2015 17:26:24 +0100 Subject: [PATCH 122/148] drivers: PL011: add ACPI probing for SBSA UART Add the necessary driver boilerplate to let the driver be used when the respective ACPI table is discovered by the ACPI subsystem. [Andre: change table name, add MODULE_DEVICE_TABLE entry and improve commit message] Reviewed-by: Hanjun Guo Signed-off-by: Graeme Gregory Tested-by: Mark Langsdorf Tested-by: Naresh Bhat Signed-off-by: Andre Przywara Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index c0850f3cc439..50cf5b10ceed 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -58,6 +58,7 @@ #include #include #include +#include #define UART_NR 14 @@ -2486,12 +2487,19 @@ static const struct of_device_id sbsa_uart_of_match[] = { }; MODULE_DEVICE_TABLE(of, sbsa_uart_of_match); +static const struct acpi_device_id sbsa_uart_acpi_match[] = { + { "ARMH0011", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, sbsa_uart_acpi_match); + static struct platform_driver arm_sbsa_uart_platform_driver = { .probe = sbsa_uart_probe, .remove = sbsa_uart_remove, .driver = { .name = "sbsa-uart", .of_match_table = of_match_ptr(sbsa_uart_of_match), + .acpi_match_table = ACPI_PTR(sbsa_uart_acpi_match), }, }; From a5edce42184844239bc209c727435654cd0b5f2d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 21 May 2015 09:58:03 +0200 Subject: [PATCH 123/148] fbcon: Avoid deleting a timer in IRQ context Commit 27a4c827c34a ("fbcon: use the cursor blink interval provided by vt") unconditionally removes the cursor blink timer. Unfortunately that wreaks havoc under some circumstances. An easily reproducible way is to use both the framebuffer console and a debug serial port as the console output for kernel messages (e.g. "console=ttyS0 console=tty1" on the kernel command-line. Upon boot this triggers a warning from within the del_timer_sync() function because it is called from IRQ context: [ 5.070096] ------------[ cut here ]------------ [ 5.070110] WARNING: CPU: 0 PID: 0 at ../kernel/time/timer.c:1098 del_timer_sync+0x4c/0x54() [ 5.070115] Modules linked in: [ 5.070120] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.1.0-rc4-next-20150519 #1 [ 5.070123] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) [ 5.070142] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 5.070156] [] (show_stack) from [] (dump_stack+0x70/0xbc) [ 5.070164] [] (dump_stack) from [] (warn_slowpath_common+0x74/0xb0) [ 5.070169] [] (warn_slowpath_common) from [] (warn_slowpath_null+0x1c/0x24) [ 5.070174] [] (warn_slowpath_null) from [] (del_timer_sync+0x4c/0x54) [ 5.070183] [] (del_timer_sync) from [] (fbcon_del_cursor_timer+0x2c/0x40) [ 5.070190] [] (fbcon_del_cursor_timer) from [] (fbcon_cursor+0x9c/0x180) [ 5.070198] [] (fbcon_cursor) from [] (hide_cursor+0x30/0x98) [ 5.070204] [] (hide_cursor) from [] (vt_console_print+0x2a8/0x340) [ 5.070212] [] (vt_console_print) from [] (call_console_drivers.constprop.23+0xc8/0xec) [ 5.070218] [] (call_console_drivers.constprop.23) from [] (console_unlock+0x498/0x4f0) [ 5.070223] [] (console_unlock) from [] (vprintk_emit+0x1f0/0x508) [ 5.070228] [] (vprintk_emit) from [] (vprintk_default+0x24/0x2c) [ 5.070234] [] (vprintk_default) from [] (printk+0x70/0x88) After which the system starts spewing all kinds of weird and seemingly unrelated error messages. This commit fixes this by restoring the condition under which the call to fbcon_del_cursor_timer() happens. Reported-by: Daniel Stone Reported-by: Kevin Hilman Tested-by: Kevin Hilman Tested-by: Scot Doyle Signed-off-by: Thierry Reding Tested-by: Andrew Vagin Tested-by: Tomeu Vizoso Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/fbcon.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 05b1d1a71ef9..658c34bb9076 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -1310,8 +1310,9 @@ static void fbcon_cursor(struct vc_data *vc, int mode) return; ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); - fbcon_del_cursor_timer(info); - if (!(vc->vc_cursor_type & 0x10)) + if (vc->vc_cursor_type & 0x10) + fbcon_del_cursor_timer(info); + else fbcon_add_cursor_timer(info); ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; From 34d2e4584ae594eff29d1595d47d7d044e57f834 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 26 May 2015 13:28:48 +0900 Subject: [PATCH 124/148] serial: 8250: include from serial_8250.h The header file, include/linux/serial_8250.h, contains references to UART_LSR_BRK_ERROR_BITS and UART_MSR_ANY_DELTA that are defined in . Signed-off-by: Masahiro Yamada Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_8250.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index f0c68d88b6f4..ba82c07feb95 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -12,6 +12,7 @@ #define _LINUX_SERIAL_8250_H #include +#include #include /* From 2c837a8a8f9f182b0286f5644ed50374b8434867 Mon Sep 17 00:00:00 2001 From: Rama Kiran Kumar Indrakanti Date: Mon, 25 May 2015 11:51:09 +0530 Subject: [PATCH 125/148] sc16is7xx: spi interface is added spi interface for sc16is7xx is added along with Kconfig flag to enable spi or i2c, thus in a instance we can have either spi or i2c or both, in sync to the hw. Signed-off-by: Rama Kiran Kumar Indrakanti Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 43 +++++++++++--- drivers/tty/serial/Makefile | 2 +- drivers/tty/serial/sc16is7xx.c | 105 ++++++++++++++++++++++++++++++++- 3 files changed, 140 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index f5a88e75dda6..da45877e79fb 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1179,15 +1179,42 @@ config SERIAL_SCCNXP_CONSOLE help Support for console on SCCNXP serial ports. +config SERIAL_SC16IS7XX_CORE + tristate + config SERIAL_SC16IS7XX - tristate "SC16IS7xx serial support" - depends on I2C - select SERIAL_CORE - select REGMAP_I2C if I2C - help - This selects support for SC16IS7xx serial ports. - Supported ICs are SC16IS740, SC16IS741, SC16IS750, SC16IS752, - SC16IS760 and SC16IS762. + tristate "SC16IS7xx serial support" + select SERIAL_CORE + depends on I2C || SPI_MASTER + help + This selects support for SC16IS7xx serial ports. + Supported ICs are SC16IS740, SC16IS741, SC16IS750, SC16IS752, + SC16IS760 and SC16IS762. Select supported buses using options below. + +config SERIAL_SC16IS7XX_I2C + bool "SC16IS7xx for I2C interface" + depends on SERIAL_SC16IS7XX + depends on I2C + select SERIAL_SC16IS7XX_CORE if SERIAL_SC16IS7XX + select REGMAP_I2C if I2C + default y + help + Enable SC16IS7xx driver on I2C bus, + If required say y, and say n to i2c if not required, + Enabled by default to support oldconfig. + You must select at least one bus for the driver to be built. + +config SERIAL_SC16IS7XX_SPI + bool "SC16IS7xx for spi interface" + depends on SERIAL_SC16IS7XX + depends on SPI_MASTER + select SERIAL_SC16IS7XX_CORE if SERIAL_SC16IS7XX + select REGMAP_SPI if SPI_MASTER + help + Enable SC16IS7xx driver on SPI bus, + If required say y, and say n to spi if not required, + This is additional support to exsisting driver. + You must select at least one bus for the driver to be built. config SERIAL_BFIN_SPORT tristate "Blackfin SPORT emulate UART" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 432cb6465227..d296cee2e132 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -53,7 +53,7 @@ obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o obj-$(CONFIG_SERIAL_ETRAXFS) += etraxfs-uart.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o -obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o +obj-$(CONFIG_SERIAL_SC16IS7XX_CORE) += sc16is7xx.o obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 468354ef7baa..d1c046da7853 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #define SC16IS7XX_NAME "sc16is7xx" @@ -1204,6 +1205,73 @@ static struct regmap_config regcfg = { .precious_reg = sc16is7xx_regmap_precious, }; +#ifdef CONFIG_SERIAL_SC16IS7XX_SPI +static int sc16is7xx_spi_probe(struct spi_device *spi) +{ + struct sc16is7xx_devtype *devtype; + unsigned long flags = 0; + struct regmap *regmap; + int ret; + + /* Setup SPI bus */ + spi->bits_per_word = 8; + /* only supports mode 0 on SC16IS762 */ + spi->mode = spi->mode ? : SPI_MODE_0; + spi->max_speed_hz = spi->max_speed_hz ? : 15000000; + ret = spi_setup(spi); + if (ret) + return ret; + + if (spi->dev.of_node) { + const struct of_device_id *of_id = + of_match_device(sc16is7xx_dt_ids, &spi->dev); + + devtype = (struct sc16is7xx_devtype *)of_id->data; + } else { + const struct spi_device_id *id_entry = spi_get_device_id(spi); + + devtype = (struct sc16is7xx_devtype *)id_entry->driver_data; + flags = IRQF_TRIGGER_FALLING; + } + + regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) | + (devtype->nr_uart - 1); + regmap = devm_regmap_init_spi(spi, ®cfg); + + return sc16is7xx_probe(&spi->dev, devtype, regmap, spi->irq, flags); +} + +static int sc16is7xx_spi_remove(struct spi_device *spi) +{ + return sc16is7xx_remove(&spi->dev); +} + +static const struct spi_device_id sc16is7xx_spi_id_table[] = { + { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, + { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, + { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, + { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, + { } +}; + +MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table); + +static struct spi_driver sc16is7xx_spi_uart_driver = { + .driver = { + .name = SC16IS7XX_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sc16is7xx_dt_ids), + }, + .probe = sc16is7xx_spi_probe, + .remove = sc16is7xx_spi_remove, + .id_table = sc16is7xx_spi_id_table, +}; + +MODULE_ALIAS("spi:sc16is7xx"); +#endif + +#ifdef CONFIG_SERIAL_SC16IS7XX_I2C static int sc16is7xx_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1253,8 +1321,43 @@ static struct i2c_driver sc16is7xx_i2c_uart_driver = { .remove = sc16is7xx_i2c_remove, .id_table = sc16is7xx_i2c_id_table, }; -module_i2c_driver(sc16is7xx_i2c_uart_driver); + MODULE_ALIAS("i2c:sc16is7xx"); +#endif + +static int __init sc16is7xx_init(void) +{ + int ret = 0; +#ifdef CONFIG_SERIAL_SC16IS7XX_I2C + ret = i2c_add_driver(&sc16is7xx_i2c_uart_driver); + if (ret < 0) { + pr_err("failed to init sc16is7xx i2c --> %d\n", ret); + return ret; + } +#endif + +#ifdef CONFIG_SERIAL_SC16IS7XX_SPI + ret = spi_register_driver(&sc16is7xx_spi_uart_driver); + if (ret < 0) { + pr_err("failed to init sc16is7xx spi --> %d\n", ret); + return ret; + } +#endif + return ret; +} +module_init(sc16is7xx_init); + +static void __exit sc16is7xx_exit(void) +{ +#ifdef CONFIG_SERIAL_SC16IS7XX_I2C + i2c_del_driver(&sc16is7xx_i2c_uart_driver); +#endif + +#ifdef CONFIG_SERIAL_SC16IS7XX_SPI + spi_unregister_driver(&sc16is7xx_spi_uart_driver); +#endif +} +module_exit(sc16is7xx_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jon Ringle "); From 8f4a91202203624cbd8fe565cbdd5c876b652a5d Mon Sep 17 00:00:00 2001 From: Rama Kiran Kumar Indrakanti Date: Mon, 25 May 2015 11:51:10 +0530 Subject: [PATCH 126/148] sc16is7xx: spi interface documentation Updated the documentation for spi interface. Signed-off-by: Rama Kiran Kumar Indrakanti Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/nxp,sc16is7xx.txt | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Documentation/devicetree/bindings/serial/nxp,sc16is7xx.txt b/Documentation/devicetree/bindings/serial/nxp,sc16is7xx.txt index 246c795668dc..fbfe53635a3a 100644 --- a/Documentation/devicetree/bindings/serial/nxp,sc16is7xx.txt +++ b/Documentation/devicetree/bindings/serial/nxp,sc16is7xx.txt @@ -1,4 +1,5 @@ * NXP SC16IS7xx advanced Universal Asynchronous Receiver-Transmitter (UART) +* i2c as bus Required properties: - compatible: Should be one of the following: @@ -31,3 +32,39 @@ Example: gpio-controller; #gpio-cells = <2>; }; + +* spi as bus + +Required properties: +- compatible: Should be one of the following: + - "nxp,sc16is740" for NXP SC16IS740, + - "nxp,sc16is741" for NXP SC16IS741, + - "nxp,sc16is750" for NXP SC16IS750, + - "nxp,sc16is752" for NXP SC16IS752, + - "nxp,sc16is760" for NXP SC16IS760, + - "nxp,sc16is762" for NXP SC16IS762. +- reg: SPI chip select number. +- interrupt-parent: The phandle for the interrupt controller that + services interrupts for this IC. +- interrupts: Specifies the interrupt source of the parent interrupt + controller. The format of the interrupt specifier depends on the + parent interrupt controller. +- clocks: phandle to the IC source clock. + +Optional properties: +- gpio-controller: Marks the device node as a GPIO controller. +- #gpio-cells: Should be two. The first cell is the GPIO number and + the second cell is used to specify the GPIO polarity: + 0 = active high, + 1 = active low. + +Example: + sc16is750: sc16is750@0 { + compatible = "nxp,sc16is750"; + reg = <0>; + clocks = <&clk20m>; + interrupt-parent = <&gpio3>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + gpio-controller; + #gpio-cells = <2>; + }; From 5451bb29f725c099e7ea78262610c8c1b6caf008 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 29 May 2015 21:20:26 +0200 Subject: [PATCH 127/148] sc16is7xx: remove RS-485 delay RTS handling Users of RS-485 can request via ioctl that RTS signals should be activated selected number of milliseconds before the actual data transmission or delay reception certain number of milli- seconds after the transmission is finished. In sc16is7xx, however, RTS signalling is handled by the hardware and driver has no way of providing this feature. We still try to provide .delay_rts_before_send by delaying transmission but without actual effect on the RTS line. Note: this change will make the driver return -EINVAL when the feature is requested (.delay_rts_after_send is set). Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index d1c046da7853..64b1b9db1299 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -652,21 +652,6 @@ static void sc16is7xx_wq_proc(struct work_struct *ws) static void sc16is7xx_stop_tx(struct uart_port* port) { - struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - struct circ_buf *xmit = &one->port.state->xmit; - - /* handle rs485 */ - if (port->rs485.flags & SER_RS485_ENABLED) { - /* do nothing if current tx not yet completed */ - int lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); - if (!(lsr & SC16IS7XX_LSR_TEMT_BIT)) - return; - - if (uart_circ_empty(xmit) && - (port->rs485.delay_rts_after_send > 0)) - mdelay(port->rs485.delay_rts_after_send); - } - sc16is7xx_port_update(port, SC16IS7XX_IER_REG, SC16IS7XX_IER_THRI_BIT, 0); @@ -852,6 +837,14 @@ static int sc16is7xx_config_rs485(struct uart_port *port, dev_err(port->dev, "unsupported RTS signalling on_send:%d after_send:%d - exactly one of RS485 RTS flags should be set\n", rts_during_tx, rts_during_rx); + + /* + * RTS signal is handled by HW, it's timing can't be influenced. + * However, it's sometimes useful to delay TX even without RTS + * control therefore we try to handle .delay_rts_before_send. + */ + if (rs485->delay_rts_after_send) + return -EINVAL; } sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, mask, efcr); From 4117a60c8e4c8d5f9fc05578e359d09d0fdf9d07 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 29 May 2015 21:20:27 +0200 Subject: [PATCH 128/148] sc16is7xx: add missing compatible strings Without matching bus-specific strings driver will not be loaded automatically. Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 64b1b9db1299..0509be025399 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1241,6 +1241,8 @@ static int sc16is7xx_spi_remove(struct spi_device *spi) static const struct spi_device_id sc16is7xx_spi_id_table[] = { { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, @@ -1296,6 +1298,8 @@ static int sc16is7xx_i2c_remove(struct i2c_client *client) static const struct i2c_device_id sc16is7xx_i2c_id_table[] = { { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, From 4ae82e5d23961515796d76850499db3866c5e73b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 29 May 2015 21:20:28 +0200 Subject: [PATCH 129/148] sc16is7xx: use LSR_TEMT_BIT in .tx_empty() LSR_TEMT_BIT (LSR bit 6) provides us exactly the information we need to determine if transmission is finished - FIFO level and shift register empty. We can save ourselves reading FIFO level explicitly if we use this bit. Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 0509be025399..ea61a297b463 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -683,12 +683,11 @@ static void sc16is7xx_start_tx(struct uart_port *port) static unsigned int sc16is7xx_tx_empty(struct uart_port *port) { - unsigned int lvl, lsr; + unsigned int lsr; - lvl = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); - return ((lsr & SC16IS7XX_LSR_THRE_BIT) && !lvl) ? TIOCSER_TEMT : 0; + return (lsr & SC16IS7XX_LSR_TEMT_BIT) ? TIOCSER_TEMT : 0; } static unsigned int sc16is7xx_get_mctrl(struct uart_port *port) From 9e6f4ca3e567d5d9e4133d2da8bae8f476f85873 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 29 May 2015 21:20:29 +0200 Subject: [PATCH 130/148] sc16is7xx: use kthread_worker for tx_work and irq Convert workqueue usage to a real-time kworker. The problem with workqueues is that we cannot set real-time priorities on our work and asynchronous reconfiguration can be blocked by less important tasks. We need kthread for the interrupt anyway and because we will now be using single kthread for all TX-related operations we can get rid of the port mutex. Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 66 ++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index ea61a297b463..e6553d04a551 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -303,7 +303,7 @@ struct sc16is7xx_devtype { struct sc16is7xx_one { struct uart_port port; - struct work_struct tx_work; + struct kthread_work tx_work; struct work_struct md_work; }; @@ -311,15 +311,18 @@ struct sc16is7xx_port { struct uart_driver uart; struct sc16is7xx_devtype *devtype; struct regmap *regmap; - struct mutex mutex; struct clk *clk; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio; #endif unsigned char buf[SC16IS7XX_FIFO_SIZE]; + struct kthread_worker kworker; + struct task_struct *kworker_task; + struct kthread_work irq_work; struct sc16is7xx_one p[0]; }; +#define to_sc16is7xx_port(p,e) ((container_of((p), struct sc16is7xx_port, e))) #define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e))) static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg) @@ -616,9 +619,7 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) !!(msr & SC16IS7XX_MSR_CTS_BIT)); break; case SC16IS7XX_IIR_THRI_SRC: - mutex_lock(&s->mutex); sc16is7xx_handle_tx(port); - mutex_unlock(&s->mutex); break; default: dev_err_ratelimited(port->dev, @@ -629,25 +630,29 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) } while (1); } -static irqreturn_t sc16is7xx_ist(int irq, void *dev_id) +static void sc16is7xx_ist(struct kthread_work *ws) { - struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id; + struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work); int i; for (i = 0; i < s->uart.nr; ++i) sc16is7xx_port_irq(s, i); +} + +static irqreturn_t sc16is7xx_irq(int irq, void *dev_id) +{ + struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id; + + queue_kthread_work(&s->kworker, &s->irq_work); return IRQ_HANDLED; } -static void sc16is7xx_wq_proc(struct work_struct *ws) +static void sc16is7xx_tx_proc(struct kthread_work *ws) { struct sc16is7xx_one *one = to_sc16is7xx_one(ws, tx_work); - struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev); - mutex_lock(&s->mutex); sc16is7xx_handle_tx(&one->port); - mutex_unlock(&s->mutex); } static void sc16is7xx_stop_tx(struct uart_port* port) @@ -669,6 +674,7 @@ static void sc16is7xx_stop_rx(struct uart_port* port) static void sc16is7xx_start_tx(struct uart_port *port) { + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); /* handle rs485 */ @@ -677,8 +683,7 @@ static void sc16is7xx_start_tx(struct uart_port *port) mdelay(port->rs485.delay_rts_before_send); } - if (!work_pending(&one->tx_work)) - schedule_work(&one->tx_work); + queue_kthread_work(&s->kworker, &one->tx_work); } static unsigned int sc16is7xx_tx_empty(struct uart_port *port) @@ -909,6 +914,8 @@ static int sc16is7xx_startup(struct uart_port *port) static void sc16is7xx_shutdown(struct uart_port *port) { + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + /* Disable all interrupts */ sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0); /* Disable TX/RX */ @@ -919,6 +926,8 @@ static void sc16is7xx_shutdown(struct uart_port *port) SC16IS7XX_EFCR_TXDISABLE_BIT); sc16is7xx_power(port, 0); + + flush_kthread_worker(&s->kworker); } static const char *sc16is7xx_type(struct uart_port *port) @@ -1036,6 +1045,7 @@ static int sc16is7xx_probe(struct device *dev, struct sc16is7xx_devtype *devtype, struct regmap *regmap, int irq, unsigned long flags) { + struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 }; unsigned long freq, *pfreq = dev_get_platdata(dev); int i, ret; struct sc16is7xx_port *s; @@ -1077,6 +1087,16 @@ static int sc16is7xx_probe(struct device *dev, goto out_clk; } + init_kthread_worker(&s->kworker); + init_kthread_work(&s->irq_work, sc16is7xx_ist); + s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker, + "sc16is7xx"); + if (IS_ERR(s->kworker_task)) { + ret = PTR_ERR(s->kworker_task); + goto out_uart; + } + sched_setscheduler(s->kworker_task, SCHED_FIFO, &sched_param); + #ifdef CONFIG_GPIOLIB if (devtype->nr_gpio) { /* Setup GPIO cotroller */ @@ -1092,12 +1112,10 @@ static int sc16is7xx_probe(struct device *dev, s->gpio.can_sleep = 1; ret = gpiochip_add(&s->gpio); if (ret) - goto out_uart; + goto out_thread; } #endif - mutex_init(&s->mutex); - for (i = 0; i < devtype->nr_uart; ++i) { /* Initialize port data */ s->p[i].port.line = i; @@ -1117,7 +1135,7 @@ static int sc16is7xx_probe(struct device *dev, SC16IS7XX_EFCR_RXDISABLE_BIT | SC16IS7XX_EFCR_TXDISABLE_BIT); /* Initialize queue for start TX */ - INIT_WORK(&s->p[i].tx_work, sc16is7xx_wq_proc); + init_kthread_work(&s->p[i].tx_work, sc16is7xx_tx_proc); /* Initialize queue for changing mode */ INIT_WORK(&s->p[i].md_work, sc16is7xx_md_proc); /* Register port */ @@ -1127,22 +1145,23 @@ static int sc16is7xx_probe(struct device *dev, } /* Setup interrupt */ - ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_ist, - IRQF_ONESHOT | flags, dev_name(dev), s); + ret = devm_request_irq(dev, irq, sc16is7xx_irq, + IRQF_ONESHOT | flags, dev_name(dev), s); if (!ret) return 0; for (i = 0; i < s->uart.nr; i++) uart_remove_one_port(&s->uart, &s->p[i].port); - mutex_destroy(&s->mutex); - #ifdef CONFIG_GPIOLIB if (devtype->nr_gpio) gpiochip_remove(&s->gpio); -out_uart: +out_thread: #endif + kthread_stop(s->kworker_task); + +out_uart: uart_unregister_driver(&s->uart); out_clk: @@ -1163,13 +1182,14 @@ static int sc16is7xx_remove(struct device *dev) #endif for (i = 0; i < s->uart.nr; i++) { - cancel_work_sync(&s->p[i].tx_work); cancel_work_sync(&s->p[i].md_work); uart_remove_one_port(&s->uart, &s->p[i].port); sc16is7xx_power(&s->p[i].port, 0); } - mutex_destroy(&s->mutex); + flush_kthread_worker(&s->kworker); + kthread_stop(s->kworker_task); + uart_unregister_driver(&s->uart); if (!IS_ERR(s->clk)) clk_disable_unprepare(s->clk); From dbe5a40cba2de69678401362b7cc9bbbe7bc30be Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 29 May 2015 21:20:30 +0200 Subject: [PATCH 131/148] sc16is7xx: move RTS delay to workqueue Instead of spinning under port->lock let's just sleep inside the kthread. It should be equivalent as TX cannot proceed when thread is blocked. Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index e6553d04a551..8247bce5f4ad 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -650,9 +650,13 @@ static irqreturn_t sc16is7xx_irq(int irq, void *dev_id) static void sc16is7xx_tx_proc(struct kthread_work *ws) { - struct sc16is7xx_one *one = to_sc16is7xx_one(ws, tx_work); + struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port); - sc16is7xx_handle_tx(&one->port); + if ((port->rs485.flags & SER_RS485_ENABLED) && + (port->rs485.delay_rts_before_send > 0)) + msleep(port->rs485.delay_rts_before_send); + + sc16is7xx_handle_tx(port); } static void sc16is7xx_stop_tx(struct uart_port* port) @@ -677,12 +681,6 @@ static void sc16is7xx_start_tx(struct uart_port *port) struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - /* handle rs485 */ - if ((port->rs485.flags & SER_RS485_ENABLED) && - (port->rs485.delay_rts_before_send > 0)) { - mdelay(port->rs485.delay_rts_before_send); - } - queue_kthread_work(&s->kworker, &one->tx_work); } From a0104085362ff90438151e3b96513900a274d241 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 29 May 2015 21:20:31 +0200 Subject: [PATCH 132/148] sc16is7xx: use kworker for md_proc Convert md_proc into general async reconfiguration procedure. Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 47 ++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 8247bce5f4ad..f218e24463c9 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -301,10 +301,17 @@ struct sc16is7xx_devtype { int nr_uart; }; +#define SC16IS7XX_RECONF_MD (1 << 0) + +struct sc16is7xx_one_config { + unsigned int flags; +}; + struct sc16is7xx_one { struct uart_port port; struct kthread_work tx_work; - struct work_struct md_work; + struct kthread_work reg_work; + struct sc16is7xx_one_config config; }; struct sc16is7xx_port { @@ -659,6 +666,24 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws) sc16is7xx_handle_tx(port); } +static void sc16is7xx_reg_proc(struct kthread_work *ws) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(ws, reg_work); + struct sc16is7xx_one_config config; + unsigned long irqflags; + + spin_lock_irqsave(&one->port.lock, irqflags); + config = one->config; + memset(&one->config, 0, sizeof(one->config)); + spin_unlock_irqrestore(&one->port.lock, irqflags); + + if (config.flags & SC16IS7XX_RECONF_MD) + sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_LOOP_BIT, + (one->port.mctrl & TIOCM_LOOP) ? + SC16IS7XX_MCR_LOOP_BIT : 0); +} + static void sc16is7xx_stop_tx(struct uart_port* port) { sc16is7xx_port_update(port, SC16IS7XX_IER_REG, @@ -701,21 +726,13 @@ static unsigned int sc16is7xx_get_mctrl(struct uart_port *port) return TIOCM_DSR | TIOCM_CAR; } -static void sc16is7xx_md_proc(struct work_struct *ws) -{ - struct sc16is7xx_one *one = to_sc16is7xx_one(ws, md_work); - - sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, - SC16IS7XX_MCR_LOOP_BIT, - (one->port.mctrl & TIOCM_LOOP) ? - SC16IS7XX_MCR_LOOP_BIT : 0); -} - static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl) { + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - schedule_work(&one->md_work); + one->config.flags |= SC16IS7XX_RECONF_MD; + queue_kthread_work(&s->kworker, &one->reg_work); } static void sc16is7xx_break_ctl(struct uart_port *port, int break_state) @@ -1132,10 +1149,9 @@ static int sc16is7xx_probe(struct device *dev, sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG, SC16IS7XX_EFCR_RXDISABLE_BIT | SC16IS7XX_EFCR_TXDISABLE_BIT); - /* Initialize queue for start TX */ + /* Initialize kthread work structs */ init_kthread_work(&s->p[i].tx_work, sc16is7xx_tx_proc); - /* Initialize queue for changing mode */ - INIT_WORK(&s->p[i].md_work, sc16is7xx_md_proc); + init_kthread_work(&s->p[i].reg_work, sc16is7xx_reg_proc); /* Register port */ uart_add_one_port(&s->uart, &s->p[i].port); /* Go to suspend mode */ @@ -1180,7 +1196,6 @@ static int sc16is7xx_remove(struct device *dev) #endif for (i = 0; i < s->uart.nr; i++) { - cancel_work_sync(&s->p[i].md_work); uart_remove_one_port(&s->uart, &s->p[i].port); sc16is7xx_power(&s->p[i].port, 0); } From 059d5815304bdbe440d0ffc957d832aebf076ca3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 29 May 2015 21:20:32 +0200 Subject: [PATCH 133/148] sc16is7xx: use kworker to update ier bits .stop_rx/tx() are called in atomic context, we cannot use blocking I/O. While at it correct the name of RX bit and '*' placement in pointer declarations. Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index f218e24463c9..86a679a5870b 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -302,9 +302,11 @@ struct sc16is7xx_devtype { }; #define SC16IS7XX_RECONF_MD (1 << 0) +#define SC16IS7XX_RECONF_IER (1 << 1) struct sc16is7xx_one_config { unsigned int flags; + u8 ier_clear; }; struct sc16is7xx_one { @@ -682,23 +684,30 @@ static void sc16is7xx_reg_proc(struct kthread_work *ws) SC16IS7XX_MCR_LOOP_BIT, (one->port.mctrl & TIOCM_LOOP) ? SC16IS7XX_MCR_LOOP_BIT : 0); + + if (config.flags & SC16IS7XX_RECONF_IER) + sc16is7xx_port_update(&one->port, SC16IS7XX_IER_REG, + config.ier_clear, 0); } -static void sc16is7xx_stop_tx(struct uart_port* port) -{ - sc16is7xx_port_update(port, SC16IS7XX_IER_REG, - SC16IS7XX_IER_THRI_BIT, - 0); -} - -static void sc16is7xx_stop_rx(struct uart_port* port) +static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit) { + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - one->port.read_status_mask &= ~SC16IS7XX_LSR_DR_BIT; - sc16is7xx_port_update(port, SC16IS7XX_IER_REG, - SC16IS7XX_LSR_DR_BIT, - 0); + one->config.flags |= SC16IS7XX_RECONF_IER; + one->config.ier_clear |= bit; + queue_kthread_work(&s->kworker, &one->reg_work); +} + +static void sc16is7xx_stop_tx(struct uart_port *port) +{ + sc16is7xx_ier_clear(port, SC16IS7XX_IER_THRI_BIT); +} + +static void sc16is7xx_stop_rx(struct uart_port *port) +{ + sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT); } static void sc16is7xx_start_tx(struct uart_port *port) From 478d1051a0293b8a8857267bb0f768ebcb05cfde Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 29 May 2015 21:20:33 +0200 Subject: [PATCH 134/148] sc16is7xx: use kworker for RS-485 configuration RS-485 configuration is also done under the spinlock so no blocking I/O allowed. Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 41 ++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 86a679a5870b..9e6576004a42 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -303,6 +303,7 @@ struct sc16is7xx_devtype { #define SC16IS7XX_RECONF_MD (1 << 0) #define SC16IS7XX_RECONF_IER (1 << 1) +#define SC16IS7XX_RECONF_RS485 (1 << 2) struct sc16is7xx_one_config { unsigned int flags; @@ -668,6 +669,26 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws) sc16is7xx_handle_tx(port); } +static void sc16is7xx_reconf_rs485(struct uart_port *port) +{ + const u32 mask = SC16IS7XX_EFCR_AUTO_RS485_BIT | + SC16IS7XX_EFCR_RTS_INVERT_BIT; + u32 efcr = 0; + struct serial_rs485 *rs485 = &port->rs485; + unsigned long irqflags; + + spin_lock_irqsave(&port->lock, irqflags); + if (rs485->flags & SER_RS485_ENABLED) { + efcr |= SC16IS7XX_EFCR_AUTO_RS485_BIT; + + if (rs485->flags & SER_RS485_RTS_AFTER_SEND) + efcr |= SC16IS7XX_EFCR_RTS_INVERT_BIT; + } + spin_unlock_irqrestore(&port->lock, irqflags); + + sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, mask, efcr); +} + static void sc16is7xx_reg_proc(struct kthread_work *ws) { struct sc16is7xx_one *one = to_sc16is7xx_one(ws, reg_work); @@ -688,6 +709,9 @@ static void sc16is7xx_reg_proc(struct kthread_work *ws) if (config.flags & SC16IS7XX_RECONF_IER) sc16is7xx_port_update(&one->port, SC16IS7XX_IER_REG, config.ier_clear, 0); + + if (config.flags & SC16IS7XX_RECONF_RS485) + sc16is7xx_reconf_rs485(&one->port); } static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit) @@ -845,9 +869,8 @@ static void sc16is7xx_set_termios(struct uart_port *port, static int sc16is7xx_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) { - const u32 mask = SC16IS7XX_EFCR_AUTO_RS485_BIT | - SC16IS7XX_EFCR_RTS_INVERT_BIT; - u32 efcr = 0; + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); if (rs485->flags & SER_RS485_ENABLED) { bool rts_during_rx, rts_during_tx; @@ -855,13 +878,7 @@ static int sc16is7xx_config_rs485(struct uart_port *port, rts_during_rx = rs485->flags & SER_RS485_RTS_AFTER_SEND; rts_during_tx = rs485->flags & SER_RS485_RTS_ON_SEND; - efcr |= SC16IS7XX_EFCR_AUTO_RS485_BIT; - - if (!rts_during_rx && rts_during_tx) - /* default */; - else if (rts_during_rx && !rts_during_tx) - efcr |= SC16IS7XX_EFCR_RTS_INVERT_BIT; - else + if (rts_during_rx == rts_during_tx) dev_err(port->dev, "unsupported RTS signalling on_send:%d after_send:%d - exactly one of RS485 RTS flags should be set\n", rts_during_tx, rts_during_rx); @@ -875,9 +892,9 @@ static int sc16is7xx_config_rs485(struct uart_port *port, return -EINVAL; } - sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, mask, efcr); - port->rs485 = *rs485; + one->config.flags |= SC16IS7XX_RECONF_RS485; + queue_kthread_work(&s->kworker, &one->reg_work); return 0; } From ba02406e9bee5726a604a4c8628aac56cb076fef Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 11 May 2015 12:30:22 +0900 Subject: [PATCH 135/148] serial: 8250: remove return statements from void function serial8250_set_mctrl() is a void type function. Returning something does not look nice. Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 242e796480de..37fff12dd4d0 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2006,8 +2006,9 @@ EXPORT_SYMBOL_GPL(serial8250_do_set_mctrl); static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) { if (port->set_mctrl) - return port->set_mctrl(port, mctrl); - return serial8250_do_set_mctrl(port, mctrl); + port->set_mctrl(port, mctrl); + else + serial8250_do_set_mctrl(port, mctrl); } static void serial8250_break_ctl(struct uart_port *port, int break_state) From 326707ed88028283cf04d86848a0436cb966b01e Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Tue, 26 May 2015 09:35:58 +0000 Subject: [PATCH 136/148] serial: sirf: fix system hung on console log output A corner case exists in the current driver. if an app opens the console device, and before writing to console device, and there are huge kernel ogs to print out, system will hang on sirfsoc_uart_console_putchar: while (rd_regl(port, ureg->sirfsoc_tx_fifo_status) & ufifo_st->ff_full(port->line)) cpu_relax(); as in sirfsoc_uart_startup(), the driver assigns tx_fifo_op to 0 will stop TX FIFO, this loop will be endless. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 8d759629ffb0..6b1c92c1c27f 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -272,6 +272,7 @@ static void sirfsoc_uart_start_tx(struct uart_port *port) if (sirfport->uart_reg->uart_type == SIRF_USP_UART) wr_regl(port, ureg->sirfsoc_tx_rx_en, rd_regl(port, ureg->sirfsoc_tx_rx_en) | SIRFUART_TX_EN); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP); sirfsoc_uart_pio_tx_chars(sirfport, port->fifosize); wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); if (!sirfport->is_atlas7) @@ -1117,7 +1118,6 @@ static int sirfsoc_uart_startup(struct uart_port *port) SIRFSOC_USP_ENDIAN_CTRL_LSBF | SIRFSOC_USP_EN); wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_RESET); - wr_regl(port, ureg->sirfsoc_tx_fifo_op, 0); wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl, SIRFUART_FIFO_THD(port)); From 86459b0e407798cc16d52d493a624251e05ce6ad Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Tue, 26 May 2015 09:35:59 +0000 Subject: [PATCH 137/148] serial: sirf: correct the fifo empty_bit Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index 3ab3141e1aae..644f86529571 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -117,7 +117,7 @@ u32 uart_usp_ff_empty_mask(struct uart_port *port) { u32 empty_bit; - empty_bit = ilog2(port->fifosize); + empty_bit = ilog2(port->fifosize) + 1; return (1 << empty_bit); } struct sirfsoc_uart_register sirfsoc_usp = { From 0f17e3b478a77e00e873f6ca235644322843c81e Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Tue, 26 May 2015 09:36:00 +0000 Subject: [PATCH 138/148] serial: sirf: use hrtimer for data rx when the serial works as a bluetooth sink, due to audio realtime requirement, the driver should have something similar with ALSA: 1. one big DMA buffer to easy the schedule jitter 2. split this big DMA buffer to multiple small periods, for each period, we get a DMA interrupt, then push the data to userspace. the small periods will easy the audio latency. so ALSA generally uses a cyclic chained DMA. but for sirfsoc, the dma hardware has the limitation: we have only two loops in the cyclic mode, so we can only support two small periods to switch. if we make the DMA buffer too big, we get long latency, if we make the DMA buffer too little, we get miss in scheduling for audio realtime. so this patch moves to use a hrtimer to simulate the cyclic DMA, then we can have a big buffer, and also have a timely data push to users as the hrtimer can generate in small period then actual HW interrupts. with this patch, we also delete a lot of complex codes to handle loop buffers, and RX timeout interrupt since the RX work can be completely handled from hrtimer interrupt. tests show using this way will make our bad audio streaming be- come smooth. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 341 +++++++++++------------------- drivers/tty/serial/sirfsoc_uart.h | 20 +- 2 files changed, 135 insertions(+), 226 deletions(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 6b1c92c1c27f..b6116413ca0d 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -36,8 +36,6 @@ sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count); static struct uart_driver sirfsoc_uart_drv; static void sirfsoc_uart_tx_dma_complete_callback(void *param); -static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port); -static void sirfsoc_uart_rx_dma_complete_callback(void *param); static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = { {4000000, 2359296}, {3500000, 1310721}, @@ -465,144 +463,6 @@ static void sirfsoc_uart_tx_dma_complete_callback(void *param) spin_unlock_irqrestore(&port->lock, flags); } -static void sirfsoc_uart_insert_rx_buf_to_tty( - struct sirfsoc_uart_port *sirfport, int count) -{ - struct uart_port *port = &sirfport->port; - struct tty_port *tport = &port->state->port; - int inserted; - - inserted = tty_insert_flip_string(tport, - sirfport->rx_dma_items[sirfport->rx_completed].xmit.buf, count); - port->icount.rx += inserted; -} - -static void sirfsoc_rx_submit_one_dma_desc(struct uart_port *port, int index) -{ - struct sirfsoc_uart_port *sirfport = to_sirfport(port); - - sirfport->rx_dma_items[index].xmit.tail = - sirfport->rx_dma_items[index].xmit.head = 0; - sirfport->rx_dma_items[index].desc = - dmaengine_prep_slave_single(sirfport->rx_dma_chan, - sirfport->rx_dma_items[index].dma_addr, SIRFSOC_RX_DMA_BUF_SIZE, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); - if (IS_ERR_OR_NULL(sirfport->rx_dma_items[index].desc)) { - dev_err(port->dev, "DMA slave single fail\n"); - return; - } - sirfport->rx_dma_items[index].desc->callback = - sirfsoc_uart_rx_dma_complete_callback; - sirfport->rx_dma_items[index].desc->callback_param = sirfport; - sirfport->rx_dma_items[index].cookie = - dmaengine_submit(sirfport->rx_dma_items[index].desc); - dma_async_issue_pending(sirfport->rx_dma_chan); -} - -static void sirfsoc_rx_tmo_process_tl(unsigned long param) -{ - struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; - struct uart_port *port = &sirfport->port; - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; - unsigned int count; - struct dma_tx_state tx_state; - unsigned long flags; - int i = 0; - - spin_lock_irqsave(&port->lock, flags); - 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++; - sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT; - i++; - if (i > SIRFSOC_RX_LOOP_BUF_CNT) - break; - } - count = CIRC_CNT(sirfport->rx_dma_items[sirfport->rx_issued].xmit.head, - sirfport->rx_dma_items[sirfport->rx_issued].xmit.tail, - SIRFSOC_RX_DMA_BUF_SIZE); - if (count > 0) - sirfsoc_uart_insert_rx_buf_to_tty(sirfport, count); - wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, - rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | - SIRFUART_IO_MODE); - sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count); - if (sirfport->rx_io_count == 4) { - sirfport->rx_io_count = 0; - wr_regl(port, ureg->sirfsoc_int_st_reg, - uint_st->sirfsoc_rx_done); - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) & - ~(uint_en->sirfsoc_rx_done_en)); - else - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, - uint_en->sirfsoc_rx_done_en); - sirfsoc_uart_start_next_rx_dma(port); - } else { - wr_regl(port, ureg->sirfsoc_int_st_reg, - uint_st->sirfsoc_rx_done); - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) | - (uint_en->sirfsoc_rx_done_en)); - else - wr_regl(port, ureg->sirfsoc_int_en_reg, - uint_en->sirfsoc_rx_done_en); - } - spin_unlock_irqrestore(&port->lock, flags); - tty_flip_buffer_push(&port->state->port); -} - -static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport) -{ - struct uart_port *port = &sirfport->port; - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - struct dma_tx_state tx_state; - dmaengine_tx_status(sirfport->rx_dma_chan, - sirfport->rx_dma_items[sirfport->rx_issued].cookie, &tx_state); - dmaengine_terminate_all(sirfport->rx_dma_chan); - sirfport->rx_dma_items[sirfport->rx_issued].xmit.head = - SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue; - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) & - ~(uint_en->sirfsoc_rx_timeout_en)); - else - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, - uint_en->sirfsoc_rx_timeout_en); - tasklet_schedule(&sirfport->rx_tmo_process_tasklet); -} - -static void sirfsoc_uart_handle_rx_done(struct sirfsoc_uart_port *sirfport) -{ - struct uart_port *port = &sirfport->port; - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; - - sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count); - if (sirfport->rx_io_count == 4) { - sirfport->rx_io_count = 0; - if (!sirfport->is_atlas7) - wr_regl(port, ureg->sirfsoc_int_en_reg, - rd_regl(port, ureg->sirfsoc_int_en_reg) & - ~(uint_en->sirfsoc_rx_done_en)); - else - wr_regl(port, ureg->sirfsoc_int_en_clr_reg, - uint_en->sirfsoc_rx_done_en); - wr_regl(port, ureg->sirfsoc_int_st_reg, - uint_st->sirfsoc_rx_timeout); - sirfsoc_uart_start_next_rx_dma(port); - } -} - static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) { unsigned long intr_status; @@ -659,12 +519,8 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) uart_handle_cts_change(port, cts_status); wake_up_interruptible(&state->port.delta_msr_wait); } - if (sirfport->rx_dma_chan) { - if (intr_status & uint_st->sirfsoc_rx_timeout) - sirfsoc_uart_handle_rx_tmo(sirfport); - if (intr_status & uint_st->sirfsoc_rx_done) - sirfsoc_uart_handle_rx_done(sirfport); - } else if (intr_status & SIRFUART_RX_IO_INT_ST(uint_st)) { + if (!sirfport->rx_dma_chan && + (intr_status & SIRFUART_RX_IO_INT_ST(uint_st))) { /* * chip will trigger continuous RX_TIMEOUT interrupt * in RXFIFO empty and not trigger if RXFIFO recevice @@ -734,47 +590,8 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param) -{ - struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; - struct uart_port *port = &sirfport->port; - struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; - struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - struct dma_tx_state tx_state; - unsigned long flags; - int i = 0; - - spin_lock_irqsave(&port->lock, flags); - 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) & - uint_en->sirfsoc_rx_timeout_en) - sirfsoc_rx_submit_one_dma_desc(port, - sirfport->rx_completed++); - else - sirfport->rx_completed++; - sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT; - i++; - if (i > SIRFSOC_RX_LOOP_BUF_CNT) - break; - } - spin_unlock_irqrestore(&port->lock, flags); - tty_flip_buffer_push(&port->state->port); -} - static void sirfsoc_uart_rx_dma_complete_callback(void *param) { - struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; - unsigned long flags; - - spin_lock_irqsave(&sirfport->port.lock, flags); - sirfport->rx_issued++; - sirfport->rx_issued %= SIRFSOC_RX_LOOP_BUF_CNT; - tasklet_schedule(&sirfport->rx_dma_complete_tasklet); - spin_unlock_irqrestore(&sirfport->port.lock, flags); } /* submit rx dma task into dmaengine */ @@ -783,14 +600,27 @@ static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port) struct sirfsoc_uart_port *sirfport = to_sirfport(port); struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; - int i; sirfport->rx_io_count = 0; wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) & ~SIRFUART_IO_MODE); - for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++) - sirfsoc_rx_submit_one_dma_desc(port, i); - sirfport->rx_completed = sirfport->rx_issued = 0; + sirfport->rx_dma_items.xmit.tail = + sirfport->rx_dma_items.xmit.head = 0; + sirfport->rx_dma_items.desc = + dmaengine_prep_dma_cyclic(sirfport->rx_dma_chan, + sirfport->rx_dma_items.dma_addr, SIRFSOC_RX_DMA_BUF_SIZE, + SIRFSOC_RX_DMA_BUF_SIZE / 2, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + if (IS_ERR_OR_NULL(sirfport->rx_dma_items.desc)) { + dev_err(port->dev, "DMA slave single fail\n"); + return; + } + sirfport->rx_dma_items.desc->callback = + sirfsoc_uart_rx_dma_complete_callback; + sirfport->rx_dma_items.desc->callback_param = sirfport; + sirfport->rx_dma_items.cookie = + dmaengine_submit(sirfport->rx_dma_items.desc); + dma_async_issue_pending(sirfport->rx_dma_chan); if (!sirfport->is_atlas7) wr_regl(port, ureg->sirfsoc_int_en_reg, rd_regl(port, ureg->sirfsoc_int_en_reg) | @@ -1059,6 +889,7 @@ static void sirfsoc_uart_set_termios(struct uart_port *port, wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_DMA_MODE); else wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_IO_MODE); + sirfport->rx_period_time = 20000000; /* Reset Rx/Tx FIFO Threshold level for proper baudrate */ if (set_baud < 1000000) threshold_div = 1; @@ -1110,6 +941,9 @@ static int sirfsoc_uart_startup(struct uart_port *port) wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | SIRFUART_IO_MODE); + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) & + ~SIRFUART_RX_DMA_FLUSH); wr_regl(port, ureg->sirfsoc_tx_dma_io_len, 0); wr_regl(port, ureg->sirfsoc_rx_dma_io_len, 0); wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_RX_EN | SIRFUART_TX_EN); @@ -1147,8 +981,16 @@ static int sirfsoc_uart_startup(struct uart_port *port) goto init_rx_err; } } - enable_irq(port->irq); + if (sirfport->rx_dma_chan && !sirfport->is_hrt_enabled) { + sirfport->is_hrt_enabled = true; + sirfport->rx_period_time = 20000000; + sirfport->rx_dma_items.xmit.tail = + sirfport->rx_dma_items.xmit.head = 0; + hrtimer_start(&sirfport->hrt, + ns_to_ktime(sirfport->rx_period_time), + HRTIMER_MODE_REL); + } return 0; init_rx_err: @@ -1176,6 +1018,13 @@ static void sirfsoc_uart_shutdown(struct uart_port *port) } if (sirfport->tx_dma_chan) sirfport->tx_dma_state = TX_DMA_IDLE; + if (sirfport->rx_dma_chan && sirfport->is_hrt_enabled) { + while ((rd_regl(port, ureg->sirfsoc_rx_fifo_status) & + SIRFUART_RX_FIFO_MASK) > 0) + ; + sirfport->is_hrt_enabled = false; + hrtimer_cancel(&sirfport->hrt); + } } static const char *sirfsoc_uart_type(struct uart_port *port) @@ -1310,6 +1159,70 @@ static struct uart_driver sirfsoc_uart_drv = { #endif }; +static enum hrtimer_restart + sirfsoc_uart_rx_dma_hrtimer_callback(struct hrtimer *hrt) +{ + struct sirfsoc_uart_port *sirfport; + struct uart_port *port; + int count, inserted; + struct dma_tx_state tx_state; + struct tty_struct *tty; + struct sirfsoc_register *ureg; + struct circ_buf *xmit; + + sirfport = container_of(hrt, struct sirfsoc_uart_port, hrt); + port = &sirfport->port; + inserted = 0; + tty = port->state->port.tty; + ureg = &sirfport->uart_reg->uart_reg; + xmit = &sirfport->rx_dma_items.xmit; + dmaengine_tx_status(sirfport->rx_dma_chan, + sirfport->rx_dma_items.cookie, &tx_state); + xmit->head = SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue; + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, + SIRFSOC_RX_DMA_BUF_SIZE); + while (count > 0) { + inserted = tty_insert_flip_string(tty->port, + (const unsigned char *)&xmit->buf[xmit->tail], count); + if (!inserted) + goto next_hrt; + port->icount.rx += inserted; + xmit->tail = (xmit->tail + inserted) & + (SIRFSOC_RX_DMA_BUF_SIZE - 1); + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, + SIRFSOC_RX_DMA_BUF_SIZE); + tty_flip_buffer_push(tty->port); + } + /* + * if RX DMA buffer data have all push into tty buffer, and there is + * only little data(less than a dma transfer unit) left in rxfifo, + * fetch it out in pio mode and switch back to dma immediately + */ + if (!inserted && !count && + ((rd_regl(port, ureg->sirfsoc_rx_fifo_status) & + SIRFUART_RX_FIFO_MASK) > 0)) { + /* switch to pio mode */ + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | + SIRFUART_IO_MODE); + while ((rd_regl(port, ureg->sirfsoc_rx_fifo_status) & + SIRFUART_RX_FIFO_MASK) > 0) { + if (sirfsoc_uart_pio_rx_chars(port, 16) > 0) + tty_flip_buffer_push(tty->port); + } + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START); + /* switch back to dma mode */ + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) & + ~SIRFUART_IO_MODE); + } +next_hrt: + hrtimer_forward_now(hrt, ns_to_ktime(sirfport->rx_period_time)); + return HRTIMER_RESTART; +} + static struct of_device_id sirfsoc_uart_ids[] = { { .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,}, { .compatible = "sirf,atlas7-uart", .data = &sirfsoc_uart}, @@ -1325,7 +1238,6 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) struct uart_port *port; struct resource *res; int ret; - int i, j; struct dma_slave_config slv_cfg = { .src_maxburst = 2, }; @@ -1413,12 +1325,9 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) ret = -EFAULT; goto err; } - tasklet_init(&sirfport->rx_dma_complete_tasklet, - sirfsoc_uart_rx_dma_complete_tl, (unsigned long)sirfport); - tasklet_init(&sirfport->rx_tmo_process_tasklet, - sirfsoc_rx_tmo_process_tl, (unsigned long)sirfport); port->mapbase = res->start; - port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + port->membase = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); if (!port->membase) { dev_err(&pdev->dev, "Cannot remap resource.\n"); ret = -ENOMEM; @@ -1450,30 +1359,32 @@ static int sirfsoc_uart_probe(struct platform_device *pdev) } sirfport->rx_dma_chan = dma_request_slave_channel(port->dev, "rx"); - for (i = 0; sirfport->rx_dma_chan && i < SIRFSOC_RX_LOOP_BUF_CNT; i++) { - sirfport->rx_dma_items[i].xmit.buf = - dma_alloc_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, - &sirfport->rx_dma_items[i].dma_addr, GFP_KERNEL); - if (!sirfport->rx_dma_items[i].xmit.buf) { - dev_err(port->dev, "Uart alloc bufa failed\n"); - ret = -ENOMEM; - goto alloc_coherent_err; - } - sirfport->rx_dma_items[i].xmit.head = - sirfport->rx_dma_items[i].xmit.tail = 0; + sirfport->rx_dma_items.xmit.buf = + dma_alloc_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, + &sirfport->rx_dma_items.dma_addr, GFP_KERNEL); + if (!sirfport->rx_dma_items.xmit.buf) { + dev_err(port->dev, "Uart alloc bufa failed\n"); + ret = -ENOMEM; + goto alloc_coherent_err; } + sirfport->rx_dma_items.xmit.head = + sirfport->rx_dma_items.xmit.tail = 0; if (sirfport->rx_dma_chan) dmaengine_slave_config(sirfport->rx_dma_chan, &slv_cfg); sirfport->tx_dma_chan = dma_request_slave_channel(port->dev, "tx"); if (sirfport->tx_dma_chan) dmaengine_slave_config(sirfport->tx_dma_chan, &tx_slv_cfg); + if (sirfport->rx_dma_chan) { + hrtimer_init(&sirfport->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + sirfport->hrt.function = sirfsoc_uart_rx_dma_hrtimer_callback; + sirfport->is_hrt_enabled = false; + } return 0; alloc_coherent_err: - for (j = 0; j < i; j++) - dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, - sirfport->rx_dma_items[j].xmit.buf, - sirfport->rx_dma_items[j].dma_addr); + dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, + sirfport->rx_dma_items.xmit.buf, + sirfport->rx_dma_items.dma_addr); dma_release_channel(sirfport->rx_dma_chan); err: return ret; @@ -1485,13 +1396,11 @@ static int sirfsoc_uart_remove(struct platform_device *pdev) struct uart_port *port = &sirfport->port; uart_remove_one_port(&sirfsoc_uart_drv, port); if (sirfport->rx_dma_chan) { - int i; dmaengine_terminate_all(sirfport->rx_dma_chan); dma_release_channel(sirfport->rx_dma_chan); - for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++) - dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, - sirfport->rx_dma_items[i].xmit.buf, - sirfport->rx_dma_items[i].dma_addr); + dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, + sirfport->rx_dma_items.xmit.buf, + sirfport->rx_dma_items.dma_addr); } if (sirfport->tx_dma_chan) { dmaengine_terminate_all(sirfport->tx_dma_chan); diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index 644f86529571..eb162b012eec 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -7,6 +7,7 @@ */ #include #include +#include struct sirfsoc_uart_param { const char *uart_name; const char *port_name; @@ -293,6 +294,7 @@ struct sirfsoc_uart_register sirfsoc_uart = { #define SIRFUART_IO_MODE BIT(0) #define SIRFUART_DMA_MODE 0x0 +#define SIRFUART_RX_DMA_FLUSH 0x4 /* Baud Rate Calculation */ #define SIRF_USP_MIN_SAMPLE_DIV 0x1 @@ -353,8 +355,7 @@ struct sirfsoc_uart_register sirfsoc_uart = { uint_st->sirfsoc_rx_timeout) #define SIRFUART_CTS_INT_ST(uint_st) (uint_st->sirfsoc_cts) #define SIRFUART_RX_DMA_INT_EN(uint_en, uart_type) \ - (uint_en->sirfsoc_rx_timeout_en |\ - uint_en->sirfsoc_frm_err_en |\ + (uint_en->sirfsoc_frm_err_en |\ uint_en->sirfsoc_rx_oflow_en |\ uint_en->sirfsoc_rxd_brk_en |\ ((uart_type != SIRF_REAL_UART) ? \ @@ -369,7 +370,7 @@ struct sirfsoc_uart_register sirfsoc_uart = { #define SIRFSOC_PORT_TYPE 0xa5 /* Uart Common Use Macro*/ -#define SIRFSOC_RX_DMA_BUF_SIZE 256 +#define SIRFSOC_RX_DMA_BUF_SIZE (1024 * 32) #define BYTES_TO_ALIGN(dma_addr) ((unsigned long)(dma_addr) & 0x3) /* Uart Fifo Level Chk */ #define SIRFUART_TX_FIFO_SC_OFFSET 0 @@ -385,8 +386,8 @@ struct sirfsoc_uart_register sirfsoc_uart = { #define SIRFUART_RX_FIFO_CHK_SC SIRFUART_TX_FIFO_CHK_SC #define SIRFUART_RX_FIFO_CHK_LC SIRFUART_TX_FIFO_CHK_LC #define SIRFUART_RX_FIFO_CHK_HC SIRFUART_TX_FIFO_CHK_HC +#define SIRFUART_RX_FIFO_MASK 0x7f /* Indicate how many buffers used */ -#define SIRFSOC_RX_LOOP_BUF_CNT 2 /* For Fast Baud Rate Calculation */ struct sirfsoc_baudrate_to_regv { @@ -400,7 +401,7 @@ enum sirfsoc_tx_state { TX_DMA_PAUSE, }; -struct sirfsoc_loop_buffer { +struct sirfsoc_rx_buffer { struct circ_buf xmit; dma_cookie_t cookie; struct dma_async_tx_descriptor *desc; @@ -420,17 +421,16 @@ struct sirfsoc_uart_port { struct dma_chan *tx_dma_chan; dma_addr_t tx_dma_addr; struct dma_async_tx_descriptor *tx_dma_desc; - struct tasklet_struct rx_dma_complete_tasklet; - struct tasklet_struct rx_tmo_process_tasklet; unsigned int rx_io_count; unsigned long transfer_size; enum sirfsoc_tx_state tx_dma_state; unsigned int cts_gpio; unsigned int rts_gpio; - struct sirfsoc_loop_buffer rx_dma_items[SIRFSOC_RX_LOOP_BUF_CNT]; - int rx_completed; - int rx_issued; + struct sirfsoc_rx_buffer rx_dma_items; + struct hrtimer hrt; + bool is_hrt_enabled; + unsigned long rx_period_time; }; /* Register Access Control */ From 6e63be3fee141cc6d122f648b524a66160dbe6aa Mon Sep 17 00:00:00 2001 From: Noam Camus Date: Mon, 25 May 2015 06:54:28 +0300 Subject: [PATCH 139/148] serial: earlycon: Add support for big-endian MMIO accesses Support command line parameters of the form: earlycon=,io|mmio|mmio32|mmio32be,, This commit seem to be needed even after commit: serial: 8250: Add support for big-endian MMIO accesses c627f2ceb692e8a9358b64ac2d139314e7bb0d17 Signed-off-by: Noam Camus Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 9 +++++---- drivers/tty/serial/earlycon.c | 9 ++++++--- drivers/tty/serial/serial_core.c | 7 +++++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 61ab1628a057..55bb093a08a3 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -959,14 +959,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted. uart[8250],io,[,options] uart[8250],mmio,[,options] uart[8250],mmio32,[,options] + uart[8250],mmio32be,[,options] uart[8250],0x[,options] Start an early, polled-mode console on the 8250/16550 UART at the specified I/O port or MMIO address. MMIO inter-register address stride is either 8-bit - (mmio) or 32-bit (mmio32). - If none of [io|mmio|mmio32], is assumed to be - equivalent to 'mmio'. 'options' are specified in the - same format described for "console=ttyS"; if + (mmio) or 32-bit (mmio32 or mmio32be). + If none of [io|mmio|mmio32|mmio32be], is assumed + to be equivalent to 'mmio'. 'options' are specified + in the same format described for "console=ttyS"; if unspecified, the h/w is not initialized. pl011, diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 6dc471e30e79..f09636083426 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -72,6 +72,7 @@ static int __init parse_options(struct earlycon_device *device, char *options) switch (port->iotype) { case UPIO_MEM32: + case UPIO_MEM32BE: port->regshift = 2; /* fall-through */ case UPIO_MEM: port->mapbase = addr; @@ -90,9 +91,11 @@ static int __init parse_options(struct earlycon_device *device, char *options) strlcpy(device->options, options, length); } - if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32) + if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32 || + port->iotype == UPIO_MEM32BE) pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n", - (port->iotype == UPIO_MEM32) ? "32" : "", + (port->iotype == UPIO_MEM) ? "" : + (port->iotype == UPIO_MEM32) ? "32" : "32be", (unsigned long long)port->mapbase, device->options); else @@ -133,7 +136,7 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match) * * Registers the earlycon console matching the earlycon specified * in the param string @buf. Acceptable param strings are of the form - * ,io|mmio|mmio32,, + * ,io|mmio|mmio32|mmio32be,, * ,0x, * , * diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index eec067d8eedb..860e59fd6ef4 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1814,8 +1814,8 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) * @options: ptr for field; NULL if not present (out) * * Decodes earlycon kernel command line parameters of the form - * earlycon=,io|mmio|mmio32,, - * console=,io|mmio|mmio32,, + * earlycon=,io|mmio|mmio32|mmio32be,, + * console=,io|mmio|mmio32|mmio32be,, * * The optional form * earlycon=,0x, @@ -1833,6 +1833,9 @@ int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, } else if (strncmp(p, "mmio32,", 7) == 0) { *iotype = UPIO_MEM32; p += 7; + } else if (strncmp(p, "mmio32be,", 9) == 0) { + *iotype = UPIO_MEM32BE; + p += 9; } else if (strncmp(p, "io,", 3) == 0) { *iotype = UPIO_PORT; p += 3; From 3a63d22425ff229c8fb51e7aec1de1e9a4e19b34 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 25 May 2015 14:57:43 +0900 Subject: [PATCH 140/148] serial: of_serial: use devm_clk_get() instead of clk_get() The probe method of this driver calls clk_get(), but clk_put() is missing from the remove callback. Using the managed clk function is easier than fixing other parts. Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/of_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 28b9b4789bbf..d353fdf55ac1 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -67,7 +67,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (of_property_read_u32(np, "clock-frequency", &clk)) { /* Get clk rate through clk driver if present */ - info->clk = clk_get(&ofdev->dev, NULL); + info->clk = devm_clk_get(&ofdev->dev, NULL); if (IS_ERR(info->clk)) { dev_warn(&ofdev->dev, "clk or clock-frequency not defined\n"); From 6f0c3091e73df7ad1393c3400d168b9777b4a63c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 25 May 2015 15:03:32 +0900 Subject: [PATCH 141/148] serial: of_serial: check the return value of clk_prepare_enable() The function clk_prepare_enable() may fail, and in that case it does not make sense to proceed. Let's check its return code and error out if it is a negative value. Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/of_serial.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index d353fdf55ac1..6823df99bd76 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -74,7 +74,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev, return PTR_ERR(info->clk); } - clk_prepare_enable(info->clk); + ret = clk_prepare_enable(info->clk); + if (ret < 0) + return ret; + clk = clk_get_rate(info->clk); } /* If current-speed was set, then try not to change it. */ From 0788c39b955151d92711800ce14f41154095bfa6 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 26 May 2015 15:59:32 +0300 Subject: [PATCH 142/148] serial: 8250_dw: support ACPI platforms with integrated DMA engine On many new Intel SoCs the UART has an integrated DMA engine (iDMA). In order to use it a special filter function is needed. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 176f18f2e3ab..d48b50641e9a 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -377,6 +377,16 @@ static int dw8250_probe_of(struct uart_port *p, return 0; } +static bool dw8250_idma_filter(struct dma_chan *chan, void *param) +{ + struct device *dev = param; + + if (dev != chan->device->dev->parent) + return false; + + return true; +} + static int dw8250_probe_acpi(struct uart_8250_port *up, struct dw8250_data *data) { @@ -389,8 +399,15 @@ static int dw8250_probe_acpi(struct uart_8250_port *up, p->serial_out = dw8250_serial_out32; p->regshift = 2; - up->dma = &data->dma; + /* Platforms with iDMA */ + if (platform_get_resource_byname(to_platform_device(up->port.dev), + IORESOURCE_MEM, "lpss_priv")) { + data->dma.rx_param = up->port.dev->parent; + data->dma.tx_param = up->port.dev->parent; + data->dma.fn = dw8250_idma_filter; + } + up->dma = &data->dma; up->dma->rxconf.src_maxburst = p->fifosize / 4; up->dma->txconf.dst_maxburst = p->fifosize / 4; From 1a8d2903cb6a92ce47cfc8841951b8227c09e505 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 29 May 2015 15:04:31 +0900 Subject: [PATCH 143/148] serial: 8250_uniphier: add UniPhier serial driver Add the driver for on-chip UART used on UniPhier SoCs. This hardware is similar to 8250, but the register mapping is slightly different: - The offset to FCR, MCR is different. - The divisor latch access bit does not exist. Instead, the divisor latch register is available at offset 9. This driver overrides serial_{in,out}, dl_{read,write} callbacks, but wants to borrow most of code from 8250_core.c. Signed-off-by: Masahiro Yamada Reviewed-by: Matthias Brugger Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_uniphier.c | 257 ++++++++++++++++++++++++ drivers/tty/serial/8250/Kconfig | 7 + drivers/tty/serial/8250/Makefile | 1 + 3 files changed, 265 insertions(+) create mode 100644 drivers/tty/serial/8250/8250_uniphier.c diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c new file mode 100644 index 000000000000..7d79425c2b09 --- /dev/null +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2015 Masahiro Yamada + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "8250.h" + +/* Most (but not all) of UniPhier UART devices have 64-depth FIFO. */ +#define UNIPHIER_UART_DEFAULT_FIFO_SIZE 64 + +#define UNIPHIER_UART_CHAR_FCR 3 /* Character / FIFO Control Register */ +#define UNIPHIER_UART_LCR_MCR 4 /* Line/Modem Control Register */ +#define UNIPHIER_UART_LCR_SHIFT 8 +#define UNIPHIER_UART_DLR 9 /* Divisor Latch Register */ + +struct uniphier8250_priv { + int line; + struct clk *clk; + spinlock_t atomic_write_lock; +}; + +/* + * The register map is slightly different from that of 8250. + * IO callbacks must be overridden for correct access to FCR, LCR, and MCR. + */ +static unsigned int uniphier_serial_in(struct uart_port *p, int offset) +{ + unsigned int valshift = 0; + + switch (offset) { + case UART_LCR: + valshift = UNIPHIER_UART_LCR_SHIFT; + /* fall through */ + case UART_MCR: + offset = UNIPHIER_UART_LCR_MCR; + break; + default: + break; + } + + offset <<= p->regshift; + + /* + * The return value must be masked with 0xff because LCR and MCR reside + * in the same register that must be accessed by 32-bit write/read. + * 8 or 16 bit access to this hardware result in unexpected behavior. + */ + return (readl(p->membase + offset) >> valshift) & 0xff; +} + +static void uniphier_serial_out(struct uart_port *p, int offset, int value) +{ + unsigned int valshift = 0; + bool normal = false; + + switch (offset) { + case UART_FCR: + offset = UNIPHIER_UART_CHAR_FCR; + break; + case UART_LCR: + valshift = UNIPHIER_UART_LCR_SHIFT; + /* Divisor latch access bit does not exist. */ + value &= ~(UART_LCR_DLAB << valshift); + /* fall through */ + case UART_MCR: + offset = UNIPHIER_UART_LCR_MCR; + break; + default: + normal = true; + break; + } + + offset <<= p->regshift; + + if (normal) { + writel(value, p->membase + offset); + } else { + /* + * Special case: two registers share the same address that + * must be 32-bit accessed. As this is not longer atomic safe, + * take a lock just in case. + */ + struct uniphier8250_priv *priv = p->private_data; + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&priv->atomic_write_lock, flags); + tmp = readl(p->membase + offset); + tmp &= ~(0xff << valshift); + tmp |= value << valshift; + writel(tmp, p->membase + offset); + spin_unlock_irqrestore(&priv->atomic_write_lock, flags); + } +} + +/* + * This hardware does not have the divisor latch access bit. + * The divisor latch register exists at different address. + * Override dl_read/write callbacks. + */ +static int uniphier_serial_dl_read(struct uart_8250_port *up) +{ + return readl(up->port.membase + UNIPHIER_UART_DLR); +} + +static void uniphier_serial_dl_write(struct uart_8250_port *up, int value) +{ + writel(value, up->port.membase + UNIPHIER_UART_DLR); +} + +static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port, + struct uniphier8250_priv *priv) +{ + int ret; + u32 prop; + struct device_node *np = dev->of_node; + + ret = of_alias_get_id(np, "serial"); + if (ret < 0) { + dev_err(dev, "failed to get alias id\n"); + return ret; + } + port->line = priv->line = ret; + + /* Get clk rate through clk driver */ + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get clock\n"); + return PTR_ERR(priv->clk); + } + + ret = clk_prepare_enable(priv->clk); + if (ret < 0) + return ret; + + port->uartclk = clk_get_rate(priv->clk); + + /* Check for fifo size */ + if (of_property_read_u32(np, "fifo-size", &prop) == 0) + port->fifosize = prop; + else + port->fifosize = UNIPHIER_UART_DEFAULT_FIFO_SIZE; + + return 0; +} + +static int uniphier_uart_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uart_8250_port up; + struct uniphier8250_priv *priv; + struct resource *regs; + void __iomem *membase; + int irq; + int ret; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(dev, "failed to get memory resource"); + return -EINVAL; + } + + membase = devm_ioremap(dev, regs->start, resource_size(regs)); + if (!membase) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "failed to get IRQ number"); + return irq; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + memset(&up, 0, sizeof(up)); + + ret = uniphier_of_serial_setup(dev, &up.port, priv); + if (ret < 0) + return ret; + + spin_lock_init(&priv->atomic_write_lock); + + up.port.dev = dev; + up.port.private_data = priv; + up.port.mapbase = regs->start; + up.port.mapsize = resource_size(regs); + up.port.membase = membase; + up.port.irq = irq; + + up.port.type = PORT_16550A; + up.port.iotype = UPIO_MEM32; + up.port.regshift = 2; + up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE; + up.capabilities = UART_CAP_FIFO; + + up.port.serial_in = uniphier_serial_in; + up.port.serial_out = uniphier_serial_out; + up.dl_read = uniphier_serial_dl_read; + up.dl_write = uniphier_serial_dl_write; + + ret = serial8250_register_8250_port(&up); + if (ret < 0) { + dev_err(dev, "failed to register 8250 port\n"); + return ret; + } + + platform_set_drvdata(pdev, priv); + + return 0; +} + +static int uniphier_uart_remove(struct platform_device *pdev) +{ + struct uniphier8250_priv *priv = platform_get_drvdata(pdev); + + serial8250_unregister_port(priv->line); + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct of_device_id uniphier_uart_match[] = { + { .compatible = "socionext,uniphier-uart" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, uniphier_uart_match); + +static struct platform_driver uniphier_uart_platform_driver = { + .probe = uniphier_uart_probe, + .remove = uniphier_uart_remove, + .driver = { + .name = "uniphier-uart", + .of_match_table = uniphier_uart_match, + }, +}; +module_platform_driver(uniphier_uart_platform_driver); + +MODULE_AUTHOR("Masahiro Yamada "); +MODULE_DESCRIPTION("UniPhier UART driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index c35070356528..3e1ae4462cba 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -342,3 +342,10 @@ config SERIAL_8250_MT6577 help If you have a Mediatek based board and want to use the serial port, say Y to this option. If unsure, say N. + +config SERIAL_8250_UNIPHIER + tristate "Support for UniPhier on-chip UART" + depends on SERIAL_8250 && ARCH_UNIPHIER + help + If you have a UniPhier based board and want to use the on-chip + serial ports, say Y to this option. If unsure, say N. diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 31e7cdc6865c..f7ca8c3ce9e7 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -23,3 +23,4 @@ obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o +obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o From d2aef35a5cc543292dae7130734c390f2a251f73 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 30 May 2015 23:14:44 +0200 Subject: [PATCH 144/148] serial: 8250: add LPC18xx/43xx UART driver Serial port driver for the 8250-based UART found on LPC18xx/43xx devices. The UART is 16550A compatible with additional features like RS485 support, synchronous mode, IrDA, and DMA. For now only basic UART and RS485 operation is supported. Signed-off-by: Joachim Eastwood Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpc18xx.c | 230 +++++++++++++++++++++++++ drivers/tty/serial/8250/Kconfig | 8 + drivers/tty/serial/8250/Makefile | 1 + 3 files changed, 239 insertions(+) create mode 100644 drivers/tty/serial/8250/8250_lpc18xx.c diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c new file mode 100644 index 000000000000..99cd478851ff --- /dev/null +++ b/drivers/tty/serial/8250/8250_lpc18xx.c @@ -0,0 +1,230 @@ +/* + * Serial port driver for NXP LPC18xx/43xx UART + * + * Copyright (C) 2015 Joachim Eastwood + * + * Based on 8250_mtk.c: + * Copyright (c) 2014 MundoReader S.L. + * Matthias Brugger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include + +#include "8250.h" + +/* Additional LPC18xx/43xx 8250 registers and bits */ +#define LPC18XX_UART_RS485CTRL (0x04c / sizeof(u32)) +#define LPC18XX_UART_RS485CTRL_NMMEN BIT(0) +#define LPC18XX_UART_RS485CTRL_DCTRL BIT(4) +#define LPC18XX_UART_RS485CTRL_OINV BIT(5) +#define LPC18XX_UART_RS485DLY (0x054 / sizeof(u32)) +#define LPC18XX_UART_RS485DLY_MAX 255 + +struct lpc18xx_uart_data { + struct uart_8250_dma dma; + struct clk *clk_uart; + struct clk *clk_reg; + int line; +}; + +static int lpc18xx_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u32 rs485_ctrl_reg = 0; + u32 rs485_dly_reg = 0; + unsigned baud_clk; + + if (rs485->flags & SER_RS485_ENABLED) + memset(rs485->padding, 0, sizeof(rs485->padding)); + else + memset(rs485, 0, sizeof(*rs485)); + + rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND; + + if (rs485->flags & SER_RS485_ENABLED) { + rs485_ctrl_reg |= LPC18XX_UART_RS485CTRL_NMMEN | + LPC18XX_UART_RS485CTRL_DCTRL; + + if (rs485->flags & SER_RS485_RTS_ON_SEND) { + rs485_ctrl_reg |= LPC18XX_UART_RS485CTRL_OINV; + rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; + } else { + rs485->flags |= SER_RS485_RTS_AFTER_SEND; + } + } + + if (rs485->delay_rts_after_send) { + baud_clk = port->uartclk / up->dl_read(up); + rs485_dly_reg = DIV_ROUND_UP(rs485->delay_rts_after_send + * baud_clk, MSEC_PER_SEC); + + if (rs485_dly_reg > LPC18XX_UART_RS485DLY_MAX) + rs485_dly_reg = LPC18XX_UART_RS485DLY_MAX; + + /* Calculate the resulting delay in ms */ + rs485->delay_rts_after_send = (rs485_dly_reg * MSEC_PER_SEC) + / baud_clk; + } + + /* Delay RTS before send not supported */ + rs485->delay_rts_before_send = 0; + + serial_out(up, LPC18XX_UART_RS485CTRL, rs485_ctrl_reg); + serial_out(up, LPC18XX_UART_RS485DLY, rs485_dly_reg); + + port->rs485 = *rs485; + + return 0; +} + +static void lpc18xx_uart_serial_out(struct uart_port *p, int offset, int value) +{ + /* + * For DMA mode one must ensure that the UART_FCR_DMA_SELECT + * bit is set when FIFO is enabled. Even if DMA is not used + * setting this bit doesn't seem to affect anything. + */ + if (offset == UART_FCR && (value & UART_FCR_ENABLE_FIFO)) + value |= UART_FCR_DMA_SELECT; + + offset = offset << p->regshift; + writel(value, p->membase + offset); +} + +static int lpc18xx_serial_probe(struct platform_device *pdev) +{ + struct lpc18xx_uart_data *data; + struct uart_8250_port uart; + struct resource *res; + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "irq not found"); + return irq; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "memory resource not found"); + return -EINVAL; + } + + memset(&uart, 0, sizeof(uart)); + + uart.port.membase = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!uart.port.membase) + return -ENOMEM; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->clk_uart = devm_clk_get(&pdev->dev, "uartclk"); + if (IS_ERR(data->clk_uart)) { + dev_err(&pdev->dev, "uart clock not found\n"); + return PTR_ERR(data->clk_uart); + } + + data->clk_reg = devm_clk_get(&pdev->dev, "reg"); + if (IS_ERR(data->clk_reg)) { + dev_err(&pdev->dev, "reg clock not found\n"); + return PTR_ERR(data->clk_reg); + } + + ret = clk_prepare_enable(data->clk_reg); + if (ret) { + dev_err(&pdev->dev, "unable to enable reg clock\n"); + return ret; + } + + ret = clk_prepare_enable(data->clk_uart); + if (ret) { + dev_err(&pdev->dev, "unable to enable uart clock\n"); + goto dis_clk_reg; + } + + ret = of_alias_get_id(pdev->dev.of_node, "serial"); + if (ret >= 0) + uart.port.line = ret; + + data->dma.rx_param = data; + data->dma.tx_param = data; + + spin_lock_init(&uart.port.lock); + uart.port.dev = &pdev->dev; + uart.port.irq = irq; + uart.port.iotype = UPIO_MEM32; + uart.port.mapbase = res->start; + uart.port.regshift = 2; + uart.port.type = PORT_16550A; + uart.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST; + uart.port.uartclk = clk_get_rate(data->clk_uart); + uart.port.private_data = data; + uart.port.rs485_config = lpc18xx_rs485_config; + uart.port.serial_out = lpc18xx_uart_serial_out; + + uart.dma = &data->dma; + uart.dma->rxconf.src_maxburst = 1; + uart.dma->txconf.dst_maxburst = 1; + + ret = serial8250_register_8250_port(&uart); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register 8250 port\n"); + goto dis_uart_clk; + } + + data->line = ret; + platform_set_drvdata(pdev, data); + + return 0; + +dis_uart_clk: + clk_disable_unprepare(data->clk_uart); +dis_clk_reg: + clk_disable_unprepare(data->clk_reg); + return ret; +} + +static int lpc18xx_serial_remove(struct platform_device *pdev) +{ + struct lpc18xx_uart_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); + clk_disable_unprepare(data->clk_uart); + clk_disable_unprepare(data->clk_reg); + + return 0; +} + +static const struct of_device_id lpc18xx_serial_match[] = { + { .compatible = "nxp,lpc1850-uart" }, + { }, +}; +MODULE_DEVICE_TABLE(of, lpc18xx_serial_match); + +static struct platform_driver lpc18xx_serial_driver = { + .probe = lpc18xx_serial_probe, + .remove = lpc18xx_serial_remove, + .driver = { + .name = "lpc18xx-uart", + .of_match_table = lpc18xx_serial_match, + }, +}; +module_platform_driver(lpc18xx_serial_driver); + +MODULE_AUTHOR("Joachim Eastwood "); +MODULE_DESCRIPTION("Serial port driver NXP LPC18xx/43xx devices"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 3e1ae4462cba..a74a8e4717d4 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -336,6 +336,14 @@ config SERIAL_8250_FINTEK LPC to 4 UART. This device has some RS485 functionality not available through the PNP driver. If unsure, say N. +config SERIAL_8250_LPC18XX + bool "NXP LPC18xx/43xx serial port support" + depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST) + default ARCH_LPC18XX + help + If you have a LPC18xx/43xx based board and want to use the + serial port, say Y to this option. If unsure, say Y. + config SERIAL_8250_MT6577 bool "Mediatek serial port support" depends on SERIAL_8250 && ARCH_MEDIATEK diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index f7ca8c3ce9e7..6fa22ffad63d 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -22,5 +22,6 @@ obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o +obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o From a7a215283703231bcde0740710569f6c0e39ac53 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 30 May 2015 23:14:45 +0200 Subject: [PATCH 145/148] doc: dt: add documentation for nxp,lpc1850-uart Add device tree binding documentation for nxp,lpc1850-uart. Signed-off-by: Joachim Eastwood Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/nxp,lpc1850-uart.txt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/nxp,lpc1850-uart.txt diff --git a/Documentation/devicetree/bindings/serial/nxp,lpc1850-uart.txt b/Documentation/devicetree/bindings/serial/nxp,lpc1850-uart.txt new file mode 100644 index 000000000000..04e23e63ee4f --- /dev/null +++ b/Documentation/devicetree/bindings/serial/nxp,lpc1850-uart.txt @@ -0,0 +1,28 @@ +* NXP LPC1850 UART + +Required properties: +- compatible : "nxp,lpc1850-uart", "ns16550a". +- reg : offset and length of the register set for the device. +- interrupts : should contain uart interrupt. +- clocks : phandle to the input clocks. +- clock-names : required elements: "uartclk", "reg". + +Optional properties: +- dmas : Two or more DMA channel specifiers following the + convention outlined in bindings/dma/dma.txt +- dma-names : Names for the dma channels, if present. There must + be at least one channel named "tx" for transmit + and named "rx" for receive. + +Since it's also possible to also use the of_serial.c driver all +parameters from 8250.txt also apply but are optional. + +Example: +uart0: serial@40081000 { + compatible = "nxp,lpc1850-uart", "ns16550a"; + reg = <0x40081000 0x1000>; + reg-shift = <2>; + interrupts = <24>; + clocks = <&ccu2 CLK_APB0_UART0>, <&ccu1 CLK_CPU_UART0>; + clock-names = "uartclk", "reg"; +}; From 1c4b1d73bacc546ba4e42f7eb4cb88c54139820b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 27 May 2015 13:57:46 +0200 Subject: [PATCH 146/148] tty: move linux/gsmmux.h to uapi linux/gsmmux.h defines a user interface and therefore should be installed with other headers. Make the file include: * linux/if.h for IFNAMSIZ * linux/ioctl.h for _IO* macros Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/Kbuild | 1 + include/{ => uapi}/linux/gsmmux.h | 3 +++ 2 files changed, 4 insertions(+) rename include/{ => uapi}/linux/gsmmux.h (94%) diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 1a0006a76b00..1d3db6a74d1f 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -138,6 +138,7 @@ header-y += genetlink.h header-y += gen_stats.h header-y += gfs2_ondisk.h header-y += gigaset_dev.h +header-y += gsmmux.h header-y += hdlcdrv.h header-y += hdlc.h header-y += hdreg.h diff --git a/include/linux/gsmmux.h b/include/uapi/linux/gsmmux.h similarity index 94% rename from include/linux/gsmmux.h rename to include/uapi/linux/gsmmux.h index c25e9477f7c3..c06742d52856 100644 --- a/include/linux/gsmmux.h +++ b/include/uapi/linux/gsmmux.h @@ -1,6 +1,9 @@ #ifndef _LINUX_GSMMUX_H #define _LINUX_GSMMUX_H +#include +#include + struct gsm_config { unsigned int adaption; From ee83bd3b6483d59b0d4b509e46dae688d8e5759d Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 9 Jun 2015 23:35:00 -0700 Subject: [PATCH 147/148] serial: omap: Switch wake-up interrupt to generic wakeirq We can now use generic wakeirq handling and remove the custom handling for the wake-up interrupts. Acked-by: Greg Kroah-Hartman Signed-off-by: Tony Lindgren --- drivers/tty/serial/omap-serial.c | 35 ++++---------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 7f49172ccd86..7a2172b5e93c 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -160,7 +161,6 @@ struct uart_omap_port { unsigned long port_activity; int context_loss_cnt; u32 errata; - u8 wakeups_enabled; u32 features; int rts_gpio; @@ -209,28 +209,11 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up) return pdata->get_context_loss_count(up->dev); } -static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up, - bool enable) -{ - if (!up->wakeirq) - return; - - if (enable) - enable_irq(up->wakeirq); - else - disable_irq_nosync(up->wakeirq); -} - +/* REVISIT: Remove this when omap3 boots in device tree only mode */ static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) { struct omap_uart_port_info *pdata = dev_get_platdata(up->dev); - if (enable == up->wakeups_enabled) - return; - - serial_omap_enable_wakeirq(up, enable); - up->wakeups_enabled = enable; - if (!pdata || !pdata->enable_wakeup) return; @@ -750,13 +733,11 @@ static int serial_omap_startup(struct uart_port *port) /* Optional wake-up IRQ */ if (up->wakeirq) { - retval = request_irq(up->wakeirq, serial_omap_irq, - up->port.irqflags, up->name, up); + retval = dev_pm_set_dedicated_wake_irq(up->dev, up->wakeirq); if (retval) { free_irq(up->port.irq, up); return retval; } - disable_irq(up->wakeirq); } dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line); @@ -845,8 +826,7 @@ static void serial_omap_shutdown(struct uart_port *port) pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); free_irq(up->port.irq, up); - if (up->wakeirq) - free_irq(up->wakeirq, up); + dev_pm_clear_wake_irq(up->dev); } static void serial_omap_uart_qos_work(struct work_struct *work) @@ -1139,13 +1119,6 @@ serial_omap_pm(struct uart_port *port, unsigned int state, serial_out(up, UART_EFR, efr); serial_out(up, UART_LCR, 0); - if (!device_may_wakeup(up->dev)) { - if (!state) - pm_runtime_forbid(up->dev); - else - pm_runtime_allow(up->dev); - } - pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); } From a3e362f18c01f28abcb6d8e5d00be8b5d97ea09d Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 9 Jun 2015 23:35:00 -0700 Subject: [PATCH 148/148] serial: 8250_omap: Move wake-up interrupt to generic wakeirq We can now use generic wakeirq handling and remove the custom handling for the wake-up interrupts. Acked-by: Greg Kroah-Hartman Signed-off-by: Tony Lindgren --- drivers/tty/serial/8250/8250_omap.c | 58 ++--------------------------- 1 file changed, 4 insertions(+), 54 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 978204333c94..d75a66c72750 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "8250.h" @@ -552,17 +553,6 @@ static void omap8250_uart_qos_work(struct work_struct *work) pm_qos_update_request(&priv->pm_qos_request, priv->latency); } -static irqreturn_t omap_wake_irq(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - int ret; - - ret = port->handle_irq(port); - if (ret) - return IRQ_HANDLED; - return IRQ_NONE; -} - #ifdef CONFIG_SERIAL_8250_DMA static int omap_8250_dma_handle_irq(struct uart_port *port); #endif @@ -596,11 +586,9 @@ static int omap_8250_startup(struct uart_port *port) int ret; if (priv->wakeirq) { - ret = request_irq(priv->wakeirq, omap_wake_irq, - port->irqflags, "uart wakeup irq", port); + ret = dev_pm_set_dedicated_wake_irq(port->dev, priv->wakeirq); if (ret) return ret; - disable_irq(priv->wakeirq); } pm_runtime_get_sync(port->dev); @@ -649,8 +637,7 @@ static int omap_8250_startup(struct uart_port *port) err: pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); - if (priv->wakeirq) - free_irq(priv->wakeirq, port); + dev_pm_clear_wake_irq(port->dev); return ret; } @@ -682,10 +669,8 @@ static void omap_8250_shutdown(struct uart_port *port) pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); - free_irq(port->irq, port); - if (priv->wakeirq) - free_irq(priv->wakeirq, port); + dev_pm_clear_wake_irq(port->dev); } static void omap_8250_throttle(struct uart_port *port) @@ -1226,31 +1211,6 @@ static int omap8250_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM - -static inline void omap8250_enable_wakeirq(struct omap8250_priv *priv, - bool enable) -{ - if (!priv->wakeirq) - return; - - if (enable) - enable_irq(priv->wakeirq); - else - disable_irq_nosync(priv->wakeirq); -} - -static void omap8250_enable_wakeup(struct omap8250_priv *priv, - bool enable) -{ - if (enable == priv->wakeups_enabled) - return; - - omap8250_enable_wakeirq(priv, enable); - priv->wakeups_enabled = enable; -} -#endif - #ifdef CONFIG_PM_SLEEP static int omap8250_prepare(struct device *dev) { @@ -1277,11 +1237,6 @@ static int omap8250_suspend(struct device *dev) serial8250_suspend_port(priv->line); flush_work(&priv->qos_work); - - if (device_may_wakeup(dev)) - omap8250_enable_wakeup(priv, true); - else - omap8250_enable_wakeup(priv, false); return 0; } @@ -1289,9 +1244,6 @@ static int omap8250_resume(struct device *dev) { struct omap8250_priv *priv = dev_get_drvdata(dev); - if (device_may_wakeup(dev)) - omap8250_enable_wakeup(priv, false); - serial8250_resume_port(priv->line); return 0; } @@ -1333,7 +1285,6 @@ static int omap8250_runtime_suspend(struct device *dev) return -EBUSY; } - omap8250_enable_wakeup(priv, true); if (up->dma) omap_8250_rx_dma(up, UART_IIR_RX_TIMEOUT); @@ -1354,7 +1305,6 @@ static int omap8250_runtime_resume(struct device *dev) return 0; up = serial8250_get_port(priv->line); - omap8250_enable_wakeup(priv, false); loss_cntx = omap8250_lost_context(up); if (loss_cntx)