* Re: [PATCH v4] serial/arc-uart: Add New Driver
From: Vineet Gupta @ 2012-10-26 6:16 UTC (permalink / raw)
To: Greg KH; +Cc: alan, arc-linux-dev, linux-serial, linux-kernel
In-Reply-To: <20121025183643.GA22504@kroah.com>
On Friday 26 October 2012 12:06 AM, Greg KH wrote:
> On Thu, Oct 25, 2012 at 12:00:07PM +0530, Vineet.Gupta1@synopsys.com wrote:
>> From: Vineet Gupta <vgupta@synopsys.com>
>>
>> Hi Greg,
>>
>> Rebased off of tty-next and verfied that it builds fine.
> Did you run it?
Absolutely ! What I meant was, this specific version (rebased off of
tty-next) can't be run for ARC platform yet (for which this driver is
originally meant). The preceding version based off of 3.6 (including all
of Alan's review comments) already runs for our 3.6 kernel.
>
>> Please consider applying.
> I can't, see my review comments.
Updated revision to follow
Thx,
-Vineet
>
> thanks,
>
> greg k-h
^ permalink raw reply
* [PATCH] n_gsm: prevent crash due to dereferencing NULL gsm->tty
From: Guillaume Juan @ 2012-10-26 8:11 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-serial, Alan Cox, linux-kernel
From: Guillaume Juan <guillaume.juan@sagemcom.com>
If gsm->tty happens to be NULL in gsmld_output, avoid crashing the kernel (the crash is replaced by a warning dump).
Prevent at earlier level such situation:
- gsmtty_hangup does no longer call gsm_dlci_begin_close when called synchronously from gsm_cleanup_mux, because this would arm a timer that will asynchronously lead to use gms->tty, potentially after it is nullified.
- in gsm_cleanup_mux, release DLCIs (other than DLCI#0 which is the control one) before closing mux. Else, it can happen that the T2 timer is rearmed by another thread scheduled between del_timer_sync and gsm_dlci release
(for example by the following call-stack: fput => tty_release => tty_port_close_start => tty_port_lower_dtr_rts => gsm_dtr_rts => gsmtty_modem_update)
Signed-off-by: Guillaume Juan <guillaume.juan@sagemcom.com>
---
drivers/tty/n_gsm.c | 35 ++++++++++++++++++++++++++---------
1 files changed, 26 insertions(+), 9 deletions(-)
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 1e8e8ce..fe3c6ad 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -1510,8 +1510,8 @@ static void gsm_dlci_begin_open(struct gsm_dlci *dlci)
}
/**
- * gsm_dlci_begin_close - start channel open procedure
- * @dlci: DLCI to open
+ * gsm_dlci_begin_close - start channel close procedure
+ * @dlci: DLCI to close
*
* Commence closing a DLCI from the Linux side. We issue DISC messages
* to the modem which should then reply with a UA, at which point we
@@ -2031,6 +2031,13 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
spin_unlock(&gsm_mux_lock);
WARN_ON(i == MAX_MUX);
+ /* Free up any link layer users */
+ if (dlci)
+ dlci->dead = 1;
+ for (i = 1; i < NUM_DLCI; i++)
+ if (gsm->dlci[i])
+ gsm_dlci_release(gsm->dlci[i]);
+
/* In theory disconnecting DLCI 0 is sufficient but for some
modems this is apparently not the case. */
if (dlci) {
@@ -2041,15 +2048,11 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
del_timer_sync(&gsm->t2_timer);
/* Now we are sure T2 has stopped */
if (dlci) {
- dlci->dead = 1;
gsm_dlci_begin_close(dlci);
wait_event_interruptible(gsm->event,
dlci->state == DLCI_CLOSED);
+ gsm_dlci_release(dlci);
}
- /* Free up any link layer users */
- for (i = 0; i < NUM_DLCI; i++)
- if (gsm->dlci[i])
- gsm_dlci_release(gsm->dlci[i]);
/* Now wipe the queues */
list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
kfree(txq);
@@ -2192,6 +2195,10 @@ EXPORT_SYMBOL_GPL(gsm_alloc_mux);
static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len)
{
+ if (gsm->tty == NULL) {
+ WARN(1, "NULL gsm->tty pointer in gsmld_output\n");
+ return -EINVAL;
+ }
if (tty_write_room(gsm->tty) < len) {
set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags);
return -ENOSPC;
@@ -2323,7 +2330,7 @@ static void gsmld_flush_buffer(struct tty_struct *tty)
* @tty: device
*
* Called from the terminal layer when this line discipline is
- * being shut down, either because of a close or becsuse of a
+ * being shut down, either because of a close or because of a
* discipline change. The function will not be called while other
* ldisc methods are in progress.
*/
@@ -2954,6 +2961,15 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
gsm = dlci->gsm;
if (tty_port_close_start(&dlci->port, tty, filp) == 0)
goto out;
+ /* Should not happen because the DLCI ttys are hung up (which causes
+ tty_port_close_start to return 0) by gsm_dlci_release before setting
+ gsm->tty to NULL */
+ if (gsm->tty == NULL) {
+ WARN(1, "NULL gsm->tty pointer in gsmtty_close for %s\n",
+ tty->name);
+ goto out;
+ }
+
gsm_dlci_begin_close(dlci);
tty_port_close_end(&dlci->port, tty);
tty_port_tty_set(&dlci->port, NULL);
@@ -2967,7 +2983,8 @@ static void gsmtty_hangup(struct tty_struct *tty)
{
struct gsm_dlci *dlci = tty->driver_data;
tty_port_hangup(&dlci->port);
- gsm_dlci_begin_close(dlci);
+ if (!dlci->gsm->dead)
+ gsm_dlci_begin_close(dlci);
}
static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf,
--
1.7.0.4
#
" Ce courriel et les documents qui lui sont joints peuvent contenir des
informations confidentielles ou ayant un caractère privé. S'ils ne vous sont
pas destinés, nous vous signalons qu'il est strictement interdit de les
divulguer, de les reproduire ou d'en utiliser de quelque manière que ce
soit le contenu. Si ce message vous a été transmis par erreur, merci d'en
informer l'expéditeur et de supprimer immédiatement de votre système
informatique ce courriel ainsi que tous les documents qui y sont attachés."
******
" This e-mail and any attached documents may contain confidential or
proprietary information. If you are not the intended recipient, you are
notified that any dissemination, copying of this e-mail and any attachments
thereto or use of their contents by any means whatsoever is strictly
prohibited. If you have received this e-mail in error, please advise the
sender immediately and delete this e-mail and all attached documents
from your computer system."
#
--
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 related
* [PATCH v5] serial/arc-uart: Add new driver
From: Vineet.Gupta1 @ 2012-10-26 12:03 UTC (permalink / raw)
To: gregkh, alan; +Cc: arc-linux-dev, linux-serial, linux-kernel, Vineet Gupta
From: Vineet Gupta <vgupta@synopsys.com>
Hi Greg,
Here's the updated revision with fixes per your review comments.
-Rebased off tty-next tip.
-Build tested for x86, both as builtin and as module
-Same driver (as module) verified on ARCAngel4 Board.
Please consider applying.
Thanks,
Vineet
v5:
* Driver now builds as module; default !y
* Fixed some build wreckage due to SERIAL_ARC && !SERIAL_ARC_CONSOLE
* Fixed a sparse warning due to forced cast, hence reg offsets now in bytes
v4:
* UAPI disintegration fallout for serial_core.h
* rebased off of tty-next
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 | 23 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/arc_uart.c | 754 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/serial_core.h | 2 +
4 files changed, 780 insertions(+), 0 deletions(-)
create mode 100644 drivers/tty/serial/arc_uart.c
--
1.7.4.1
^ permalink raw reply
* [PATCH v5] serial/arc-uart: Add new driver
From: Vineet.Gupta1 @ 2012-10-26 12:03 UTC (permalink / raw)
To: gregkh, alan; +Cc: arc-linux-dev, linux-serial, linux-kernel, Vineet Gupta
In-Reply-To: <1351252996-28484-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 | 23 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/arc_uart.c | 754 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/serial_core.h | 2 +
4 files changed, 780 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 2a53be5..b176801 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1423,4 +1423,27 @@ config SERIAL_EFM32_UART_CONSOLE
depends on SERIAL_EFM32_UART=y
select SERIAL_CORE_CONSOLE
+config SERIAL_ARC
+ tristate "ARC UART driver support"
+ select SERIAL_CORE
+ help
+ Driver for on-chip UART for ARC(Synopsys) for the legacy
+ FPGA Boards (ML50x/ARCAngel4)
+
+config SERIAL_ARC_CONSOLE
+ bool "Console on ARC UART"
+ depends on SERIAL_ARC=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Enable system Console on ARC UART
+
+config SERIAL_ARC_NR_PORTS
+ int "Number of ARC UART ports"
+ depends on SERIAL_ARC
+ range 1 3
+ default "1"
+ 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..c12efae
--- /dev/null
+++ b/drivers/tty/serial/arc_uart.c
@@ -0,0 +1,754 @@
+/*
+ * 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 4
+#define R_ID2 8
+#define R_ID3 12
+#define R_DATA 16
+#define R_STS 20
+#define R_BAUDL 24
+#define R_BAUDH 28
+
+/* 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) (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;
+}
+
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE)
+
+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);
+}
+#endif
+
+#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;
+}
+#else
+static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_SERIAL_ARC_CONSOLE */
+
+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/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 7e1ab20..ebcc73f 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -215,5 +215,7 @@
/* Energy Micro efm32 SoC */
#define PORT_EFMUART 100
+/* ARC (Synopsys) on-chip UART */
+#define PORT_ARC 101
#endif /* _UAPILINUX_SERIAL_CORE_H */
--
1.7.4.1
^ permalink raw reply related
* Re: [PATCH v5] serial/arc-uart: Add new driver
From: Felipe Balbi @ 2012-10-26 12:10 UTC (permalink / raw)
To: Vineet.Gupta1; +Cc: gregkh, alan, arc-linux-dev, linux-serial, linux-kernel
In-Reply-To: <1351252996-28484-2-git-send-email-vgupta@synopsys.com>
[-- Attachment #1: Type: text/plain, Size: 25302 bytes --]
On Fri, Oct 26, 2012 at 05:33:16PM +0530, Vineet.Gupta1@synopsys.com wrote:
> 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 | 23 ++
> drivers/tty/serial/Makefile | 1 +
> drivers/tty/serial/arc_uart.c | 754 ++++++++++++++++++++++++++++++++++++++
> include/uapi/linux/serial_core.h | 2 +
> 4 files changed, 780 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 2a53be5..b176801 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1423,4 +1423,27 @@ config SERIAL_EFM32_UART_CONSOLE
> depends on SERIAL_EFM32_UART=y
> select SERIAL_CORE_CONSOLE
>
> +config SERIAL_ARC
> + tristate "ARC UART driver support"
> + select SERIAL_CORE
> + help
> + Driver for on-chip UART for ARC(Synopsys) for the legacy
> + FPGA Boards (ML50x/ARCAngel4)
> +
> +config SERIAL_ARC_CONSOLE
> + bool "Console on ARC UART"
> + depends on SERIAL_ARC=y
> + select SERIAL_CORE_CONSOLE
> + help
> + Enable system Console on ARC UART
> +
> +config SERIAL_ARC_NR_PORTS
> + int "Number of ARC UART ports"
> + depends on SERIAL_ARC
> + range 1 3
> + default "1"
> + 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..c12efae
> --- /dev/null
> +++ b/drivers/tty/serial/arc_uart.c
> @@ -0,0 +1,754 @@
> +/*
> + * 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 4
> +#define R_ID2 8
> +#define R_ID3 12
> +#define R_DATA 16
> +#define R_STS 20
> +#define R_BAUDL 24
> +#define R_BAUDH 28
> +
> +/* 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) (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,
else branch isn't necessary. The structure is declared static and
everything is initialized to zero unless overwritten by code.
> +#endif
> +};
> +
> +static void arc_serial_stop_rx(struct uart_port *port)
> +{
> + struct arc_uart_port *uart = (struct arc_uart_port *)port;
I would suggest using container_of() here. It's very unlikely to happen,
but if another field is added before struct uart_port member in your
structure, this will break.
> + 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;
else is unnecessary you can convert this into:
if (stat & TXEMPTY)
return TIOCSER_TEMT;
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;
can this really happen ? why would you receive characters while tty is
NULL ?
> + /*
> + * 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;
> +}
why all these empty functions with wrong comments above them ??
> +/*
> + * 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;
> +}
> +
> +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE)
> +
> +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);
> +}
> +#endif
> +
> +#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;
> +}
> +#else
> +static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev)
> +{
> + return -ENODEV;
> +}
> +#endif /* CONFIG_SERIAL_ARC_CONSOLE */
> +
> +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");
please remove this line, it's just useless IMHO.
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH v5] serial/arc-uart: Add new driver
From: Vineet Gupta @ 2012-10-26 12:47 UTC (permalink / raw)
To: balbi; +Cc: gregkh, alan, arc-linux-dev, linux-serial, linux-kernel
In-Reply-To: <20121026121032.GD26342@arwen.pp.htv.fi>
On Friday 26 October 2012 05:40 PM, Felipe Balbi wrote:
> On Fri, Oct 26, 2012 at 05:33:16PM +0530, Vineet.Gupta1@synopsys.com wrote:
>> 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 | 23 ++
>> drivers/tty/serial/Makefile | 1 +
>> drivers/tty/serial/arc_uart.c | 754 ++++++++++++++++++++++++++++++++++++++
>> include/uapi/linux/serial_core.h | 2 +
>> 4 files changed, 780 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 2a53be5..b176801 100644
>> --- a/drivers/tty/serial/Kconfig
>> +++ b/drivers/tty/serial/Kconfig
>> @@ -1423,4 +1423,27 @@ config SERIAL_EFM32_UART_CONSOLE
>> depends on SERIAL_EFM32_UART=y
>> select SERIAL_CORE_CONSOLE
>>
>> +config SERIAL_ARC
>> + tristate "ARC UART driver support"
>> + select SERIAL_CORE
>> + help
>> + Driver for on-chip UART for ARC(Synopsys) for the legacy
>> + FPGA Boards (ML50x/ARCAngel4)
>> +
>> +config SERIAL_ARC_CONSOLE
>> + bool "Console on ARC UART"
>> + depends on SERIAL_ARC=y
>> + select SERIAL_CORE_CONSOLE
>> + help
>> + Enable system Console on ARC UART
>> +
>> +config SERIAL_ARC_NR_PORTS
>> + int "Number of ARC UART ports"
>> + depends on SERIAL_ARC
>> + range 1 3
>> + default "1"
>> + 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..c12efae
>> --- /dev/null
>> +++ b/drivers/tty/serial/arc_uart.c
>> @@ -0,0 +1,754 @@
>> +/*
>> + * 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 4
>> +#define R_ID2 8
>> +#define R_ID3 12
>> +#define R_DATA 16
>> +#define R_STS 20
>> +#define R_BAUDL 24
>> +#define R_BAUDH 28
>> +
>> +/* 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) (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,
> else branch isn't necessary. The structure is declared static and
> everything is initialized to zero unless overwritten by code.
Correct - Fixed in next ver !
>> +#endif
>> +};
>> +
>> +static void arc_serial_stop_rx(struct uart_port *port)
>> +{
>> + struct arc_uart_port *uart = (struct arc_uart_port *)port;
> I would suggest using container_of() here. It's very unlikely to happen,
> but if another field is added before struct uart_port member in your
> structure, this will break.
I agree that container_of() would make it future safe - but I don't
foresee any significant changes to driver specially the arc_uart_port
structure.
>> + 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;
> else is unnecessary you can convert this into:
>
> if (stat & TXEMPTY)
> return TIOCSER_TEMT;
>
> return 0;
OK !
>
>> +}
>> +
>> +/*
>> + * 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;
> can this really happen ? why would you receive characters while tty is
> NULL ?
Since we are getting a ref to tty - it makes sense to check if the
pointer is not NULL. Alan had pointed to a possible hangup race which
could yield a NULL tty. But I'm not really an expert in serial core to
be sure if at all this will happen - so added the check.
>> + /*
>> + * 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;
>> +}
> why all these empty functions with wrong comments above them ??
Copy/paste cruft. Empty functions deleted in next ver !
Regarding verify_port, I'm not sure whether it needs to elaborately
check for PORT_UNKNOWN -> PORT_ARC or can we simply continue to return
0. But IMHO the comment in there is right. No ?
>> +/*
>> + * 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;
>> +}
>> +
>> +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE)
>> +
>> +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);
>> +}
>> +#endif
>> +
>> +#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;
>> +}
>> +#else
>> +static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev)
>> +{
>> + return -ENODEV;
>> +}
>> +#endif /* CONFIG_SERIAL_ARC_CONSOLE */
>> +
>> +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");
> please remove this line, it's just useless IMHO.
It has helped me enough in past when debugging the uncoupling of driver
from ARC platform code. I'd rather keep it !
Many thanks for your review ! Once I see your reply I'll respin the next
version.
-Vineet
^ permalink raw reply
* [PATCH v6] serial/arc-uart: Add new driver
From: Vineet.Gupta1 @ 2012-10-26 13:59 UTC (permalink / raw)
To: gregkh, alan, balbi
Cc: arc-linux-dev, linux-serial, linux-kernel, Vineet Gupta
From: Vineet Gupta <vgupta@synopsys.com>
Hi,
Here's the updated revision with fixes per Greg and Felipe' review comments.
-Rebased off tty-next tip.
-Build tested for x86, both as builtin and as module
-Same driver (as module) verified on ARCAngel4 Board.
Please consider applying.
Thanks,
Vineet
v6:
* syntactical changes per comments by Felipe
* Empty (request|release)_port removed
v5:
* Driver now builds as module; default !y
* Fixed some build wreckage due to SERIAL_ARC && !SERIAL_ARC_CONSOLE
* Fixed a sparse warning due to forced cast, hence reg offsets now in bytes
v4:
* UAPI disintegration fallout for serial_core.h
* rebased off of tty-next
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 | 23 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/arc_uart.c | 754 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/serial_core.h | 2 +
4 files changed, 780 insertions(+), 0 deletions(-)
create mode 100644 drivers/tty/serial/arc_uart.c
--
1.7.4.1
^ permalink raw reply
* [PATCH v6] serial/arc-uart: Add new driver
From: Vineet.Gupta1 @ 2012-10-26 13:59 UTC (permalink / raw)
To: gregkh, alan, balbi
Cc: arc-linux-dev, linux-serial, linux-kernel, Vineet Gupta
In-Reply-To: <1351259973-16989-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 | 23 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/arc_uart.c | 734 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/serial_core.h | 2 +
4 files changed, 760 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 2a53be5..b176801 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1423,4 +1423,27 @@ config SERIAL_EFM32_UART_CONSOLE
depends on SERIAL_EFM32_UART=y
select SERIAL_CORE_CONSOLE
+config SERIAL_ARC
+ tristate "ARC UART driver support"
+ select SERIAL_CORE
+ help
+ Driver for on-chip UART for ARC(Synopsys) for the legacy
+ FPGA Boards (ML50x/ARCAngel4)
+
+config SERIAL_ARC_CONSOLE
+ bool "Console on ARC UART"
+ depends on SERIAL_ARC=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Enable system Console on ARC UART
+
+config SERIAL_ARC_NR_PORTS
+ int "Number of ARC UART ports"
+ depends on SERIAL_ARC
+ range 1 3
+ default "1"
+ 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..ea311f8
--- /dev/null
+++ b/drivers/tty/serial/arc_uart.c
@@ -0,0 +1,734 @@
+/*
+ * 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 4
+#define R_ID2 8
+#define R_ID3 12
+#define R_DATA 16
+#define R_STS 20
+#define R_BAUDL 24
+#define R_BAUDH 28
+
+/* 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) (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,
+#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;
+
+ 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;
+}
+
+/*
+ * 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)
+ uart->port.type = PORT_ARC;
+}
+
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE)
+
+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);
+}
+#endif
+
+#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,
+ .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;
+}
+#else
+static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_SERIAL_ARC_CONSOLE */
+
+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/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 7e1ab20..ebcc73f 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -215,5 +215,7 @@
/* Energy Micro efm32 SoC */
#define PORT_EFMUART 100
+/* ARC (Synopsys) on-chip UART */
+#define PORT_ARC 101
#endif /* _UAPILINUX_SERIAL_CORE_H */
--
1.7.4.1
^ permalink raw reply related
* Re: [PATCH v5] serial/arc-uart: Add new driver
From: Felipe Balbi @ 2012-10-26 13:54 UTC (permalink / raw)
To: Vineet Gupta
Cc: balbi, gregkh, alan, arc-linux-dev, linux-serial, linux-kernel
In-Reply-To: <508A865E.6030903@synopsys.com>
[-- Attachment #1: Type: text/plain, Size: 18902 bytes --]
hi,
On Fri, Oct 26, 2012 at 06:17:26PM +0530, Vineet Gupta wrote:
> >> +#endif
> >> +};
> >> +
> >> +static void arc_serial_stop_rx(struct uart_port *port)
> >> +{
> >> + struct arc_uart_port *uart = (struct arc_uart_port *)port;
> > I would suggest using container_of() here. It's very unlikely to happen,
> > but if another field is added before struct uart_port member in your
> > structure, this will break.
>
> I agree that container_of() would make it future safe - but I don't
> foresee any significant changes to driver specially the arc_uart_port
> structure.
I would still do it. The way code is right now, container_of() will be
optmized into the same cast you have today, so no impacts there.
> >> +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;
> > can this really happen ? why would you receive characters while tty is
> > NULL ?
>
> Since we are getting a ref to tty - it makes sense to check if the
> pointer is not NULL. Alan had pointed to a possible hangup race which
> could yield a NULL tty. But I'm not really an expert in serial core to
> be sure if at all this will happen - so added the check.
fair enough...
> >> + /*
> >> + * 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;
> >> +}
> > why all these empty functions with wrong comments above them ??
>
> Copy/paste cruft. Empty functions deleted in next ver !
> Regarding verify_port, I'm not sure whether it needs to elaborately
> check for PORT_UNKNOWN -> PORT_ARC or can we simply continue to return
> 0. But IMHO the comment in there is right. No ?
nope, you say that you should verify the new serial_struct, but you
don't verify anything, just return.
> >> +/*
> >> + * 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;
> >> +}
> >> +
> >> +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE)
> >> +
> >> +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);
> >> +}
> >> +#endif
> >> +
> >> +#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;
> >> +}
> >> +#else
> >> +static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev)
> >> +{
> >> + return -ENODEV;
> >> +}
> >> +#endif /* CONFIG_SERIAL_ARC_CONSOLE */
> >> +
> >> +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");
> > please remove this line, it's just useless IMHO.
>
> It has helped me enough in past when debugging the uncoupling of driver
> from ARC platform code. I'd rather keep it !
then make it a debugging print ;-)
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH] serial: 8250 check iir rdi in interrupt
From: Alan Cox @ 2012-10-26 14:19 UTC (permalink / raw)
To: Min Zhang; +Cc: gregkh, linux-serial, linux-kernel
In-Reply-To: <CAJ0e-Fofs48N9VnYr1_8Kch6+yHjGx9iCWBRtRsqpD-B_rWutQ@mail.gmail.com>
> It is racing. For "too much work for irq", here is sequence events
> analyzed by a Motorola engineer:
Thanks - this is enormously helpful in understanding the report.
> 5) The LSR indicates that the transmitter needs data,
> but also indicates the presence of data in the FIFO (0x61 in the LSR)
> 6) The processing function receives the characters, and
> outputs data to the FIFO
> 7) At the exact time (very very small window) that the
> character is read from the FIFO, the FIFO timeout occurs locking in an
> interrupt cause
> 8) The next loop through the interrupt code begins
> 9) The IIR now indicates the data timeout interrupt
> (0xCC in the IIR)
> 10) The processing function is called and it reads the
> LSR
> 11) The LSR is 0 indicating nothing to do
> 12) The interrupt loop continues (the IIR won't clear
> until a character is pulled) until it reaches its max count and
> displays the error.
So we only need to check this in serial8250_handle_irq when IIR indicates
a data timeout interrupt ?
Can we do
if ((iir & 0x0F) == 0x0C) {
/* Expensive RDI check */
}
Alan
^ permalink raw reply
* [PATCH -next] 8250_pci: use module_pci_driver to simplify the code
From: Wei Yongjun @ 2012-10-26 15:04 UTC (permalink / raw)
To: alan, gregkh; +Cc: yongjun_wei, linux-serial
From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
Use the module_pci_driver() macro to make the code simpler
by eliminating module_init and module_exit calls.
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/8250/8250_pci.c | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index cec8852..508063b 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -4332,18 +4332,7 @@ static struct pci_driver serial_pci_driver = {
.err_handler = &serial8250_err_handler,
};
-static int __init serial8250_pci_init(void)
-{
- return pci_register_driver(&serial_pci_driver);
-}
-
-static void __exit serial8250_pci_exit(void)
-{
- pci_unregister_driver(&serial_pci_driver);
-}
-
-module_init(serial8250_pci_init);
-module_exit(serial8250_pci_exit);
+module_pci_driver(serial_pci_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module");
^ permalink raw reply related
* Re: [PATCH] n_gsm: prevent crash due to dereferencing NULL gsm->tty
From: Greg Kroah-Hartman @ 2012-10-26 15:20 UTC (permalink / raw)
To: Guillaume Juan; +Cc: linux-serial, Alan Cox, linux-kernel
In-Reply-To: <26026_1351239109_508A45C4_26026_63_1_508A45C1.3010407@sagemcom.com>
On Fri, Oct 26, 2012 at 10:11:45AM +0200, Guillaume Juan wrote:
> From: Guillaume Juan <guillaume.juan@sagemcom.com>
>
> If gsm->tty happens to be NULL in gsmld_output, avoid crashing the kernel (the crash is replaced by a warning dump).
How can ->tty be NULL here?
> Prevent at earlier level such situation:
> - gsmtty_hangup does no longer call gsm_dlci_begin_close when called synchronously from gsm_cleanup_mux, because this would arm a timer that will asynchronously lead to use gms->tty, potentially after it is nullified.
> - in gsm_cleanup_mux, release DLCIs (other than DLCI#0 which is the control one) before closing mux. Else, it can happen that the T2 timer is rearmed by another thread scheduled between del_timer_sync and gsm_dlci release
> (for example by the following call-stack: fput => tty_release => tty_port_close_start => tty_port_lower_dtr_rts => gsm_dtr_rts => gsmtty_modem_update)
Please wrap your changelog comments at 72 columns.
>
> Signed-off-by: Guillaume Juan <guillaume.juan@sagemcom.com>
> ---
> drivers/tty/n_gsm.c | 35 ++++++++++++++++++++++++++---------
> 1 files changed, 26 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
> index 1e8e8ce..fe3c6ad 100644
> --- a/drivers/tty/n_gsm.c
> +++ b/drivers/tty/n_gsm.c
> @@ -1510,8 +1510,8 @@ static void gsm_dlci_begin_open(struct gsm_dlci *dlci)
> }
>
> /**
> - * gsm_dlci_begin_close - start channel open procedure
> - * @dlci: DLCI to open
> + * gsm_dlci_begin_close - start channel close procedure
> + * @dlci: DLCI to close
> *
> * Commence closing a DLCI from the Linux side. We issue DISC messages
> * to the modem which should then reply with a UA, at which point we
> @@ -2031,6 +2031,13 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
> spin_unlock(&gsm_mux_lock);
> WARN_ON(i == MAX_MUX);
>
> + /* Free up any link layer users */
> + if (dlci)
> + dlci->dead = 1;
> + for (i = 1; i < NUM_DLCI; i++)
> + if (gsm->dlci[i])
> + gsm_dlci_release(gsm->dlci[i]);
> +
> /* In theory disconnecting DLCI 0 is sufficient but for some
> modems this is apparently not the case. */
> if (dlci) {
> @@ -2041,15 +2048,11 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
> del_timer_sync(&gsm->t2_timer);
> /* Now we are sure T2 has stopped */
> if (dlci) {
> - dlci->dead = 1;
> gsm_dlci_begin_close(dlci);
> wait_event_interruptible(gsm->event,
> dlci->state == DLCI_CLOSED);
> + gsm_dlci_release(dlci);
> }
> - /* Free up any link layer users */
> - for (i = 0; i < NUM_DLCI; i++)
> - if (gsm->dlci[i])
> - gsm_dlci_release(gsm->dlci[i]);
> /* Now wipe the queues */
> list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
> kfree(txq);
> @@ -2192,6 +2195,10 @@ EXPORT_SYMBOL_GPL(gsm_alloc_mux);
>
> static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len)
> {
> + if (gsm->tty == NULL) {
> + WARN(1, "NULL gsm->tty pointer in gsmld_output\n");
> + return -EINVAL;
> + }
> if (tty_write_room(gsm->tty) < len) {
> set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags);
> return -ENOSPC;
> @@ -2323,7 +2330,7 @@ static void gsmld_flush_buffer(struct tty_struct *tty)
> * @tty: device
> *
> * Called from the terminal layer when this line discipline is
> - * being shut down, either because of a close or becsuse of a
> + * being shut down, either because of a close or because of a
> * discipline change. The function will not be called while other
> * ldisc methods are in progress.
> */
> @@ -2954,6 +2961,15 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
> gsm = dlci->gsm;
> if (tty_port_close_start(&dlci->port, tty, filp) == 0)
> goto out;
> + /* Should not happen because the DLCI ttys are hung up (which causes
> + tty_port_close_start to return 0) by gsm_dlci_release before setting
> + gsm->tty to NULL */
> + if (gsm->tty == NULL) {
> + WARN(1, "NULL gsm->tty pointer in gsmtty_close for %s\n",
> + tty->name);
> + goto out;
> + }
> +
> gsm_dlci_begin_close(dlci);
> tty_port_close_end(&dlci->port, tty);
> tty_port_tty_set(&dlci->port, NULL);
> @@ -2967,7 +2983,8 @@ static void gsmtty_hangup(struct tty_struct *tty)
> {
> struct gsm_dlci *dlci = tty->driver_data;
> tty_port_hangup(&dlci->port);
> - gsm_dlci_begin_close(dlci);
> + if (!dlci->gsm->dead)
> + gsm_dlci_begin_close(dlci);
> }
>
> static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf,
> --
> 1.7.0.4
>
>
>
> #
> " Ce courriel et les documents qui lui sont joints peuvent contenir des
> informations confidentielles ou ayant un caractère privé. S'ils ne vous sont
> pas destinés, nous vous signalons qu'il est strictement interdit de les
> divulguer, de les reproduire ou d'en utiliser de quelque manière que ce
> soit le contenu. Si ce message vous a été transmis par erreur, merci d'en
> informer l'expéditeur et de supprimer immédiatement de votre système
> informatique ce courriel ainsi que tous les documents qui y sont attachés."
>
>
> ******
>
> " This e-mail and any attached documents may contain confidential or
> proprietary information. If you are not the intended recipient, you are
> notified that any dissemination, copying of this e-mail and any attachments
> thereto or use of their contents by any means whatsoever is strictly
> prohibited. If you have received this e-mail in error, please advise the
> sender immediately and delete this e-mail and all attached documents
> from your computer system."
> #
Because of that footer, I am not allowed to accept this patch at all.
Please remove it and resend.
thanks,
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
* Re: [PATCH] n_gsm: prevent crash due to dereferencing NULL gsm->tty
From: Guillaume Juan @ 2012-10-26 16:34 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-serial, Alan Cox, linux-kernel
In-Reply-To: <20121026152052.GA15840@kroah.com>
Le 26/10/2012 17:20, Greg Kroah-Hartman a écrit :
> On Fri, Oct 26, 2012 at 10:11:45AM +0200, Guillaume Juan wrote:
>> From: Guillaume Juan <guillaume.juan@sagemcom.com>
>>
>> If gsm->tty happens to be NULL in gsmld_output, avoid crashing the kernel (the crash is replaced by a warning dump).
>
> How can ->tty be NULL here?
>
I had some crashes and identified 2 causes: this is what I tried to explain in the 2nd part of the changelog ("Prevent at earlier level such situation: [...]"). Basically it is because the T1 and T2 timers may be rearmed while gsm_cleanup_mux executes (T1 is rearmed by gsmtty_hangup called by gsm_cleanup_mux itself via gsm_dlci_release, whereas T2 may be rearmed by a concurrent call to gsmtty_close).
My patch is intended to remove theses causes, but I thought safer to avoid the crash in gsmld_output if ever there are other causes remaining. Do you mean I should let the crash happen for these unproven cases?
>> Prevent at earlier level such situation:
>> - gsmtty_hangup does no longer call gsm_dlci_begin_close when called synchronously from gsm_cleanup_mux, because this would arm a timer that will asynchronously lead to use gms->tty, potentially after it is nullified.
>> - in gsm_cleanup_mux, release DLCIs (other than DLCI#0 which is the control one) before closing mux. Else, it can happen that the T2 timer is rearmed by another thread scheduled between del_timer_sync and gsm_dlci release
>> (for example by the following call-stack: fput => tty_release => tty_port_close_start => tty_port_lower_dtr_rts => gsm_dtr_rts => gsmtty_modem_update)
>
> Please wrap your changelog comments at 72 columns.
>
Sorry, I didn't know this rule (it is the 1st time I post a patch and the 1st time I use git). I will when I re-send.
>>
>> Signed-off-by: Guillaume Juan <guillaume.juan@sagemcom.com>
>> ---
>> drivers/tty/n_gsm.c | 35 ++++++++++++++++++++++++++---------
>> 1 files changed, 26 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
>> index 1e8e8ce..fe3c6ad 100644
>> --- a/drivers/tty/n_gsm.c
>> +++ b/drivers/tty/n_gsm.c
>> @@ -1510,8 +1510,8 @@ static void gsm_dlci_begin_open(struct gsm_dlci *dlci)
>> }
>>
>> /**
>> - * gsm_dlci_begin_close - start channel open procedure
>> - * @dlci: DLCI to open
>> + * gsm_dlci_begin_close - start channel close procedure
>> + * @dlci: DLCI to close
>> *
>> * Commence closing a DLCI from the Linux side. We issue DISC messages
>> * to the modem which should then reply with a UA, at which point we
>> @@ -2031,6 +2031,13 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
>> spin_unlock(&gsm_mux_lock);
>> WARN_ON(i == MAX_MUX);
>>
>> + /* Free up any link layer users */
>> + if (dlci)
>> + dlci->dead = 1;
>> + for (i = 1; i < NUM_DLCI; i++)
>> + if (gsm->dlci[i])
>> + gsm_dlci_release(gsm->dlci[i]);
>> +
>> /* In theory disconnecting DLCI 0 is sufficient but for some
>> modems this is apparently not the case. */
>> if (dlci) {
>> @@ -2041,15 +2048,11 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
>> del_timer_sync(&gsm->t2_timer);
>> /* Now we are sure T2 has stopped */
>> if (dlci) {
>> - dlci->dead = 1;
>> gsm_dlci_begin_close(dlci);
>> wait_event_interruptible(gsm->event,
>> dlci->state == DLCI_CLOSED);
>> + gsm_dlci_release(dlci);
>> }
>> - /* Free up any link layer users */
>> - for (i = 0; i < NUM_DLCI; i++)
>> - if (gsm->dlci[i])
>> - gsm_dlci_release(gsm->dlci[i]);
>> /* Now wipe the queues */
>> list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
>> kfree(txq);
>> @@ -2192,6 +2195,10 @@ EXPORT_SYMBOL_GPL(gsm_alloc_mux);
>>
>> static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len)
>> {
>> + if (gsm->tty == NULL) {
>> + WARN(1, "NULL gsm->tty pointer in gsmld_output\n");
>> + return -EINVAL;
>> + }
>> if (tty_write_room(gsm->tty) < len) {
>> set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags);
>> return -ENOSPC;
>> @@ -2323,7 +2330,7 @@ static void gsmld_flush_buffer(struct tty_struct *tty)
>> * @tty: device
>> *
>> * Called from the terminal layer when this line discipline is
>> - * being shut down, either because of a close or becsuse of a
>> + * being shut down, either because of a close or because of a
>> * discipline change. The function will not be called while other
>> * ldisc methods are in progress.
>> */
>> @@ -2954,6 +2961,15 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
>> gsm = dlci->gsm;
>> if (tty_port_close_start(&dlci->port, tty, filp) == 0)
>> goto out;
>> + /* Should not happen because the DLCI ttys are hung up (which causes
>> + tty_port_close_start to return 0) by gsm_dlci_release before setting
>> + gsm->tty to NULL */
>> + if (gsm->tty == NULL) {
>> + WARN(1, "NULL gsm->tty pointer in gsmtty_close for %s\n",
>> + tty->name);
>> + goto out;
>> + }
>> +
>> gsm_dlci_begin_close(dlci);
>> tty_port_close_end(&dlci->port, tty);
>> tty_port_tty_set(&dlci->port, NULL);
>> @@ -2967,7 +2983,8 @@ static void gsmtty_hangup(struct tty_struct *tty)
>> {
>> struct gsm_dlci *dlci = tty->driver_data;
>> tty_port_hangup(&dlci->port);
>> - gsm_dlci_begin_close(dlci);
>> + if (!dlci->gsm->dead)
>> + gsm_dlci_begin_close(dlci);
>> }
>>
>> static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf,
>> --
>> 1.7.0.4
>>
>>
>>
>> #
>> " Ce courriel et les documents qui lui sont joints peuvent contenir des
>> informations confidentielles ou ayant un caractère privé. S'ils ne vous sont
>> pas destinés, nous vous signalons qu'il est strictement interdit de les
>> divulguer, de les reproduire ou d'en utiliser de quelque manière que ce
>> soit le contenu. Si ce message vous a été transmis par erreur, merci d'en
>> informer l'expéditeur et de supprimer immédiatement de votre système
>> informatique ce courriel ainsi que tous les documents qui y sont attachés."
>>
>>
>> ******
>>
>> " This e-mail and any attached documents may contain confidential or
>> proprietary information. If you are not the intended recipient, you are
>> notified that any dissemination, copying of this e-mail and any attachments
>> thereto or use of their contents by any means whatsoever is strictly
>> prohibited. If you have received this e-mail in error, please advise the
>> sender immediately and delete this e-mail and all attached documents
>> from your computer system."
>> #
>
> Because of that footer, I am not allowed to accept this patch at all.
> Please remove it and resend.
>
> thanks,
>
> greg k-h
Argh. I'm afraid I don't know how to remove it, since it seems added automatically by my company mail server. I probably will have to struggle with the admins. But since you ARE the "intended recipient" and the content is NOT "confidential or proprietary", does it really forbid you to use the patch?
#
" Ce courriel et les documents qui lui sont joints peuvent contenir des
informations confidentielles ou ayant un caractère privé. S'ils ne vous sont
pas destinés, nous vous signalons qu'il est strictement interdit de les
divulguer, de les reproduire ou d'en utiliser de quelque manière que ce
soit le contenu. Si ce message vous a été transmis par erreur, merci d'en
informer l'expéditeur et de supprimer immédiatement de votre système
informatique ce courriel ainsi que tous les documents qui y sont attachés."
******
" This e-mail and any attached documents may contain confidential or
proprietary information. If you are not the intended recipient, you are
notified that any dissemination, copying of this e-mail and any attachments
thereto or use of their contents by any means whatsoever is strictly
prohibited. If you have received this e-mail in error, please advise the
sender immediately and delete this e-mail and all attached documents
from your computer system."
#
--
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
* Re: [PATCH] n_gsm: prevent crash due to dereferencing NULL gsm->tty
From: Greg Kroah-Hartman @ 2012-10-26 16:47 UTC (permalink / raw)
To: Guillaume Juan; +Cc: linux-serial, Alan Cox, linux-kernel
In-Reply-To: <3252_1351269266_508ABB91_3252_975_1_508ABB8F.5040906@sagemcom.com>
On Fri, Oct 26, 2012 at 06:34:23PM +0200, Guillaume Juan wrote:
> Le 26/10/2012 17:20, Greg Kroah-Hartman a écrit :
>
> > On Fri, Oct 26, 2012 at 10:11:45AM +0200, Guillaume Juan wrote:
> >> From: Guillaume Juan <guillaume.juan@sagemcom.com>
> >>
> >> If gsm->tty happens to be NULL in gsmld_output, avoid crashing the kernel (the crash is replaced by a warning dump).
> >
> > How can ->tty be NULL here?
> >
>
> I had some crashes and identified 2 causes: this is what I tried to explain in the 2nd part of the changelog ("Prevent at earlier level such situation: [...]"). Basically it is because the T1 and T2 timers may be rearmed while gsm_cleanup_mux executes (T1 is rearmed by gsmtty_hangup called by gsm_cleanup_mux itself via gsm_dlci_release, whereas T2 may be rearmed by a concurrent call to gsmtty_close).
>
> My patch is intended to remove theses causes, but I thought safer to avoid the crash in gsmld_output if ever there are other causes remaining. Do you mean I should let the crash happen for these unproven cases?
If you leave it, and it happens, then you know you didn't really fix the
real problem :)
> >> " This e-mail and any attached documents may contain confidential or
> >> proprietary information. If you are not the intended recipient, you are
> >> notified that any dissemination, copying of this e-mail and any attachments
> >> thereto or use of their contents by any means whatsoever is strictly
> >> prohibited. If you have received this e-mail in error, please advise the
> >> sender immediately and delete this e-mail and all attached documents
> >> from your computer system."
> >> #
> >
> > Because of that footer, I am not allowed to accept this patch at all.
> > Please remove it and resend.
>
> Argh. I'm afraid I don't know how to remove it, since it seems added automatically by my company mail server. I probably will have to struggle with the admins. But since you ARE the "intended recipient" and the content is NOT "confidential or proprietary", does it really forbid you to use the patch?
Yes it does, it goes against the "Signed-off-by:" line in the patch, and
because of that, I am not allowed to accept it. If you wish to do
kernel development, either get your email admins to fix it, or use an
external email account (sadly the only solution for a lot of people.)
If you use an external account, be sure you properly set the "From:"
line in the patch to your properly account, see the file,
Documentation/SubmittingPatches for details.
thanks,
greg k-h
^ permalink raw reply
* [GIT PATCH] TTY/Serial fix for 3.7-rc3
From: Greg KH @ 2012-10-26 17:16 UTC (permalink / raw)
To: Linus Torvalds, Alan Cox, Jiri Slaby
Cc: Andrew Morton, linux-kernel, linux-serial
The following changes since commit 6f0c0580b70c89094b3422ba81118c7b959c7556:
Linux 3.7-rc2 (2012-10-20 12:11:32 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git/ tags/tty-3.7-rc3
for you to fetch changes up to a4f743851f74fc3e0cc40c13082e65c24139f481:
Revert "serial: omap: fix software flow control" (2012-10-24 11:57:21 -0700)
----------------------------------------------------------------
Serial fix for 3.7-rc3
Here is one patch, a revert of a omap serial driver patch that was causing
problems, for your 3.7-rc tree.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----------------------------------------------------------------
Felipe Balbi (1):
Revert "serial: omap: fix software flow control"
arch/arm/plat-omap/include/plat/omap-serial.h | 4 ++--
drivers/tty/serial/omap-serial.c | 12 ++++++------
2 files changed, 8 insertions(+), 8 deletions(-)
^ permalink raw reply
* Re: [PATCH] serial: 8250 check iir rdi in interrupt
From: Min Zhang @ 2012-10-26 21:57 UTC (permalink / raw)
To: Alan Cox; +Cc: gregkh, linux-serial, linux-kernel
In-Reply-To: <20121026151902.0a382d16@pyramind.ukuu.org.uk>
On Fri, Oct 26, 2012 at 7:19 AM, Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:
> So we only need to check this in serial8250_handle_irq when IIR indicates
> a data timeout interrupt ?
>
> Can we do
>
> if ((iir & 0x0F) == 0x0C) {
> /* Expensive RDI check */
> }
>
>
>
> Alan
Checking data timeout interrupt is only for "too much work for irq"
problem. There is another console freeze problem which is caused by
reading receive FIFO when there is no RDI interrupt, such as during
THRI transmit interrupt, so no timeout interrupt. This time one has to
check two both IIR and LSR. After all these checking, it becomes the
original patch anyway.
I am posting another simpler revision to exclude timer handler from
this workaround, and make this workaround default off and inline.
^ permalink raw reply
* [PATCHv2] serial: 8250 check iir rdi in interrupt
From: Min Zhang @ 2012-10-26 22:16 UTC (permalink / raw)
To: Alan Cox; +Cc: gregkh, linux-serial, linux-kernel
In-Reply-To: <20121026151902.0a382d16@pyramind.ukuu.org.uk>
The patch works around two UART interrupt bugs when the serial console is
flooded with inputs:
1. syslog shows "serial8250: too much works for irq"
2. serial console stops responding to key stroke
serial8250_handle_irq() checks UART_IIR_RDI before reading receive fifo
and clears bogus interrupt UART_IIR_RDI without accompanying UART_LSR_DR,
otherwise RDI interrupt could freeze or too many unhandled RDI interrupts.
Added module parameter skip_rdi_check to opt out this workaround.
Tested on Radisys ATCA 46XX which uses FPGA 16550-compatible and
other generic 16550 UART. It takes from an hour to days to reproduce by
pumping inputs to serial console continously using TeraTerm script:
Signed-off-by: Min Zhang <mzhang@mvista.com>
---
drivers/tty/serial/8250/8250.c | 50 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 50 insertions(+), 0 deletions(-)
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index 3ba4234..dfc13d1 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -64,6 +64,7 @@ static int serial_index(struct uart_port *port)
}
static unsigned int skip_txen_test; /* force skip of txen test at init time */
+static unsigned int skip_rdi_check = 1; /* skip of IIR RDI check in interrupt */
/*
* Debugging.
@@ -1479,6 +1480,46 @@ unsigned int serial8250_modem_status(struct uart_8250_port *up)
EXPORT_SYMBOL_GPL(serial8250_modem_status);
/*
+ * Check if status UART_LSR_RD accompanies with interrupt UART_IIR_RDI.
+ * If they are mismatch, massage the status or interupt cause accordingly:
+ *
+ * Return a cleared UART_LSR_RD status if there is no accompanying
+ * UART_IIR_RDI. Hopefully the new status is used by interrupt handler
+ * to skip reading receive FIFO. Otherwise some UART controller stops
+ * generating RDI interrupt after this unnotified FIFO read, until other
+ * interrupts maybe transmit interrupt reads UART_LSR again.
+ *
+ * Or clear interrupt cause UART_IIR_RDI without UART_LSR_RD. The UART sets
+ * UART_IIR_RDI *even* if the received data has been read out from the FIFO
+ * before the timeout occurs. To clear UART_IIR_RDI, read receive buffer
+ * register. Reading it also clears timeout interrupt for 16550+. Otherwise
+ * the uncleared UART_IIR_RDI will keep triggering IRQ but interrupt
+ * handler finds nothing to do.
+ *
+ * Skip this workaround if interrupt is not expected, such as backup timer,
+ * so that handler can still solely rely on original status register.
+ */
+static inline unsigned char serial8250_iir_rdi_check(struct uart_8250_port *up,
+ unsigned char status,
+ unsigned int iir)
+{
+ unsigned int rdi_stat, rdi_intr;
+
+ /* skip for timer based handler */
+ if (up->timer.data)
+ return status;
+
+ rdi_stat = status & UART_LSR_DR;
+ rdi_intr = iir & UART_IIR_RDI;
+
+ if (rdi_stat && !rdi_intr)
+ status &= ~UART_LSR_DR;
+ else if (!rdi_stat && rdi_intr)
+ serial_in(up, UART_RX);
+ return status;
+}
+
+/*
* This handles the interrupt from one port.
*/
int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
@@ -1497,6 +1538,12 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
DEBUG_INTR("status = %x...", status);
+ /* Some UART controller has mismatched UART_IIR_RDI and UART_LSR_DR,
+ which causes either too many interrupts or interrupt freeze
+ */
+ if (!skip_rdi_check)
+ status = serial8250_iir_rdi_check(up, status, iir);
+
if (status & (UART_LSR_DR | UART_LSR_BI))
status = serial8250_rx_chars(up, status);
serial8250_modem_status(up);
@@ -3338,6 +3385,9 @@ MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STR
module_param(skip_txen_test, uint, 0644);
MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time");
+module_param(skip_rdi_check, uint, 0644);
+MODULE_PARM_DESC(skip_rdi_check, "Skip checking IIR RDI bug in interrupt");
+
#ifdef CONFIG_SERIAL_8250_RSA
module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444);
MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");
--
1.7.0.1
^ permalink raw reply related
* Re: [PATCH v5] serial/arc-uart: Add new driver
From: Vineet Gupta @ 2012-10-27 6:54 UTC (permalink / raw)
To: balbi; +Cc: gregkh, alan, arc-linux-dev, linux-serial, linux-kernel
In-Reply-To: <20121026121032.GD26342@arwen.pp.htv.fi>
On Friday 26 October 2012 05:40 PM, Felipe Balbi wrote:
> On Fri, Oct 26, 2012 at 05:33:16PM +0530, Vineet.Gupta1@synopsys.com wrote:
>> +/*
>> + * 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;
>> +}
> why all these empty functions with wrong comments above them ??
Actually serial_core.c invokes the reqest/release callbacks w/o
verifying for a NULL pointer check. Thus they need to be in there even
if empty. I've removed the offending comments though !
-Vineet
^ permalink raw reply
* [PATCH v7] serial/arc-uart: Add new driver
From: Vineet.Gupta1 @ 2012-10-27 7:17 UTC (permalink / raw)
To: gregkh, alan, balbi
Cc: arc-linux-dev, linux-serial, linux-kernel, Vineet Gupta
From: Vineet Gupta <vgupta@synopsys.com>
Hi,
Here's the updated revision with all of Felipe' review comments.
-Rebased off tty-next tip.
-Build tested for x86, both as builtin and as module
-Same driver (as module) verified on ARCAngel4 Board.
Please consider applying.
Thanks,
Vineet
v7:
* Reinstated (request|release)_port() as they are called w/o NULL check by core
* verify_port() now does some basic checking
* Use container_of() for converting embedded uart_port to arc_uart_port
* dev_* instead of pr_*
v6:
* syntactical changes per comments by Felipe
* Empty (request|release)_port removed
v5:
* Driver now builds as module; default !y
* Fixed some build wreckage due to SERIAL_ARC && !SERIAL_ARC_CONSOLE
* Fixed a sparse warning due to forced cast, hence reg offsets now in bytes
v4:
* UAPI disintegration fallout for serial_core.h
* rebased off of tty-next
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 | 23 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/arc_uart.c | 754 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/serial_core.h | 2 +
4 files changed, 780 insertions(+), 0 deletions(-)
create mode 100644 drivers/tty/serial/arc_uart.c
--
1.7.4.1
^ permalink raw reply
* [PATCH v7] serial/arc-uart: Add new driver
From: Vineet.Gupta1 @ 2012-10-27 7:17 UTC (permalink / raw)
To: gregkh, alan, balbi
Cc: arc-linux-dev, linux-serial, linux-kernel, Vineet Gupta
In-Reply-To: <1351322232-32137-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 | 23 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/arc_uart.c | 746 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/serial_core.h | 2 +
4 files changed, 772 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 2a53be5..b176801 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1423,4 +1423,27 @@ config SERIAL_EFM32_UART_CONSOLE
depends on SERIAL_EFM32_UART=y
select SERIAL_CORE_CONSOLE
+config SERIAL_ARC
+ tristate "ARC UART driver support"
+ select SERIAL_CORE
+ help
+ Driver for on-chip UART for ARC(Synopsys) for the legacy
+ FPGA Boards (ML50x/ARCAngel4)
+
+config SERIAL_ARC_CONSOLE
+ bool "Console on ARC UART"
+ depends on SERIAL_ARC=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Enable system Console on ARC UART
+
+config SERIAL_ARC_NR_PORTS
+ int "Number of ARC UART ports"
+ depends on SERIAL_ARC
+ range 1 3
+ default "1"
+ 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..e9c61d1
--- /dev/null
+++ b/drivers/tty/serial/arc_uart.c
@@ -0,0 +1,746 @@
+/*
+ * 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 4
+#define R_ID2 8
+#define R_ID3 12
+#define R_DATA 16
+#define R_STS 20
+#define R_BAUDL 24
+#define R_BAUDH 28
+
+/* 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) (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 */
+};
+
+#define to_arc_port(uport) container_of(uport, struct arc_uart_port, port)
+
+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,
+#endif
+};
+
+static void arc_serial_stop_rx(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+
+ UART_RX_IRQ_DISABLE(uart);
+}
+
+static void arc_serial_stop_tx(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_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 = to_arc_port(port);
+ unsigned int stat;
+
+ stat = UART_GET_STATUS(uart);
+ if (stat & TXEMPTY)
+ return TIOCSER_TEMT;
+
+ 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 = to_arc_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 = to_arc_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)) {
+ dev_warn(uart->port.dev, "Unable to attach ARC UART intr\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 = to_arc_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 = to_arc_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 = to_arc_port(port);
+
+ return uart->port.type == PORT_ARC ? DRIVER_NAME : NULL;
+}
+
+static void arc_serial_release_port(struct uart_port *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)
+{
+ if (port->type != PORT_UNKNOWN && ser->type != PORT_ARC)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void arc_serial_config_port(struct uart_port *port, int flags)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+
+ if (flags & UART_CONFIG_TYPE)
+ uart->port.type = PORT_ARC;
+}
+
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE)
+
+static void arc_serial_poll_putchar(struct uart_port *port, unsigned char chr)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+
+ while (!(UART_GET_STATUS(uart) & TXEMPTY))
+ cpu_relax();
+
+ UART_SET_DATA(uart, chr);
+}
+#endif
+
+#ifdef CONFIG_CONSOLE_POLL
+static int arc_serial_poll_getchar(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_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 dev_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;
+}
+#else
+static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_SERIAL_ARC_CONSOLE */
+
+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;
+
+ ret = uart_register_driver(&arc_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&arc_platform_driver);
+ if (ret)
+ 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/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 7e1ab20..ebcc73f 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -215,5 +215,7 @@
/* Energy Micro efm32 SoC */
#define PORT_EFMUART 100
+/* ARC (Synopsys) on-chip UART */
+#define PORT_ARC 101
#endif /* _UAPILINUX_SERIAL_CORE_H */
--
1.7.4.1
^ permalink raw reply related
* Re: [tty:tty-next 17/23] drivers/staging/dgrp/dgrp_net_ops.c:229:8: error: 'struct tty_struct' has no member named 'read_cnt'
From: Jiri Slaby @ 2012-10-27 12:20 UTC (permalink / raw)
To: Alan Cox
Cc: Fengguang Wu, Yuanhan Liu, Greg Kroah-Hartman, linux-serial,
Bill Pemberton
In-Reply-To: <20121023105330.13d31938@pyramind.ukuu.org.uk>
On 10/23/2012 11:53 AM, Alan Cox wrote:
> On Tue, 23 Oct 2012 10:30:54 +0800
> Fengguang Wu <fengguang.wu@intel.com> wrote:
>
>> Hi Jiri,
>>
>> FYI, kernel build failed on
>>
>> tree: git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-next
>> head: ecbbfd44a08fa80e0d664814efd4c187721b85f6
>> commit: ba2e68ac6157004ee4922fb39ebd9459bbae883e [17/23] TTY: move ldisc data from tty_struct: read_* and echo_* and canon_* stuff
>> config: x86_64-allmodconfig # make ARCH=x86_64 allmodconfig
>>
>> All error/warnings:
>>
>> drivers/staging/dgrp/dgrp_net_ops.c: In function 'dgrp_input':
>> drivers/staging/dgrp/dgrp_net_ops.c:216:27: error: 'struct tty_struct' has no member named 'real_raw'
>> drivers/staging/dgrp/dgrp_net_ops.c:229:8: error: 'struct tty_struct' has no member named 'read_cnt'
>> drivers/staging/dgrp/dgrp_net_ops.c:229:8: error: 'struct tty_struct' has no member named 'read_cnt'
>> drivers/staging/dgrp/dgrp_net_ops.c:261:30: error: 'struct tty_struct' has no member named 'real_raw'
>> drivers/staging/dgrp/dgrp_net_ops.c:276:28: error: 'struct tty_struct' has no member named 'real_raw'
>>
>> vim +229 drivers/staging/dgrp/dgrp_net_ops.c
>>
>> 0b52b749 Bill Pemberton 2012-09-20 228 /* take into consideration length of ldisc */
>> 0b52b749 Bill Pemberton 2012-09-20 @229 len = min(len, (N_TTY_BUF_SIZE - 1) - tty->read_cnt);
>
> This is broken and unsafe. It's always been broken and unsafe. Probably
> the report wants directing to whoever signed up to fix it all in staging.
And that function looks strange alltogether. It's like it's trying to
optimize in case of raw TTY by sending the data directly to ldisc. In a
completely racy way. I think that it should remain marked as BROKEN
until this is converted to tty_prepare_flip_string.
Or is there any use-case this optimization makes a difference?
thanks,
--
js
suse labs
^ permalink raw reply
* Re: [tty:tty-next 17/23] drivers/staging/dgrp/dgrp_net_ops.c:229:8: error: 'struct tty_struct' has no member named 'read_cnt'
From: Greg Kroah-Hartman @ 2012-10-27 15:01 UTC (permalink / raw)
To: Jiri Slaby
Cc: Alan Cox, Fengguang Wu, Yuanhan Liu, linux-serial, Bill Pemberton
In-Reply-To: <508BD18C.4070602@suse.cz>
On Sat, Oct 27, 2012 at 02:20:28PM +0200, Jiri Slaby wrote:
> On 10/23/2012 11:53 AM, Alan Cox wrote:
> > On Tue, 23 Oct 2012 10:30:54 +0800
> > Fengguang Wu <fengguang.wu@intel.com> wrote:
> >
> >> Hi Jiri,
> >>
> >> FYI, kernel build failed on
> >>
> >> tree: git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-next
> >> head: ecbbfd44a08fa80e0d664814efd4c187721b85f6
> >> commit: ba2e68ac6157004ee4922fb39ebd9459bbae883e [17/23] TTY: move ldisc data from tty_struct: read_* and echo_* and canon_* stuff
> >> config: x86_64-allmodconfig # make ARCH=x86_64 allmodconfig
> >>
> >> All error/warnings:
> >>
> >> drivers/staging/dgrp/dgrp_net_ops.c: In function 'dgrp_input':
> >> drivers/staging/dgrp/dgrp_net_ops.c:216:27: error: 'struct tty_struct' has no member named 'real_raw'
> >> drivers/staging/dgrp/dgrp_net_ops.c:229:8: error: 'struct tty_struct' has no member named 'read_cnt'
> >> drivers/staging/dgrp/dgrp_net_ops.c:229:8: error: 'struct tty_struct' has no member named 'read_cnt'
> >> drivers/staging/dgrp/dgrp_net_ops.c:261:30: error: 'struct tty_struct' has no member named 'real_raw'
> >> drivers/staging/dgrp/dgrp_net_ops.c:276:28: error: 'struct tty_struct' has no member named 'real_raw'
> >>
> >> vim +229 drivers/staging/dgrp/dgrp_net_ops.c
> >>
> >> 0b52b749 Bill Pemberton 2012-09-20 228 /* take into consideration length of ldisc */
> >> 0b52b749 Bill Pemberton 2012-09-20 @229 len = min(len, (N_TTY_BUF_SIZE - 1) - tty->read_cnt);
> >
> > This is broken and unsafe. It's always been broken and unsafe. Probably
> > the report wants directing to whoever signed up to fix it all in staging.
>
> And that function looks strange alltogether. It's like it's trying to
> optimize in case of raw TTY by sending the data directly to ldisc. In a
> completely racy way. I think that it should remain marked as BROKEN
> until this is converted to tty_prepare_flip_string.
That code has been ripped out of the driver now, and it builds fine in
my tree, so the BROKEN marking linux-next can be removed.
thanks,
greg k-h
^ permalink raw reply
* Re: [GIT PATCH] TTY patches for 3.7-rc1
From: Jiri Slaby @ 2012-10-28 10:04 UTC (permalink / raw)
To: Greg KH; +Cc: Stanislav Kozina, Alan Cox, linux-kernel, linux-serial
In-Reply-To: <20121001221350.GA10172@kroah.com>
On 10/02/2012 12:13 AM, Greg KH wrote:
> On Mon, Oct 01, 2012 at 11:48:58PM +0200, Jiri Slaby wrote:
>> On 10/01/2012 08:30 PM, Greg KH wrote:
>>> Stanislav Kozina (2):
>>> tty: Fix possible race in n_tty_read()
>>
>> The (pretty hopeless) commit log says:
>> Fix possible panic caused by unlocked access to tty->read_cnt in
>> while-loop condition in n_tty_read().
>>
>> Just curious, what kind of panic that can cause, can you be more
>> concrete on that? I suppose two readers, one eats everything, the other
>> is a killer? And should we backport to -stable?
>
> Stanislav, you are the one who could reproduce this, any answers here?
Ping?
--
js
suse labs
^ permalink raw reply
* [PATCH] jsm: Convert jsm_printk to jsm_dbg
From: Joe Perches @ 2012-10-28 10:48 UTC (permalink / raw)
To: Lucas Tavares; +Cc: Alan Cox, Greg Kroah-Hartman, linux-serial, linux-kernel
These printks should all be emitted at KERN_DEBUG level.
Make them dependent on CONFIG_DEBUG or (#define DEBUG)
simplify the code a bit.
Add missing newlines where appropriate.
Most all of these messages could be deleted too.
Signed-off-by: Joe Perches <joe@perches.com>
---
drivers/tty/serial/jsm/jsm.h | 8 ++-
drivers/tty/serial/jsm/jsm_driver.c | 3 +-
drivers/tty/serial/jsm/jsm_neo.c | 116 +++++++++++++++++++----------------
drivers/tty/serial/jsm/jsm_tty.c | 102 ++++++++++++++++---------------
4 files changed, 120 insertions(+), 109 deletions(-)
diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h
index 529bec6..844d5e4 100644
--- a/drivers/tty/serial/jsm/jsm.h
+++ b/drivers/tty/serial/jsm/jsm.h
@@ -57,9 +57,11 @@ enum {
DBG_CARR = 0x10000,
};
-#define jsm_printk(nlevel, klevel, pdev, fmt, args...) \
- if ((DBG_##nlevel & jsm_debug)) \
- dev_printk(KERN_##klevel, pdev->dev, fmt, ## args)
+#define jsm_dbg(nlevel, pdev, fmt, ...) \
+do { \
+ if (DBG_##nlevel & jsm_debug) \
+ dev_dbg(pdev->dev, fmt, ##__VA_ARGS__); \
+} while (0)
#define MAXLINES 256
#define MAXPORTS 8
diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c
index 5ab3c3b..8e05ce9 100644
--- a/drivers/tty/serial/jsm/jsm_driver.c
+++ b/drivers/tty/serial/jsm/jsm_driver.c
@@ -107,8 +107,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device
brd->irq = pdev->irq;
- jsm_printk(INIT, INFO, &brd->pci_dev,
- "jsm_found_board - NEO adapter\n");
+ jsm_dbg(INIT, &brd->pci_dev, "jsm_found_board - NEO adapter\n");
/* get the PCI Base Address Registers */
brd->membase = pci_resource_start(pdev, 0);
diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c
index 81dfafa..dfaf488 100644
--- a/drivers/tty/serial/jsm/jsm_neo.c
+++ b/drivers/tty/serial/jsm/jsm_neo.c
@@ -52,7 +52,7 @@ static void neo_set_cts_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n");
/* Turn on auto CTS flow control */
ier |= (UART_17158_IER_CTSDSR);
@@ -83,7 +83,7 @@ static void neo_set_rts_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n");
/* Turn on auto RTS flow control */
ier |= (UART_17158_IER_RTSDTR);
@@ -123,7 +123,7 @@ static void neo_set_ixon_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n");
/* Turn off auto CTS flow control */
ier &= ~(UART_17158_IER_CTSDSR);
@@ -160,7 +160,7 @@ static void neo_set_ixoff_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n");
/* Turn off auto RTS flow control */
ier &= ~(UART_17158_IER_RTSDTR);
@@ -198,7 +198,7 @@ static void neo_set_no_input_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n");
/* Turn off auto RTS flow control */
ier &= ~(UART_17158_IER_RTSDTR);
@@ -237,7 +237,7 @@ static void neo_set_no_output_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n");
/* Turn off auto CTS flow control */
ier &= ~(UART_17158_IER_CTSDSR);
@@ -276,7 +276,7 @@ static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch)
if (ch->ch_c_cflag & CRTSCTS)
return;
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "start\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "start\n");
/* Tell UART what start/stop chars it should be looking for */
writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
@@ -455,7 +455,7 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)
* I hope thats okay with everyone? Yes? Good.
*/
while (qleft < 1) {
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
"Queue full, dropping DATA:%x LSR:%x\n",
ch->ch_rqueue[tail], ch->ch_equeue[tail]);
@@ -467,8 +467,8 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)
memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1);
ch->ch_equeue[head] = (u8) linestatus;
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
- "DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]);
+ jsm_dbg(READ, &ch->ch_bd->pci_dev, "DATA/LSR pair: %x %x\n",
+ ch->ch_rqueue[head], ch->ch_equeue[head]);
/* Ditch any remaining linestatus value. */
linestatus = 0;
@@ -521,8 +521,8 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
ch->ch_cached_lsr &= ~(UART_LSR_THRE);
writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx);
- jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev,
- "Tx data: %x\n", circ->buf[circ->tail]);
+ jsm_dbg(WRITE, &ch->ch_bd->pci_dev,
+ "Tx data: %x\n", circ->buf[circ->tail]);
circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1);
ch->ch_txcount++;
}
@@ -575,8 +575,9 @@ static void neo_parse_modem(struct jsm_channel *ch, u8 signals)
{
u8 msignals = signals;
- jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev,
- "neo_parse_modem: port: %d msignals: %x\n", ch->ch_portnum, msignals);
+ jsm_dbg(MSIGS, &ch->ch_bd->pci_dev,
+ "neo_parse_modem: port: %d msignals: %x\n",
+ ch->ch_portnum, msignals);
/* Scrub off lower bits. They signify delta's, which I don't care about */
/* Keep DDCD and DDSR though */
@@ -606,8 +607,8 @@ static void neo_parse_modem(struct jsm_channel *ch, u8 signals)
else
ch->ch_mistat &= ~UART_MSR_CTS;
- jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev,
- "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
+ jsm_dbg(MSIGS, &ch->ch_bd->pci_dev,
+ "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
ch->ch_portnum,
!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR),
!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS),
@@ -649,8 +650,8 @@ static void neo_flush_uart_write(struct jsm_channel *ch)
/* Check to see if the UART feels it completely flushed the FIFO. */
tmp = readb(&ch->ch_neo_uart->isr_fcr);
if (tmp & 4) {
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
- "Still flushing TX UART... i: %d\n", i);
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev,
+ "Still flushing TX UART... i: %d\n", i);
udelay(10);
}
else
@@ -681,8 +682,8 @@ static void neo_flush_uart_read(struct jsm_channel *ch)
/* Check to see if the UART feels it completely flushed the FIFO. */
tmp = readb(&ch->ch_neo_uart->isr_fcr);
if (tmp & 2) {
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
- "Still flushing RX UART... i: %d\n", i);
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev,
+ "Still flushing RX UART... i: %d\n", i);
udelay(10);
}
else
@@ -705,8 +706,9 @@ static void neo_clear_break(struct jsm_channel *ch, int force)
writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
ch->ch_flags &= ~(CH_BREAK_SENDING);
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
- "clear break Finishing UART_LCR_SBC! finished: %lx\n", jiffies);
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev,
+ "clear break Finishing UART_LCR_SBC! finished: %lx\n",
+ jiffies);
/* flush write operation */
neo_pci_posting_flush(ch->ch_bd);
@@ -748,8 +750,8 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
*/
isr &= ~(UART_17158_IIR_FIFO_ENABLED);
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "%s:%d isr: %x\n", __FILE__, __LINE__, isr);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d isr: %x\n",
+ __FILE__, __LINE__, isr);
if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) {
/* Read data from uart -> queue */
@@ -772,8 +774,9 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
if (isr & UART_17158_IIR_XONXOFF) {
cause = readb(&ch->ch_neo_uart->xoffchar1);
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "Port %d. Got ISR_XONXOFF: cause:%x\n", port, cause);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "Port %d. Got ISR_XONXOFF: cause:%x\n",
+ port, cause);
/*
* Since the UART detected either an XON or
@@ -786,17 +789,19 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
if (brd->channels[port]->ch_flags & CH_STOP) {
ch->ch_flags &= ~(CH_STOP);
}
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "Port %d. XON detected in incoming data\n", port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "Port %d. XON detected in incoming data\n",
+ port);
}
else if (cause == UART_17158_XOFF_DETECT) {
if (!(brd->channels[port]->ch_flags & CH_STOP)) {
ch->ch_flags |= CH_STOP;
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "Setting CH_STOP\n");
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "Setting CH_STOP\n");
}
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "Port: %d. XOFF detected in incoming data\n", port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "Port: %d. XOFF detected in incoming data\n",
+ port);
}
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
}
@@ -825,8 +830,8 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
}
/* Parse any modem signal changes */
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "MOD_STAT: sending to parse_modem_sigs\n");
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "MOD_STAT: sending to parse_modem_sigs\n");
neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
}
}
@@ -849,8 +854,8 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
linestatus = readb(&ch->ch_neo_uart->lsr);
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "%s:%d port: %d linestatus: %x\n", __FILE__, __LINE__, port, linestatus);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d port: %d linestatus: %x\n",
+ __FILE__, __LINE__, port, linestatus);
ch->ch_cached_lsr |= linestatus;
@@ -869,7 +874,7 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
*to do the special RX+LSR read for this FIFO load.
*/
if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
- jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
"%s:%d Port: %d Got an RX error, need to parse LSR\n",
__FILE__, __LINE__, port);
@@ -880,20 +885,21 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
if (linestatus & UART_LSR_PE) {
ch->ch_err_parity++;
- jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
- "%s:%d Port: %d. PAR ERR!\n", __FILE__, __LINE__, port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d Port: %d. PAR ERR!\n",
+ __FILE__, __LINE__, port);
}
if (linestatus & UART_LSR_FE) {
ch->ch_err_frame++;
- jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
- "%s:%d Port: %d. FRM ERR!\n", __FILE__, __LINE__, port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d Port: %d. FRM ERR!\n",
+ __FILE__, __LINE__, port);
}
if (linestatus & UART_LSR_BI) {
ch->ch_err_break++;
- jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
- "%s:%d Port: %d. BRK INTR!\n", __FILE__, __LINE__, port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "%s:%d Port: %d. BRK INTR!\n",
+ __FILE__, __LINE__, port);
}
if (linestatus & UART_LSR_OE) {
@@ -904,8 +910,9 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
* Probably we should eventually have an orun stat in our driver...
*/
ch->ch_err_overrun++;
- jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
- "%s:%d Port: %d. Rx Overrun!\n", __FILE__, __LINE__, port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "%s:%d Port: %d. Rx Overrun!\n",
+ __FILE__, __LINE__, port);
}
if (linestatus & UART_LSR_THRE) {
@@ -1128,11 +1135,11 @@ static irqreturn_t neo_intr(int irq, void *voidbrd)
*/
uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET);
- jsm_printk(INTR, INFO, &brd->pci_dev,
- "%s:%d uart_poll: %x\n", __FILE__, __LINE__, uart_poll);
+ jsm_dbg(INTR, &brd->pci_dev, "%s:%d uart_poll: %x\n",
+ __FILE__, __LINE__, uart_poll);
if (!uart_poll) {
- jsm_printk(INTR, INFO, &brd->pci_dev,
+ jsm_dbg(INTR, &brd->pci_dev,
"Kernel interrupted to me, but no pending interrupts...\n");
spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
return IRQ_NONE;
@@ -1158,15 +1165,15 @@ static irqreturn_t neo_intr(int irq, void *voidbrd)
continue;
}
- jsm_printk(INTR, INFO, &brd->pci_dev,
- "%s:%d port: %x type: %x\n", __FILE__, __LINE__, port, type);
+ jsm_dbg(INTR, &brd->pci_dev, "%s:%d port: %x type: %x\n",
+ __FILE__, __LINE__, port, type);
/* Remove this port + type from uart_poll */
uart_poll &= ~(jsm_offset_table[port]);
if (!type) {
/* If no type, just ignore it, and move onto next port */
- jsm_printk(INTR, ERR, &brd->pci_dev,
+ jsm_dbg(INTR, &brd->pci_dev,
"Interrupt with no type! port: %d\n", port);
continue;
}
@@ -1231,15 +1238,16 @@ static irqreturn_t neo_intr(int irq, void *voidbrd)
* these once and awhile.
* Its harmless, just ignore it and move on.
*/
- jsm_printk(INTR, ERR, &brd->pci_dev,
- "%s:%d Unknown Interrupt type: %x\n", __FILE__, __LINE__, type);
+ jsm_dbg(INTR, &brd->pci_dev,
+ "%s:%d Unknown Interrupt type: %x\n",
+ __FILE__, __LINE__, type);
continue;
}
}
spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
- jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n");
+ jsm_dbg(INTR, &brd->pci_dev, "finish\n");
return IRQ_HANDLED;
}
diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c
index 7139796..7d2c1f3 100644
--- a/drivers/tty/serial/jsm/jsm_tty.c
+++ b/drivers/tty/serial/jsm/jsm_tty.c
@@ -43,7 +43,7 @@ static inline int jsm_get_mstat(struct jsm_channel *ch)
unsigned char mstat;
unsigned result;
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "start\n");
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "start\n");
mstat = (ch->ch_mostat | ch->ch_mistat);
@@ -62,7 +62,7 @@ static inline int jsm_get_mstat(struct jsm_channel *ch)
if (mstat & UART_MSR_DCD)
result |= TIOCM_CD;
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n");
return result;
}
@@ -79,14 +79,14 @@ static unsigned int jsm_tty_get_mctrl(struct uart_port *port)
int result;
struct jsm_channel *channel = (struct jsm_channel *)port;
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");
result = jsm_get_mstat(channel);
if (result < 0)
return -ENXIO;
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
return result;
}
@@ -100,7 +100,7 @@ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct jsm_channel *channel = (struct jsm_channel *)port;
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");
if (mctrl & TIOCM_RTS)
channel->ch_mostat |= UART_MCR_RTS;
@@ -114,7 +114,7 @@ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl)
channel->ch_bd->bd_ops->assert_modem_signals(channel);
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
udelay(10);
}
@@ -135,23 +135,23 @@ static void jsm_tty_start_tx(struct uart_port *port)
{
struct jsm_channel *channel = (struct jsm_channel *)port;
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");
channel->ch_flags &= ~(CH_STOP);
jsm_tty_write(port);
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
}
static void jsm_tty_stop_tx(struct uart_port *port)
{
struct jsm_channel *channel = (struct jsm_channel *)port;
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");
channel->ch_flags |= (CH_STOP);
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
}
static void jsm_tty_send_xchar(struct uart_port *port, char ch)
@@ -216,16 +216,16 @@ static int jsm_tty_open(struct uart_port *port)
if (!channel->ch_rqueue) {
channel->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL);
if (!channel->ch_rqueue) {
- jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
- "unable to allocate read queue buf");
+ jsm_dbg(INIT, &channel->ch_bd->pci_dev,
+ "unable to allocate read queue buf\n");
return -ENOMEM;
}
}
if (!channel->ch_equeue) {
channel->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL);
if (!channel->ch_equeue) {
- jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
- "unable to allocate error queue buf");
+ jsm_dbg(INIT, &channel->ch_bd->pci_dev,
+ "unable to allocate error queue buf\n");
return -ENOMEM;
}
}
@@ -234,7 +234,7 @@ static int jsm_tty_open(struct uart_port *port)
/*
* Initialize if neither terminal is open.
*/
- jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev,
+ jsm_dbg(OPEN, &channel->ch_bd->pci_dev,
"jsm_open: initializing channel in open...\n");
/*
@@ -270,7 +270,7 @@ static int jsm_tty_open(struct uart_port *port)
channel->ch_open_count++;
- jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "finish\n");
return 0;
}
@@ -280,7 +280,7 @@ static void jsm_tty_close(struct uart_port *port)
struct ktermios *ts;
struct jsm_channel *channel = (struct jsm_channel *)port;
- jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n");
+ jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "start\n");
bd = channel->ch_bd;
ts = &port->state->port.tty->termios;
@@ -293,7 +293,7 @@ static void jsm_tty_close(struct uart_port *port)
* If we have HUPCL set, lower DTR and RTS
*/
if (channel->ch_c_cflag & HUPCL) {
- jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev,
+ jsm_dbg(CLOSE, &channel->ch_bd->pci_dev,
"Close. HUPCL set, dropping DTR/RTS\n");
/* Drop RTS/DTR */
@@ -304,7 +304,7 @@ static void jsm_tty_close(struct uart_port *port)
/* Turn off UART interrupts for this port */
channel->ch_bd->bd_ops->uart_off(channel);
- jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "finish\n");
}
static void jsm_tty_set_termios(struct uart_port *port,
@@ -380,7 +380,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd)
if (!brd)
return -ENXIO;
- jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+ jsm_dbg(INIT, &brd->pci_dev, "start\n");
/*
* Initialize board structure elements.
@@ -401,9 +401,9 @@ int __devinit jsm_tty_init(struct jsm_board *brd)
*/
brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL);
if (!brd->channels[i]) {
- jsm_printk(CORE, ERR, &brd->pci_dev,
+ jsm_dbg(CORE, &brd->pci_dev,
"%s:%d Unable to allocate memory for channel struct\n",
- __FILE__, __LINE__);
+ __FILE__, __LINE__);
}
}
}
@@ -431,7 +431,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd)
init_waitqueue_head(&ch->ch_flags_wait);
}
- jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+ jsm_dbg(INIT, &brd->pci_dev, "finish\n");
return 0;
}
@@ -444,7 +444,7 @@ int jsm_uart_port_init(struct jsm_board *brd)
if (!brd)
return -ENXIO;
- jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+ jsm_dbg(INIT, &brd->pci_dev, "start\n");
/*
* Initialize board structure elements.
@@ -481,7 +481,7 @@ int jsm_uart_port_init(struct jsm_board *brd)
printk(KERN_INFO "jsm: Port %d added\n", i);
}
- jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+ jsm_dbg(INIT, &brd->pci_dev, "finish\n");
return 0;
}
@@ -493,7 +493,7 @@ int jsm_remove_uart_port(struct jsm_board *brd)
if (!brd)
return -ENXIO;
- jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+ jsm_dbg(INIT, &brd->pci_dev, "start\n");
/*
* Initialize board structure elements.
@@ -513,7 +513,7 @@ int jsm_remove_uart_port(struct jsm_board *brd)
uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);
}
- jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+ jsm_dbg(INIT, &brd->pci_dev, "finish\n");
return 0;
}
@@ -531,7 +531,7 @@ void jsm_input(struct jsm_channel *ch)
int s = 0;
int i = 0;
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
+ jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n");
if (!ch)
return;
@@ -560,7 +560,7 @@ void jsm_input(struct jsm_channel *ch)
return;
}
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
+ jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n");
/*
*If the device is not open, or CREAD is off, flush
@@ -569,8 +569,9 @@ void jsm_input(struct jsm_channel *ch)
if (!tp ||
!(tp->termios.c_cflag & CREAD) ) {
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
- "input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum);
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
+ "input. dropping %d bytes on port %d...\n",
+ data_len, ch->ch_portnum);
ch->ch_r_head = tail;
/* Force queue flow control to be released, if needed */
@@ -585,17 +586,17 @@ void jsm_input(struct jsm_channel *ch)
*/
if (ch->ch_flags & CH_STOPI) {
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
"Port %d throttled, not reading any data. head: %x tail: %x\n",
ch->ch_portnum, head, tail);
return;
}
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start 2\n");
+ jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n");
if (data_len <= 0) {
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "jsm_input 1\n");
+ jsm_dbg(READ, &ch->ch_bd->pci_dev, "jsm_input 1\n");
return;
}
@@ -653,7 +654,7 @@ void jsm_input(struct jsm_channel *ch)
/* Tell the tty layer its okay to "eat" the data now */
tty_flip_buffer_push(tp);
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n");
}
static void jsm_carrier(struct jsm_channel *ch)
@@ -663,7 +664,7 @@ static void jsm_carrier(struct jsm_channel *ch)
int virt_carrier = 0;
int phys_carrier = 0;
- jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n");
+ jsm_dbg(CARR, &ch->ch_bd->pci_dev, "start\n");
if (!ch)
return;
@@ -673,16 +674,16 @@ static void jsm_carrier(struct jsm_channel *ch)
return;
if (ch->ch_mistat & UART_MSR_DCD) {
- jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
- "mistat: %x D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD);
+ jsm_dbg(CARR, &ch->ch_bd->pci_dev, "mistat: %x D_CD: %x\n",
+ ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD);
phys_carrier = 1;
}
if (ch->ch_c_cflag & CLOCAL)
virt_carrier = 1;
- jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
- "DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier);
+ jsm_dbg(CARR, &ch->ch_bd->pci_dev, "DCD: physical: %d virt: %d\n",
+ phys_carrier, virt_carrier);
/*
* Test for a VIRTUAL carrier transition to HIGH.
@@ -694,8 +695,7 @@ static void jsm_carrier(struct jsm_channel *ch)
* for carrier in the open routine.
*/
- jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
- "carrier: virt DCD rose\n");
+ jsm_dbg(CARR, &ch->ch_bd->pci_dev, "carrier: virt DCD rose\n");
if (waitqueue_active(&(ch->ch_flags_wait)))
wake_up_interruptible(&ch->ch_flags_wait);
@@ -711,7 +711,7 @@ static void jsm_carrier(struct jsm_channel *ch)
* for carrier in the open routine.
*/
- jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+ jsm_dbg(CARR, &ch->ch_bd->pci_dev,
"carrier: physical DCD rose\n");
if (waitqueue_active(&(ch->ch_flags_wait)))
@@ -790,8 +790,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
if(!(ch->ch_flags & CH_RECEIVER_OFF)) {
bd_ops->disable_receiver(ch);
ch->ch_flags |= (CH_RECEIVER_OFF);
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
- "Internal queue hit hilevel mark (%d)! Turning off interrupts.\n",
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
+ "Internal queue hit hilevel mark (%d)! Turning off interrupts\n",
qleft);
}
}
@@ -800,8 +800,9 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
bd_ops->send_stop_character(ch);
ch->ch_stops_sent++;
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
- "Sending stop char! Times sent: %x\n", ch->ch_stops_sent);
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
+ "Sending stop char! Times sent: %x\n",
+ ch->ch_stops_sent);
}
}
}
@@ -827,8 +828,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
if (ch->ch_flags & CH_RECEIVER_OFF) {
bd_ops->enable_receiver(ch);
ch->ch_flags &= ~(CH_RECEIVER_OFF);
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
- "Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n",
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
+ "Internal queue hit lowlevel mark (%d)! Turning on interrupts\n",
qleft);
}
}
@@ -836,7 +837,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
ch->ch_stops_sent = 0;
bd_ops->send_start_character(ch);
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n");
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
+ "Sending start char!\n");
}
}
}
^ permalink raw reply related
* Re: [GIT PATCH] TTY patches for 3.7-rc1
From: Alan Cox @ 2012-10-28 15:22 UTC (permalink / raw)
To: Jiri Slaby; +Cc: Greg KH, Stanislav Kozina, linux-kernel, linux-serial
In-Reply-To: <508D033E.8030109@suse.cz>
On Sun, 28 Oct 2012 11:04:46 +0100
Jiri Slaby <jslaby@suse.cz> wrote:
> On 10/02/2012 12:13 AM, Greg KH wrote:
> > On Mon, Oct 01, 2012 at 11:48:58PM +0200, Jiri Slaby wrote:
> >> On 10/01/2012 08:30 PM, Greg KH wrote:
> >>> Stanislav Kozina (2):
> >>> tty: Fix possible race in n_tty_read()
> >>
> >> The (pretty hopeless) commit log says:
> >> Fix possible panic caused by unlocked access to tty->read_cnt in
> >> while-loop condition in n_tty_read().
> >>
> >> Just curious, what kind of panic that can cause, can you be more
> >> concrete on that? I suppose two readers, one eats everything, the other
> >> is a killer? And should we backport to -stable?
> >
> > Stanislav, you are the one who could reproduce this, any answers here?
>
> Ping?
Thread here
http://permalink.gmane.org/gmane.linux.serial/8365
^ 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