* Re: [RFT/PATCH] serial: omap: prevent resume if device is not suspended.
From: Paul Walmsley @ 2012-10-11 18:28 UTC (permalink / raw)
To: Poddar\, Sourav, Felipe Balbi
Cc: Kevin Hilman, Russell King - ARM Linux, gregkh, tony,
linux-kernel, santosh.shilimkar, linux-serial, linux-omap,
linux-arm-kernel, alan
In-Reply-To: <87ipas2y4h.fsf@deeprootsystems.com>
Hi Sourav, Felipe,
any progress on fixing the N800 problem? Would be good to keep it booting
since we use it as our primary 2420 test platform.
- Paul
^ permalink raw reply
* [PATCH] serial: clps711x: reworked driver version
From: Alexander Shiyan @ 2012-10-11 15:51 UTC (permalink / raw)
To: linux-serial; +Cc: Alan Cox, Greg Kroah-Hartman, Alexander Shiyan
This patch presents reworked version of CLPS711X serial driver.
The changes from the old version:
- Driver converted to platform_device.
- Using CPU clock subsystem for getting base UART speed, since CPU can run
on different speeds.
- Remove console_initcall and make console dynamically. Earler messages in
this case can be retrieved by using "earlyprintk" kernel option.
- Using resource-managed functions (devm_xx).
- Make all variables dynamically (reduce BSS).
- Cleanup code & comments.
Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
---
drivers/tty/serial/clps711x.c | 594 +++++++++++++++++++----------------------
1 files changed, 277 insertions(+), 317 deletions(-)
diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c
index d0f719f..77b83be 100644
--- a/drivers/tty/serial/clps711x.c
+++ b/drivers/tty/serial/clps711x.c
@@ -10,15 +10,6 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
@@ -26,172 +17,169 @@
#endif
#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/spinlock.h>
#include <linux/device.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
+#include <linux/console.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
#include <mach/hardware.h>
-#include <asm/irq.h>
-
-#define UART_NR 2
-#define SERIAL_CLPS711X_MAJOR 204
-#define SERIAL_CLPS711X_MINOR 40
-#define SERIAL_CLPS711X_NR UART_NR
-
-/*
- * We use the relevant SYSCON register as a base address for these ports.
- */
-#define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1)
-#define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1)
-#define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1)
-#define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1)
-
-#define TX_IRQ(port) ((port)->irq)
-#define RX_IRQ(port) ((port)->irq + 1)
-
-#define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR)
-
-#define tx_enabled(port) ((port)->unused[0])
+#define UART_CLPS711X_NAME "uart-clps711x"
+#define UART_CLPS711X_NR 2
+#define UART_CLPS711X_MAJOR 204
+#define UART_CLPS711X_MINOR 40
+
+#define UBRLCR(port) ((port)->line ? UBRLCR2 : UBRLCR1)
+#define UARTDR(port) ((port)->line ? UARTDR2 : UARTDR1)
+#define SYSFLG(port) ((port)->line ? SYSFLG2 : SYSFLG1)
+#define SYSCON(port) ((port)->line ? SYSCON2 : SYSCON1)
+#define TX_IRQ(port) ((port)->line ? IRQ_UTXINT2 : IRQ_UTXINT1)
+#define RX_IRQ(port) ((port)->line ? IRQ_URXINT2 : IRQ_URXINT1)
+
+struct clps711x_port {
+ struct uart_driver uart;
+ struct clk *uart_clk;
+ struct uart_port port[UART_CLPS711X_NR];
+ int tx_enabled[UART_CLPS711X_NR];
+#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
+ struct console console;
+#endif
+};
-static void clps711xuart_stop_tx(struct uart_port *port)
+static void uart_clps711x_stop_tx(struct uart_port *port)
{
- if (tx_enabled(port)) {
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+
+ if (s->tx_enabled[port->line]) {
disable_irq(TX_IRQ(port));
- tx_enabled(port) = 0;
+ s->tx_enabled[port->line] = 0;
}
}
-static void clps711xuart_start_tx(struct uart_port *port)
+static void uart_clps711x_start_tx(struct uart_port *port)
{
- if (!tx_enabled(port)) {
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+
+ if (!s->tx_enabled[port->line]) {
enable_irq(TX_IRQ(port));
- tx_enabled(port) = 1;
+ s->tx_enabled[port->line] = 1;
}
}
-static void clps711xuart_stop_rx(struct uart_port *port)
+static void uart_clps711x_stop_rx(struct uart_port *port)
{
disable_irq(RX_IRQ(port));
}
-static void clps711xuart_enable_ms(struct uart_port *port)
+static void uart_clps711x_enable_ms(struct uart_port *port)
{
+ /* Do nothing */
}
-static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id)
+static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_struct *tty = tty_port_tty_get(&port->state->port);
unsigned int status, ch, flg;
- status = clps_readl(SYSFLG(port));
- while (!(status & SYSFLG_URXFE)) {
- ch = clps_readl(UARTDR(port));
+ if (!tty)
+ return IRQ_HANDLED;
- port->icount.rx++;
+ for (;;) {
+ status = clps_readl(SYSFLG(port));
+ if (status & SYSFLG_URXFE)
+ break;
+
+ ch = clps_readw(UARTDR(port));
+ status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR);
+ ch &= 0xff;
+ port->icount.rx++;
flg = TTY_NORMAL;
- /*
- * Note that the error handling code is
- * out of the main execution path
- */
- if (unlikely(ch & UART_ANY_ERR)) {
- if (ch & UARTDR_PARERR)
+ if (unlikely(status)) {
+ if (status & UARTDR_PARERR)
port->icount.parity++;
- else if (ch & UARTDR_FRMERR)
+ else if (status & UARTDR_FRMERR)
port->icount.frame++;
- if (ch & UARTDR_OVERR)
+ else if (status & UARTDR_OVERR)
port->icount.overrun++;
- ch &= port->read_status_mask;
+ status &= port->read_status_mask;
- if (ch & UARTDR_PARERR)
+ if (status & UARTDR_PARERR)
flg = TTY_PARITY;
- else if (ch & UARTDR_FRMERR)
+ else if (status & UARTDR_FRMERR)
flg = TTY_FRAME;
-
-#ifdef SUPPORT_SYSRQ
- port->sysrq = 0;
-#endif
+ else if (status & UARTDR_OVERR)
+ flg = TTY_OVERRUN;
}
if (uart_handle_sysrq_char(port, ch))
- goto ignore_char;
+ continue;
- /*
- * CHECK: does overrun affect the current character?
- * ASSUMPTION: it does not.
- */
- uart_insert_char(port, ch, UARTDR_OVERR, ch, flg);
+ if (status & port->ignore_status_mask)
+ continue;
- ignore_char:
- status = clps_readl(SYSFLG(port));
+ uart_insert_char(port, status, UARTDR_OVERR, ch, flg);
}
+
tty_flip_buffer_push(tty);
+
+ tty_kref_put(tty);
+
return IRQ_HANDLED;
}
-static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id)
+static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
struct circ_buf *xmit = &port->state->xmit;
- int count;
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
if (port->x_char) {
- clps_writel(port->x_char, UARTDR(port));
+ clps_writew(port->x_char, UARTDR(port));
port->icount.tx++;
port->x_char = 0;
return IRQ_HANDLED;
}
- if (uart_circ_empty(xmit) || uart_tx_stopped(port))
- goto disable_tx_irq;
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ disable_irq_nosync(TX_IRQ(port));
+ s->tx_enabled[port->line] = 0;
+ return IRQ_HANDLED;
+ }
- count = port->fifosize >> 1;
- do {
- clps_writel(xmit->buf[xmit->tail], UARTDR(port));
+ while (!uart_circ_empty(xmit)) {
+ clps_writew(xmit->buf[xmit->tail], UARTDR(port));
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
- if (uart_circ_empty(xmit))
+ if (clps_readl(SYSFLG(port) & SYSFLG_UTXFF))
break;
- } while (--count > 0);
+ }
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
- if (uart_circ_empty(xmit)) {
- disable_tx_irq:
- disable_irq_nosync(TX_IRQ(port));
- tx_enabled(port) = 0;
- }
-
return IRQ_HANDLED;
}
-static unsigned int clps711xuart_tx_empty(struct uart_port *port)
+static unsigned int uart_clps711x_tx_empty(struct uart_port *port)
{
- unsigned int status = clps_readl(SYSFLG(port));
- return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT;
+ return (clps_readl(SYSFLG(port) & SYSFLG_UBUSY)) ? 0 : TIOCSER_TEMT;
}
-static unsigned int clps711xuart_get_mctrl(struct uart_port *port)
+static unsigned int uart_clps711x_get_mctrl(struct uart_port *port)
{
- unsigned int port_addr;
- unsigned int result = 0;
- unsigned int status;
+ unsigned int status, result = 0;
- port_addr = SYSFLG(port);
- if (port_addr == SYSFLG1) {
+ if (port->line == 0) {
status = clps_readl(SYSFLG1);
if (status & SYSFLG1_DCD)
result |= TIOCM_CAR;
@@ -199,104 +187,87 @@ static unsigned int clps711xuart_get_mctrl(struct uart_port *port)
result |= TIOCM_DSR;
if (status & SYSFLG1_CTS)
result |= TIOCM_CTS;
- }
+ } else
+ result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
return result;
}
-static void
-clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl)
+static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
+ /* Do nothing */
}
-static void clps711xuart_break_ctl(struct uart_port *port, int break_state)
+static void uart_clps711x_break_ctl(struct uart_port *port, int break_state)
{
unsigned long flags;
unsigned int ubrlcr;
spin_lock_irqsave(&port->lock, flags);
+
ubrlcr = clps_readl(UBRLCR(port));
- if (break_state == -1)
+ if (break_state)
ubrlcr |= UBRLCR_BREAK;
else
ubrlcr &= ~UBRLCR_BREAK;
clps_writel(ubrlcr, UBRLCR(port));
+
spin_unlock_irqrestore(&port->lock, flags);
}
-static int clps711xuart_startup(struct uart_port *port)
+static int uart_clps711x_startup(struct uart_port *port)
{
- unsigned int syscon;
- int retval;
-
- tx_enabled(port) = 1;
-
- /*
- * Allocate the IRQs
- */
- retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0,
- "clps711xuart_tx", port);
- if (retval)
- return retval;
-
- retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0,
- "clps711xuart_rx", port);
- if (retval) {
- free_irq(TX_IRQ(port), port);
- return retval;
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+ int ret;
+
+ s->tx_enabled[port->line] = 1;
+
+ /* Allocate the IRQs */
+ ret = devm_request_irq(port->dev, TX_IRQ(port), uart_clps711x_int_tx,
+ 0, UART_CLPS711X_NAME " TX", port);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(port->dev, RX_IRQ(port), uart_clps711x_int_rx,
+ 0, UART_CLPS711X_NAME " RX", port);
+ if (ret) {
+ devm_free_irq(port->dev, TX_IRQ(port), port);
+ return ret;
}
- /*
- * enable the port
- */
- syscon = clps_readl(SYSCON(port));
- syscon |= SYSCON_UARTEN;
- clps_writel(syscon, SYSCON(port));
+ /* Enable the port */
+ clps_writel(clps_readl(SYSCON(port)) | SYSCON_UARTEN, SYSCON(port));
return 0;
}
-static void clps711xuart_shutdown(struct uart_port *port)
+static void uart_clps711x_shutdown(struct uart_port *port)
{
- unsigned int ubrlcr, syscon;
-
- /*
- * Free the interrupt
- */
- free_irq(TX_IRQ(port), port); /* TX interrupt */
- free_irq(RX_IRQ(port), port); /* RX interrupt */
+ /* Free the interrupts */
+ devm_free_irq(port->dev, TX_IRQ(port), port); /* TX interrupt */
+ devm_free_irq(port->dev, RX_IRQ(port), port); /* RX interrupt */
- /*
- * disable the port
- */
- syscon = clps_readl(SYSCON(port));
- syscon &= ~SYSCON_UARTEN;
- clps_writel(syscon, SYSCON(port));
+ /* Disable the port */
+ clps_writel(clps_readl(SYSCON(port)) & ~SYSCON_UARTEN, SYSCON(port));
- /*
- * disable break condition and fifos
- */
- ubrlcr = clps_readl(UBRLCR(port));
- ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK);
- clps_writel(ubrlcr, UBRLCR(port));
+ /* Disable break condition */
+ clps_writel(clps_readl(UBRLCR(port)) & ~UBRLCR_BREAK, UBRLCR(port));
}
-static void
-clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
+static void uart_clps711x_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
{
unsigned int ubrlcr, baud, quot;
unsigned long flags;
- /*
- * We don't implement CREAD.
- */
- termios->c_cflag |= CREAD;
+ /* Mask termios capabilities we don't support */
+ termios->c_cflag &= ~CMSPAR;
+ termios->c_iflag &= ~(BRKINT | IGNBRK);
- /*
- * Ask the core to calculate the divisor for us.
- */
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+ /* Ask the core to calculate the divisor for us */
+ baud = uart_get_baud_rate(port, termios, old, port->uartclk / 4096,
+ port->uartclk / 16);
quot = uart_get_divisor(port, baud);
switch (termios->c_cflag & CSIZE) {
@@ -309,160 +280,117 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios,
case CS7:
ubrlcr = UBRLCR_WRDLEN7;
break;
- default: // CS8
+ case CS8:
+ default:
ubrlcr = UBRLCR_WRDLEN8;
break;
}
+
if (termios->c_cflag & CSTOPB)
ubrlcr |= UBRLCR_XSTOP;
+
if (termios->c_cflag & PARENB) {
ubrlcr |= UBRLCR_PRTEN;
if (!(termios->c_cflag & PARODD))
ubrlcr |= UBRLCR_EVENPRT;
}
- if (port->fifosize > 1)
- ubrlcr |= UBRLCR_FIFOEN;
- spin_lock_irqsave(&port->lock, flags);
+ /* Enable FIFO */
+ ubrlcr |= UBRLCR_FIFOEN;
- /*
- * Update the per-port timeout.
- */
- uart_update_timeout(port, termios->c_cflag, baud);
+ spin_lock_irqsave(&port->lock, flags);
+ /* Set read status mask */
port->read_status_mask = UARTDR_OVERR;
if (termios->c_iflag & INPCK)
port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
- /*
- * Characters to ignore
- */
+ /* Set status ignore mask */
port->ignore_status_mask = 0;
- if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR;
- if (termios->c_iflag & IGNBRK) {
- /*
- * If we're ignoring parity and break indicators,
- * ignore overruns to (for real raw support).
- */
- if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= UARTDR_OVERR;
- }
+ if (!(termios->c_cflag & CREAD))
+ port->ignore_status_mask |= UARTDR_OVERR | UARTDR_PARERR |
+ UARTDR_FRMERR;
- quot -= 1;
+ uart_update_timeout(port, termios->c_cflag, baud);
- clps_writel(ubrlcr | quot, UBRLCR(port));
+ clps_writel(ubrlcr | (quot - 1), UBRLCR(port));
spin_unlock_irqrestore(&port->lock, flags);
}
-static const char *clps711xuart_type(struct uart_port *port)
+static const char *uart_clps711x_type(struct uart_port *port)
{
- return port->type == PORT_CLPS711X ? "CLPS711x" : NULL;
+ return (port->type == PORT_CLPS711X) ? "CLPS711X" : NULL;
}
-/*
- * Configure/autoconfigure the port.
- */
-static void clps711xuart_config_port(struct uart_port *port, int flags)
+static void uart_clps711x_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE)
port->type = PORT_CLPS711X;
}
-static void clps711xuart_release_port(struct uart_port *port)
+static void uart_clps711x_release_port(struct uart_port *port)
{
+ /* Do nothing */
}
-static int clps711xuart_request_port(struct uart_port *port)
+static int uart_clps711x_request_port(struct uart_port *port)
{
+ /* Do nothing */
return 0;
}
-static struct uart_ops clps711x_pops = {
- .tx_empty = clps711xuart_tx_empty,
- .set_mctrl = clps711xuart_set_mctrl_null,
- .get_mctrl = clps711xuart_get_mctrl,
- .stop_tx = clps711xuart_stop_tx,
- .start_tx = clps711xuart_start_tx,
- .stop_rx = clps711xuart_stop_rx,
- .enable_ms = clps711xuart_enable_ms,
- .break_ctl = clps711xuart_break_ctl,
- .startup = clps711xuart_startup,
- .shutdown = clps711xuart_shutdown,
- .set_termios = clps711xuart_set_termios,
- .type = clps711xuart_type,
- .config_port = clps711xuart_config_port,
- .release_port = clps711xuart_release_port,
- .request_port = clps711xuart_request_port,
-};
-
-static struct uart_port clps711x_ports[UART_NR] = {
- {
- .iobase = SYSCON1,
- .irq = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */
- .uartclk = 3686400,
- .fifosize = 16,
- .ops = &clps711x_pops,
- .line = 0,
- .flags = UPF_BOOT_AUTOCONF,
- },
- {
- .iobase = SYSCON2,
- .irq = IRQ_UTXINT2, /* IRQ_URXINT2 */
- .uartclk = 3686400,
- .fifosize = 16,
- .ops = &clps711x_pops,
- .line = 1,
- .flags = UPF_BOOT_AUTOCONF,
- }
+static const struct uart_ops uart_clps711x_ops = {
+ .tx_empty = uart_clps711x_tx_empty,
+ .set_mctrl = uart_clps711x_set_mctrl,
+ .get_mctrl = uart_clps711x_get_mctrl,
+ .stop_tx = uart_clps711x_stop_tx,
+ .start_tx = uart_clps711x_start_tx,
+ .stop_rx = uart_clps711x_stop_rx,
+ .enable_ms = uart_clps711x_enable_ms,
+ .break_ctl = uart_clps711x_break_ctl,
+ .startup = uart_clps711x_startup,
+ .shutdown = uart_clps711x_shutdown,
+ .set_termios = uart_clps711x_set_termios,
+ .type = uart_clps711x_type,
+ .config_port = uart_clps711x_config_port,
+ .release_port = uart_clps711x_release_port,
+ .request_port = uart_clps711x_request_port,
};
#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
-static void clps711xuart_console_putchar(struct uart_port *port, int ch)
+static void uart_clps711x_console_putchar(struct uart_port *port, int ch)
{
while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF)
barrier();
- clps_writel(ch, UARTDR(port));
+
+ clps_writew(ch, UARTDR(port));
}
-/*
- * Print a string to the serial port trying not to disturb
- * any possible real use of the port...
- *
- * The console_lock must be held when we get here.
- *
- * Note that this is called with interrupts already disabled
- */
-static void
-clps711xuart_console_write(struct console *co, const char *s,
- unsigned int count)
+static void uart_clps711x_console_write(struct console *co, const char *c,
+ unsigned n)
{
- struct uart_port *port = clps711x_ports + co->index;
- unsigned int status, syscon;
+ struct clps711x_port *s = (struct clps711x_port *)co->data;
+ struct uart_port *port = &s->port[co->index];
+ u32 syscon;
- /*
- * Ensure that the port is enabled.
- */
+ /* Ensure that the port is enabled */
syscon = clps_readl(SYSCON(port));
clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));
- uart_console_write(port, s, count, clps711xuart_console_putchar);
+ uart_console_write(port, c, n, uart_clps711x_console_putchar);
- /*
- * Finally, wait for transmitter to become empty
- * and restore the uart state.
- */
- do {
- status = clps_readl(SYSFLG(port));
- } while (status & SYSFLG_UBUSY);
+ /* Wait for transmitter to become empty */
+ while (clps_readl(SYSFLG(port)) & SYSFLG_UBUSY)
+ barrier();
+ /* Restore the uart state */
clps_writel(syscon, SYSCON(port));
}
-static void __init
-clps711xuart_console_get_options(struct uart_port *port, int *baud,
- int *parity, int *bits)
+static void uart_clps711x_console_get_options(struct uart_port *port,
+ int *baud, int *parity,
+ int *bits)
{
if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) {
unsigned int ubrlcr, quot;
@@ -487,92 +415,124 @@ clps711xuart_console_get_options(struct uart_port *port, int *baud,
}
}
-static int __init clps711xuart_console_setup(struct console *co, char *options)
+static int uart_clps711x_console_setup(struct console *co, char *options)
{
- struct uart_port *port;
- int baud = 38400;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
-
- /*
- * Check whether an invalid uart number has been specified, and
- * if so, search for the first available port that does have
- * console support.
- */
- port = uart_get_console(clps711x_ports, UART_NR, co);
+ int baud = 38400, bits = 8, parity = 'n', flow = 'n';
+ struct clps711x_port *s = (struct clps711x_port *)co->data;
+ struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0];
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
- clps711xuart_console_get_options(port, &baud, &parity, &bits);
+ uart_clps711x_console_get_options(port, &baud, &parity, &bits);
return uart_set_options(port, co, baud, parity, bits, flow);
}
+#endif
-static struct uart_driver clps711x_reg;
-static struct console clps711x_console = {
- .name = "ttyCL",
- .write = clps711xuart_console_write,
- .device = uart_console_device,
- .setup = clps711xuart_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &clps711x_reg,
-};
-
-static int __init clps711xuart_console_init(void)
+static int __devinit uart_clps711x_probe(struct platform_device *pdev)
{
- register_console(&clps711x_console);
- return 0;
-}
-console_initcall(clps711xuart_console_init);
+ struct clps711x_port *s;
+ int ret, i;
+
+ s = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_port), GFP_KERNEL);
+ if (!s) {
+ dev_err(&pdev->dev, "Error allocating port structure\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, s);
-#define CLPS711X_CONSOLE &clps711x_console
-#else
-#define CLPS711X_CONSOLE NULL
+ s->uart_clk = devm_clk_get(&pdev->dev, "uart");
+ if (IS_ERR(s->uart_clk)) {
+ dev_err(&pdev->dev, "Can't get UART clocks\n");
+ ret = PTR_ERR(s->uart_clk);
+ goto err_out;
+ }
+
+ s->uart.owner = THIS_MODULE;
+ s->uart.dev_name = "ttyCL";
+ s->uart.major = UART_CLPS711X_MAJOR;
+ s->uart.minor = UART_CLPS711X_MINOR;
+ s->uart.nr = UART_CLPS711X_NR;
+#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
+ s->uart.cons = &s->console;
+ s->uart.cons->device = uart_console_device;
+ s->uart.cons->write = uart_clps711x_console_write;
+ s->uart.cons->setup = uart_clps711x_console_setup;
+ s->uart.cons->flags = CON_PRINTBUFFER;
+ s->uart.cons->index = -1;
+ s->uart.cons->data = s;
+ strcpy(s->uart.cons->name, "ttyCL");
#endif
+ ret = uart_register_driver(&s->uart);
+ if (ret) {
+ dev_err(&pdev->dev, "Registering UART driver failed\n");
+ devm_clk_put(&pdev->dev, s->uart_clk);
+ goto err_out;
+ }
-static struct uart_driver clps711x_reg = {
- .driver_name = "ttyCL",
- .dev_name = "ttyCL",
- .major = SERIAL_CLPS711X_MAJOR,
- .minor = SERIAL_CLPS711X_MINOR,
- .nr = UART_NR,
+ for (i = 0; i < UART_CLPS711X_NR; i++) {
+ s->port[i].line = i;
+ s->port[i].dev = &pdev->dev;
+ s->port[i].irq = TX_IRQ(&s->port[i]);
+ s->port[i].iobase = SYSCON(&s->port[i]);
+ s->port[i].type = PORT_CLPS711X;
+ s->port[i].fifosize = 16;
+ s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE;
+ s->port[i].uartclk = clk_get_rate(s->uart_clk);
+ s->port[i].ops = &uart_clps711x_ops;
+ WARN_ON(uart_add_one_port(&s->uart, &s->port[i]));
+ }
- .cons = CLPS711X_CONSOLE,
-};
+ return 0;
-static int __init clps711xuart_init(void)
-{
- int ret, i;
+err_out:
+ platform_set_drvdata(pdev, NULL);
- printk(KERN_INFO "Serial: CLPS711x driver\n");
+ return ret;
+}
- ret = uart_register_driver(&clps711x_reg);
- if (ret)
- return ret;
+static int __devexit uart_clps711x_remove(struct platform_device *pdev)
+{
+ struct clps711x_port *s = platform_get_drvdata(pdev);
+ int i;
- for (i = 0; i < UART_NR; i++)
- uart_add_one_port(&clps711x_reg, &clps711x_ports[i]);
+ for (i = 0; i < UART_CLPS711X_NR; i++)
+ uart_remove_one_port(&s->uart, &s->port[i]);
+
+ devm_clk_put(&pdev->dev, s->uart_clk);
+ uart_unregister_driver(&s->uart);
+ platform_set_drvdata(pdev, NULL);
return 0;
}
-static void __exit clps711xuart_exit(void)
-{
- int i;
+static struct platform_driver clps711x_uart_driver = {
+ .driver = {
+ .name = UART_CLPS711X_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = uart_clps711x_probe,
+ .remove = __devexit_p(uart_clps711x_remove),
+};
+module_platform_driver(clps711x_uart_driver);
- for (i = 0; i < UART_NR; i++)
- uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]);
+static struct platform_device clps711x_uart_device = {
+ .name = UART_CLPS711X_NAME,
+};
- uart_unregister_driver(&clps711x_reg);
+static int __init uart_clps711x_init(void)
+{
+ return platform_device_register(&clps711x_uart_device);
}
+module_init(uart_clps711x_init);
-module_init(clps711xuart_init);
-module_exit(clps711xuart_exit);
+static void __exit uart_clps711x_exit(void)
+{
+ platform_device_unregister(&clps711x_uart_device);
+}
+module_exit(uart_clps711x_exit);
MODULE_AUTHOR("Deep Blue Solutions Ltd");
-MODULE_DESCRIPTION("CLPS-711x generic serial driver");
+MODULE_DESCRIPTION("CLPS711X serial driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR);
--
1.7.8.6
^ permalink raw reply related
* Re: [RFC 00/24] OMAP serial driver flow control fixes, and preparation for DMA engine conversion
From: Jon Hunter @ 2012-10-11 14:05 UTC (permalink / raw)
To: Sourav
Cc: Russell King - ARM Linux, Kevin Hilman, Greg Kroah-Hartman,
Tony Lindgren, linux-serial, linux-omap, linux-arm-kernel,
Alan Cox
In-Reply-To: <50769D8C.8030401@ti.com>
Hi Sourav,
On 10/11/2012 05:21 AM, Sourav wrote:
[snip]
> I already enable software flow control and did the testing on beagle,
> where things are working fine
> after off mode.
> But if I enable hardware flow control, the teraterm does not allow me to
> load my fs and uImage from mmc.
> If you have any pointers on how to test hardware flow control, I will
> like to do that on my beagle board.
For h/w flow control, you need to check if the CTS and RTS signals are
being brought out to the serial header on the beagle-board. Looking at
the schematics for the 3430 beagle board, it appears that only the UART
RX and TX signals are available on the serial header. Therefore, I don't
believe you can test h/w flow control using the console with that board.
I do see that you can access all the UART2 signals (RX/TX/CTS/RTS) via
the expansion connector on the beagle. However, to connect to a serial
port on a PC you need to have a voltage level-shifter in-between. There
are some companies out there that make them [1], but you need to make
sure you have one that can support a 1.8V input.
Cheers
Jon
[1] http://elinux.org/RS232_Level_Shifter
^ permalink raw reply
* Re: [RFC 00/24] OMAP serial driver flow control fixes, and preparation for DMA engine conversion
From: Russell King - ARM Linux @ 2012-10-11 11:08 UTC (permalink / raw)
To: Sourav
Cc: Kevin Hilman, Tony Lindgren, Greg Kroah-Hartman, linux-serial,
linux-omap, linux-arm-kernel, Alan Cox
In-Reply-To: <50769D8C.8030401@ti.com>
On Thu, Oct 11, 2012 at 03:51:00PM +0530, Sourav wrote:
> True. I missed that point while doing the testing. Sorry for that.
> I further looked into it and saw some two options in my minicom
> settings(Hardware Flow Control/ Software Flow Control) Which I am
> thinking are the ones used to enable the flow control ? and they are
> both set to NO.
>
> I already enable software flow control and did the testing on beagle,
> where things are working fine
> after off mode.
> But if I enable hardware flow control, the teraterm does not allow me to
> load my fs and uImage from mmc.
> If you have any pointers on how to test hardware flow control, I will
> like to do that on my beagle board.
Okay, it sounds like I need to do a teach-in on flow control...
First, hardware flow control. Hardware flow control is operated by two
signals: RTS and CTS.
In conventional setups, CTS is an input to the transmitter, and controls
whether the transmitter may start the transmission of a new character.
If CTS is deasserted, the transmitter will stop after the completion of
the previous character. When hardware flow control is disabled, the
transmitter ignores this signal.
RTS is an output, and is generally used to control the remote transmitter.
(There are setups where RTS means something else, but the kernel doesn't
support other schemes directly.) RTS is asserted when either hardware
flow control is disabled, or there is sufficient space to receive more
characters from the remote end.
This is a symetrical setup, so that two UARTs connected together using
this scheme will have the RTS of one connected to the CTS of the other.
This way, each can signal whether characters should be transmitted.
So, in minicom, when hardware flow control is disabled, your hosts
transmitter will ignore the state of the CTS signal, and will hold its
RTS asserted.
If hardware flow control in minicom is enabled, then that tells the
kernel (and possibly hardware) to take note of the CTS signal, and pause
transmission when CTS is deasserted. It will also cause the RTS signal
to be manipulated according to available buffer space on the receive
side.
Obviously minicom will try to ensure that any characters received are
displayed as quickly as possible, so it's unlikely that the receive side
will fill up.
When you're logged into a system via a serial line, the hardware flow
control state is controlled by the CRTSCTS termios flag. That can be
seen and manipulated by stty. stty -a to see all flags. stty -crtscts
to disable, stty crtscts to enable.
Now, for software flow control. It operates in the same way as above,
but instead of a hardware signal reporting the state, characters are
embedded into the stream.
In normal situations, these characters are the standard ^Q (noramlly XON)
and ^S (XOFF) characters. You'll find that works in gnome-terminals,
xterms, and many places because it's part of the standard terminal
interface. You can type these characters into minicom with or without
software flow control disabled; it just passes them through unmodified.
When software flow control is enabled, and the tty receive buffers start
to fill up, the kernel will queue a high-priority XOFF character for the
UART to transmit to the remote end. Once the tty buffers have emptied
sufficiently, it will queue a high-priority XON character. If software
flow control is disabled, it will ignore this.
When hardware assisted software flow control is enabled, this will be
done by the hardware itself in response to the UART FIFO filling up and
emptying.
For the target, software flow control has more configuration options:
ixon: controls whether the transmitter starts/stops on reception
of xon/xoff characters
ixoff: controls the generation of xon/xoff characters
ixany: permits any received character (including xon) to restart
transmission
stop <char>: sets the xoff character to the specified character
start <char>: sets the xon character to the specified character
xon and xoff default to ^Q and ^S respectively, there's no need to
'initialize' them prior to use. So, to enable software flow control
(which is probably already enabled on the target):
stty ixon ixoff
and then you can type ^S and ^Q into minicom to stop/start the target's
transmit output.
Finally, to make the target's input buffer fill up, arrange for the
target not to read from the controlling tty at all. sleep 120 will
do that for two minutes, after which the input will be gobbled up by
the shell (which it'll try to interpret as commands.) So, probably
better to do:
sleep 120; echo Finished; cat >/dev/null
instead, and then send lots of data, and check whether the transmission
stops, whether the right xon/xoff characters are transmitted, and whether
any overrun errors are reported.
Going the other way, you can suspend minicom (^a z) and then arrange for
the target to send lots of data, and again check what happens.
There's a gotcha there though: with standard 8250-based serial ports,
we have /proc/tty/driver/serial which gives easy access to the port
statistics. With USB stuff, those statistics are not available, so it
becomes much harder to test. You have to arrange for the target to send
a known pattern, and find some way to check at the host end that it was
correctly received, including over the flow control events.
No characters should be lost when flow control is being used; after
all, that's the whole point of the facility.
^ permalink raw reply
* Re: [RFC 00/24] OMAP serial driver flow control fixes, and preparation for DMA engine conversion
From: Sourav @ 2012-10-11 10:21 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Kevin Hilman, Tony Lindgren, Greg Kroah-Hartman, linux-serial,
linux-omap, linux-arm-kernel, Alan Cox
In-Reply-To: <20121011095443.GH28061@n2100.arm.linux.org.uk>
Hi Russell,
On Thursday 11 October 2012 03:24 PM, Russell King - ARM Linux wrote:
> On Thu, Oct 11, 2012 at 03:13:43PM +0530, Sourav wrote:
>> Hi Kevin,
>> On Wednesday 10 October 2012 11:59 PM, Kevin Hilman wrote:
>>> Hi Sourav,
>>>
>>> Sourav <sourav.poddar@ti.com> writes:
>>>
>>> [...]
>>>
>>>> Boot Tested this patch series against v3.6 tag(applied cleanly) on
>>>> panda board and
>>>> PM tested(hitting off in Idle and suspend) on omap3630 based beagle board.
>>>>
>>>> Note, I also tested the patches against the current master but only
>>>> after rebasing, since the current master includes serial patches from
>>>> Felipe Balbi[1].
>>>> [1] https://lkml.org/lkml/2012/8/24/139
>>>>
>>>> Tested-by: Sourav Poddar <sourav.poddar@ti.com>
>>> Did you test flow control after off-mode transitons?
>>>
>>> Russell indicated that the context save/restore is not saving important
>>> bits related to HW flow control, so I suspect some more testing,
>>> specifically of flow control after off-mode is needed.
>>>
>>> Kevin
>> The testing done was without any flow control enabled.
> So, as the patch set is about fixing the flow control stuff, would
> you say that your testing without flow control enabled has much value?
True. I missed that point while doing the testing. Sorry for that.
I further looked into it and saw some two options in my minicom
settings(Hardware Flow Control/ Software Flow Control) Which I am
thinking are the ones used to enable the flow control ? and they are
both set to NO.
I already enable software flow control and did the testing on beagle,
where things are working fine
after off mode.
But if I enable hardware flow control, the teraterm does not allow me to
load my fs and uImage from mmc.
If you have any pointers on how to test hardware flow control, I will
like to do that on my beagle board.
Thanks,
Sourav
>> I will try to figure out how to do a hardware flow control testing
>> and see the status after the off mode.
> It's software flow control which isn't properly restored...
^ permalink raw reply
* Re: [RFC 00/24] OMAP serial driver flow control fixes, and preparation for DMA engine conversion
From: Russell King - ARM Linux @ 2012-10-11 9:54 UTC (permalink / raw)
To: Sourav
Cc: Kevin Hilman, Tony Lindgren, Greg Kroah-Hartman, linux-serial,
linux-omap, linux-arm-kernel, Alan Cox
In-Reply-To: <507694CF.3020204@ti.com>
On Thu, Oct 11, 2012 at 03:13:43PM +0530, Sourav wrote:
>
> Hi Kevin,
> On Wednesday 10 October 2012 11:59 PM, Kevin Hilman wrote:
>> Hi Sourav,
>>
>> Sourav <sourav.poddar@ti.com> writes:
>>
>> [...]
>>
>>> Boot Tested this patch series against v3.6 tag(applied cleanly) on
>>> panda board and
>>> PM tested(hitting off in Idle and suspend) on omap3630 based beagle board.
>>>
>>> Note, I also tested the patches against the current master but only
>>> after rebasing, since the current master includes serial patches from
>>> Felipe Balbi[1].
>>> [1] https://lkml.org/lkml/2012/8/24/139
>>>
>>> Tested-by: Sourav Poddar <sourav.poddar@ti.com>
>> Did you test flow control after off-mode transitons?
>>
>> Russell indicated that the context save/restore is not saving important
>> bits related to HW flow control, so I suspect some more testing,
>> specifically of flow control after off-mode is needed.
>>
>> Kevin
> The testing done was without any flow control enabled.
So, as the patch set is about fixing the flow control stuff, would
you say that your testing without flow control enabled has much value?
> I will try to figure out how to do a hardware flow control testing
> and see the status after the off mode.
It's software flow control which isn't properly restored...
^ permalink raw reply
* Re: [RFC 00/24] OMAP serial driver flow control fixes, and preparation for DMA engine conversion
From: Sourav @ 2012-10-11 9:43 UTC (permalink / raw)
To: Kevin Hilman
Cc: Russell King - ARM Linux, Tony Lindgren, Greg Kroah-Hartman,
linux-serial, linux-omap, linux-arm-kernel, Alan Cox
In-Reply-To: <87mwzuxjtc.fsf@deeprootsystems.com>
Hi Kevin,
On Wednesday 10 October 2012 11:59 PM, Kevin Hilman wrote:
> Hi Sourav,
>
> Sourav <sourav.poddar@ti.com> writes:
>
> [...]
>
>> Boot Tested this patch series against v3.6 tag(applied cleanly) on
>> panda board and
>> PM tested(hitting off in Idle and suspend) on omap3630 based beagle board.
>>
>> Note, I also tested the patches against the current master but only
>> after rebasing, since the current master includes serial patches from
>> Felipe Balbi[1].
>> [1] https://lkml.org/lkml/2012/8/24/139
>>
>> Tested-by: Sourav Poddar <sourav.poddar@ti.com>
> Did you test flow control after off-mode transitons?
>
> Russell indicated that the context save/restore is not saving important
> bits related to HW flow control, so I suspect some more testing,
> specifically of flow control after off-mode is needed.
>
> Kevin
The testing done was without any flow control enabled.
I will try to figure out how to do a hardware flow control testing
and see the status after the off mode.
~Sourav
^ permalink raw reply
* [RESEND PATCH v3] serial/arc-uart: Add new driver
From: Vineet.Gupta1 @ 2012-10-11 8:43 UTC (permalink / raw)
To: alan, gregkh; +Cc: linux-serial, linux-kernel, Vineet Gupta
In-Reply-To: <1349945001-31909-1-git-send-email-vgupta@synopsys.com>
From: Vineet Gupta <vgupta@synopsys.com>
Driver for non-standard on-chip UART, instantiated in the ARC (Synopsys)
FPGA Boards such as ARCAngel4/ML50x
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
---
drivers/tty/serial/Kconfig | 25 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/arc_uart.c | 747 +++++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 3 +
4 files changed, 776 insertions(+), 0 deletions(-)
create mode 100644 drivers/tty/serial/arc_uart.c
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 233fbaa..0a87741 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1423,4 +1423,29 @@ config SERIAL_EFM32_UART_CONSOLE
depends on SERIAL_EFM32_UART=y
select SERIAL_CORE_CONSOLE
+config SERIAL_ARC
+ bool "ARC UART driver support"
+ select SERIAL_CORE
+ default y
+ help
+ Driver for on-chip UART for ARC(Synopsys) for the legacy
+ FPGA Boards (ML50x/ARCAngel4)
+
+config SERIAL_ARC_CONSOLE
+ bool
+ select SERIAL_CORE_CONSOLE
+ depends on SERIAL_ARC=y
+ default y
+ help
+ Enable system Console on ARC UART
+
+config SERIAL_ARC_NR_PORTS
+ int 'Number of ports'
+ range 1 3
+ default 1
+ depends on SERIAL_ARC
+ help
+ Set this to the number of serial ports you want the driver
+ to support.
+
endmenu
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 4f694da..df1b998 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -82,3 +82,4 @@ obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o
obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o
obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
+obj-$(CONFIG_SERIAL_ARC) += arc_uart.o
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
new file mode 100644
index 0000000..9215bf4
--- /dev/null
+++ b/drivers/tty/serial/arc_uart.c
@@ -0,0 +1,747 @@
+/*
+ * ARC On-Chip(fpga) UART Driver
+ *
+ * Copyright (C) 2010-2012 Synopsys, Inc. (www.synopsys.com)
+ *
+ * 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.
+ *
+ * vineetg: July 10th 2012
+ * -Decoupled the driver from arch/arc
+ * +Using platform_get_resource() for irq/membase (thx to bfin_uart.c)
+ * +Using early_platform_xxx() for early console (thx to mach-shmobile/xxx)
+ *
+ * Vineetg: Aug 21st 2010
+ * -Is uart_tx_stopped() not done in tty write path as it has already been
+ * taken care of, in serial core
+ *
+ * Vineetg: Aug 18th 2010
+ * -New Serial Core based ARC UART driver
+ * -Derived largely from blackfin driver albiet with some major tweaks
+ *
+ * TODO:
+ * -check if sysreq works
+ */
+
+#if defined(CONFIG_SERIAL_ARC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/io.h>
+
+/*************************************
+ * ARC UART Hardware Specs
+ ************************************/
+#define ARC_UART_TX_FIFO_SIZE 1
+
+/*
+ * UART Register set (this is not a Standards Compliant IP)
+ * Also each reg is Word aligned, but only 8 bits wide
+ */
+#define R_ID0 0
+#define R_ID1 1
+#define R_ID2 2
+#define R_ID3 3
+#define R_DATA 4
+#define R_STS 5
+#define R_BAUDL 6
+#define R_BAUDH 7
+
+/* Bits for UART Status Reg (R/W) */
+#define RXIENB 0x04 /* Receive Interrupt Enable */
+#define TXIENB 0x40 /* Transmit Interrupt Enable */
+
+#define RXEMPTY 0x20 /* Receive FIFO Empty: No char receivede */
+#define TXEMPTY 0x80 /* Transmit FIFO Empty, thus char can be written into */
+
+#define RXFULL 0x08 /* Receive FIFO full */
+#define RXFULL1 0x10 /* Receive FIFO has space for 1 char (tot space=4) */
+
+#define RXFERR 0x01 /* Frame Error: Stop Bit not detected */
+#define RXOERR 0x02 /* OverFlow Err: Char recv but RXFULL still set */
+
+/* Uart bit fiddling helpers: lowest level */
+#define RBASE(uart, reg) ((unsigned int *)uart->port.membase + reg)
+#define UART_REG_SET(u, r, v) writeb((v), RBASE(u, r))
+#define UART_REG_GET(u, r) readb(RBASE(u, r))
+
+#define UART_REG_OR(u, r, v) UART_REG_SET(u, r, UART_REG_GET(u, r) | (v))
+#define UART_REG_CLR(u, r, v) UART_REG_SET(u, r, UART_REG_GET(u, r) & ~(v))
+
+/* Uart bit fiddling helpers: API level */
+#define UART_SET_DATA(uart, val) UART_REG_SET(uart, R_DATA, val)
+#define UART_GET_DATA(uart) UART_REG_GET(uart, R_DATA)
+
+#define UART_SET_BAUDH(uart, val) UART_REG_SET(uart, R_BAUDH, val)
+#define UART_SET_BAUDL(uart, val) UART_REG_SET(uart, R_BAUDL, val)
+
+#define UART_CLR_STATUS(uart, val) UART_REG_CLR(uart, R_STS, val)
+#define UART_GET_STATUS(uart) UART_REG_GET(uart, R_STS)
+
+#define UART_ALL_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, RXIENB|TXIENB)
+#define UART_RX_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, RXIENB)
+#define UART_TX_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, TXIENB)
+
+#define UART_ALL_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, RXIENB|TXIENB)
+#define UART_RX_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, RXIENB)
+#define UART_TX_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, TXIENB)
+
+#define ARC_SERIAL_DEV_NAME "ttyARC"
+
+struct arc_uart_port {
+ struct uart_port port;
+ unsigned long baud;
+ int is_emulated; /* H/w vs. Instruction Set Simulator */
+};
+
+static struct arc_uart_port arc_uart_ports[CONFIG_SERIAL_ARC_NR_PORTS];
+
+#ifdef CONFIG_SERIAL_ARC_CONSOLE
+static struct console arc_console;
+#endif
+
+#define DRIVER_NAME "arc-uart"
+
+static struct uart_driver arc_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = ARC_SERIAL_DEV_NAME,
+ .major = 0,
+ .minor = 0,
+ .nr = CONFIG_SERIAL_ARC_NR_PORTS,
+#ifdef CONFIG_SERIAL_ARC_CONSOLE
+ .cons = &arc_console,
+#else
+ .cons = NULL,
+#endif
+};
+
+static void arc_serial_stop_rx(struct uart_port *port)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+
+ UART_RX_IRQ_DISABLE(uart);
+}
+
+static void arc_serial_stop_tx(struct uart_port *port)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+
+ while (!(UART_GET_STATUS(uart) & TXEMPTY))
+ cpu_relax();
+
+ UART_TX_IRQ_DISABLE(uart);
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int arc_serial_tx_empty(struct uart_port *port)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+ unsigned int stat;
+
+ stat = UART_GET_STATUS(uart);
+ if (stat & TXEMPTY)
+ return TIOCSER_TEMT;
+ else
+ return 0;
+}
+
+/*
+ * Driver internal routine, used by both tty(serial core) as well as tx-isr
+ * -Called under spinlock in either cases
+ * -also tty->stopped / tty->hw_stopped has already been checked
+ * = by uart_start( ) before calling us
+ * = tx_ist checks that too before calling
+ */
+static void arc_serial_tx_chars(struct arc_uart_port *uart)
+{
+ struct circ_buf *xmit = &uart->port.state->xmit;
+ int sent = 0;
+ unsigned char ch;
+
+ if (unlikely(uart->port.x_char)) {
+ UART_SET_DATA(uart, uart->port.x_char);
+ uart->port.icount.tx++;
+ uart->port.x_char = 0;
+ sent = 1;
+ } else if (xmit->tail != xmit->head) { /* TODO: uart_circ_empty */
+ ch = xmit->buf[xmit->tail];
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ uart->port.icount.tx++;
+ while (!(UART_GET_STATUS(uart) & TXEMPTY))
+ cpu_relax();
+ UART_SET_DATA(uart, ch);
+ sent = 1;
+ }
+
+ /*
+ * If num chars in xmit buffer are too few, ask tty layer for more.
+ * By Hard ISR to schedule processing in software interrupt part
+ */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&uart->port);
+
+ if (sent)
+ UART_TX_IRQ_ENABLE(uart);
+}
+
+/*
+ * port is locked and interrupts are disabled
+ * uart_start( ) calls us under the port spinlock irqsave
+ */
+static void arc_serial_start_tx(struct uart_port *port)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+
+ arc_serial_tx_chars(uart);
+}
+
+static void arc_serial_rx_chars(struct arc_uart_port *uart)
+{
+ struct tty_struct *tty = tty_port_tty_get(&uart->port.state->port);
+ unsigned int status, ch, flg = 0;
+
+ if (!tty)
+ return;
+
+ /*
+ * UART has 4 deep RX-FIFO. Driver's recongnition of this fact
+ * is very subtle. Here's how ...
+ * Upon getting a RX-Intr, such that RX-EMPTY=0, meaning data available,
+ * driver reads the DATA Reg and keeps doing that in a loop, until
+ * RX-EMPTY=1. Multiple chars being avail, with a single Interrupt,
+ * before RX-EMPTY=0, implies some sort of buffering going on in the
+ * controller, which is indeed the Rx-FIFO.
+ */
+ while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)) {
+
+ ch = UART_GET_DATA(uart);
+ uart->port.icount.rx++;
+
+ if (unlikely(status & (RXOERR | RXFERR))) {
+ if (status & RXOERR) {
+ uart->port.icount.overrun++;
+ flg = TTY_OVERRUN;
+ UART_CLR_STATUS(uart, RXOERR);
+ }
+
+ if (status & RXFERR) {
+ uart->port.icount.frame++;
+ flg = TTY_FRAME;
+ UART_CLR_STATUS(uart, RXFERR);
+ }
+ } else
+ flg = TTY_NORMAL;
+
+ if (unlikely(uart_handle_sysrq_char(&uart->port, ch)))
+ goto done;
+
+ uart_insert_char(&uart->port, status, RXOERR, ch, flg);
+
+done:
+ tty_flip_buffer_push(tty);
+ }
+
+ tty_kref_put(tty);
+}
+
+/*
+ * A note on the Interrupt handling state machine of this driver
+ *
+ * kernel printk writes funnel thru the console driver framework and in order
+ * to keep things simple as well as efficient, it writes to UART in polled
+ * mode, in one shot, and exits.
+ *
+ * OTOH, Userland output (via tty layer), uses interrupt based writes as there
+ * can be undeterministic delay between char writes.
+ *
+ * Thus Rx-interrupts are always enabled, while tx-interrupts are by default
+ * disabled.
+ *
+ * When tty has some data to send out, serial core calls driver's start_tx
+ * which
+ * -checks-if-tty-buffer-has-char-to-send
+ * -writes-data-to-uart
+ * -enable-tx-intr
+ *
+ * Once data bits are pushed out, controller raises the Tx-room-avail-Interrupt.
+ * The first thing Tx ISR does is disable further Tx interrupts (as this could
+ * be the last char to send, before settling down into the quiet polled mode).
+ * It then calls the exact routine used by tty layer write to send out any
+ * more char in tty buffer. In case of sending, it re-enables Tx-intr. In case
+ * of no data, it remains disabled.
+ * This is how the transmit state machine is dynamically switched on/off
+ */
+
+static irqreturn_t arc_serial_isr(int irq, void *dev_id)
+{
+ struct arc_uart_port *uart = dev_id;
+ unsigned int status;
+
+ status = UART_GET_STATUS(uart);
+
+ /*
+ * Single IRQ for both Rx (data available) Tx (room available) Interrupt
+ * notifications from the UART Controller.
+ * To demultiplex between the two, we check the relevant bits
+ */
+ if ((status & RXIENB) && !(status & RXEMPTY)) {
+
+ /* already in ISR, no need of xx_irqsave */
+ spin_lock(&uart->port.lock);
+ arc_serial_rx_chars(uart);
+ spin_unlock(&uart->port.lock);
+ }
+
+ if ((status & TXIENB) && (status & TXEMPTY)) {
+
+ /* Unconditionally disable further Tx-Interrupts.
+ * will be enabled by tx_chars() if needed.
+ */
+ UART_TX_IRQ_DISABLE(uart);
+
+ spin_lock(&uart->port.lock);
+
+ if (!uart_tx_stopped(&uart->port))
+ arc_serial_tx_chars(uart);
+
+ spin_unlock(&uart->port.lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int arc_serial_get_mctrl(struct uart_port *port)
+{
+ /*
+ * Pretend we have a Modem status reg and following bits are
+ * always set, to satify the serial core state machine
+ * (DSR) Data Set Ready
+ * (CTS) Clear To Send
+ * (CAR) Carrier Detect
+ */
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void arc_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* MCR not present */
+}
+
+/* Enable Modem Status Interrupts */
+
+static void arc_serial_enable_ms(struct uart_port *port)
+{
+ /* MSR not present */
+}
+
+static void arc_serial_break_ctl(struct uart_port *port, int break_state)
+{
+ /* ARC UART doesn't support sending Break signal */
+}
+
+static int arc_serial_startup(struct uart_port *port)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+
+ /* Before we hook up the ISR, Disable all UART Interrupts */
+ UART_ALL_IRQ_DISABLE(uart);
+
+ if (request_irq(uart->port.irq, arc_serial_isr, 0, "arc uart rx-tx",
+ uart)) {
+ pr_warn("Unable to attach ARC UART interrupt\n");
+ return -EBUSY;
+ }
+
+ UART_RX_IRQ_ENABLE(uart); /* Only Rx IRQ enabled to begin with */
+
+ return 0;
+}
+
+/* This is not really needed */
+static void arc_serial_shutdown(struct uart_port *port)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+ free_irq(uart->port.irq, uart);
+}
+
+static void
+arc_serial_set_termios(struct uart_port *port, struct ktermios *new,
+ struct ktermios *old)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+ unsigned int baud, uartl, uarth, hw_val;
+ unsigned long flags;
+
+ /*
+ * Use the generic handler so that any specially encoded baud rates
+ * such as SPD_xx flags or "%B0" can be handled
+ * Max Baud I suppose will not be more than current 115K * 4
+ * Formula for ARC UART is: hw-val = ((CLK/(BAUD*4)) -1)
+ * spread over two 8-bit registers
+ */
+ baud = uart_get_baud_rate(port, new, old, 0, 460800);
+
+ hw_val = port->uartclk / (uart->baud * 4) - 1;
+ uartl = hw_val & 0xFF;
+ uarth = (hw_val >> 8) & 0xFF;
+
+ /*
+ * UART ISS(Instruction Set simulator) emulation has a subtle bug:
+ * A existing value of Baudh = 0 is used as a indication to startup
+ * it's internal state machine.
+ * Thus if baudh is set to 0, 2 times, it chokes.
+ * This happens with BAUD=115200 and the formaula above
+ * Until that is fixed, when running on ISS, we will set baudh to !0
+ */
+ if (uart->is_emulated)
+ uarth = 1;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ UART_ALL_IRQ_DISABLE(uart);
+
+ UART_SET_BAUDL(uart, uartl);
+ UART_SET_BAUDH(uart, uarth);
+
+ UART_RX_IRQ_ENABLE(uart);
+
+ /*
+ * UART doesn't support Parity/Hardware Flow Control;
+ * Only supports 8N1 character size
+ */
+ new->c_cflag &= ~(CMSPAR|CRTSCTS|CSIZE);
+ new->c_cflag |= CS8;
+
+ if (old)
+ tty_termios_copy_hw(new, old);
+
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(new))
+ tty_termios_encode_baud_rate(new, baud, baud);
+
+ uart_update_timeout(port, new->c_cflag, baud);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *arc_serial_type(struct uart_port *port)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+
+ return uart->port.type == PORT_ARC ? DRIVER_NAME : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void arc_serial_release_port(struct uart_port *port)
+{
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int arc_serial_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int
+arc_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void arc_serial_config_port(struct uart_port *port, int flags)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+
+ if (flags & UART_CONFIG_TYPE &&
+ arc_serial_request_port(&uart->port) == 0)
+ uart->port.type = PORT_ARC;
+}
+
+static void arc_serial_poll_putchar(struct uart_port *port, unsigned char chr)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+
+ while (!(UART_GET_STATUS(uart) & TXEMPTY))
+ cpu_relax();
+
+ UART_SET_DATA(uart, chr);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int arc_serial_poll_getchar(struct uart_port *port)
+{
+ struct arc_uart_port *uart = (struct arc_uart_port *)port;
+ unsigned char chr;
+
+ while (!(UART_GET_STATUS(uart) & RXEMPTY))
+ cpu_relax();
+
+ chr = UART_GET_DATA(uart);
+ return chr;
+}
+#endif
+
+static struct uart_ops arc_serial_pops = {
+ .tx_empty = arc_serial_tx_empty,
+ .set_mctrl = arc_serial_set_mctrl,
+ .get_mctrl = arc_serial_get_mctrl,
+ .stop_tx = arc_serial_stop_tx,
+ .start_tx = arc_serial_start_tx,
+ .stop_rx = arc_serial_stop_rx,
+ .enable_ms = arc_serial_enable_ms,
+ .break_ctl = arc_serial_break_ctl,
+ .startup = arc_serial_startup,
+ .shutdown = arc_serial_shutdown,
+ .set_termios = arc_serial_set_termios,
+ .type = arc_serial_type,
+ .release_port = arc_serial_release_port,
+ .request_port = arc_serial_request_port,
+ .config_port = arc_serial_config_port,
+ .verify_port = arc_serial_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_put_char = arc_serial_poll_putchar,
+ .poll_get_char = arc_serial_poll_getchar,
+#endif
+};
+
+static int __devinit
+arc_uart_init_one(struct platform_device *pdev, struct arc_uart_port *uart)
+{
+ struct resource *res, *res2;
+ unsigned long *plat_data;
+
+ if (pdev->id < 0 || pdev->id >= CONFIG_SERIAL_ARC_NR_PORTS) {
+ dev_err(&pdev->dev, "Wrong uart platform device id.\n");
+ return -ENOENT;
+ }
+
+ plat_data = ((unsigned long *)(pdev->dev.platform_data));
+ uart->baud = plat_data[0];
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res2)
+ return -ENODEV;
+
+ uart->port.mapbase = res->start;
+ uart->port.membase = ioremap_nocache(res->start, resource_size(res));
+ if (!uart->port.membase)
+ /* No point of pr_err since UART itself is hosed here */
+ return -ENXIO;
+
+ uart->port.irq = res2->start;
+ uart->port.dev = &pdev->dev;
+ uart->port.iotype = UPIO_MEM;
+ uart->port.flags = UPF_BOOT_AUTOCONF;
+ uart->port.line = pdev->id;
+ uart->port.ops = &arc_serial_pops;
+
+ uart->port.uartclk = plat_data[1];
+ uart->port.fifosize = ARC_UART_TX_FIFO_SIZE;
+
+ /*
+ * uart_insert_char( ) uses it in decideding whether to ignore a
+ * char or not. Explicitly setting it here, removes the subtelty
+ */
+ uart->port.ignore_status_mask = 0;
+
+ /* Real Hardware vs. emulated to work around a bug */
+ uart->is_emulated = !!plat_data[2];
+
+ return 0;
+}
+
+#ifdef CONFIG_SERIAL_ARC_CONSOLE
+
+static int __devinit arc_serial_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index < 0 || co->index >= CONFIG_SERIAL_ARC_NR_PORTS)
+ return -ENODEV;
+
+ /*
+ * The uart port backing the console (e.g. ttyARC1) might not have been
+ * init yet. If so, defer the console setup to after the port.
+ */
+ port = &arc_uart_ports[co->index].port;
+ if (!port->membase)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ /*
+ * Serial core will call port->ops->set_termios( )
+ * which will set the baud reg
+ */
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static void arc_serial_console_putchar(struct uart_port *port, int ch)
+{
+ arc_serial_poll_putchar(port, (unsigned char)ch);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void arc_serial_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct uart_port *port = &arc_uart_ports[co->index].port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ uart_console_write(port, s, count, arc_serial_console_putchar);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static struct console arc_console = {
+ .name = ARC_SERIAL_DEV_NAME,
+ .write = arc_serial_console_write,
+ .device = uart_console_device,
+ .setup = arc_serial_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &arc_uart_driver
+};
+
+static __init void early_serial_write(struct console *con, const char *s,
+ unsigned int n)
+{
+ struct uart_port *port = &arc_uart_ports[con->index].port;
+ unsigned int i;
+
+ for (i = 0; i < n; i++, s++) {
+ if (*s == '\n')
+ arc_serial_poll_putchar(port, '\r');
+ arc_serial_poll_putchar(port, *s);
+ }
+}
+
+static struct __initdata console arc_early_serial_console = {
+ .name = "early_ARCuart",
+ .write = early_serial_write,
+ .flags = CON_PRINTBUFFER | CON_BOOT,
+ .index = -1
+};
+
+static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev)
+{
+ arc_early_serial_console.index = pdev->id;
+
+ arc_uart_init_one(pdev, &arc_uart_ports[pdev->id]);
+
+ arc_serial_console_setup(&arc_early_serial_console, NULL);
+
+ register_console(&arc_early_serial_console);
+ return 0;
+}
+#endif
+
+
+static int __devinit arc_serial_probe(struct platform_device *pdev)
+{
+ struct arc_uart_port *uart;
+ int rc;
+
+ if (is_early_platform_device(pdev))
+ return arc_serial_probe_earlyprintk(pdev);
+
+ uart = &arc_uart_ports[pdev->id];
+ rc = arc_uart_init_one(pdev, uart);
+ if (rc)
+ return rc;
+
+ return uart_add_one_port(&arc_uart_driver, &uart->port);
+}
+
+static int __devexit arc_serial_remove(struct platform_device *pdev)
+{
+ /* This will never be called */
+ return 0;
+}
+
+static struct platform_driver arc_platform_driver = {
+ .probe = arc_serial_probe,
+ .remove = __devexit_p(arc_serial_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+#ifdef CONFIG_SERIAL_ARC_CONSOLE
+/*
+ * Register an early platform driver of "earlyprintk" class.
+ * ARCH platform code installs the driver and probes the early devices
+ * The installation could rely on user specifying earlyprintk=xyx in cmd line
+ * or it could be done independently, for all "earlyprintk" class drivers.
+ * [see arch/arc/plat-arcfpga/platform.c]
+ */
+early_platform_init("earlyprintk", &arc_platform_driver);
+
+#endif /* CONFIG_SERIAL_ARC_CONSOLE */
+
+static int __init arc_serial_init(void)
+{
+ int ret;
+
+ pr_info("Serial: ARC serial driver: platform register\n");
+
+ ret = uart_register_driver(&arc_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&arc_platform_driver);
+ if (ret) {
+ pr_debug("uart register failed\n");
+ uart_unregister_driver(&arc_uart_driver);
+ }
+
+ return ret;
+}
+
+static void __exit arc_serial_exit(void)
+{
+ platform_driver_unregister(&arc_platform_driver);
+ uart_unregister_driver(&arc_uart_driver);
+}
+
+module_init(arc_serial_init);
+module_exit(arc_serial_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("plat-arcfpga/uart");
+MODULE_AUTHOR("Vineet Gupta");
+MODULE_DESCRIPTION("ARC(Synopsys) On-Chip(fpga) serial driver");
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index f9b22ec..545666f 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -215,6 +215,9 @@
/* Energy Micro efm32 SoC */
#define PORT_EFMUART 100
+/* ARC (Synopsys) on-chip UART */
+#define PORT_ARC 101
+
#ifdef __KERNEL__
#include <linux/compiler.h>
--
1.7.4.1
^ permalink raw reply related
* [RESEND PATCH v3] serial/arc-uart: Add new driver
From: Vineet.Gupta1 @ 2012-10-11 8:43 UTC (permalink / raw)
To: alan, gregkh; +Cc: linux-serial, linux-kernel, Vineet Gupta
From: Vineet Gupta <vgupta@synopsys.com>
Hi,
Please find following ARC UART driver with all the review comments incorporated.
It's rebased off of current tip (commit 12250d843e)
Please consider merging.
Thx,
-Vineet
v3:
* Removed empty arc_serial_set_ldisc()
* More set_termios fixes - CSIZE forced to CS8 (for 8N1)
* global @running_on_iss replaced with platform data, saved in device
specific port structure.
v2:
* ttyARC used as device name
* Dynamic assignment of major/minor numbers.
* Ref counting tty in rx routine to prevent it from disappearing in
case of a hangup
* set_termios fixes:
- hardware flow control/parity are marked as unsupported
- baud written back to termios
* cosmetics such as commenting the need for @running_on_iss, empty lines
etc
Vineet Gupta (1):
serial/arc-uart: Add new driver
drivers/tty/serial/Kconfig | 25 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/arc_uart.c | 747 +++++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 3 +
4 files changed, 776 insertions(+), 0 deletions(-)
create mode 100644 drivers/tty/serial/arc_uart.c
--
1.7.4.1
^ permalink raw reply
* Re: [RFC 00/24] OMAP serial driver flow control fixes, and preparation for DMA engine conversion
From: Kevin Hilman @ 2012-10-10 18:29 UTC (permalink / raw)
To: Sourav
Cc: Russell King - ARM Linux, Tony Lindgren, Greg Kroah-Hartman,
linux-serial, linux-omap, linux-arm-kernel, Alan Cox
In-Reply-To: <507427DA.6020409@ti.com>
Hi Sourav,
Sourav <sourav.poddar@ti.com> writes:
[...]
> Boot Tested this patch series against v3.6 tag(applied cleanly) on
> panda board and
> PM tested(hitting off in Idle and suspend) on omap3630 based beagle board.
>
> Note, I also tested the patches against the current master but only
> after rebasing, since the current master includes serial patches from
> Felipe Balbi[1].
> [1] https://lkml.org/lkml/2012/8/24/139
>
> Tested-by: Sourav Poddar <sourav.poddar@ti.com>
Did you test flow control after off-mode transitons?
Russell indicated that the context save/restore is not saving important
bits related to HW flow control, so I suspect some more testing,
specifically of flow control after off-mode is needed.
Kevin
^ permalink raw reply
* Re: [REPOST] RFC: sched: Prevent wakeup to enter critical section needlessly
From: Andi Kleen @ 2012-10-10 14:02 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Peter Zijlstra, Andi Kleen, Ivo Sieben, linux-kernel, Ingo Molnar,
linux-serial, Alan Cox, Greg KH
In-Reply-To: <20121009151729.GA3521@redhat.com>
> wait_event:
>
> prepare_to_wait(wq) // takes wq->lock
>
> if (!CONDITION)
> schedule();
>
> Now,
>
> CONDITION = 1;
> wake_up(wq);
>
> at least need the full mb() before lits_empty().
You're right, but it would probably only matter for inlining with LTO
(if the LTO compiler ever decides to do that)
Without that a call should be always enough barrier in practice.
So yes I would add the mb, but most likely it will not make much
difference. Just make sure to comment it.
-Andi
^ permalink raw reply
* New serial card development
From: Matt Schulte @ 2012-10-09 18:43 UTC (permalink / raw)
To: linux-serial
Hello, my name is Matt and I have recently developed a PCIe card based
on the Exar 17v35x series of PCIe multiport UART chips.
We have written Linux drivers for our other products in the past but
they have never been what I would call the best practice for Linux
development.
This time I would like to write a driver that uses the best practices
and possibly submit it to the kernel when I'm done.
I am a little bit confused about what method would be best for this
device. I have been examining the sample driver provided by Exar
and it seems that the only things that they have that are really
different from the generic 8250 serial driver are the interrupt
handler (optimized for their multiple ports), the transmit and
receive character functions (modified to use their 256 byte FIFOs)
and the function that calculates the baud. Also I have two features
specific to my card that I would likely need to use an ioctl for.
If these are the only differences would I be able to create a driver
like what is seen in the /drivers/tty/serial/8250 directory (8250_dw.c
for example)? Or would I need to do something similar to what I find in
the /drviers/tty/serial directory?
Thank you for your time.
Matt Schulte
^ permalink raw reply
* Re: [tty:tty-next 9/39] drivers/staging/dgrp/dgrp_tty.c:3177 dgrp_tty_init() error: potential null dereference 'nd->nd_serial_ttdriver'.
From: Alan Cox @ 2012-10-09 18:42 UTC (permalink / raw)
To: Fengguang Wu
Cc: Bill Pemberton, kernel-janitors, Greg Kroah-Hartman, linux-serial
In-Reply-To: <20121007123549.GA24374@localhost>
I think thats the tip of the iceberg at best 8)
Alan
^ permalink raw reply
* Re: [PATCH-v2] tty: Use raw spin lock to protect RX flip buffer
From: Thomas Gleixner @ 2012-10-09 17:43 UTC (permalink / raw)
To: Ivo Sieben
Cc: Steven Rostedt, linux-serial, linux-rt-users, Alan Cox, Greg KH
In-Reply-To: <1349781313-8670-1-git-send-email-meltedpianoman@gmail.com>
On Tue, 9 Oct 2012, Ivo Sieben wrote:
> The "normal" spin lock that guards the RX flip buffer is replaced by a raw
> spin lock.
>
> On a PREEMP_RT system this prevents unwanted scheduling overhead when data is
> read at the same time as data is being received: while RX IRQ threaded handling
> is busy a TTY read call is performed from a RT priority > threaded IRQ priority.
> The read call tries to take the flip buffer spin lock (held by the threaded IRQ)
> which blocks and causes a context switch to/from the threaded IRQ handler until
> the spin lock is unlocked.
>
> On a 200 MHz AT91SAM9261 processor setup this fixes about 100us of scheduling
> overhead on the TTY read call.
Cute, but ...
> Signed-off-by: Ivo Sieben <meltedpianoman@gmail.com>
> ---
>
> @Thomas Gleixner & Steven Rostedt:
> Alan Cox has responded to this patch: "I've no real opinion on the spin/raw_spin
> choices as it's basically an RT question not a tty one so if the rt folks are
> happy with it so am I." Do you agree?
>
> v2:
> Patch was based on another - abandoned - patch om mine, what introduced a bug in
> the tty_schedule_flip() function. Fixed this and rebased it on the latest kernel
> tree.
>
>
> drivers/tty/tty_buffer.c | 44 ++++++++++++++++++++++----------------------
> include/linux/kbd_kern.h | 4 ++--
> include/linux/tty.h | 2 +-
> 2 files changed, 25 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
> index 91e326f..70cf324 100644
> --- a/drivers/tty/tty_buffer.c
> +++ b/drivers/tty/tty_buffer.c
> @@ -135,20 +135,20 @@ static void __tty_buffer_flush(struct tty_struct *tty)
> void tty_buffer_flush(struct tty_struct *tty)
> {
> unsigned long flags;
> - spin_lock_irqsave(&tty->buf.lock, flags);
> + raw_spin_lock_irqsave(&tty->buf.lock, flags);
>
> /* If the data is being pushed to the tty layer then we can't
> process it here. Instead set a flag and the flush_to_ldisc
> path will process the flush request before it exits */
> if (test_bit(TTY_FLUSHING, &tty->flags)) {
> set_bit(TTY_FLUSHPENDING, &tty->flags);
> - spin_unlock_irqrestore(&tty->buf.lock, flags);
> + raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
> wait_event(tty->read_wait,
> test_bit(TTY_FLUSHPENDING, &tty->flags) == 0);
> return;
> } else
> __tty_buffer_flush(tty);
__tty_buffer_flush() can call tty_buffer_free() which in turn can call
kfree(), which is a nono on RT with preemption and interrupts
disabled. Enabling CONFIG_DEBUG_ATOMIC_SLEEP and extensive testing
should have told you that.
I did not look at the other places where this lock is used, but I
suspect there is more fallout lurking.
Thanks,
tglx
^ permalink raw reply
* Re: [tty:tty-next 9/39] drivers/staging/dgrp/dgrp_tty.c:3177
From: Bill Pemberton @ 2012-10-09 16:46 UTC (permalink / raw)
To: Fengguang Wu; +Cc: kernel-janitors, Greg Kroah-Hartman, linux-serial
In-Reply-To: <20121007123549.GA24374@localhost>
Fengguang Wu writes:
>
> Hi Bill,
>
> FYI, there are new smatch warnings show up in
>
> + drivers/staging/dgrp/dgrp_net_ops.c:277 dgrp_input() error: we previously assumed 'ld' could be null (see line 238)
This one looks fine, if ld is NULL then len is set to 0. ld is then
only dereferenced if len != 0
> --
> + drivers/staging/dgrp/dgrp_specproc.c:273 register_proc_table() error: we previously assumed 'table->child' could be null (see line 263)
It seems weird where smatch is seeing the NULL test vs. the call to
register_proc_table(), but it is correct that register_proc_table will
do bad things if the first arg is a NULL. I'll submit a patch for it.
> --
> + drivers/staging/dgrp/dgrp_tty.c:3177 dgrp_tty_init() error: potential null dereference 'nd->nd_serial_ttdriver'. (alloc_tty_driver returns null)
> + drivers/staging/dgrp/dgrp_tty.c:3236 dgrp_tty_init() error: potential null dereference 'nd->nd_callout_ttdriver'. (alloc_tty_driver returns null)
> + drivers/staging/dgrp/dgrp_tty.c:3273 dgrp_tty_init() error: potential null dereference 'nd->nd_xprint_ttdriver'. (alloc_tty_driver returns null)
>
Yes, the return value of alloc_tty_driver needs to be checked. I'll
fix these.
--
Bill
^ permalink raw reply
* Re: [REPOST] RFC: sched: Prevent wakeup to enter critical section needlessly
From: Oleg Nesterov @ 2012-10-09 15:17 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Andi Kleen, Ivo Sieben, linux-kernel, Ingo Molnar, linux-serial,
Alan Cox, Greg KH
In-Reply-To: <1349792104.7880.41.camel@twins>
On 10/09, Peter Zijlstra wrote:
>
> One thing you might need to consider is the memory ordering, will the
> list_empty -- either careful or not -- observe the right list pointer,
> or could it -- when racing with wait_event()/prepare_to_wait() --
> observe a stale value. Or.. is that all already covered in on the use
> site.
I agree.
Without spin_lock(q->lock) (or some other barriers) wait_event-like
code can miss an event.
wait_event:
prepare_to_wait(wq) // takes wq->lock
if (!CONDITION)
schedule();
Now,
CONDITION = 1;
wake_up(wq);
at least need the full mb() before lits_empty().
Oleg.
^ permalink raw reply
* Re: [REPOST] RFC: sched: Prevent wakeup to enter critical section needlessly
From: Peter Zijlstra @ 2012-10-09 14:15 UTC (permalink / raw)
To: Andi Kleen
Cc: Ivo Sieben, linux-kernel, Ingo Molnar, linux-serial, Alan Cox,
Greg KH, Oleg Nesterov
In-Reply-To: <m2r4p7u5pk.fsf@firstfloor.org>
On Tue, 2012-10-09 at 06:37 -0700, Andi Kleen wrote:
> Ivo Sieben <meltedpianoman@gmail.com> writes:
>
> > Check the waitqueue task list to be non empty before entering the critical
> > section. This prevents locking the spin lock needlessly in case the queue
> > was empty, and therefor also prevent scheduling overhead on a PREEMPT_RT
> > system.
> >
> > Signed-off-by: Ivo Sieben <meltedpianoman@gmail.com>
> > ---
> >
> > REPOST:
> > Request for comments:
> > - Does this make any sense?
>
> Looks good to me. Avoiding any spinlock is good. I don't even think you
> need "careful", if you miss an update it was just as it happened a
> little later.
Yeah, so I've started looking at this several times, but never had the
time to actually think it through. I think I'll agree with you that
using the careful list empty check isn't needed.
In general there's already the race of doing a wakeup before the wait
and if the code using the waitqueue doesn't deal with that its already
broken, so in that respect you should be good, since you're simply
shifting the timing a little.
One thing you might need to consider is the memory ordering, will the
list_empty -- either careful or not -- observe the right list pointer,
or could it -- when racing with wait_event()/prepare_to_wait() --
observe a stale value. Or.. is that all already covered in on the use
site.
I started auditing a few users to see what they all assume, if they're
already broken and or if they would now be broken.. but like said, I
didn't get anywhere.
I'd like to see this patch/Changelog amended to at least cover these
points so that I feel all warm and fuzzy when I read it and not have to
think too hard ;-)
^ permalink raw reply
* Re: [REPOST] RFC: sched: Prevent wakeup to enter critical section needlessly
From: Andi Kleen @ 2012-10-09 13:37 UTC (permalink / raw)
To: Ivo Sieben
Cc: linux-kernel, Ingo Molnar, Peter Zijlstra, linux-serial, Alan Cox,
Greg KH
In-Reply-To: <1349782235-8896-1-git-send-email-meltedpianoman@gmail.com>
Ivo Sieben <meltedpianoman@gmail.com> writes:
> Check the waitqueue task list to be non empty before entering the critical
> section. This prevents locking the spin lock needlessly in case the queue
> was empty, and therefor also prevent scheduling overhead on a PREEMPT_RT
> system.
>
> Signed-off-by: Ivo Sieben <meltedpianoman@gmail.com>
> ---
>
> REPOST:
> Request for comments:
> - Does this make any sense?
Looks good to me. Avoiding any spinlock is good. I don't even think you
need "careful", if you miss an update it was just as it happened a
little later.
-Andi
--
ak@linux.intel.com -- Speaking for myself only
^ permalink raw reply
* Re: [RFC 00/24] OMAP serial driver flow control fixes, and preparation for DMA engine conversion
From: Sourav @ 2012-10-09 13:34 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Alan Cox, Greg Kroah-Hartman, linux-arm-kernel, linux-omap,
linux-serial, Tony Lindgren
In-Reply-To: <20121006123803.GD15246@n2100.arm.linux.org.uk>
Hi,
On Saturday 06 October 2012 06:08 PM, Russell King - ARM Linux wrote:
> Hi,
>
> This series of patches fixes multiple flow control issues with the OMAP
> serial driver, and prepares the driver for DMA engine conversion. We
> require hardware assisted flow control to work properly for DMA support
> otherwise we have no way to properly pause the transmitter.
>
> This is generated against v3.6, and has been developed mainly by testing
> on the OMAP4430 SDP platform.
>
> Flow control seems to be really broken in the OMAP serial driver as things
> stand today. It just about works with software flow control because the
> generic serial core layer is inserting those characters, but only when the
> legacy DMA support is not being used. Otherwise, flow control is
> completely non-functional.
>
> Issues identified in the OMAP serial driver are:
> - set_mctrl() can only assert modem control lines, once asserted it
> is not possible to deassert them.
> - IXOFF controls sending of XON/XOFF characters, not the reception of
> these sequences.
> - IXON controls the recognition of XON/XOFF characters, not the transmission
> of the same.
> - Wrong bitmasks for hardware assisted software flow control. Bit 2
> in EFR enables sending of XON2/XOFF2 which are never set.
> - No point comparing received characters against XOFF2 ('special character
> detect') as XOFF2 is not set.
> - Fix multiple places where bits 6 and 5 of MCR are attempted to be
> altered, but because EFR ECB is unset, these bits remain unaffected.
> This effectively prevents us accessing the right XON/XOFF/TCR/TLR
> registers.
> - Remove unnecessary read-backs of EFR/MCR/LCR registers - these registers
> don't change beneath us, they are configuration registers which hold their
> values. Not only does this simplify the code, but it makes it more
> readable, and more importantly ensures that we work from a consistent
> state where ->efr never has ECB set, and ->mcr never has the TCRTLR
> bit set.
> - Fix disablement of hardware flow control and IXANY modes; once enabled
> these could never be disabled because nothing in the code ever clears
> these configuration bits.
>
> Once that lot is fixed, these patches expand serial_core to permit hardware
> assisted flow control by:
> - adding throttle/unthrottle callbacks into low level serial drivers,
> which allow them to take whatever action is necessary with hardware
> assisted flow control to throttle the remote end. In the case of
> OMAP serial, this means disabling the RX interrupts so that the FIFO
> fills to the watermark.
>
> We then have a number of cleanups to the OMAP serial code to make the
> set_termios() function clearer and less prone to the kinds of mistakes
> identified above. This results in a great simplification of the flow
> control configuration code.
>
> The OMAP serial driver hacks around with the transmit buffer allocation;
> lets clean that up so that drivers can cleanly allocate their transmitter
> buffer using coherent memory if that's what they desire.
>
> Finally, the last few patches clean up the plat/omap-serial.h header file,
> moving most of its contents into the OMAP serial driver itself. Most of
> this is private to the OMAP serial driver and should never have been
> shared with anything else.
>
> I have omitted to include the conversion of the transmit paths to DMA
> engine. Even with all the above fixed, it has issues when DMA transmit
> is in progress, and a program issues a TCSETS call (as `less' does after
> it has written its prompt.) At the moment, this causes lots of junk to
> be emitted from the serial port when issuing `dmesg | less' which sometimes
> brings the port to a complete halt.
>
> As the OMAP DMA hardware does not have a clean pause when performing a
> MEM->DEV transfer (it discards its FIFO) I do not see a solution to this,
> which probably means that we can _not_ ever support transmit DMA on OMAP
> platforms.
>
> This means the xmit buffer allocation patches are not that useful unless
> a solution to that can be found.
>
> Now, the remaining question is, how much of this patch set do we think
> about merging, and when. Given that flow control in this driver has been
> broken for a very long time, and no one has apparantly noticed, I don't
> think there's any urgency to this, so given its size, my preference would
> be to queue it up for the next merge window. The thing that would worry
> me about applying some of the initial patches is that they may change
> the behaviour today and make any problems here more visible.
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Boot Tested this patch series against v3.6 tag(applied cleanly) on panda
board and
PM tested(hitting off in Idle and suspend) on omap3630 based beagle board.
Note, I also tested the patches against the current master but only
after rebasing, since the current master includes serial patches from
Felipe Balbi[1].
[1] https://lkml.org/lkml/2012/8/24/139
Tested-by: Sourav Poddar <sourav.poddar@ti.com>
Thanks,
Sourav
^ permalink raw reply
* [REPOST] RFC: sched: Prevent wakeup to enter critical section needlessly
From: Ivo Sieben @ 2012-10-09 11:30 UTC (permalink / raw)
To: linux-kernel, Ingo Molnar, Peter Zijlstra
Cc: linux-serial, Alan Cox, Greg KH, Ivo Sieben
In-Reply-To: <1348491997-30898-1-git-send-email-meltedpianoman@gmail.com>
Check the waitqueue task list to be non empty before entering the critical
section. This prevents locking the spin lock needlessly in case the queue
was empty, and therefor also prevent scheduling overhead on a PREEMPT_RT
system.
Signed-off-by: Ivo Sieben <meltedpianoman@gmail.com>
---
REPOST:
Request for comments:
- Does this make any sense?
- I assume that I can safely use the list_empty_careful() function here, but is
that correct?
Background to this patch:
Testing on a PREEMPT_RT system with TTY serial communication. Each time the TTY
line discipline is dereferenced the Idle handling wait queue is woken up (see
function put_ldisc in /drivers/tty/tty_ldisc.c)
However line discipline idle handling is not used very often so the wait queue
is empty most of the time. But still the wake_up() function enters the critical
section guarded by spin locks. This causes additional scheduling overhead when
a lower priority thread has control of that same lock.
kernel/sched/core.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index c177472..c1667c4 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3090,9 +3090,19 @@ void __wake_up(wait_queue_head_t *q, unsigned int mode,
{
unsigned long flags;
- spin_lock_irqsave(&q->lock, flags);
- __wake_up_common(q, mode, nr_exclusive, 0, key);
- spin_unlock_irqrestore(&q->lock, flags);
+ /*
+ * We can check for list emptiness outside the lock by using the
+ * "careful" check that verifies both the next and prev pointers, so
+ * that there cannot be any half-pending updates in progress.
+ *
+ * This prevents the wake up to enter the critical section needlessly
+ * when the task list is empty.
+ */
+ if (!list_empty_careful(&q->task_list)) {
+ spin_lock_irqsave(&q->lock, flags);
+ __wake_up_common(q, mode, nr_exclusive, 0, key);
+ spin_unlock_irqrestore(&q->lock, flags);
+ }
}
EXPORT_SYMBOL(__wake_up);
--
1.7.9.5
^ permalink raw reply related
* [PATCH-v2] tty: Use raw spin lock to protect RX flip buffer
From: Ivo Sieben @ 2012-10-09 11:15 UTC (permalink / raw)
To: Thomas Gleixner, Steven Rostedt, linux-serial, linux-rt-users
Cc: Alan Cox, Greg KH, Ivo Sieben
In-Reply-To: <1348478680-11505-1-git-send-email-meltedpianoman@gmail.com>
The "normal" spin lock that guards the RX flip buffer is replaced by a raw
spin lock.
On a PREEMP_RT system this prevents unwanted scheduling overhead when data is
read at the same time as data is being received: while RX IRQ threaded handling
is busy a TTY read call is performed from a RT priority > threaded IRQ priority.
The read call tries to take the flip buffer spin lock (held by the threaded IRQ)
which blocks and causes a context switch to/from the threaded IRQ handler until
the spin lock is unlocked.
On a 200 MHz AT91SAM9261 processor setup this fixes about 100us of scheduling
overhead on the TTY read call.
Signed-off-by: Ivo Sieben <meltedpianoman@gmail.com>
---
@Thomas Gleixner & Steven Rostedt:
Alan Cox has responded to this patch: "I've no real opinion on the spin/raw_spin
choices as it's basically an RT question not a tty one so if the rt folks are
happy with it so am I." Do you agree?
v2:
Patch was based on another - abandoned - patch om mine, what introduced a bug in
the tty_schedule_flip() function. Fixed this and rebased it on the latest kernel
tree.
drivers/tty/tty_buffer.c | 44 ++++++++++++++++++++++----------------------
include/linux/kbd_kern.h | 4 ++--
include/linux/tty.h | 2 +-
2 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index 91e326f..70cf324 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -135,20 +135,20 @@ static void __tty_buffer_flush(struct tty_struct *tty)
void tty_buffer_flush(struct tty_struct *tty)
{
unsigned long flags;
- spin_lock_irqsave(&tty->buf.lock, flags);
+ raw_spin_lock_irqsave(&tty->buf.lock, flags);
/* If the data is being pushed to the tty layer then we can't
process it here. Instead set a flag and the flush_to_ldisc
path will process the flush request before it exits */
if (test_bit(TTY_FLUSHING, &tty->flags)) {
set_bit(TTY_FLUSHPENDING, &tty->flags);
- spin_unlock_irqrestore(&tty->buf.lock, flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
wait_event(tty->read_wait,
test_bit(TTY_FLUSHPENDING, &tty->flags) == 0);
return;
} else
__tty_buffer_flush(tty);
- spin_unlock_irqrestore(&tty->buf.lock, flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
}
/**
@@ -238,9 +238,9 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size)
unsigned long flags;
int length;
- spin_lock_irqsave(&tty->buf.lock, flags);
+ raw_spin_lock_irqsave(&tty->buf.lock, flags);
length = __tty_buffer_request_room(tty, size);
- spin_unlock_irqrestore(&tty->buf.lock, flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
return length;
}
EXPORT_SYMBOL_GPL(tty_buffer_request_room);
@@ -268,18 +268,18 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty,
unsigned long flags;
struct tty_buffer *tb;
- spin_lock_irqsave(&tty->buf.lock, flags);
+ raw_spin_lock_irqsave(&tty->buf.lock, flags);
space = __tty_buffer_request_room(tty, goal);
tb = tty->buf.tail;
/* If there is no space then tb may be NULL */
if (unlikely(space == 0)) {
- spin_unlock_irqrestore(&tty->buf.lock, flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
break;
}
memcpy(tb->char_buf_ptr + tb->used, chars, space);
memset(tb->flag_buf_ptr + tb->used, flag, space);
tb->used += space;
- spin_unlock_irqrestore(&tty->buf.lock, flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
copied += space;
chars += space;
/* There is a small chance that we need to split the data over
@@ -313,18 +313,18 @@ int tty_insert_flip_string_flags(struct tty_struct *tty,
unsigned long __flags;
struct tty_buffer *tb;
- spin_lock_irqsave(&tty->buf.lock, __flags);
+ raw_spin_lock_irqsave(&tty->buf.lock, __flags);
space = __tty_buffer_request_room(tty, goal);
tb = tty->buf.tail;
/* If there is no space then tb may be NULL */
if (unlikely(space == 0)) {
- spin_unlock_irqrestore(&tty->buf.lock, __flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, __flags);
break;
}
memcpy(tb->char_buf_ptr + tb->used, chars, space);
memcpy(tb->flag_buf_ptr + tb->used, flags, space);
tb->used += space;
- spin_unlock_irqrestore(&tty->buf.lock, __flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, __flags);
copied += space;
chars += space;
flags += space;
@@ -349,10 +349,10 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags);
void tty_schedule_flip(struct tty_struct *tty)
{
unsigned long flags;
- spin_lock_irqsave(&tty->buf.lock, flags);
+ raw_spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
- spin_unlock_irqrestore(&tty->buf.lock, flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
schedule_work(&tty->buf.work);
}
EXPORT_SYMBOL(tty_schedule_flip);
@@ -379,7 +379,7 @@ int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
unsigned long flags;
struct tty_buffer *tb;
- spin_lock_irqsave(&tty->buf.lock, flags);
+ raw_spin_lock_irqsave(&tty->buf.lock, flags);
space = __tty_buffer_request_room(tty, size);
tb = tty->buf.tail;
@@ -388,7 +388,7 @@ int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
tb->used += space;
}
- spin_unlock_irqrestore(&tty->buf.lock, flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
return space;
}
EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
@@ -416,7 +416,7 @@ int tty_prepare_flip_string_flags(struct tty_struct *tty,
unsigned long __flags;
struct tty_buffer *tb;
- spin_lock_irqsave(&tty->buf.lock, __flags);
+ raw_spin_lock_irqsave(&tty->buf.lock, __flags);
space = __tty_buffer_request_room(tty, size);
tb = tty->buf.tail;
@@ -425,7 +425,7 @@ int tty_prepare_flip_string_flags(struct tty_struct *tty,
*flags = tb->flag_buf_ptr + tb->used;
tb->used += space;
}
- spin_unlock_irqrestore(&tty->buf.lock, __flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, __flags);
return space;
}
EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
@@ -455,7 +455,7 @@ static void flush_to_ldisc(struct work_struct *work)
if (disc == NULL) /* !TTY_LDISC */
return;
- spin_lock_irqsave(&tty->buf.lock, flags);
+ raw_spin_lock_irqsave(&tty->buf.lock, flags);
if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {
struct tty_buffer *head;
@@ -484,10 +484,10 @@ static void flush_to_ldisc(struct work_struct *work)
char_buf = head->char_buf_ptr + head->read;
flag_buf = head->flag_buf_ptr + head->read;
head->read += count;
- spin_unlock_irqrestore(&tty->buf.lock, flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->ops->receive_buf(tty, char_buf,
flag_buf, count);
- spin_lock_irqsave(&tty->buf.lock, flags);
+ raw_spin_lock_irqsave(&tty->buf.lock, flags);
}
clear_bit(TTY_FLUSHING, &tty->flags);
}
@@ -499,7 +499,7 @@ static void flush_to_ldisc(struct work_struct *work)
clear_bit(TTY_FLUSHPENDING, &tty->flags);
wake_up(&tty->read_wait);
}
- spin_unlock_irqrestore(&tty->buf.lock, flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
tty_ldisc_deref(disc);
}
@@ -533,10 +533,10 @@ void tty_flush_to_ldisc(struct tty_struct *tty)
void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;
- spin_lock_irqsave(&tty->buf.lock, flags);
+ raw_spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
- spin_unlock_irqrestore(&tty->buf.lock, flags);
+ raw_spin_unlock_irqrestore(&tty->buf.lock, flags);
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work);
@@ -557,7 +557,7 @@ EXPORT_SYMBOL(tty_flip_buffer_push);
void tty_buffer_init(struct tty_struct *tty)
{
- spin_lock_init(&tty->buf.lock);
+ raw_spin_lock_init(&tty->buf.lock);
tty->buf.head = NULL;
tty->buf.tail = NULL;
tty->buf.free = NULL;
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 4f6c59a..c6299c5 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -87,7 +87,7 @@ struct tty_buffer {
struct tty_bufhead {
struct work_struct work;
- spinlock_t lock;
+ raw_spinlock_t lock;
struct tty_buffer *head; /* Queue head */
struct tty_buffer *tail; /* Active buffer */
struct tty_buffer *free; /* Free queue head */
--
1.7.9.5
^ permalink raw reply related
* [PATCH v2] serial: vt8500: fix possible memory leak in vt8500_serial_probe()
From: Wei Yongjun @ 2012-10-08 2:35 UTC (permalink / raw)
To: linux, alan, gregkh, grant.likely, rob.herring
Cc: yongjun_wei, linux-arm-kernel, linux-serial, devicetree-discuss
From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
vt8500_port is malloced in vt8500_serial_probe() and should be freed
before leaving from the error handling cases, otherwise it will
cause memory leak.
Fix it by move the allocation of vt8500_port after those test.
dpatch engine is used to auto generate this patch.
(https://github.com/weiyj/dpatch)
Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
Acked-by: Tony Prisk <linux@prisktech.co.nz>
---
v1 -> v2: move the allocation of vt8500_port after those test
---
drivers/tty/serial/vt8500_serial.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 205d4cf..4354fe5 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -567,10 +567,6 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
if (!mmres || !irqres)
return -ENODEV;
- vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
- if (!vt8500_port)
- return -ENOMEM;
-
if (np)
port = of_alias_get_id(np, "serial");
if (port > VT8500_MAX_PORTS)
@@ -593,6 +589,10 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
return -EBUSY;
}
+ vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
+ if (!vt8500_port)
+ return -ENOMEM;
+
vt8500_port->uart.type = PORT_VT8500;
vt8500_port->uart.iotype = UPIO_MEM;
vt8500_port->uart.mapbase = mmres->start;
^ permalink raw reply related
* Re: [PATCH] serial: vt8500: fix possible memory leak in vt8500_serial_probe()
From: Tony Prisk @ 2012-10-08 1:03 UTC (permalink / raw)
To: Wei Yongjun
Cc: gregkh, devicetree-discuss, rob.herring, grant.likely,
yongjun_wei, linux-serial, linux-arm-kernel, alan
In-Reply-To: <CAPgLHd9NrFCsp5NhjBJ4tYgY3-SfbTA-ps=rZgpmoywM6tX6KQ@mail.gmail.com>
On Mon, 2012-10-08 at 08:47 +0800, Wei Yongjun wrote:
> From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
>
> vt8500_port is malloced in vt8500_serial_probe() and should be freed
> before leaving from the error handling cases, otherwise it will
> cause memory leak.
>
> dpatch engine is used to auto generate this patch.
> (https://github.com/weiyj/dpatch)
>
> Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
> ---
> drivers/tty/serial/vt8500_serial.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
> index 205d4cf..43fc369 100644
> --- a/drivers/tty/serial/vt8500_serial.c
> +++ b/drivers/tty/serial/vt8500_serial.c
> @@ -584,13 +584,16 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
> sizeof(vt8500_ports_in_use));
> }
>
> - if (port > VT8500_MAX_PORTS)
> - return -ENODEV;
> + if (port > VT8500_MAX_PORTS) {
> + ret = -ENODEV;
> + goto err;
> + }
>
> /* reserve the port id */
> if (test_and_set_bit(port, &vt8500_ports_in_use)) {
> /* port already in use - shouldn't really happen */
> - return -EBUSY;
> + ret = -EBUSY;
> + goto err;
> }
>
> vt8500_port->uart.type = PORT_VT8500;
>
You are correct - this is an error but wouldn't it be better to simply
move the allocation of vt8500_port after these test since it's not used
until later.
It would save two possible instances of having to alloc/release a block
of memory that is never used and possibly create unnecessary memory
fragmentation.
Either way, this patch fixes the problem, so you can add my Ack'd if my
suggestion is considered nit-picky.
Acked-by: Tony Prisk <linux@prisktech.co.nz>
Regards
Tony P
^ permalink raw reply
* [PATCH] serial: vt8500: fix possible memory leak in vt8500_serial_probe()
From: Wei Yongjun @ 2012-10-08 0:47 UTC (permalink / raw)
To: linux, alan, gregkh, grant.likely, rob.herring
Cc: yongjun_wei, linux-arm-kernel, linux-serial, devicetree-discuss
From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
vt8500_port is malloced in vt8500_serial_probe() and should be freed
before leaving from the error handling cases, otherwise it will
cause memory leak.
dpatch engine is used to auto generate this patch.
(https://github.com/weiyj/dpatch)
Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
---
drivers/tty/serial/vt8500_serial.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 205d4cf..43fc369 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -584,13 +584,16 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
sizeof(vt8500_ports_in_use));
}
- if (port > VT8500_MAX_PORTS)
- return -ENODEV;
+ if (port > VT8500_MAX_PORTS) {
+ ret = -ENODEV;
+ goto err;
+ }
/* reserve the port id */
if (test_and_set_bit(port, &vt8500_ports_in_use)) {
/* port already in use - shouldn't really happen */
- return -EBUSY;
+ ret = -EBUSY;
+ goto err;
}
vt8500_port->uart.type = PORT_VT8500;
^ permalink raw reply related
* Re: PL2303 Driver Issue
From: Greg KH @ 2012-10-07 14:47 UTC (permalink / raw)
To: Reddy Kaveri, Praveen Kumar; +Cc: linux-serial@vger.kernel.org
In-Reply-To: <523E9909A6DFCF469669581BEB8DA7AA56928B25@G2W2441.americas.hpqcorp.net>
On Sat, Oct 06, 2012 at 06:33:19PM +0000, Reddy Kaveri, Praveen Kumar wrote:
> Hi,
>
> I am using Telxon PTC-710 device in linux environment and I am using
> RS232 Serial to USB converter. When I connect the device to USB port
> it is detecting and showing in the system log as "attached to
> ttyUSB0". I have written a java program to display all the available
> ports. But this program is displaying only ttyS0 &ttyS1. I have tried
> to set all permissions to the user as well as to port. Please help me
> if I am missing any configuration related to this driver.
You are going to have to modify your userspace program to know to look
for the ttyUSBX devices as well. There's nothing the kernel can do
about this.
As you wrote the program, how are you getting the list of serial devices
in the system?
greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox