summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/tty/serial/st-asc.txt18
-rw-r--r--drivers/net/irda/irtty-sir.c8
-rw-r--r--drivers/staging/comedi/comedidev.h1
-rw-r--r--drivers/staging/dgrp/dgrp_tty.c2
-rw-r--r--drivers/tty/hvc/hvc_console.c11
-rw-r--r--drivers/tty/hvc/hvc_console.h3
-rw-r--r--drivers/tty/hvc/hvc_iucv.c64
-rw-r--r--drivers/tty/n_gsm.c29
-rw-r--r--drivers/tty/n_tty.c1401
-rw-r--r--drivers/tty/pty.c18
-rw-r--r--drivers/tty/serial/8250/8250_em.c27
-rw-r--r--drivers/tty/serial/8250/8250_pci.c15
-rw-r--r--drivers/tty/serial/8250/Kconfig2
-rw-r--r--drivers/tty/serial/Kconfig26
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/ar933x_uart.c1
-rw-r--r--drivers/tty/serial/atmel_serial.c9
-rw-r--r--drivers/tty/serial/bcm63xx_uart.c1
-rw-r--r--drivers/tty/serial/bfin_uart.c4
-rw-r--r--drivers/tty/serial/clps711x.c11
-rw-r--r--drivers/tty/serial/efm32-uart.c4
-rw-r--r--drivers/tty/serial/fsl_lpuart.c7
-rw-r--r--drivers/tty/serial/imx.c507
-rw-r--r--drivers/tty/serial/max310x.c996
-rw-r--r--drivers/tty/serial/msm_serial.c184
-rw-r--r--drivers/tty/serial/msm_serial.h19
-rw-r--r--drivers/tty/serial/mxs-auart.c8
-rw-r--r--drivers/tty/serial/netx-serial.c2
-rw-r--r--drivers/tty/serial/omap-serial.c41
-rw-r--r--drivers/tty/serial/pch_uart.c71
-rw-r--r--drivers/tty/serial/pmac_zilog.c1
-rw-r--r--drivers/tty/serial/pnx8xxx_uart.c2
-rw-r--r--drivers/tty/serial/pxa.c2
-rw-r--r--drivers/tty/serial/sa1100.c2
-rw-r--r--drivers/tty/serial/sccnxp.c3
-rw-r--r--drivers/tty/serial/serial_core.c4
-rw-r--r--drivers/tty/serial/sirfsoc_uart.c3
-rw-r--r--drivers/tty/serial/st-asc.c937
-rw-r--r--drivers/tty/serial/vt8500_serial.c1
-rw-r--r--drivers/tty/tty_buffer.c417
-rw-r--r--drivers/tty/tty_io.c28
-rw-r--r--drivers/tty/tty_ioctl.c90
-rw-r--r--drivers/tty/tty_ldisc.c461
-rw-r--r--drivers/tty/vt/keyboard.c21
-rw-r--r--drivers/tty/vt/selection.c8
-rw-r--r--drivers/tty/vt/vt.c4
-rw-r--r--include/linux/kbd_kern.h3
-rw-r--r--include/linux/llist.h23
-rw-r--r--include/linux/pci_ids.h4
-rw-r--r--include/linux/platform_data/max310x.h9
-rw-r--r--include/linux/tty.h66
-rw-r--r--include/linux/tty_flip.h8
-rw-r--r--include/linux/tty_ldisc.h16
-rw-r--r--include/uapi/linux/serial_core.h3
-rw-r--r--include/uapi/linux/serial_reg.h1
55 files changed, 3620 insertions, 1988 deletions
diff --git a/Documentation/devicetree/bindings/tty/serial/st-asc.txt b/Documentation/devicetree/bindings/tty/serial/st-asc.txt
new file mode 100644
index 000000000000..75d877f5968f
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/st-asc.txt
@@ -0,0 +1,18 @@
+*st-asc(Serial Port)
+
+Required properties:
+- compatible : Should be "st,asc".
+- reg, reg-names, interrupts, interrupt-names : Standard way to define device
+ resources with names. look in
+ Documentation/devicetree/bindings/resource-names.txt
+
+Optional properties:
+- st,hw-flow-ctrl bool flag to enable hardware flow control.
+- st,force-m1 bool flat to force asc to be in Mode-1 recommeded
+ for high bit rates (above 19.2K)
+Example:
+serial@fe440000{
+ compatible = "st,asc";
+ reg = <0xfe440000 0x2c>;
+ interrupts = <0 209 0>;
+};
diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c
index a41267197839..177441afeb96 100644
--- a/drivers/net/irda/irtty-sir.c
+++ b/drivers/net/irda/irtty-sir.c
@@ -123,14 +123,14 @@ static int irtty_change_speed(struct sir_dev *dev, unsigned speed)
tty = priv->tty;
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
old_termios = tty->termios;
cflag = tty->termios.c_cflag;
tty_encode_baud_rate(tty, speed, speed);
if (tty->ops->set_termios)
tty->ops->set_termios(tty, &old_termios);
priv->io.speed = speed;
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
return 0;
}
@@ -280,7 +280,7 @@ static inline void irtty_stop_receiver(struct tty_struct *tty, int stop)
struct ktermios old_termios;
int cflag;
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
old_termios = tty->termios;
cflag = tty->termios.c_cflag;
@@ -292,7 +292,7 @@ static inline void irtty_stop_receiver(struct tty_struct *tty, int stop)
tty->termios.c_cflag = cflag;
if (tty->ops->set_termios)
tty->ops->set_termios(tty, &old_termios);
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
}
/*****************************************************************/
diff --git a/drivers/staging/comedi/comedidev.h b/drivers/staging/comedi/comedidev.h
index b75915f30f48..9f4f73fa652c 100644
--- a/drivers/staging/comedi/comedidev.h
+++ b/drivers/staging/comedi/comedidev.h
@@ -400,7 +400,6 @@ int comedi_driver_unregister(struct comedi_driver *);
*/
#define PCI_VENDOR_ID_KOLTER 0x1001
#define PCI_VENDOR_ID_ICP 0x104c
-#define PCI_VENDOR_ID_AMCC 0x10e8
#define PCI_VENDOR_ID_DT 0x1116
#define PCI_VENDOR_ID_IOTECH 0x1616
#define PCI_VENDOR_ID_CONTEC 0x1221
diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c
index 654f6010b473..0d52de3729c6 100644
--- a/drivers/staging/dgrp/dgrp_tty.c
+++ b/drivers/staging/dgrp/dgrp_tty.c
@@ -1120,7 +1120,9 @@ static void dgrp_tty_close(struct tty_struct *tty, struct file *file)
if (!sent_printer_offstr)
dgrp_tty_flush_buffer(tty);
+ spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
tty_ldisc_flush(tty);
+ spin_lock_irqsave(&nd->nd_lock, lock_flags);
break;
}
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index eb255e807c06..9eba119bcdd3 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -361,7 +361,12 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
tty->driver_data = NULL;
tty_port_put(&hp->port);
printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);
- }
+ } else
+ /* We are ready... raise DTR/RTS */
+ if (C_BAUD(tty))
+ if (hp->ops->dtr_rts)
+ hp->ops->dtr_rts(hp, 1);
+
/* Force wakeup of the polling thread */
hvc_kick();
@@ -393,6 +398,10 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
/* We are done with the tty pointer now. */
tty_port_tty_set(&hp->port, NULL);
+ if (C_HUPCL(tty))
+ if (hp->ops->dtr_rts)
+ hp->ops->dtr_rts(hp, 0);
+
if (hp->ops->notifier_del)
hp->ops->notifier_del(hp, hp->data);
diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h
index 674d23cb919a..913101980827 100644
--- a/drivers/tty/hvc/hvc_console.h
+++ b/drivers/tty/hvc/hvc_console.h
@@ -75,6 +75,9 @@ struct hv_ops {
/* tiocmget/set implementation */
int (*tiocmget)(struct hvc_struct *hp);
int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear);
+
+ /* Callbacks to handle tty ports */
+ void (*dtr_rts)(struct hvc_struct *hp, int raise);
};
/* Register a vterm and a slot index for use as a console (console_init) */
diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c
index 9d47f50c2755..fd17a9b804b8 100644
--- a/drivers/tty/hvc/hvc_iucv.c
+++ b/drivers/tty/hvc/hvc_iucv.c
@@ -656,21 +656,64 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
}
/**
+ * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS
+ * @hp: Pointer the HVC device (struct hvc_struct)
+ * @raise: Non-zero to raise or zero to lower DTR/RTS lines
+ *
+ * This routine notifies the HVC back-end to raise or lower DTR/RTS
+ * lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to
+ * drop the IUCV connection (similar to hang up the modem).
+ */
+static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise)
+{
+ struct hvc_iucv_private *priv;
+ struct iucv_path *path;
+
+ /* Raising the DTR/RTS is ignored as IUCV connections can be
+ * established at any times.
+ */
+ if (raise)
+ return;
+
+ priv = hvc_iucv_get_private(hp->vtermno);
+ if (!priv)
+ return;
+
+ /* Lowering the DTR/RTS lines disconnects an established IUCV
+ * connection.
+ */
+ flush_sndbuf_sync(priv);
+
+ spin_lock_bh(&priv->lock);
+ path = priv->path; /* save reference to IUCV path */
+ priv->path = NULL;
+ priv->iucv_state = IUCV_DISCONN;
+ spin_unlock_bh(&priv->lock);
+
+ /* Sever IUCV path outside of priv->lock due to lock ordering of:
+ * priv->lock <--> iucv_table_lock */
+ if (path) {
+ iucv_path_sever(path, NULL);
+ iucv_path_free(path);
+ }
+}
+
+/**
* hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time.
* @hp: Pointer to the HVC device (struct hvc_struct)
* @id: Additional data (originally passed to hvc_alloc):
* the index of an struct hvc_iucv_private instance.
*
* This routine notifies the HVC back-end that the last tty device fd has been
- * closed. The function calls hvc_iucv_cleanup() to clean up the struct
- * hvc_iucv_private instance.
+ * closed. The function cleans up tty resources. The clean-up of the IUCV
+ * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios
+ * control setting.
*
* Locking: struct hvc_iucv_private->lock
*/
static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
{
struct hvc_iucv_private *priv;
- struct iucv_path *path;
priv = hvc_iucv_get_private(id);
if (!priv)
@@ -679,17 +722,11 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
flush_sndbuf_sync(priv);
spin_lock_bh(&priv->lock);
- path = priv->path; /* save reference to IUCV path */
- priv->path = NULL;
- hvc_iucv_cleanup(priv);
+ destroy_tty_buffer_list(&priv->tty_outqueue);
+ destroy_tty_buffer_list(&priv->tty_inqueue);
+ priv->tty_state = TTY_CLOSED;
+ priv->sndbuf_len = 0;
spin_unlock_bh(&priv->lock);
-
- /* sever IUCV path outside of priv->lock due to lock ordering of:
- * priv->lock <--> iucv_table_lock */
- if (path) {
- iucv_path_sever(path, NULL);
- iucv_path_free(path);
- }
}
/**
@@ -931,6 +968,7 @@ static const struct hv_ops hvc_iucv_ops = {
.notifier_add = hvc_iucv_notifier_add,
.notifier_del = hvc_iucv_notifier_del,
.notifier_hangup = hvc_iucv_notifier_hangup,
+ .dtr_rts = hvc_iucv_dtr_rts,
};
/* Suspend / resume device operations */
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 642239015b46..c0f76da55304 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -807,7 +807,7 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
int h = dlci->adaption - 1;
total_size = 0;
- while(1) {
+ while (1) {
len = kfifo_len(dlci->fifo);
if (len == 0)
return total_size;
@@ -827,8 +827,8 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
switch (dlci->adaption) {
case 1: /* Unstructured */
break;
- case 2: /* Unstructed with modem bits. Always one byte as we never
- send inline break data */
+ case 2: /* Unstructed with modem bits.
+ Always one byte as we never send inline break data */
*dp++ = gsm_encode_modem(dlci);
break;
}
@@ -968,7 +968,7 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci)
unsigned long flags;
int sweep;
- if (dlci->constipated)
+ if (dlci->constipated)
return;
spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
@@ -981,7 +981,7 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci)
gsm_dlci_data_output(dlci->gsm, dlci);
}
if (sweep)
- gsm_dlci_data_sweep(dlci->gsm);
+ gsm_dlci_data_sweep(dlci->gsm);
spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags);
}
@@ -1138,7 +1138,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen)
static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen)
{
struct tty_port *port;
- unsigned int addr = 0 ;
+ unsigned int addr = 0;
u8 bits;
int len = clen;
u8 *dp = data;
@@ -1740,10 +1740,11 @@ static void gsm_queue(struct gsm_mux *gsm)
if ((gsm->control & ~PF) == UI)
gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len);
- if (gsm->encoding == 0){
- /* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only.
- In this case it contain the last piece of data
- required to generate final CRC */
+ if (gsm->encoding == 0) {
+ /* WARNING: gsm->received_fcs is used for
+ gsm->encoding = 0 only.
+ In this case it contain the last piece of data
+ required to generate final CRC */
gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
}
if (gsm->fcs != GOOD_FCS) {
@@ -2904,9 +2905,11 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
gsm = gsm_mux[mux];
if (gsm->dead)
return -EL2HLT;
- /* If DLCI 0 is not yet fully open return an error. This is ok from a locking
- perspective as we don't have to worry about this if DLCI0 is lost */
- if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN)
+ /* If DLCI 0 is not yet fully open return an error.
+ This is ok from a locking
+ perspective as we don't have to worry about this
+ if DLCI0 is lost */
+ if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN)
return -EL2NSYNC;
dlci = gsm->dlci[line];
if (dlci == NULL) {
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 4bf0fc0843d7..dd8ae0cad1d5 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -50,6 +50,7 @@
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/ratelimit.h>
+#include <linux/vmalloc.h>
/* number of characters left in xmit buffer before select has we have room */
@@ -74,37 +75,81 @@
#define ECHO_OP_SET_CANON_COL 0x81
#define ECHO_OP_ERASE_TAB 0x82
+#define ECHO_COMMIT_WATERMARK 256
+#define ECHO_BLOCK 256
+#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32)
+
+
+#undef N_TTY_TRACE
+#ifdef N_TTY_TRACE
+# define n_tty_trace(f, args...) trace_printk(f, ##args)
+#else
+# define n_tty_trace(f, args...)
+#endif
+
struct n_tty_data {
- unsigned int column;
+ /* producer-published */
+ size_t read_head;
+ size_t canon_head;
+ size_t echo_head;
+ size_t echo_commit;
+ DECLARE_BITMAP(char_map, 256);
+
+ /* private to n_tty_receive_overrun (single-threaded) */
unsigned long overrun_time;
int num_overrun;
+ /* non-atomic */
+ bool no_room;
+
+ /* must hold exclusive termios_rwsem to reset these */
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
- unsigned char echo_overrun:1;
- DECLARE_BITMAP(process_char_map, 256);
+ /* shared by producer and consumer */
+ char read_buf[N_TTY_BUF_SIZE];
DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE);
+ unsigned char echo_buf[N_TTY_BUF_SIZE];
- char *read_buf;
- int read_head;
- int read_tail;
- int read_cnt;
int minimum_to_wake;
- unsigned char *echo_buf;
- unsigned int echo_pos;
- unsigned int echo_cnt;
+ /* consumer-published */
+ size_t read_tail;
+ size_t line_start;
- int canon_data;
- unsigned long canon_head;
+ /* protected by output lock */
+ unsigned int column;
unsigned int canon_column;
+ size_t echo_tail;
struct mutex atomic_read_lock;
struct mutex output_lock;
- struct mutex echo_lock;
- raw_spinlock_t read_lock;
};
+static inline size_t read_cnt(struct n_tty_data *ldata)
+{
+ return ldata->read_head - ldata->read_tail;
+}
+
+static inline unsigned char read_buf(struct n_tty_data *ldata, size_t i)
+{
+ return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
+}
+
+static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i)
+{
+ return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
+}
+
+static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i)
+{
+ return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)];
+}
+
+static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i)
+{
+ return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)];
+}
+
static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
unsigned char __user *ptr)
{
@@ -114,33 +159,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
return put_user(x, ptr);
}
-/**
- * n_tty_set_room - receive space
- * @tty: terminal
- *
- * Updates tty->receive_room to reflect the currently available space
- * in the input buffer, and re-schedules the flip buffer work if space
- * just became available.
- *
- * Locks: Concurrent update is protected with read_lock
- */
-
-static int set_room(struct tty_struct *tty)
+static int receive_room(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
int left;
- int old_left;
- unsigned long flags;
-
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
if (I_PARMRK(tty)) {
/* Multiply read_cnt by 3, since each byte might take up to
* three times as many spaces when PARMRK is set (depending on
* its flags, e.g. parity error). */
- left = N_TTY_BUF_SIZE - ldata->read_cnt * 3 - 1;
+ left = N_TTY_BUF_SIZE - read_cnt(ldata) * 3 - 1;
} else
- left = N_TTY_BUF_SIZE - ldata->read_cnt - 1;
+ left = N_TTY_BUF_SIZE - read_cnt(ldata) - 1;
/*
* If we are doing input canonicalization, and there are no
@@ -149,19 +179,31 @@ static int set_room(struct tty_struct *tty)
* characters will be beeped.
*/
if (left <= 0)
- left = ldata->icanon && !ldata->canon_data;
- old_left = tty->receive_room;
- tty->receive_room = left;
-
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
+ left = ldata->icanon && ldata->canon_head == ldata->read_tail;
- return left && !old_left;
+ return left;
}
+/**
+ * n_tty_set_room - receive space
+ * @tty: terminal
+ *
+ * Re-schedules the flip buffer work if space just became available.
+ *
+ * Caller holds exclusive termios_rwsem
+ * or
+ * n_tty_read()/consumer path:
+ * holds non-exclusive termios_rwsem
+ */
+
static void n_tty_set_room(struct tty_struct *tty)
{
+ struct n_tty_data *ldata = tty->disc_data;
+
/* Did this open up the receive buffer? We may need to flip */
- if (set_room(tty)) {
+ if (unlikely(ldata->no_room) && receive_room(tty)) {
+ ldata->no_room = 0;
+
WARN_RATELIMIT(tty->port->itty == NULL,
"scheduling with invalid itty\n");
/* see if ldisc has been killed - if so, this means that
@@ -170,17 +212,93 @@ static void n_tty_set_room(struct tty_struct *tty)
*/
WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags),
"scheduling buffer work for halted ldisc\n");
- schedule_work(&tty->port->buf.work);
+ queue_work(system_unbound_wq, &tty->port->buf.work);
}
}
-static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata)
+static ssize_t chars_in_buffer(struct tty_struct *tty)
{
- if (ldata->read_cnt < N_TTY_BUF_SIZE) {
- ldata->read_buf[ldata->read_head] = c;
- ldata->read_head = (ldata->read_head + 1) & (N_TTY_BUF_SIZE-1);
- ldata->read_cnt++;
+ struct n_tty_data *ldata = tty->disc_data;
+ ssize_t n = 0;
+
+ if (!ldata->icanon)
+ n = read_cnt(ldata);
+ else
+ n = ldata->canon_head - ldata->read_tail;
+ return n;
+}
+
+/**
+ * n_tty_write_wakeup - asynchronous I/O notifier
+ * @tty: tty device
+ *
+ * Required for the ptys, serial driver etc. since processes
+ * that attach themselves to the master and rely on ASYNC
+ * IO must be woken up
+ */
+
+static void n_tty_write_wakeup(struct tty_struct *tty)
+{
+ if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags))
+ kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
+}
+
+static void n_tty_check_throttle(struct tty_struct *tty)
+{
+ if (tty->driver->type == TTY_DRIVER_TYPE_PTY)
+ return;
+ /*
+ * Check the remaining room for the input canonicalization
+ * mode. We don't want to throttle the driver if we're in
+ * canonical mode and don't have a newline yet!
+ */
+ while (1) {
+ int throttled;
+ tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
+ if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE)
+ break;
+ throttled = tty_throttle_safe(tty);
+ if (!throttled)
+ break;
+ }
+ __tty_set_flow_change(tty, 0);
+}
+
+static void n_tty_check_unthrottle(struct tty_struct *tty)
+{
+ if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+ tty->link->ldisc->ops->write_wakeup == n_tty_write_wakeup) {
+ if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
+ return;
+ if (!tty->count)
+ return;
+ n_tty_set_room(tty);
+ n_tty_write_wakeup(tty->link);
+ wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT);
+ return;
+ }
+
+ /* If there is enough space in the read buffer now, let the
+ * low-level driver know. We use chars_in_buffer() to
+ * check the buffer, as it now knows about canonical mode.
+ * Otherwise, if the driver is throttled and the line is
+ * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
+ * we won't get any more characters.
+ */
+
+ while (1) {
+ int unthrottled;
+ tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
+ if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
+ break;
+ if (!tty->count)
+ break;
+ n_tty_set_room(tty);
+ unthrottled = tty_unthrottle_safe(tty);
+ if (!unthrottled)
+ break;
}
+ __tty_set_flow_change(tty, 0);
}
/**
@@ -188,21 +306,19 @@ static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata)
* @c: character
* @ldata: n_tty data
*
- * Add a character to the tty read_buf queue. This is done under the
- * read_lock to serialize character addition and also to protect us
- * against parallel reads or flushes
+ * Add a character to the tty read_buf queue.
+ *
+ * n_tty_receive_buf()/producer path:
+ * caller holds non-exclusive termios_rwsem
+ * modifies read_head
+ *
+ * read_head is only considered 'published' if canonical mode is
+ * not active.
*/
-static void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
+static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
{
- unsigned long flags;
- /*
- * The problem of stomping on the buffers ends here.
- * Why didn't anyone see this one coming? --AJK
- */
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- put_tty_queue_nolock(c, ldata);
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
+ *read_buf_addr(ldata, ldata->read_head++) = c;
}
/**
@@ -212,22 +328,17 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
* Reset the read buffer counters and clear the flags.
* Called from n_tty_open() and n_tty_flush_buffer().
*
- * Locking: tty_read_lock for read fields.
+ * Locking: caller holds exclusive termios_rwsem
+ * (or locking is not required)
*/
static void reset_buffer_flags(struct n_tty_data *ldata)
{
- unsigned long flags;
+ ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
+ ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
+ ldata->line_start = 0;
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- ldata->read_head = ldata->read_tail = ldata->read_cnt = 0;
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
-
- mutex_lock(&ldata->echo_lock);
- ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0;
- mutex_unlock(&ldata->echo_lock);
-
- ldata->canon_head = ldata->canon_data = ldata->erasing = 0;
+ ldata->erasing = 0;
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
}
@@ -251,16 +362,21 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty)
* buffer flushed (eg at hangup) or when the N_TTY line discipline
* internally has to clean the pending queue (for example some signals).
*
- * Locking: ctrl_lock, read_lock.
+ * Holds termios_rwsem to exclude producer/consumer while
+ * buffer indices are reset.
+ *
+ * Locking: ctrl_lock, exclusive termios_rwsem
*/
static void n_tty_flush_buffer(struct tty_struct *tty)
{
+ down_write(&tty->termios_rwsem);
reset_buffer_flags(tty->disc_data);
n_tty_set_room(tty);
if (tty->link)
n_tty_packet_mode_flush(tty);
+ up_write(&tty->termios_rwsem);
}
/**
@@ -270,24 +386,18 @@ static void n_tty_flush_buffer(struct tty_struct *tty)
* Report the number of characters buffered to be delivered to user
* at this instant in time.
*
- * Locking: read_lock
+ * Locking: exclusive termios_rwsem
*/
static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
{
- struct n_tty_data *ldata = tty->disc_data;
- unsigned long flags;
- ssize_t n = 0;
+ ssize_t n;
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- if (!ldata->icanon) {
- n = ldata->read_cnt;
- } else if (ldata->canon_data) {
- n = (ldata->canon_head > ldata->read_tail) ?
- ldata->canon_head - ldata->read_tail :
- ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail);
- }
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
+ WARN_ONCE(1, "%s is deprecated and scheduled for removal.", __func__);
+
+ down_write(&tty->termios_rwsem);
+ n = chars_in_buffer(tty);
+ up_write(&tty->termios_rwsem);
return n;
}
@@ -532,33 +642,23 @@ break_out:
* are prioritized. Also, when control characters are echoed with a
* prefixed "^", the pair is treated atomically and thus not separated.
*
- * Locking: output_lock to protect column state and space left,
- * echo_lock to protect the echo buffer
+ * Locking: callers must hold output_lock
*/
-static void process_echoes(struct tty_struct *tty)
+static size_t __process_echoes(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
- int space, nr;
+ int space, old_space;
+ size_t tail;
unsigned char c;
- unsigned char *cp, *buf_end;
- if (!ldata->echo_cnt)
- return;
-
- mutex_lock(&ldata->output_lock);
- mutex_lock(&ldata->echo_lock);
+ old_space = space = tty_write_room(tty);
- space = tty_write_room(tty);
-
- buf_end = ldata->echo_buf + N_TTY_BUF_SIZE;
- cp = ldata->echo_buf + ldata->echo_pos;
- nr = ldata->echo_cnt;
- while (nr > 0) {
- c = *cp;
+ tail = ldata->echo_tail;
+ while (ldata->echo_commit != tail) {
+ c = echo_buf(ldata, tail);
if (c == ECHO_OP_START) {
unsigned char op;
- unsigned char *opp;
int no_space_left = 0;
/*
@@ -566,18 +666,13 @@ static void process_echoes(struct tty_struct *tty)
* operation, get the next byte, which is either the
* op code or a control character value.
*/
- opp = cp + 1;
- if (opp == buf_end)
- opp -= N_TTY_BUF_SIZE;
- op = *opp;
+ op = echo_buf(ldata, tail + 1);
switch (op) {
unsigned int num_chars, num_bs;
case ECHO_OP_ERASE_TAB:
- if (++opp == buf_end)
- opp -= N_TTY_BUF_SIZE;
- num_chars = *opp;
+ num_chars = echo_buf(ldata, tail + 2);
/*
* Determine how many columns to go back
@@ -603,21 +698,18 @@ static void process_echoes(struct tty_struct *tty)
if (ldata->column > 0)
ldata->column--;
}
- cp += 3;
- nr -= 3;
+ tail += 3;
break;
case ECHO_OP_SET_CANON_COL:
ldata->canon_column = ldata->column;
- cp += 2;
- nr -= 2;
+ tail += 2;
break;
case ECHO_OP_MOVE_BACK_COL:
if (ldata->column > 0)
ldata->column--;
- cp += 2;
- nr -= 2;
+ tail += 2;
break;
case ECHO_OP_START:
@@ -629,8 +721,7 @@ static void process_echoes(struct tty_struct *tty)
tty_put_char(tty, ECHO_OP_START);
ldata->column++;
space--;
- cp += 2;
- nr -= 2;
+ tail += 2;
break;
default:
@@ -651,8 +742,7 @@ static void process_echoes(struct tty_struct *tty)
tty_put_char(tty, op ^ 0100);
ldata->column += 2;
space -= 2;
- cp += 2;
- nr -= 2;
+ tail += 2;
}
if (no_space_left)
@@ -669,80 +759,92 @@ static void process_echoes(struct tty_struct *tty)
tty_put_char(tty, c);
space -= 1;
}
- cp += 1;
- nr -= 1;
+ tail += 1;
}
-
- /* When end of circular buffer reached, wrap around */
- if (cp >= buf_end)
- cp -= N_TTY_BUF_SIZE;
}
- if (nr == 0) {
- ldata->echo_pos = 0;
- ldata->echo_cnt = 0;
- ldata->echo_overrun = 0;
- } else {
- int num_processed = ldata->echo_cnt - nr;
- ldata->echo_pos += num_processed;
- ldata->echo_pos &= N_TTY_BUF_SIZE - 1;
- ldata->echo_cnt = nr;
- if (num_processed > 0)
- ldata->echo_overrun = 0;
+ /* If the echo buffer is nearly full (so that the possibility exists
+ * of echo overrun before the next commit), then discard enough
+ * data at the tail to prevent a subsequent overrun */
+ while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) {
+ if (echo_buf(ldata, tail == ECHO_OP_START)) {
+ if (echo_buf(ldata, tail) == ECHO_OP_ERASE_TAB)
+ tail += 3;
+ else
+ tail += 2;
+ } else
+ tail++;
}
- mutex_unlock(&ldata->echo_lock);
+ ldata->echo_tail = tail;
+ return old_space - space;
+}
+
+static void commit_echoes(struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ size_t nr, old, echoed;
+ size_t head;
+
+ head = ldata->echo_head;
+ old = ldata->echo_commit - ldata->echo_tail;
+
+ /* Process committed echoes if the accumulated # of bytes
+ * is over the threshold (and try again each time another
+ * block is accumulated) */
+ nr = head - ldata->echo_tail;
+ if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK))
+ return;
+
+ mutex_lock(&ldata->output_lock);
+ ldata->echo_commit = head;
+ echoed = __process_echoes(tty);
+ mutex_unlock(&ldata->output_lock);
+
+ if (echoed && tty->ops->flush_chars)
+ tty->ops->flush_chars(tty);
+}
+
+static void process_echoes(struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ size_t echoed;
+
+ if (!L_ECHO(tty) || ldata->echo_commit == ldata->echo_tail)
+ return;
+
+ mutex_lock(&ldata->output_lock);
+ echoed = __process_echoes(tty);
mutex_unlock(&ldata->output_lock);
- if (tty->ops->flush_chars)
+ if (echoed && tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
+static void flush_echoes(struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (!L_ECHO(tty) || ldata->echo_commit == ldata->echo_head)
+ return;
+
+ mutex_lock(&ldata->output_lock);
+ ldata->echo_commit = ldata->echo_head;
+ __process_echoes(tty);
+ mutex_unlock(&ldata->output_lock);
+}
+
/**
* add_echo_byte - add a byte to the echo buffer
* @c: unicode byte to echo
* @ldata: n_tty data
*
* Add a character or operation byte to the echo buffer.
- *
- * Should be called under the echo lock to protect the echo buffer.
*/
-static void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
+static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
{
- int new_byte_pos;
-
- if (ldata->echo_cnt == N_TTY_BUF_SIZE) {
- /* Circular buffer is already at capacity */
- new_byte_pos = ldata->echo_pos;
-
- /*
- * Since the buffer start position needs to be advanced,
- * be sure to step by a whole operation byte group.
- */
- if (ldata->echo_buf[ldata->echo_pos] == ECHO_OP_START) {
- if (ldata->echo_buf[(ldata->echo_pos + 1) &
- (N_TTY_BUF_SIZE - 1)] ==
- ECHO_OP_ERASE_TAB) {
- ldata->echo_pos += 3;
- ldata->echo_cnt -= 2;
- } else {
- ldata->echo_pos += 2;
- ldata->echo_cnt -= 1;
- }
- } else {
- ldata->echo_pos++;
- }
- ldata->echo_pos &= N_TTY_BUF_SIZE - 1;
-
- ldata->echo_overrun = 1;
- } else {
- new_byte_pos = ldata->echo_pos + ldata->echo_cnt;
- new_byte_pos &= N_TTY_BUF_SIZE - 1;
- ldata->echo_cnt++;
- }
-
- ldata->echo_buf[new_byte_pos] = c;
+ *echo_buf_addr(ldata, ldata->echo_head++) = c;
}
/**
@@ -750,16 +852,12 @@ static void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
* @ldata: n_tty data
*
* Add an operation to the echo buffer to move back one column.
- *
- * Locking: echo_lock to protect the echo buffer
*/
static void echo_move_back_col(struct n_tty_data *ldata)
{
- mutex_lock(&ldata->echo_lock);
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata);
- mutex_unlock(&ldata->echo_lock);
}
/**
@@ -768,16 +866,12 @@ static void echo_move_back_col(struct n_tty_data *ldata)
*
* Add an operation to the echo buffer to set the canon column
* to the current column.
- *
- * Locking: echo_lock to protect the echo buffer
*/
static void echo_set_canon_col(struct n_tty_data *ldata)
{
- mutex_lock(&ldata->echo_lock);
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_SET_CANON_COL, ldata);
- mutex_unlock(&ldata->echo_lock);
}
/**
@@ -793,15 +887,11 @@ static void echo_set_canon_col(struct n_tty_data *ldata)
* of input. This information will be used later, along with
* canon column (if applicable), to go back the correct number
* of columns.
- *
- * Locking: echo_lock to protect the echo buffer
*/
static void echo_erase_tab(unsigned int num_chars, int after_tab,
struct n_tty_data *ldata)
{
- mutex_lock(&ldata->echo_lock);
-
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_ERASE_TAB, ldata);
@@ -813,8 +903,6 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab,
num_chars |= 0x80;
add_echo_byte(num_chars, ldata);
-
- mutex_unlock(&ldata->echo_lock);
}
/**
@@ -826,20 +914,16 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab,
* L_ECHO(tty) is true. Called from the driver receive_buf path.
*
* This variant does not treat control characters specially.
- *
- * Locking: echo_lock to protect the echo buffer
*/
static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
{
- mutex_lock(&ldata->echo_lock);
if (c == ECHO_OP_START) {
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_START, ldata);
} else {
add_echo_byte(c, ldata);
}
- mutex_unlock(&ldata->echo_lock);
}
/**
@@ -852,16 +936,12 @@ static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
*
* This variant tags control characters to be echoed as "^X"
* (where X is the letter representing the control char).
- *
- * Locking: echo_lock to protect the echo buffer
*/
static void echo_char(unsigned char c, struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
- mutex_lock(&ldata->echo_lock);
-
if (c == ECHO_OP_START) {
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_START, ldata);
@@ -870,8 +950,6 @@ static void echo_char(unsigned char c, struct tty_struct *tty)
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(c, ldata);
}
-
- mutex_unlock(&ldata->echo_lock);
}
/**
@@ -896,17 +974,22 @@ static inline void finish_erasing(struct n_tty_data *ldata)
* present in the stream from the driver layer. Handles the complexities
* of UTF-8 multibyte symbols.
*
- * Locking: read_lock for tty buffers
+ * n_tty_receive_buf()/producer path:
+ * caller holds non-exclusive termios_rwsem
+ * modifies read_head
+ *
+ * Modifying the read_head is not considered a publish in this context
+ * because canonical mode is active -- only canon_head publishes
*/
static void eraser(unsigned char c, struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
enum { ERASE, WERASE, KILL } kill_type;
- int head, seen_alnums, cnt;
- unsigned long flags;
+ size_t head;
+ size_t cnt;
+ int seen_alnums;
- /* FIXME: locking needed ? */
if (ldata->read_head == ldata->canon_head) {
/* process_output('\a', tty); */ /* what do you think? */
return;
@@ -917,19 +1000,11 @@ static void eraser(unsigned char c, struct tty_struct *tty)
kill_type = WERASE;
else {
if (!L_ECHO(tty)) {
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) &
- (N_TTY_BUF_SIZE - 1));
ldata->read_head = ldata->canon_head;
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
return;
}
if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) &
- (N_TTY_BUF_SIZE - 1));
ldata->read_head = ldata->canon_head;
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
finish_erasing(ldata);
echo_char(KILL_CHAR(tty), tty);
/* Add a newline if ECHOK is on and ECHOKE is off. */
@@ -941,14 +1016,13 @@ static void eraser(unsigned char c, struct tty_struct *tty)
}
seen_alnums = 0;
- /* FIXME: Locking ?? */
while (ldata->read_head != ldata->canon_head) {
head = ldata->read_head;
/* erase a single possibly multibyte character */
do {
- head = (head - 1) & (N_TTY_BUF_SIZE-1);
- c = ldata->read_buf[head];
+ head--;
+ c = read_buf(ldata, head);
} while (is_continuation(c, tty) && head != ldata->canon_head);
/* do not partially erase */
@@ -962,11 +1036,8 @@ static void eraser(unsigned char c, struct tty_struct *tty)
else if (seen_alnums)
break;
}
- cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1);
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
+ cnt = ldata->read_head - head;
ldata->read_head = head;
- ldata->read_cnt -= cnt;
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
if (L_ECHO(tty)) {
if (L_ECHOPRT(tty)) {
if (!ldata->erasing) {
@@ -976,9 +1047,8 @@ static void eraser(unsigned char c, struct tty_struct *tty)
/* if cnt > 1, output a multi-byte character */
echo_char(c, tty);
while (--cnt > 0) {
- head = (head+1) & (N_TTY_BUF_SIZE-1);
- echo_char_raw(ldata->read_buf[head],
- ldata);
+ head++;
+ echo_char_raw(read_buf(ldata, head), ldata);
echo_move_back_col(ldata);
}
} else if (kill_type == ERASE && !L_ECHOE(tty)) {
@@ -986,7 +1056,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
} else if (c == '\t') {
unsigned int num_chars = 0;
int after_tab = 0;
- unsigned long tail = ldata->read_head;
+ size_t tail = ldata->read_head;
/*
* Count the columns used for characters
@@ -996,8 +1066,8 @@ static void eraser(unsigned char c, struct tty_struct *tty)
* number of columns.
*/
while (tail != ldata->canon_head) {
- tail = (tail-1) & (N_TTY_BUF_SIZE-1);
- c = ldata->read_buf[tail];
+ tail--;
+ c = read_buf(ldata, tail);
if (c == '\t') {
after_tab = 1;
break;
@@ -1040,7 +1110,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
* Locking: ctrl_lock
*/
-static inline void isig(int sig, struct tty_struct *tty)
+static void isig(int sig, struct tty_struct *tty)
{
struct pid *tty_pgrp = tty_get_pgrp(tty);
if (tty_pgrp) {
@@ -1056,10 +1126,14 @@ static inline void isig(int sig, struct tty_struct *tty)
* An RS232 break event has been hit in the incoming bitstream. This
* can cause a variety of events depending upon the termios settings.
*
- * Called from the receive_buf path so single threaded.
+ * n_tty_receive_buf()/producer path:
+ * caller holds non-exclusive termios_rwsem
+ * publishes read_head via put_tty_queue()
+ *
+ * Note: may get exclusive termios_rwsem if flushing input buffer
*/
-static inline void n_tty_receive_break(struct tty_struct *tty)
+static void n_tty_receive_break(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
@@ -1068,8 +1142,11 @@ static inline void n_tty_receive_break(struct tty_struct *tty)
if (I_BRKINT(tty)) {
isig(SIGINT, tty);
if (!L_NOFLSH(tty)) {
+ /* flushing needs exclusive termios_rwsem */
+ up_read(&tty->termios_rwsem);
n_tty_flush_buffer(tty);
tty_driver_flush_buffer(tty);
+ down_read(&tty->termios_rwsem);
}
return;
}
@@ -1094,7 +1171,7 @@ static inline void n_tty_receive_break(struct tty_struct *tty)
* private.
*/
-static inline void n_tty_receive_overrun(struct tty_struct *tty)
+static void n_tty_receive_overrun(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
char buf[64];
@@ -1116,10 +1193,13 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty)
* @c: character
*
* Process a parity error and queue the right data to indicate
- * the error case if necessary. Locking as per n_tty_receive_buf.
+ * the error case if necessary.
+ *
+ * n_tty_receive_buf()/producer path:
+ * caller holds non-exclusive termios_rwsem
+ * publishes read_head via put_tty_queue()
*/
-static inline void n_tty_receive_parity_error(struct tty_struct *tty,
- unsigned char c)
+static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c)
{
struct n_tty_data *ldata = tty->disc_data;
@@ -1136,6 +1216,26 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty,
wake_up_interruptible(&tty->read_wait);
}
+static void
+n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
+{
+ if (!L_NOFLSH(tty)) {
+ /* flushing needs exclusive termios_rwsem */
+ up_read(&tty->termios_rwsem);
+ n_tty_flush_buffer(tty);
+ tty_driver_flush_buffer(tty);
+ down_read(&tty->termios_rwsem);
+ }
+ if (I_IXON(tty))
+ start_tty(tty);
+ if (L_ECHO(tty)) {
+ echo_char(c, tty);
+ commit_echoes(tty);
+ }
+ isig(signal, tty);
+ return;
+}
+
/**
* n_tty_receive_char - perform processing
* @tty: terminal device
@@ -1144,117 +1244,54 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty,
* Process an individual character of input received from the driver.
* This is serialized with respect to itself by the rules for the
* driver above.
+ *
+ * n_tty_receive_buf()/producer path:
+ * caller holds non-exclusive termios_rwsem
+ * publishes canon_head if canonical mode is active
+ * otherwise, publishes read_head via put_tty_queue()
+ *
+ * Returns 1 if LNEXT was received, else returns 0
*/
-static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+static int
+n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
{
struct n_tty_data *ldata = tty->disc_data;
- unsigned long flags;
int parmrk;
- if (ldata->raw) {
- put_tty_queue(c, ldata);
- return;
- }
-
- if (I_ISTRIP(tty))
- c &= 0x7f;
- if (I_IUCLC(tty) && L_IEXTEN(tty))
- c = tolower(c);
-
- if (L_EXTPROC(tty)) {
- put_tty_queue(c, ldata);
- return;
- }
-
- if (tty->stopped && !tty->flow_stopped && I_IXON(tty) &&
- I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) &&
- c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) {
- start_tty(tty);
- process_echoes(tty);
- }
-
- if (tty->closing) {
- if (I_IXON(tty)) {
- if (c == START_CHAR(tty)) {
- start_tty(tty);
- process_echoes(tty);
- } else if (c == STOP_CHAR(tty))
- stop_tty(tty);
- }
- return;
- }
-
- /*
- * If the previous character was LNEXT, or we know that this
- * character is not one of the characters that we'll have to
- * handle specially, do shortcut processing to speed things
- * up.
- */
- if (!test_bit(c, ldata->process_char_map) || ldata->lnext) {
- ldata->lnext = 0;
- parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
- if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) {
- /* beep if no space */
- if (L_ECHO(tty))
- process_output('\a', tty);
- return;
- }
- if (L_ECHO(tty)) {
- finish_erasing(ldata);
- /* Record the column of first canon char. */
- if (ldata->canon_head == ldata->read_head)
- echo_set_canon_col(ldata);
- echo_char(c, tty);
- process_echoes(tty);
- }
- if (parmrk)
- put_tty_queue(c, ldata);
- put_tty_queue(c, ldata);
- return;
- }
-
if (I_IXON(tty)) {
if (c == START_CHAR(tty)) {
start_tty(tty);
- process_echoes(tty);
- return;
+ commit_echoes(tty);
+ return 0;
}
if (c == STOP_CHAR(tty)) {
stop_tty(tty);
- return;
+ return 0;
}
}
if (L_ISIG(tty)) {
- int signal;
- signal = SIGINT;
- if (c == INTR_CHAR(tty))
- goto send_signal;
- signal = SIGQUIT;
- if (c == QUIT_CHAR(tty))
- goto send_signal;
- signal = SIGTSTP;
- if (c == SUSP_CHAR(tty)) {
-send_signal:
- if (!L_NOFLSH(tty)) {
- n_tty_flush_buffer(tty);
- tty_driver_flush_buffer(tty);
- }
- if (I_IXON(tty))
- start_tty(tty);
- if (L_ECHO(tty)) {
- echo_char(c, tty);
- process_echoes(tty);
- }
- isig(signal, tty);
- return;
+ if (c == INTR_CHAR(tty)) {
+ n_tty_receive_signal_char(tty, SIGINT, c);
+ return 0;
+ } else if (c == QUIT_CHAR(tty)) {
+ n_tty_receive_signal_char(tty, SIGQUIT, c);
+ return 0;
+ } else if (c == SUSP_CHAR(tty)) {
+ n_tty_receive_signal_char(tty, SIGTSTP, c);
+ return 0;
}
}
+ if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+ start_tty(tty);
+ process_echoes(tty);
+ }
+
if (c == '\r') {
if (I_IGNCR(tty))
- return;
+ return 0;
if (I_ICRNL(tty))
c = '\n';
} else if (c == '\n' && I_INLCR(tty))
@@ -1264,8 +1301,8 @@ send_signal:
if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
(c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
eraser(c, tty);
- process_echoes(tty);
- return;
+ commit_echoes(tty);
+ return 0;
}
if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
ldata->lnext = 1;
@@ -1274,42 +1311,32 @@ send_signal:
if (L_ECHOCTL(tty)) {
echo_char_raw('^', ldata);
echo_char_raw('\b', ldata);
- process_echoes(tty);
+ commit_echoes(tty);
}
}
- return;
+ return 1;
}
- if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
- L_IEXTEN(tty)) {
- unsigned long tail = ldata->canon_head;
+ if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) {
+ size_t tail = ldata->canon_head;
finish_erasing(ldata);
echo_char(c, tty);
echo_char_raw('\n', ldata);
while (tail != ldata->read_head) {
- echo_char(ldata->read_buf[tail], tty);
- tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+ echo_char(read_buf(ldata, tail), tty);
+ tail++;
}
- process_echoes(tty);
- return;
+ commit_echoes(tty);
+ return 0;
}
if (c == '\n') {
- if (ldata->read_cnt >= N_TTY_BUF_SIZE) {
- if (L_ECHO(tty))
- process_output('\a', tty);
- return;
- }
if (L_ECHO(tty) || L_ECHONL(tty)) {
echo_char_raw('\n', ldata);
- process_echoes(tty);
+ commit_echoes(tty);
}
goto handle_newline;
}
if (c == EOF_CHAR(tty)) {
- if (ldata->read_cnt >= N_TTY_BUF_SIZE)
- return;
- if (ldata->canon_head != ldata->read_head)
- set_bit(TTY_PUSH, &tty->flags);
c = __DISABLED_CHAR;
goto handle_newline;
}
@@ -1317,11 +1344,6 @@ send_signal:
(c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty))
? 1 : 0;
- if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) {
- if (L_ECHO(tty))
- process_output('\a', tty);
- return;
- }
/*
* XXX are EOL_CHAR and EOL2_CHAR echoed?!?
*/
@@ -1330,7 +1352,7 @@ send_signal:
if (ldata->canon_head == ldata->read_head)
echo_set_canon_col(ldata);
echo_char(c, tty);
- process_echoes(tty);
+ commit_echoes(tty);
}
/*
* XXX does PARMRK doubling happen for
@@ -1340,26 +1362,17 @@ send_signal:
put_tty_queue(c, ldata);
handle_newline:
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- set_bit(ldata->read_head, ldata->read_flags);
- put_tty_queue_nolock(c, ldata);
+ set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags);
+ put_tty_queue(c, ldata);
ldata->canon_head = ldata->read_head;
- ldata->canon_data++;
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
- return;
+ return 0;
}
}
parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
- if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) {
- /* beep if no space */
- if (L_ECHO(tty))
- process_output('\a', tty);
- return;
- }
if (L_ECHO(tty)) {
finish_erasing(ldata);
if (c == '\n')
@@ -1370,29 +1383,123 @@ handle_newline:
echo_set_canon_col(ldata);
echo_char(c, tty);
}
- process_echoes(tty);
+ commit_echoes(tty);
}
if (parmrk)
put_tty_queue(c, ldata);
put_tty_queue(c, ldata);
+ return 0;
}
+static inline void
+n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ int parmrk;
-/**
- * n_tty_write_wakeup - asynchronous I/O notifier
- * @tty: tty device
- *
- * Required for the ptys, serial driver etc. since processes
- * that attach themselves to the master and rely on ASYNC
- * IO must be woken up
- */
+ if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+ start_tty(tty);
+ process_echoes(tty);
+ }
+ if (L_ECHO(tty)) {
+ finish_erasing(ldata);
+ /* Record the column of first canon char. */
+ if (ldata->canon_head == ldata->read_head)
+ echo_set_canon_col(ldata);
+ echo_char(c, tty);
+ commit_echoes(tty);
+ }
+ parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
+ if (parmrk)
+ put_tty_queue(c, ldata);
+ put_tty_queue(c, ldata);
+}
-static void n_tty_write_wakeup(struct tty_struct *tty)
+static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
{
- if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags))
- kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
+ n_tty_receive_char_inline(tty, c);
+}
+
+static inline void
+n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+ start_tty(tty);
+ process_echoes(tty);
+ }
+ if (L_ECHO(tty)) {
+ finish_erasing(ldata);
+ /* Record the column of first canon char. */
+ if (ldata->canon_head == ldata->read_head)
+ echo_set_canon_col(ldata);
+ echo_char(c, tty);
+ commit_echoes(tty);
+ }
+ put_tty_queue(c, ldata);
+}
+
+static inline void
+n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c)
+{
+ if (I_ISTRIP(tty))
+ c &= 0x7f;
+ if (I_IUCLC(tty) && L_IEXTEN(tty))
+ c = tolower(c);
+
+ if (I_IXON(tty)) {
+ if (c == STOP_CHAR(tty))
+ stop_tty(tty);
+ else if (c == START_CHAR(tty) ||
+ (tty->stopped && !tty->flow_stopped && I_IXANY(tty) &&
+ c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) &&
+ c != SUSP_CHAR(tty))) {
+ start_tty(tty);
+ process_echoes(tty);
+ }
+ }
+}
+
+static void
+n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag)
+{
+ char buf[64];
+
+ switch (flag) {
+ case TTY_BREAK:
+ n_tty_receive_break(tty);
+ break;
+ case TTY_PARITY:
+ case TTY_FRAME:
+ n_tty_receive_parity_error(tty, c);
+ break;
+ case TTY_OVERRUN:
+ n_tty_receive_overrun(tty);
+ break;
+ default:
+ printk(KERN_ERR "%s: unknown flag %d\n",
+ tty_name(tty, buf), flag);
+ break;
+ }
+}
+
+static void
+n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ ldata->lnext = 0;
+ if (likely(flag == TTY_NORMAL)) {
+ if (I_ISTRIP(tty))
+ c &= 0x7f;
+ if (I_IUCLC(tty) && L_IEXTEN(tty))
+ c = tolower(c);
+ n_tty_receive_char(tty, c);
+ } else
+ n_tty_receive_char_flagged(tty, c, flag);
}
/**
@@ -1406,86 +1513,220 @@ static void n_tty_write_wakeup(struct tty_struct *tty)
* been received. This function must be called from soft contexts
* not from interrupt context. The driver is responsible for making
* calls one at a time and in order (or using flush_to_ldisc)
+ *
+ * n_tty_receive_buf()/producer path:
+ * claims non-exclusive termios_rwsem
+ * publishes read_head and canon_head
*/
-static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
- char *fp, int count)
+static void
+n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
{
struct n_tty_data *ldata = tty->disc_data;
- const unsigned char *p;
- char *f, flags = TTY_NORMAL;
- int i;
- char buf[64];
- unsigned long cpuflags;
-
- if (ldata->real_raw) {
- raw_spin_lock_irqsave(&ldata->read_lock, cpuflags);
- i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
- N_TTY_BUF_SIZE - ldata->read_head);
- i = min(count, i);
- memcpy(ldata->read_buf + ldata->read_head, cp, i);
- ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
- ldata->read_cnt += i;
- cp += i;
- count -= i;
-
- i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
- N_TTY_BUF_SIZE - ldata->read_head);
- i = min(count, i);
- memcpy(ldata->read_buf + ldata->read_head, cp, i);
- ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
- ldata->read_cnt += i;
- raw_spin_unlock_irqrestore(&ldata->read_lock, cpuflags);
- } else {
- for (i = count, p = cp, f = fp; i; i--, p++) {
- if (f)
- flags = *f++;
- switch (flags) {
- case TTY_NORMAL:
- n_tty_receive_char(tty, *p);
- break;
- case TTY_BREAK:
- n_tty_receive_break(tty);
- break;
- case TTY_PARITY:
- case TTY_FRAME:
- n_tty_receive_parity_error(tty, *p);
- break;
- case TTY_OVERRUN:
- n_tty_receive_overrun(tty);
- break;
- default:
- printk(KERN_ERR "%s: unknown flag %d\n",
- tty_name(tty, buf), flags);
- break;
+ size_t n, head;
+
+ head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
+ n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
+ n = min_t(size_t, count, n);
+ memcpy(read_buf_addr(ldata, head), cp, n);
+ ldata->read_head += n;
+ cp += n;
+ count -= n;
+
+ head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
+ n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
+ n = min_t(size_t, count, n);
+ memcpy(read_buf_addr(ldata, head), cp, n);
+ ldata->read_head += n;
+}
+
+static void
+n_tty_receive_buf_raw(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ char flag = TTY_NORMAL;
+
+ while (count--) {
+ if (fp)
+ flag = *fp++;
+ if (likely(flag == TTY_NORMAL))
+ put_tty_queue(*cp++, ldata);
+ else
+ n_tty_receive_char_flagged(tty, *cp++, flag);
+ }
+}
+
+static void
+n_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ char flag = TTY_NORMAL;
+
+ while (count--) {
+ if (fp)
+ flag = *fp++;
+ if (likely(flag == TTY_NORMAL))
+ n_tty_receive_char_closing(tty, *cp++);
+ else
+ n_tty_receive_char_flagged(tty, *cp++, flag);
+ }
+}
+
+static void
+n_tty_receive_buf_standard(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ char flag = TTY_NORMAL;
+
+ while (count--) {
+ if (fp)
+ flag = *fp++;
+ if (likely(flag == TTY_NORMAL)) {
+ unsigned char c = *cp++;
+
+ if (I_ISTRIP(tty))
+ c &= 0x7f;
+ if (I_IUCLC(tty) && L_IEXTEN(tty))
+ c = tolower(c);
+ if (L_EXTPROC(tty)) {
+ put_tty_queue(c, ldata);
+ continue;
+ }
+ if (!test_bit(c, ldata->char_map))
+ n_tty_receive_char_inline(tty, c);
+ else if (n_tty_receive_char_special(tty, c) && count) {
+ if (fp)
+ flag = *fp++;
+ n_tty_receive_char_lnext(tty, *cp++, flag);
+ count--;
+ }
+ } else
+ n_tty_receive_char_flagged(tty, *cp++, flag);
+ }
+}
+
+static void
+n_tty_receive_buf_fast(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ char flag = TTY_NORMAL;
+
+ while (count--) {
+ if (fp)
+ flag = *fp++;
+ if (likely(flag == TTY_NORMAL)) {
+ unsigned char c = *cp++;
+
+ if (!test_bit(c, ldata->char_map))
+ n_tty_receive_char_fast(tty, c);
+ else if (n_tty_receive_char_special(tty, c) && count) {
+ if (fp)
+ flag = *fp++;
+ n_tty_receive_char_lnext(tty, *cp++, flag);
+ count--;
}
+ } else
+ n_tty_receive_char_flagged(tty, *cp++, flag);
+ }
+}
+
+static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty));
+
+ if (ldata->real_raw)
+ n_tty_receive_buf_real_raw(tty, cp, fp, count);
+ else if (ldata->raw || (L_EXTPROC(tty) && !preops))
+ n_tty_receive_buf_raw(tty, cp, fp, count);
+ else if (tty->closing && !L_EXTPROC(tty))
+ n_tty_receive_buf_closing(tty, cp, fp, count);
+ else {
+ if (ldata->lnext) {
+ char flag = TTY_NORMAL;
+
+ if (fp)
+ flag = *fp++;
+ n_tty_receive_char_lnext(tty, *cp++, flag);
+ count--;
}
+
+ if (!preops && !I_PARMRK(tty))
+ n_tty_receive_buf_fast(tty, cp, fp, count);
+ else
+ n_tty_receive_buf_standard(tty, cp, fp, count);
+
+ flush_echoes(tty);
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
- set_room(tty);
-
- if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) ||
+ if ((!ldata->icanon && (read_cnt(ldata) >= ldata->minimum_to_wake)) ||
L_EXTPROC(tty)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
}
+}
+
+static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ int room, n;
+
+ down_read(&tty->termios_rwsem);
- /*
- * Check the remaining room for the input canonicalization
- * mode. We don't want to throttle the driver if we're in
- * canonical mode and don't have a newline yet!
- */
while (1) {
- tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
- if (tty->receive_room >= TTY_THRESHOLD_THROTTLE)
+ room = receive_room(tty);
+ n = min(count, room);
+ if (!n)
break;
- if (!tty_throttle_safe(tty))
+ __receive_buf(tty, cp, fp, n);
+ cp += n;
+ if (fp)
+ fp += n;
+ count -= n;
+ }
+
+ tty->receive_room = room;
+ n_tty_check_throttle(tty);
+ up_read(&tty->termios_rwsem);
+}
+
+static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ int room, n, rcvd = 0;
+
+ down_read(&tty->termios_rwsem);
+
+ while (1) {
+ room = receive_room(tty);
+ n = min(count, room);
+ if (!n) {
+ if (!room)
+ ldata->no_room = 1;
break;
+ }
+ __receive_buf(tty, cp, fp, n);
+ cp += n;
+ if (fp)
+ fp += n;
+ count -= n;
+ rcvd += n;
}
- __tty_set_flow_change(tty, 0);
+
+ tty->receive_room = room;
+ n_tty_check_throttle(tty);
+ up_read(&tty->termios_rwsem);
+
+ return rcvd;
}
int is_ignored(int sig)
@@ -1505,7 +1746,7 @@ int is_ignored(int sig)
* guaranteed that this function will not be re-entered or in progress
* when the ldisc is closed.
*
- * Locking: Caller holds tty->termios_mutex
+ * Locking: Caller holds tty->termios_rwsem
*/
static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
@@ -1517,12 +1758,13 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;
if (canon_change) {
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
+ ldata->line_start = 0;
ldata->canon_head = ldata->read_tail;
- ldata->canon_data = 0;
ldata->erasing = 0;
+ ldata->lnext = 0;
}
- if (canon_change && !L_ICANON(tty) && ldata->read_cnt)
+ if (canon_change && !L_ICANON(tty) && read_cnt(ldata))
wake_up_interruptible(&tty->read_wait);
ldata->icanon = (L_ICANON(tty) != 0);
@@ -1531,41 +1773,38 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
I_PARMRK(tty)) {
- bitmap_zero(ldata->process_char_map, 256);
+ bitmap_zero(ldata->char_map, 256);
if (I_IGNCR(tty) || I_ICRNL(tty))
- set_bit('\r', ldata->process_char_map);
+ set_bit('\r', ldata->char_map);
if (I_INLCR(tty))
- set_bit('\n', ldata->process_char_map);
+ set_bit('\n', ldata->char_map);
if (L_ICANON(tty)) {
- set_bit(ERASE_CHAR(tty), ldata->process_char_map);
- set_bit(KILL_CHAR(tty), ldata->process_char_map);
- set_bit(EOF_CHAR(tty), ldata->process_char_map);
- set_bit('\n', ldata->process_char_map);
- set_bit(EOL_CHAR(tty), ldata->process_char_map);
+ set_bit(ERASE_CHAR(tty), ldata->char_map);
+ set_bit(KILL_CHAR(tty), ldata->char_map);
+ set_bit(EOF_CHAR(tty), ldata->char_map);
+ set_bit('\n', ldata->char_map);
+ set_bit(EOL_CHAR(tty), ldata->char_map);
if (L_IEXTEN(tty)) {
- set_bit(WERASE_CHAR(tty),
- ldata->process_char_map);
- set_bit(LNEXT_CHAR(tty),
- ldata->process_char_map);
- set_bit(EOL2_CHAR(tty),
- ldata->process_char_map);
+ set_bit(WERASE_CHAR(tty), ldata->char_map);
+ set_bit(LNEXT_CHAR(tty), ldata->char_map);
+ set_bit(EOL2_CHAR(tty), ldata->char_map);
if (L_ECHO(tty))
set_bit(REPRINT_CHAR(tty),
- ldata->process_char_map);
+ ldata->char_map);
}
}
if (I_IXON(tty)) {
- set_bit(START_CHAR(tty), ldata->process_char_map);
- set_bit(STOP_CHAR(tty), ldata->process_char_map);
+ set_bit(START_CHAR(tty), ldata->char_map);
+ set_bit(STOP_CHAR(tty), ldata->char_map);
}
if (L_ISIG(tty)) {
- set_bit(INTR_CHAR(tty), ldata->process_char_map);
- set_bit(QUIT_CHAR(tty), ldata->process_char_map);
- set_bit(SUSP_CHAR(tty), ldata->process_char_map);
+ set_bit(INTR_CHAR(tty), ldata->char_map);
+ set_bit(QUIT_CHAR(tty), ldata->char_map);
+ set_bit(SUSP_CHAR(tty), ldata->char_map);
}
- clear_bit(__DISABLED_CHAR, ldata->process_char_map);
+ clear_bit(__DISABLED_CHAR, ldata->char_map);
ldata->raw = 0;
ldata->real_raw = 0;
} else {
@@ -1608,9 +1847,7 @@ static void n_tty_close(struct tty_struct *tty)
if (tty->link)
n_tty_packet_mode_flush(tty);
- kfree(ldata->read_buf);
- kfree(ldata->echo_buf);
- kfree(ldata);
+ vfree(ldata);
tty->disc_data = NULL;
}
@@ -1628,26 +1865,23 @@ static int n_tty_open(struct tty_struct *tty)
{
struct n_tty_data *ldata;
- ldata = kzalloc(sizeof(*ldata), GFP_KERNEL);
+ /* Currently a malloc failure here can panic */
+ ldata = vmalloc(sizeof(*ldata));
if (!ldata)
goto err;
ldata->overrun_time = jiffies;
mutex_init(&ldata->atomic_read_lock);
mutex_init(&ldata->output_lock);
- mutex_init(&ldata->echo_lock);
- raw_spin_lock_init(&ldata->read_lock);
-
- /* These are ugly. Currently a malloc failure here can panic */
- ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
- ldata->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
- if (!ldata->read_buf || !ldata->echo_buf)
- goto err_free_bufs;
tty->disc_data = ldata;
reset_buffer_flags(tty->disc_data);
ldata->column = 0;
+ ldata->canon_column = 0;
ldata->minimum_to_wake = 1;
+ ldata->num_overrun = 0;
+ ldata->no_room = 0;
+ ldata->lnext = 0;
tty->closing = 0;
/* indicate buffer work may resume */
clear_bit(TTY_LDISC_HALTED, &tty->flags);
@@ -1655,10 +1889,6 @@ static int n_tty_open(struct tty_struct *tty)
tty_unthrottle(tty);
return 0;
-err_free_bufs:
- kfree(ldata->read_buf);
- kfree(ldata->echo_buf);
- kfree(ldata);
err:
return -ENOMEM;
}
@@ -1667,11 +1897,10 @@ static inline int input_available_p(struct tty_struct *tty, int amt)
{
struct n_tty_data *ldata = tty->disc_data;
- tty_flush_to_ldisc(tty);
if (ldata->icanon && !L_EXTPROC(tty)) {
- if (ldata->canon_data)
+ if (ldata->canon_head != ldata->read_tail)
return 1;
- } else if (ldata->read_cnt >= (amt ? amt : 1))
+ } else if (read_cnt(ldata) >= (amt ? amt : 1))
return 1;
return 0;
@@ -1692,6 +1921,9 @@ static inline int input_available_p(struct tty_struct *tty, int amt)
*
* Called under the ldata->atomic_read_lock sem
*
+ * n_tty_read()/consumer path:
+ * caller holds non-exclusive termios_rwsem
+ * read_tail published
*/
static int copy_from_read_buf(struct tty_struct *tty,
@@ -1702,34 +1934,114 @@ static int copy_from_read_buf(struct tty_struct *tty,
struct n_tty_data *ldata = tty->disc_data;
int retval;
size_t n;
- unsigned long flags;
bool is_eof;
+ size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
retval = 0;
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail);
+ n = min(read_cnt(ldata), N_TTY_BUF_SIZE - tail);
n = min(*nr, n);
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
if (n) {
- retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n);
+ retval = copy_to_user(*b, read_buf_addr(ldata, tail), n);
n -= retval;
- is_eof = n == 1 &&
- ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty);
- tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n,
+ is_eof = n == 1 && read_buf(ldata, tail) == EOF_CHAR(tty);
+ tty_audit_add_data(tty, read_buf_addr(ldata, tail), n,
ldata->icanon);
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1);
- ldata->read_cnt -= n;
+ ldata->read_tail += n;
/* Turn single EOF into zero-length read */
- if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt)
+ if (L_EXTPROC(tty) && ldata->icanon && is_eof && !read_cnt(ldata))
n = 0;
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
*b += n;
*nr -= n;
}
return retval;
}
+/**
+ * canon_copy_from_read_buf - copy read data in canonical mode
+ * @tty: terminal device
+ * @b: user data
+ * @nr: size of data
+ *
+ * Helper function for n_tty_read. It is only called when ICANON is on;
+ * it copies one line of input up to and including the line-delimiting
+ * character into the user-space buffer.
+ *
+ * Called under the atomic_read_lock mutex
+ *
+ * n_tty_read()/consumer path:
+ * caller holds non-exclusive termios_rwsem
+ * read_tail published
+ */
+
+static int canon_copy_from_read_buf(struct tty_struct *tty,
+ unsigned char __user **b,
+ size_t *nr)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ size_t n, size, more, c;
+ size_t eol;
+ size_t tail;
+ int ret, found = 0;
+ bool eof_push = 0;
+
+ /* N.B. avoid overrun if nr == 0 */
+ n = min(*nr, read_cnt(ldata));
+ if (!n)
+ return 0;
+
+ tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
+ size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
+
+ n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n",
+ __func__, *nr, tail, n, size);
+
+ eol = find_next_bit(ldata->read_flags, size, tail);
+ more = n - (size - tail);
+ if (eol == N_TTY_BUF_SIZE && more) {
+ /* scan wrapped without finding set bit */
+ eol = find_next_bit(ldata->read_flags, more, 0);
+ if (eol != more)
+ found = 1;
+ } else if (eol != size)
+ found = 1;
+
+ size = N_TTY_BUF_SIZE - tail;
+ n = (found + eol + size) & (N_TTY_BUF_SIZE - 1);
+ c = n;
+
+ if (found && read_buf(ldata, eol) == __DISABLED_CHAR) {
+ n--;
+ eof_push = !n && ldata->read_tail != ldata->line_start;
+ }
+
+ n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n",
+ __func__, eol, found, n, c, size, more);
+
+ if (n > size) {
+ ret = copy_to_user(*b, read_buf_addr(ldata, tail), size);
+ if (ret)
+ return -EFAULT;
+ ret = copy_to_user(*b + size, ldata->read_buf, n - size);
+ } else
+ ret = copy_to_user(*b, read_buf_addr(ldata, tail), n);
+
+ if (ret)
+ return -EFAULT;
+ *b += n;
+ *nr -= n;
+
+ if (found)
+ clear_bit(eol, ldata->read_flags);
+ smp_mb__after_clear_bit();
+ ldata->read_tail += c;
+
+ if (found) {
+ ldata->line_start = ldata->read_tail;
+ tty_audit_push(tty);
+ }
+ return eof_push ? -EAGAIN : 0;
+}
+
extern ssize_t redirected_tty_write(struct file *, const char __user *,
size_t, loff_t *);
@@ -1787,6 +2099,10 @@ static int job_control(struct tty_struct *tty, struct file *file)
* a hangup. Always called in user context, may sleep.
*
* This code must be sure never to sleep through a hangup.
+ *
+ * n_tty_read()/consumer path:
+ * claims non-exclusive termios_rwsem
+ * publishes read_tail
*/
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
@@ -1798,16 +2114,16 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
int c;
int minimum, time;
ssize_t retval = 0;
- ssize_t size;
long timeout;
unsigned long flags;
int packet;
-do_it_again:
c = job_control(tty, file);
if (c < 0)
return c;
+ down_read(&tty->termios_rwsem);
+
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT;
if (!ldata->icanon) {
@@ -1829,11 +2145,15 @@ do_it_again:
* Internal serialization of reads.
*/
if (file->f_flags & O_NONBLOCK) {
- if (!mutex_trylock(&ldata->atomic_read_lock))
+ if (!mutex_trylock(&ldata->atomic_read_lock)) {
+ up_read(&tty->termios_rwsem);
return -EAGAIN;
+ }
} else {
- if (mutex_lock_interruptible(&ldata->atomic_read_lock))
+ if (mutex_lock_interruptible(&ldata->atomic_read_lock)) {
+ up_read(&tty->termios_rwsem);
return -ERESTARTSYS;
+ }
}
packet = tty->packet;
@@ -1883,7 +2203,11 @@ do_it_again:
break;
}
n_tty_set_room(tty);
+ up_read(&tty->termios_rwsem);
+
timeout = schedule_timeout(timeout);
+
+ down_read(&tty->termios_rwsem);
continue;
}
__set_current_state(TASK_RUNNING);
@@ -1899,45 +2223,11 @@ do_it_again:
}
if (ldata->icanon && !L_EXTPROC(tty)) {
- /* N.B. avoid overrun if nr == 0 */
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- while (nr && ldata->read_cnt) {
- int eol;
-
- eol = test_and_clear_bit(ldata->read_tail,
- ldata->read_flags);
- c = ldata->read_buf[ldata->read_tail];
- ldata->read_tail = ((ldata->read_tail+1) &
- (N_TTY_BUF_SIZE-1));
- ldata->read_cnt--;
- if (eol) {
- /* this test should be redundant:
- * we shouldn't be reading data if
- * canon_data is 0
- */
- if (--ldata->canon_data < 0)
- ldata->canon_data = 0;
- }
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
-
- if (!eol || (c != __DISABLED_CHAR)) {
- if (tty_put_user(tty, c, b++)) {
- retval = -EFAULT;
- b--;
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- break;
- }
- nr--;
- }
- if (eol) {
- tty_audit_push(tty);
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- break;
- }
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
- }
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
- if (retval)
+ retval = canon_copy_from_read_buf(tty, &b, &nr);
+ if (retval == -EAGAIN) {
+ retval = 0;
+ continue;
+ } else if (retval)
break;
} else {
int uncopied;
@@ -1951,24 +2241,7 @@ do_it_again:
}
}
- /* If there is enough space in the read buffer now, let the
- * low-level driver know. We use n_tty_chars_in_buffer() to
- * check the buffer, as it now knows about canonical mode.
- * Otherwise, if the driver is throttled and the line is
- * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
- * we won't get any more characters.
- */
- while (1) {
- tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
- if (n_tty_chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
- break;
- if (!tty->count)
- break;
- n_tty_set_room(tty);
- if (!tty_unthrottle_safe(tty))
- break;
- }
- __tty_set_flow_change(tty, 0);
+ n_tty_check_unthrottle(tty);
if (b - buf >= minimum)
break;
@@ -1982,15 +2255,11 @@ do_it_again:
ldata->minimum_to_wake = minimum;
__set_current_state(TASK_RUNNING);
- size = b - buf;
- if (size) {
- retval = size;
- if (nr)
- clear_bit(TTY_PUSH, &tty->flags);
- } else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
- goto do_it_again;
+ if (b - buf)
+ retval = b - buf;
n_tty_set_room(tty);
+ up_read(&tty->termios_rwsem);
return retval;
}
@@ -2031,6 +2300,8 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
return retval;
}
+ down_read(&tty->termios_rwsem);
+
/* Write out any echoed characters that are still pending */
process_echoes(tty);
@@ -2084,13 +2355,18 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
retval = -EAGAIN;
break;
}
+ up_read(&tty->termios_rwsem);
+
schedule();
+
+ down_read(&tty->termios_rwsem);
}
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
if (b - buf != nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ up_read(&tty->termios_rwsem);
return (b - buf) ? b - buf : retval;
}
@@ -2139,19 +2415,19 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
static unsigned long inq_canon(struct n_tty_data *ldata)
{
- int nr, head, tail;
+ size_t nr, head, tail;
- if (!ldata->canon_data)
+ if (ldata->canon_head == ldata->read_tail)
return 0;
head = ldata->canon_head;
tail = ldata->read_tail;
- nr = (head - tail) & (N_TTY_BUF_SIZE-1);
+ nr = head - tail;
/* Skip EOF-chars.. */
while (head != tail) {
- if (test_bit(tail, ldata->read_flags) &&
- ldata->read_buf[tail] == __DISABLED_CHAR)
+ if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) &&
+ read_buf(ldata, tail) == __DISABLED_CHAR)
nr--;
- tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+ tail++;
}
return nr;
}
@@ -2166,10 +2442,12 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
case TIOCOUTQ:
return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
case TIOCINQ:
- /* FIXME: Locking */
- retval = ldata->read_cnt;
+ down_write(&tty->termios_rwsem);
if (L_ICANON(tty))
retval = inq_canon(ldata);
+ else
+ retval = read_cnt(ldata);
+ up_write(&tty->termios_rwsem);
return put_user(retval, (unsigned int __user *) arg);
default:
return n_tty_ioctl_helper(tty, file, cmd, arg);
@@ -2203,6 +2481,7 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = {
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup,
.fasync = n_tty_fasync,
+ .receive_buf2 = n_tty_receive_buf2,
};
/**
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index abfd99089781..25c9bc783722 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -89,17 +89,13 @@ static void pty_unthrottle(struct tty_struct *tty)
* pty_space - report space left for writing
* @to: tty we are writing into
*
- * The tty buffers allow 64K but we sneak a peak and clip at 8K this
- * allows a lot of overspill room for echo and other fun messes to
- * be handled properly
+ * Limit the buffer space used by ptys to 8k.
*/
static int pty_space(struct tty_struct *to)
{
- int n = 8192 - to->port->buf.memory_used;
- if (n < 0)
- return 0;
- return n;
+ int n = tty_buffer_space_avail(to->port);
+ return min(n, 8192);
}
/**
@@ -125,10 +121,8 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
/* Stuff the data into the input queue of the other end */
c = tty_insert_flip_string(to->port, buf, c);
/* And shovel */
- if (c) {
+ if (c)
tty_flip_buffer_push(to->port);
- tty_wakeup(tty);
- }
}
return c;
}
@@ -287,7 +281,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws)
struct tty_struct *pty = tty->link;
/* For a PTY we need to lock the tty side */
- mutex_lock(&tty->termios_mutex);
+ mutex_lock(&tty->winsize_mutex);
if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
goto done;
@@ -314,7 +308,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws)
tty->winsize = *ws;
pty->winsize = *ws; /* Never used so will go away soon */
done:
- mutex_unlock(&tty->termios_mutex);
+ mutex_unlock(&tty->winsize_mutex);
return 0;
}
diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c
index 916cc19fbbda..5f3bba12c159 100644
--- a/drivers/tty/serial/8250/8250_em.c
+++ b/drivers/tty/serial/8250/8250_em.c
@@ -95,25 +95,23 @@ static int serial8250_em_probe(struct platform_device *pdev)
struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
struct serial8250_em_priv *priv;
struct uart_8250_port up;
- int ret = -EINVAL;
+ int ret;
if (!regs || !irq) {
dev_err(&pdev->dev, "missing registers or irq\n");
- goto err0;
+ return -EINVAL;
}
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(&pdev->dev, "unable to allocate private data\n");
- ret = -ENOMEM;
- goto err0;
+ return -ENOMEM;
}
- priv->sclk = clk_get(&pdev->dev, "sclk");
+ priv->sclk = devm_clk_get(&pdev->dev, "sclk");
if (IS_ERR(priv->sclk)) {
dev_err(&pdev->dev, "unable to get clock\n");
- ret = PTR_ERR(priv->sclk);
- goto err1;
+ return PTR_ERR(priv->sclk);
}
memset(&up, 0, sizeof(up));
@@ -136,20 +134,13 @@ static int serial8250_em_probe(struct platform_device *pdev)
ret = serial8250_register_8250_port(&up);
if (ret < 0) {
dev_err(&pdev->dev, "unable to register 8250 port\n");
- goto err2;
+ clk_disable(priv->sclk);
+ return ret;
}
priv->line = ret;
platform_set_drvdata(pdev, priv);
return 0;
-
- err2:
- clk_disable(priv->sclk);
- clk_put(priv->sclk);
- err1:
- kfree(priv);
- err0:
- return ret;
}
static int serial8250_em_remove(struct platform_device *pdev)
@@ -158,8 +149,6 @@ static int serial8250_em_remove(struct platform_device *pdev)
serial8250_unregister_port(priv->line);
clk_disable(priv->sclk);
- clk_put(priv->sclk);
- kfree(priv);
return 0;
}
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index c52948b368d8..c810da7c7a88 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1565,6 +1565,7 @@ pci_wch_ch353_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021
#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022
#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
+#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
@@ -1587,8 +1588,8 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
* ADDI-DATA GmbH communication cards <info@addi-data.com>
*/
{
- .vendor = PCI_VENDOR_ID_ADDIDATA_OLD,
- .device = PCI_DEVICE_ID_ADDIDATA_APCI7800,
+ .vendor = PCI_VENDOR_ID_AMCC,
+ .device = PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = addidata_apci7800_setup,
@@ -4697,8 +4698,8 @@ static struct pci_device_id serial_pci_tbl[] = {
0,
pbn_b0_1_115200 },
- { PCI_VENDOR_ID_ADDIDATA_OLD,
- PCI_DEVICE_ID_ADDIDATA_APCI7800,
+ { PCI_VENDOR_ID_AMCC,
+ PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800,
PCI_ANY_ID,
PCI_ANY_ID,
0,
@@ -4797,6 +4798,12 @@ static struct pci_device_id serial_pci_tbl[] = {
PCI_VENDOR_ID_IBM, 0x0299,
0, 0, pbn_b0_bt_2_115200 },
+ /*
+ * other NetMos 9835 devices are most likely handled by the
+ * parport_serial driver, check drivers/parport/parport_serial.c
+ * before adding them here.
+ */
+
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,
0xA000, 0x1000,
0, 0, pbn_b0_1_115200 },
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index a1ba94d64885..f3b306efaa59 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -116,6 +116,8 @@ config SERIAL_8250_PCI
This builds standard PCI serial support. You may be able to
disable this feature if you only need legacy serial support.
Saves about 9K.
+ Note that serial ports on NetMos 9835 Multi-I/O cards are handled
+ by the parport_serial driver, enabled with CONFIG_PARPORT_SERIAL.
config SERIAL_8250_HP300
tristate
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 1456673bcca0..f13624807d12 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -291,13 +291,13 @@ config SERIAL_MAX3100
config SERIAL_MAX310X
bool "MAX310X support"
- depends on SPI
+ depends on SPI_MASTER
select SERIAL_CORE
- select REGMAP_SPI if SPI
+ select REGMAP_SPI if SPI_MASTER
default n
help
This selects support for an advanced UART from Maxim (Dallas).
- Supported ICs are MAX3107, MAX3108.
+ Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830.
Each IC contains 128 words each of receive and transmit FIFO
that can be controlled through I2C or high-speed SPI.
@@ -1424,8 +1424,8 @@ config SERIAL_AR933X_NR_UARTS
to support.
config SERIAL_EFM32_UART
- tristate "EFM32 UART/USART port."
- depends on ARCH_EFM32
+ tristate "EFM32 UART/USART port"
+ depends on ARM && (ARCH_EFM32 || COMPILE_TEST)
select SERIAL_CORE
help
This driver support the USART and UART ports on
@@ -1497,6 +1497,22 @@ config SERIAL_FSL_LPUART_CONSOLE
If you have enabled the lpuart serial port on the Freescale SoCs,
you can make it the console by answering Y to this option.
+config SERIAL_ST_ASC
+ tristate "ST ASC serial port support"
+ select SERIAL_CORE
+ help
+ This driver is for the on-chip Asychronous Serial Controller on
+ STMicroelectronics STi SoCs.
+ ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality.
+ It support all industry standard baud rates.
+
+ If unsure, say N.
+
+config SERIAL_ST_ASC_CONSOLE
+ bool "Support for console on ST ASC"
+ depends on SERIAL_ST_ASC=y
+ select SERIAL_CORE_CONSOLE
+
endmenu
endif # TTY
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index cf650f0cd6e4..47b679c547e9 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c
index 27f20c57abed..9824dfb1e4fe 100644
--- a/drivers/tty/serial/ar933x_uart.c
+++ b/drivers/tty/serial/ar933x_uart.c
@@ -703,7 +703,6 @@ static int ar933x_uart_remove(struct platform_device *pdev)
struct ar933x_uart_port *up;
up = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
if (up) {
uart_remove_one_port(&ar933x_uart_driver, &up->port);
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 691265faebbe..5c17f8dac91b 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -39,7 +39,6 @@
#include <linux/atmel_pdc.h>
#include <linux/atmel_serial.h>
#include <linux/uaccess.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/atmel.h>
#include <asm/io.h>
@@ -1775,7 +1774,6 @@ static int atmel_serial_probe(struct platform_device *pdev)
struct atmel_uart_data *pdata = pdev->dev.platform_data;
void *data;
int ret = -ENODEV;
- struct pinctrl *pinctrl;
BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
@@ -1809,12 +1807,6 @@ static int atmel_serial_probe(struct platform_device *pdev)
if (ret)
goto err;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- goto err;
- }
-
if (!atmel_use_dma_rx(&port->uart)) {
ret = -ENOMEM;
data = kmalloc(sizeof(struct atmel_uart_char)
@@ -1868,7 +1860,6 @@ static int atmel_serial_remove(struct platform_device *pdev)
int ret = 0;
device_init_wakeup(&pdev->dev, 0);
- platform_set_drvdata(pdev, NULL);
ret = uart_remove_one_port(&atmel_uart, port);
diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c
index 6fa2ae77fffd..d14ba5aa2ec7 100644
--- a/drivers/tty/serial/bcm63xx_uart.c
+++ b/drivers/tty/serial/bcm63xx_uart.c
@@ -852,7 +852,6 @@ static int bcm_uart_remove(struct platform_device *pdev)
port = platform_get_drvdata(pdev);
uart_remove_one_port(&bcm_uart_driver, port);
- platform_set_drvdata(pdev, NULL);
/* mark port as free */
ports[pdev->id].membase = 0;
return 0;
diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c
index 26a3be7ced7d..72031d754dc0 100644
--- a/drivers/tty/serial/bfin_uart.c
+++ b/drivers/tty/serial/bfin_uart.c
@@ -41,10 +41,6 @@
# undef CONFIG_EARLY_PRINTK
#endif
-#ifdef CONFIG_SERIAL_BFIN_MODULE
-# undef CONFIG_EARLY_PRINTK
-#endif
-
/* UART name and device definitions */
#define BFIN_SERIAL_DEV_NAME "ttyBF"
#define BFIN_SERIAL_MAJOR 204
diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c
index bfb17968c8db..7e4e4088471c 100644
--- a/drivers/tty/serial/clps711x.c
+++ b/drivers/tty/serial/clps711x.c
@@ -438,8 +438,7 @@ static int uart_clps711x_probe(struct platform_device *pdev)
s->uart_clk = devm_clk_get(&pdev->dev, "uart");
if (IS_ERR(s->uart_clk)) {
dev_err(&pdev->dev, "Can't get UART clocks\n");
- ret = PTR_ERR(s->uart_clk);
- goto err_out;
+ return PTR_ERR(s->uart_clk);
}
s->uart.owner = THIS_MODULE;
@@ -461,7 +460,7 @@ static int uart_clps711x_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "Registering UART driver failed\n");
devm_clk_put(&pdev->dev, s->uart_clk);
- goto err_out;
+ return ret;
}
for (i = 0; i < UART_CLPS711X_NR; i++) {
@@ -478,11 +477,6 @@ static int uart_clps711x_probe(struct platform_device *pdev)
}
return 0;
-
-err_out:
- platform_set_drvdata(pdev, NULL);
-
- return ret;
}
static int uart_clps711x_remove(struct platform_device *pdev)
@@ -495,7 +489,6 @@ static int uart_clps711x_remove(struct platform_device *pdev)
devm_clk_put(&pdev->dev, s->uart_clk);
uart_unregister_driver(&s->uart);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c
index 7d199c8e1a75..868dbfb9f893 100644
--- a/drivers/tty/serial/efm32-uart.c
+++ b/drivers/tty/serial/efm32-uart.c
@@ -778,8 +778,6 @@ static int efm32_uart_remove(struct platform_device *pdev)
{
struct efm32_uart_port *efm_port = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
uart_remove_one_port(&efm32_uart_reg, &efm_port->port);
if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
@@ -790,7 +788,7 @@ static int efm32_uart_remove(struct platform_device *pdev)
return 0;
}
-static struct of_device_id efm32_uart_dt_ids[] = {
+static const struct of_device_id efm32_uart_dt_ids[] = {
{
.compatible = "efm32,uart",
}, {
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 263cfaabe9e2..8978dc9a58b7 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -342,8 +342,10 @@ static void lpuart_break_ctl(struct uart_port *port, int break_state)
static void lpuart_setup_watermark(struct lpuart_port *sport)
{
unsigned char val, cr2;
+ unsigned char cr2_saved;
cr2 = readb(sport->port.membase + UARTCR2);
+ cr2_saved = cr2;
cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_TE |
UARTCR2_RIE | UARTCR2_RE);
writeb(cr2, sport->port.membase + UARTCR2);
@@ -366,6 +368,9 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
writeb(2, sport->port.membase + UARTTWFIFO);
writeb(1, sport->port.membase + UARTRWFIFO);
+
+ /* Restore cr2 */
+ writeb(cr2_saved, sport->port.membase + UARTCR2);
}
static int lpuart_startup(struct uart_port *port)
@@ -858,7 +863,7 @@ static int __init lpuart_serial_init(void)
if (ret)
uart_unregister_driver(&lpuart_reg);
- return 0;
+ return ret;
}
static void __exit lpuart_serial_exit(void)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 415cec62073f..90655b875bab 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -47,11 +47,12 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/io.h>
+#include <linux/dma-mapping.h>
#include <asm/irq.h>
#include <linux/platform_data/serial-imx.h>
+#include <linux/platform_data/dma-imx.h>
/* Register definitions */
#define URXD0 0x0 /* Receiver Register */
@@ -83,6 +84,7 @@
#define UCR1_ADBR (1<<14) /* Auto detect baud rate */
#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */
#define UCR1_IDEN (1<<12) /* Idle condition interrupt */
+#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */
#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */
#define UCR1_IREN (1<<7) /* Infrared interface enable */
@@ -91,6 +93,7 @@
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
#define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
+#define UCR1_ATDMAEN (1<<2) /* Aging DMA Timer Enable */
#define UCR1_DOZE (1<<1) /* Doze */
#define UCR1_UARTEN (1<<0) /* UART enabled */
#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */
@@ -126,6 +129,7 @@
#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */
#define UCR4_WKEN (1<<7) /* Wake interrupt enable */
#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */
+#define UCR4_IDDMAEN (1<<6) /* DMA IDLE Condition Detected */
#define UCR4_IRSC (1<<5) /* IR special case */
#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */
#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */
@@ -187,6 +191,7 @@
enum imx_uart_type {
IMX1_UART,
IMX21_UART,
+ IMX6Q_UART,
};
/* device type dependent stuff */
@@ -209,6 +214,19 @@ struct imx_port {
struct clk *clk_ipg;
struct clk *clk_per;
const struct imx_uart_data *devdata;
+
+ /* DMA fields */
+ unsigned int dma_is_inited:1;
+ unsigned int dma_is_enabled:1;
+ unsigned int dma_is_rxing:1;
+ unsigned int dma_is_txing:1;
+ struct dma_chan *dma_chan_rx, *dma_chan_tx;
+ struct scatterlist rx_sgl, tx_sgl[2];
+ void *rx_buf;
+ unsigned int rx_bytes, tx_bytes;
+ struct work_struct tsk_dma_rx, tsk_dma_tx;
+ unsigned int dma_tx_nents;
+ wait_queue_head_t dma_wait;
};
struct imx_port_ucrs {
@@ -232,6 +250,10 @@ static struct imx_uart_data imx_uart_devdata[] = {
.uts_reg = IMX21_UTS,
.devtype = IMX21_UART,
},
+ [IMX6Q_UART] = {
+ .uts_reg = IMX21_UTS,
+ .devtype = IMX6Q_UART,
+ },
};
static struct platform_device_id imx_uart_devtype[] = {
@@ -242,12 +264,16 @@ static struct platform_device_id imx_uart_devtype[] = {
.name = "imx21-uart",
.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],
}, {
+ .name = "imx6q-uart",
+ .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART],
+ }, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
static struct of_device_id imx_uart_dt_ids[] = {
+ { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },
{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
{ /* sentinel */ }
@@ -269,6 +295,10 @@ static inline int is_imx21_uart(struct imx_port *sport)
return sport->devdata->devtype == IMX21_UART;
}
+static inline int is_imx6q_uart(struct imx_port *sport)
+{
+ return sport->devdata->devtype == IMX6Q_UART;
+}
/*
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
@@ -387,6 +417,13 @@ static void imx_stop_tx(struct uart_port *port)
return;
}
+ /*
+ * We are maybe in the SMP context, so if the DMA TX thread is running
+ * on other cpu, we have to wait for it to finish.
+ */
+ if (sport->dma_is_enabled && sport->dma_is_txing)
+ return;
+
temp = readl(sport->port.membase + UCR1);
writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
}
@@ -399,6 +436,13 @@ static void imx_stop_rx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ /*
+ * We are maybe in the SMP context, so if the DMA TX thread is running
+ * on other cpu, we have to wait for it to finish.
+ */
+ if (sport->dma_is_enabled && sport->dma_is_rxing)
+ return;
+
temp = readl(sport->port.membase + UCR2);
writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
}
@@ -434,6 +478,95 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
imx_stop_tx(&sport->port);
}
+static void dma_tx_callback(void *data)
+{
+ struct imx_port *sport = data;
+ struct scatterlist *sgl = &sport->tx_sgl[0];
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ unsigned long flags;
+
+ dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+
+ sport->dma_is_txing = 0;
+
+ /* update the stat */
+ spin_lock_irqsave(&sport->port.lock, flags);
+ xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
+ sport->port.icount.tx += sport->tx_bytes;
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&sport->port);
+
+ if (waitqueue_active(&sport->dma_wait)) {
+ wake_up(&sport->dma_wait);
+ dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
+ return;
+ }
+
+ schedule_work(&sport->tsk_dma_tx);
+}
+
+static void dma_tx_work(struct work_struct *w)
+{
+ struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_tx);
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ struct scatterlist *sgl = sport->tx_sgl;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *chan = sport->dma_chan_tx;
+ struct device *dev = sport->port.dev;
+ enum dma_status status;
+ unsigned long flags;
+ int ret;
+
+ status = chan->device->device_tx_status(chan, (dma_cookie_t)0, NULL);
+ if (DMA_IN_PROGRESS == status)
+ return;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+ sport->tx_bytes = uart_circ_chars_pending(xmit);
+ if (sport->tx_bytes == 0) {
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ return;
+ }
+
+ if (xmit->tail > xmit->head) {
+ sport->dma_tx_nents = 2;
+ sg_init_table(sgl, 2);
+ sg_set_buf(sgl, xmit->buf + xmit->tail,
+ UART_XMIT_SIZE - xmit->tail);
+ sg_set_buf(sgl + 1, xmit->buf, xmit->head);
+ } else {
+ sport->dma_tx_nents = 1;
+ sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
+ }
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+ if (ret == 0) {
+ dev_err(dev, "DMA mapping error for TX.\n");
+ return;
+ }
+ desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
+ DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+ if (!desc) {
+ dev_err(dev, "We cannot prepare for the TX slave dma!\n");
+ return;
+ }
+ desc->callback = dma_tx_callback;
+ desc->callback_param = sport;
+
+ dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
+ uart_circ_chars_pending(xmit));
+ /* fire it */
+ sport->dma_is_txing = 1;
+ dmaengine_submit(desc);
+ dma_async_issue_pending(chan);
+ return;
+}
+
/*
* interrupts disabled on entry
*/
@@ -460,8 +593,10 @@ static void imx_start_tx(struct uart_port *port)
temp |= UCR4_OREN;
writel(temp, sport->port.membase + UCR4);
- temp = readl(sport->port.membase + UCR1);
- writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+ if (!sport->dma_is_enabled) {
+ temp = readl(sport->port.membase + UCR1);
+ writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+ }
if (USE_IRDA(sport)) {
temp = readl(sport->port.membase + UCR1);
@@ -473,6 +608,15 @@ static void imx_start_tx(struct uart_port *port)
writel(temp, sport->port.membase + UCR4);
}
+ if (sport->dma_is_enabled) {
+ /*
+ * We may in the interrupt context, so arise a work_struct to
+ * do the real job.
+ */
+ schedule_work(&sport->tsk_dma_tx);
+ return;
+ }
+
if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
imx_transmit_buffer(sport);
}
@@ -588,6 +732,28 @@ out:
return IRQ_HANDLED;
}
+/*
+ * If the RXFIFO is filled with some data, and then we
+ * arise a DMA operation to receive them.
+ */
+static void imx_dma_rxint(struct imx_port *sport)
+{
+ unsigned long temp;
+
+ temp = readl(sport->port.membase + USR2);
+ if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
+ sport->dma_is_rxing = 1;
+
+ /* disable the `Recerver Ready Interrrupt` */
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~(UCR1_RRDYEN);
+ writel(temp, sport->port.membase + UCR1);
+
+ /* tell the DMA to receive the data. */
+ schedule_work(&sport->tsk_dma_rx);
+ }
+}
+
static irqreturn_t imx_int(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
@@ -596,8 +762,12 @@ static irqreturn_t imx_int(int irq, void *dev_id)
sts = readl(sport->port.membase + USR1);
- if (sts & USR1_RRDY)
- imx_rxint(irq, dev_id);
+ if (sts & USR1_RRDY) {
+ if (sport->dma_is_enabled)
+ imx_dma_rxint(sport);
+ else
+ imx_rxint(irq, dev_id);
+ }
if (sts & USR1_TRDY &&
readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
@@ -654,7 +824,8 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
if (mctrl & TIOCM_RTS)
- temp |= UCR2_CTS;
+ if (!sport->dma_is_enabled)
+ temp |= UCR2_CTS;
writel(temp, sport->port.membase + UCR2);
}
@@ -693,6 +864,226 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
return 0;
}
+#define RX_BUF_SIZE (PAGE_SIZE)
+static int start_rx_dma(struct imx_port *sport);
+static void dma_rx_work(struct work_struct *w)
+{
+ struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_rx);
+ struct tty_port *port = &sport->port.state->port;
+
+ if (sport->rx_bytes) {
+ tty_insert_flip_string(port, sport->rx_buf, sport->rx_bytes);
+ tty_flip_buffer_push(port);
+ sport->rx_bytes = 0;
+ }
+
+ if (sport->dma_is_rxing)
+ start_rx_dma(sport);
+}
+
+static void imx_rx_dma_done(struct imx_port *sport)
+{
+ unsigned long temp;
+
+ /* Enable this interrupt when the RXFIFO is empty. */
+ temp = readl(sport->port.membase + UCR1);
+ temp |= UCR1_RRDYEN;
+ writel(temp, sport->port.membase + UCR1);
+
+ sport->dma_is_rxing = 0;
+
+ /* Is the shutdown waiting for us? */
+ if (waitqueue_active(&sport->dma_wait))
+ wake_up(&sport->dma_wait);
+}
+
+/*
+ * There are three kinds of RX DMA interrupts(such as in the MX6Q):
+ * [1] the RX DMA buffer is full.
+ * [2] the Aging timer expires(wait for 8 bytes long)
+ * [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN).
+ *
+ * The [2] is trigger when a character was been sitting in the FIFO
+ * meanwhile [3] can wait for 32 bytes long when the RX line is
+ * on IDLE state and RxFIFO is empty.
+ */
+static void dma_rx_callback(void *data)
+{
+ struct imx_port *sport = data;
+ struct dma_chan *chan = sport->dma_chan_rx;
+ struct scatterlist *sgl = &sport->rx_sgl;
+ struct dma_tx_state state;
+ enum dma_status status;
+ unsigned int count;
+
+ /* unmap it first */
+ dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
+
+ status = chan->device->device_tx_status(chan, (dma_cookie_t)0, &state);
+ count = RX_BUF_SIZE - state.residue;
+ dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
+
+ if (count) {
+ sport->rx_bytes = count;
+ schedule_work(&sport->tsk_dma_rx);
+ } else
+ imx_rx_dma_done(sport);
+}
+
+static int start_rx_dma(struct imx_port *sport)
+{
+ struct scatterlist *sgl = &sport->rx_sgl;
+ struct dma_chan *chan = sport->dma_chan_rx;
+ struct device *dev = sport->port.dev;
+ struct dma_async_tx_descriptor *desc;
+ int ret;
+
+ sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE);
+ ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE);
+ if (ret == 0) {
+ dev_err(dev, "DMA mapping error for RX.\n");
+ return -EINVAL;
+ }
+ desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc) {
+ dev_err(dev, "We cannot prepare for the RX slave dma!\n");
+ return -EINVAL;
+ }
+ desc->callback = dma_rx_callback;
+ desc->callback_param = sport;
+
+ dev_dbg(dev, "RX: prepare for the DMA.\n");
+ dmaengine_submit(desc);
+ dma_async_issue_pending(chan);
+ return 0;
+}
+
+static void imx_uart_dma_exit(struct imx_port *sport)
+{
+ if (sport->dma_chan_rx) {
+ dma_release_channel(sport->dma_chan_rx);
+ sport->dma_chan_rx = NULL;
+
+ kfree(sport->rx_buf);
+ sport->rx_buf = NULL;
+ }
+
+ if (sport->dma_chan_tx) {
+ dma_release_channel(sport->dma_chan_tx);
+ sport->dma_chan_tx = NULL;
+ }
+
+ sport->dma_is_inited = 0;
+}
+
+static int imx_uart_dma_init(struct imx_port *sport)
+{
+ struct dma_slave_config slave_config;
+ struct device *dev = sport->port.dev;
+ int ret;
+
+ /* Prepare for RX : */
+ sport->dma_chan_rx = dma_request_slave_channel(dev, "rx");
+ if (!sport->dma_chan_rx) {
+ dev_dbg(dev, "cannot get the DMA channel.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ slave_config.direction = DMA_DEV_TO_MEM;
+ slave_config.src_addr = sport->port.mapbase + URXD0;
+ slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ slave_config.src_maxburst = RXTL;
+ ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config);
+ if (ret) {
+ dev_err(dev, "error in RX dma configuration.\n");
+ goto err;
+ }
+
+ sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!sport->rx_buf) {
+ dev_err(dev, "cannot alloc DMA buffer.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ sport->rx_bytes = 0;
+
+ /* Prepare for TX : */
+ sport->dma_chan_tx = dma_request_slave_channel(dev, "tx");
+ if (!sport->dma_chan_tx) {
+ dev_err(dev, "cannot get the TX DMA channel!\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ slave_config.direction = DMA_MEM_TO_DEV;
+ slave_config.dst_addr = sport->port.mapbase + URTX0;
+ slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ slave_config.dst_maxburst = TXTL;
+ ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
+ if (ret) {
+ dev_err(dev, "error in TX dma configuration.");
+ goto err;
+ }
+
+ sport->dma_is_inited = 1;
+
+ return 0;
+err:
+ imx_uart_dma_exit(sport);
+ return ret;
+}
+
+static void imx_enable_dma(struct imx_port *sport)
+{
+ unsigned long temp;
+ struct tty_port *port = &sport->port.state->port;
+
+ port->low_latency = 1;
+ INIT_WORK(&sport->tsk_dma_tx, dma_tx_work);
+ INIT_WORK(&sport->tsk_dma_rx, dma_rx_work);
+ init_waitqueue_head(&sport->dma_wait);
+
+ /* set UCR1 */
+ temp = readl(sport->port.membase + UCR1);
+ temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN |
+ /* wait for 32 idle frames for IDDMA interrupt */
+ UCR1_ICD_REG(3);
+ writel(temp, sport->port.membase + UCR1);
+
+ /* set UCR4 */
+ temp = readl(sport->port.membase + UCR4);
+ temp |= UCR4_IDDMAEN;
+ writel(temp, sport->port.membase + UCR4);
+
+ sport->dma_is_enabled = 1;
+}
+
+static void imx_disable_dma(struct imx_port *sport)
+{
+ unsigned long temp;
+ struct tty_port *port = &sport->port.state->port;
+
+ /* clear UCR1 */
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN);
+ writel(temp, sport->port.membase + UCR1);
+
+ /* clear UCR2 */
+ temp = readl(sport->port.membase + UCR2);
+ temp &= ~(UCR2_CTSC | UCR2_CTS);
+ writel(temp, sport->port.membase + UCR2);
+
+ /* clear UCR4 */
+ temp = readl(sport->port.membase + UCR4);
+ temp &= ~UCR4_IDDMAEN;
+ writel(temp, sport->port.membase + UCR4);
+
+ sport->dma_is_enabled = 0;
+ port->low_latency = 0;
+}
+
/* half the RX buffer size */
#define CTSTL 16
@@ -702,15 +1093,13 @@ static int imx_startup(struct uart_port *port)
int retval;
unsigned long flags, temp;
- if (!uart_console(port)) {
- retval = clk_prepare_enable(sport->clk_per);
- if (retval)
- goto error_out1;
- retval = clk_prepare_enable(sport->clk_ipg);
- if (retval) {
- clk_disable_unprepare(sport->clk_per);
- goto error_out1;
- }
+ retval = clk_prepare_enable(sport->clk_per);
+ if (retval)
+ goto error_out1;
+ retval = clk_prepare_enable(sport->clk_ipg);
+ if (retval) {
+ clk_disable_unprepare(sport->clk_per);
+ goto error_out1;
}
imx_setup_ufcr(sport, 0);
@@ -803,7 +1192,7 @@ static int imx_startup(struct uart_port *port)
}
}
- if (is_imx21_uart(sport)) {
+ if (!is_imx1_uart(sport)) {
temp = readl(sport->port.membase + UCR3);
temp |= IMX21_UCR3_RXDMUXSEL;
writel(temp, sport->port.membase + UCR3);
@@ -859,6 +1248,15 @@ static void imx_shutdown(struct uart_port *port)
unsigned long temp;
unsigned long flags;
+ if (sport->dma_is_enabled) {
+ /* We have to wait for the DMA to finish. */
+ wait_event(sport->dma_wait,
+ !sport->dma_is_rxing && !sport->dma_is_txing);
+ imx_stop_rx(port);
+ imx_disable_dma(sport);
+ imx_uart_dma_exit(sport);
+ }
+
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_TXEN);
@@ -901,10 +1299,8 @@ static void imx_shutdown(struct uart_port *port)
writel(temp, sport->port.membase + UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
- if (!uart_console(&sport->port)) {
- clk_disable_unprepare(sport->clk_per);
- clk_disable_unprepare(sport->clk_ipg);
- }
+ clk_disable_unprepare(sport->clk_per);
+ clk_disable_unprepare(sport->clk_ipg);
}
static void
@@ -947,6 +1343,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
if (sport->have_rtscts) {
ucr2 &= ~UCR2_IRTS;
ucr2 |= UCR2_CTSC;
+
+ /* Can we enable the DMA support? */
+ if (is_imx6q_uart(sport) && !uart_console(port)
+ && !sport->dma_is_inited)
+ imx_uart_dma_init(sport);
} else {
termios->c_cflag &= ~CRTSCTS;
}
@@ -1020,6 +1421,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
*/
div = 1;
} else {
+ /* custom-baudrate handling */
+ div = sport->port.uartclk / (baud * 16);
+ if (baud == 38400 && quot != div)
+ baud = sport->port.uartclk / (quot * 16);
+
div = sport->port.uartclk / (baud * 16);
if (div > 7)
div = 7;
@@ -1048,7 +1454,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
writel(num, sport->port.membase + UBIR);
writel(denom, sport->port.membase + UBMR);
- if (is_imx21_uart(sport))
+ if (!is_imx1_uart(sport))
writel(sport->port.uartclk / div / 1000,
sport->port.membase + IMX21_ONEMS);
@@ -1060,6 +1466,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
imx_enable_ms(&sport->port);
+ if (sport->dma_is_inited && !sport->dma_is_enabled)
+ imx_enable_dma(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
@@ -1251,6 +1659,16 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
unsigned int ucr1;
unsigned long flags = 0;
int locked = 1;
+ int retval;
+
+ retval = clk_enable(sport->clk_per);
+ if (retval)
+ return;
+ retval = clk_enable(sport->clk_ipg);
+ if (retval) {
+ clk_disable(sport->clk_per);
+ return;
+ }
if (sport->port.sysrq)
locked = 0;
@@ -1286,6 +1704,9 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
if (locked)
spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ clk_disable(sport->clk_ipg);
+ clk_disable(sport->clk_per);
}
/*
@@ -1359,6 +1780,7 @@ imx_console_setup(struct console *co, char *options)
int bits = 8;
int parity = 'n';
int flow = 'n';
+ int retval;
/*
* Check whether an invalid uart number has been specified, and
@@ -1371,6 +1793,11 @@ imx_console_setup(struct console *co, char *options)
if (sport == NULL)
return -ENODEV;
+ /* For setting the registers, we only need to enable the ipg clock. */
+ retval = clk_prepare_enable(sport->clk_ipg);
+ if (retval)
+ goto error_console;
+
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
@@ -1378,7 +1805,20 @@ imx_console_setup(struct console *co, char *options)
imx_setup_ufcr(sport, 0);
- return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+ retval = uart_set_options(&sport->port, co, baud, parity, bits, flow);
+
+ clk_disable(sport->clk_ipg);
+ if (retval) {
+ clk_unprepare(sport->clk_ipg);
+ goto error_console;
+ }
+
+ retval = clk_prepare(sport->clk_per);
+ if (retval)
+ clk_disable_unprepare(sport->clk_ipg);
+
+error_console:
+ return retval;
}
static struct uart_driver imx_reg;
@@ -1507,7 +1947,6 @@ static int serial_imx_probe(struct platform_device *pdev)
void __iomem *base;
int ret = 0;
struct resource *res;
- struct pinctrl *pinctrl;
sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
if (!sport)
@@ -1543,13 +1982,6 @@ static int serial_imx_probe(struct platform_device *pdev)
sport->timer.function = imx_timeout;
sport->timer.data = (unsigned long)sport;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret);
- return ret;
- }
-
sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(sport->clk_ipg)) {
ret = PTR_ERR(sport->clk_ipg);
@@ -1564,9 +1996,6 @@ static int serial_imx_probe(struct platform_device *pdev)
return ret;
}
- clk_prepare_enable(sport->clk_per);
- clk_prepare_enable(sport->clk_ipg);
-
sport->port.uartclk = clk_get_rate(sport->clk_per);
imx_ports[sport->port.line] = sport;
@@ -1575,7 +2004,7 @@ static int serial_imx_probe(struct platform_device *pdev)
if (pdata && pdata->init) {
ret = pdata->init(pdev);
if (ret)
- goto clkput;
+ return ret;
}
ret = uart_add_one_port(&imx_reg, &sport->port);
@@ -1583,18 +2012,10 @@ static int serial_imx_probe(struct platform_device *pdev)
goto deinit;
platform_set_drvdata(pdev, sport);
- if (!uart_console(&sport->port)) {
- clk_disable_unprepare(sport->clk_per);
- clk_disable_unprepare(sport->clk_ipg);
- }
-
return 0;
deinit:
if (pdata && pdata->exit)
pdata->exit(pdev);
-clkput:
- clk_disable_unprepare(sport->clk_per);
- clk_disable_unprepare(sport->clk_ipg);
return ret;
}
@@ -1605,8 +2026,6 @@ static int serial_imx_remove(struct platform_device *pdev)
pdata = pdev->dev.platform_data;
- platform_set_drvdata(pdev, NULL);
-
uart_remove_one_port(&imx_reg, &sport->port);
if (pdata && pdata->exit)
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 8941e6418942..4ab5b272a593 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -1,7 +1,7 @@
/*
- * Maxim (Dallas) MAX3107/8 serial driver
+ * Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver
*
- * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
+ * Copyright (C) 2012-2013 Alexander Shiyan <shc_work@mail.ru>
*
* Based on max3100.c, by Christian Pellegrin <chripell@evolware.org>
* Based on max3110.c, by Feng Tang <feng.tang@intel.com>
@@ -13,11 +13,10 @@
* (at your option) any later version.
*/
-/* TODO: MAX3109 support (Dual) */
-/* TODO: MAX14830 support (Quad) */
-
#include <linux/module.h>
+#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/bitops.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/tty.h>
@@ -25,8 +24,10 @@
#include <linux/regmap.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
+
#include <linux/platform_data/max310x.h>
+#define MAX310X_NAME "max310x"
#define MAX310X_MAJOR 204
#define MAX310X_MINOR 209
@@ -37,7 +38,8 @@
#define MAX310X_IRQSTS_REG (0x02) /* IRQ status */
#define MAX310X_LSR_IRQEN_REG (0x03) /* LSR IRQ enable */
#define MAX310X_LSR_IRQSTS_REG (0x04) /* LSR IRQ status */
-#define MAX310X_SPCHR_IRQEN_REG (0x05) /* Special char IRQ enable */
+#define MAX310X_REG_05 (0x05)
+#define MAX310X_SPCHR_IRQEN_REG MAX310X_REG_05 /* Special char IRQ en */
#define MAX310X_SPCHR_IRQSTS_REG (0x06) /* Special char IRQ status */
#define MAX310X_STS_IRQEN_REG (0x07) /* Status IRQ enable */
#define MAX310X_STS_IRQSTS_REG (0x08) /* Status IRQ status */
@@ -63,8 +65,15 @@
#define MAX310X_BRGDIVLSB_REG (0x1c) /* Baud rate divisor LSB */
#define MAX310X_BRGDIVMSB_REG (0x1d) /* Baud rate divisor MSB */
#define MAX310X_CLKSRC_REG (0x1e) /* Clock source */
-/* Only present in MAX3107 */
-#define MAX3107_REVID_REG (0x1f) /* Revision identification */
+#define MAX310X_REG_1F (0x1f)
+
+#define MAX310X_REVID_REG MAX310X_REG_1F /* Revision ID */
+
+#define MAX310X_GLOBALIRQ_REG MAX310X_REG_1F /* Global IRQ (RO) */
+#define MAX310X_GLOBALCMD_REG MAX310X_REG_1F /* Global Command (WO) */
+
+/* Extended registers */
+#define MAX310X_REVID_EXTREG MAX310X_REG_05 /* Revision ID */
/* IRQ register bits */
#define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */
@@ -246,58 +255,210 @@
#define MAX310X_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */
#define MAX310X_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */
+/* Global commands */
+#define MAX310X_EXTREG_ENBL (0xce)
+#define MAX310X_EXTREG_DSBL (0xcd)
+
/* Misc definitions */
#define MAX310X_FIFO_SIZE (128)
+#define MAX310x_REV_MASK (0xfc)
/* MAX3107 specific */
#define MAX3107_REV_ID (0xa0)
-#define MAX3107_REV_MASK (0xfe)
-
-/* IRQ status bits definitions */
-#define MAX310X_IRQ_TX (MAX310X_IRQ_TXFIFO_BIT | \
- MAX310X_IRQ_TXEMPTY_BIT)
-#define MAX310X_IRQ_RX (MAX310X_IRQ_RXFIFO_BIT | \
- MAX310X_IRQ_RXEMPTY_BIT)
-
-/* Supported chip types */
-enum {
- MAX310X_TYPE_MAX3107 = 3107,
- MAX310X_TYPE_MAX3108 = 3108,
+
+/* MAX3109 specific */
+#define MAX3109_REV_ID (0xc0)
+
+/* MAX14830 specific */
+#define MAX14830_BRGCFG_CLKDIS_BIT (1 << 6) /* Clock Disable */
+#define MAX14830_REV_ID (0xb0)
+
+struct max310x_devtype {
+ char name[9];
+ int nr;
+ int (*detect)(struct device *);
+ void (*power)(struct uart_port *, int);
};
-struct max310x_port {
- struct uart_driver uart;
+struct max310x_one {
struct uart_port port;
+ struct work_struct tx_work;
+};
- const char *name;
- int uartclk;
-
- unsigned int nr_gpio;
+struct max310x_port {
+ struct uart_driver uart;
+ struct max310x_devtype *devtype;
+ struct regmap *regmap;
+ struct regmap_config regcfg;
+ struct mutex mutex;
+ struct max310x_pdata *pdata;
+ int gpio_used;
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio;
#endif
+ struct max310x_one p[0];
+};
- struct regmap *regmap;
- struct regmap_config regcfg;
+static u8 max310x_port_read(struct uart_port *port, u8 reg)
+{
+ struct max310x_port *s = dev_get_drvdata(port->dev);
+ unsigned int val = 0;
- struct workqueue_struct *wq;
- struct work_struct tx_work;
+ regmap_read(s->regmap, port->iobase + reg, &val);
- struct mutex max310x_mutex;
+ return val;
+}
- struct max310x_pdata *pdata;
+static void max310x_port_write(struct uart_port *port, u8 reg, u8 val)
+{
+ struct max310x_port *s = dev_get_drvdata(port->dev);
+
+ regmap_write(s->regmap, port->iobase + reg, val);
+}
+
+static void max310x_port_update(struct uart_port *port, u8 reg, u8 mask, u8 val)
+{
+ struct max310x_port *s = dev_get_drvdata(port->dev);
+
+ regmap_update_bits(s->regmap, port->iobase + reg, mask, val);
+}
+
+static int max3107_detect(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ unsigned int val = 0;
+ int ret;
+
+ ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val);
+ if (ret)
+ return ret;
+
+ if (((val & MAX310x_REV_MASK) != MAX3107_REV_ID)) {
+ dev_err(dev,
+ "%s ID 0x%02x does not match\n", s->devtype->name, val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int max3108_detect(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ unsigned int val = 0;
+ int ret;
+
+ /* MAX3108 have not REV ID register, we just check default value
+ * from clocksource register to make sure everything works.
+ */
+ ret = regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val);
+ if (ret)
+ return ret;
+
+ if (val != (MAX310X_CLKSRC_EXTCLK_BIT | MAX310X_CLKSRC_PLLBYP_BIT)) {
+ dev_err(dev, "%s not present\n", s->devtype->name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int max3109_detect(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ unsigned int val = 0;
+ int ret;
+
+ ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val);
+ if (ret)
+ return ret;
+
+ if (((val & MAX310x_REV_MASK) != MAX3109_REV_ID)) {
+ dev_err(dev,
+ "%s ID 0x%02x does not match\n", s->devtype->name, val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void max310x_power(struct uart_port *port, int on)
+{
+ max310x_port_update(port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_FORCESLEEP_BIT,
+ on ? 0 : MAX310X_MODE1_FORCESLEEP_BIT);
+ if (on)
+ msleep(50);
+}
+
+static int max14830_detect(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ unsigned int val = 0;
+ int ret;
+
+ ret = regmap_write(s->regmap, MAX310X_GLOBALCMD_REG,
+ MAX310X_EXTREG_ENBL);
+ if (ret)
+ return ret;
+
+ regmap_read(s->regmap, MAX310X_REVID_EXTREG, &val);
+ regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, MAX310X_EXTREG_DSBL);
+ if (((val & MAX310x_REV_MASK) != MAX14830_REV_ID)) {
+ dev_err(dev,
+ "%s ID 0x%02x does not match\n", s->devtype->name, val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void max14830_power(struct uart_port *port, int on)
+{
+ max310x_port_update(port, MAX310X_BRGCFG_REG,
+ MAX14830_BRGCFG_CLKDIS_BIT,
+ on ? 0 : MAX14830_BRGCFG_CLKDIS_BIT);
+ if (on)
+ msleep(50);
+}
+
+static const struct max310x_devtype max3107_devtype = {
+ .name = "MAX3107",
+ .nr = 1,
+ .detect = max3107_detect,
+ .power = max310x_power,
+};
+
+static const struct max310x_devtype max3108_devtype = {
+ .name = "MAX3108",
+ .nr = 1,
+ .detect = max3108_detect,
+ .power = max310x_power,
+};
+
+static const struct max310x_devtype max3109_devtype = {
+ .name = "MAX3109",
+ .nr = 2,
+ .detect = max3109_detect,
+ .power = max310x_power,
+};
+
+static const struct max310x_devtype max14830_devtype = {
+ .name = "MAX14830",
+ .nr = 4,
+ .detect = max14830_detect,
+ .power = max14830_power,
};
-static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg)
+static bool max310x_reg_writeable(struct device *dev, unsigned int reg)
{
- switch (reg) {
+ switch (reg & 0x1f) {
case MAX310X_IRQSTS_REG:
case MAX310X_LSR_IRQSTS_REG:
case MAX310X_SPCHR_IRQSTS_REG:
case MAX310X_STS_IRQSTS_REG:
case MAX310X_TXFIFOLVL_REG:
case MAX310X_RXFIFOLVL_REG:
- case MAX3107_REVID_REG: /* Only available on MAX3107 */
return false;
default:
break;
@@ -308,7 +469,7 @@ static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg)
static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
{
- switch (reg) {
+ switch (reg & 0x1f) {
case MAX310X_RHR_REG:
case MAX310X_IRQSTS_REG:
case MAX310X_LSR_IRQSTS_REG:
@@ -317,6 +478,9 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
case MAX310X_TXFIFOLVL_REG:
case MAX310X_RXFIFOLVL_REG:
case MAX310X_GPIODATA_REG:
+ case MAX310X_BRGDIVLSB_REG:
+ case MAX310X_REG_05:
+ case MAX310X_REG_1F:
return true;
default:
break;
@@ -327,7 +491,7 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
static bool max310x_reg_precious(struct device *dev, unsigned int reg)
{
- switch (reg) {
+ switch (reg & 0x1f) {
case MAX310X_RHR_REG:
case MAX310X_IRQSTS_REG:
case MAX310X_SPCHR_IRQSTS_REG:
@@ -340,42 +504,25 @@ static bool max310x_reg_precious(struct device *dev, unsigned int reg)
return false;
}
-static void max310x_set_baud(struct max310x_port *s, int baud)
+static void max310x_set_baud(struct uart_port *port, int baud)
{
- unsigned int mode = 0, div = s->uartclk / baud;
+ unsigned int mode = 0, div = port->uartclk / baud;
if (!(div / 16)) {
/* Mode x2 */
mode = MAX310X_BRGCFG_2XMODE_BIT;
- div = (s->uartclk * 2) / baud;
+ div = (port->uartclk * 2) / baud;
}
if (!(div / 16)) {
/* Mode x4 */
mode = MAX310X_BRGCFG_4XMODE_BIT;
- div = (s->uartclk * 4) / baud;
+ div = (port->uartclk * 4) / baud;
}
- regmap_write(s->regmap, MAX310X_BRGDIVMSB_REG,
- ((div / 16) >> 8) & 0xff);
- regmap_write(s->regmap, MAX310X_BRGDIVLSB_REG, (div / 16) & 0xff);
- regmap_write(s->regmap, MAX310X_BRGCFG_REG, (div % 16) | mode);
-}
-
-static void max310x_wait_pll(struct max310x_port *s)
-{
- int tryes = 1000;
-
- /* Wait for PLL only if crystal is used */
- if (!(s->pdata->driver_flags & MAX310X_EXT_CLK)) {
- unsigned int sts = 0;
-
- while (tryes--) {
- regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &sts);
- if (sts & MAX310X_STS_CLKREADY_BIT)
- break;
- }
- }
+ max310x_port_write(port, MAX310X_BRGDIVMSB_REG, (div / 16) >> 8);
+ max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div / 16);
+ max310x_port_write(port, MAX310X_BRGCFG_REG, (div % 16) | mode);
}
static int max310x_update_best_err(unsigned long f, long *besterr)
@@ -449,49 +596,49 @@ static int max310x_set_ref_clk(struct max310x_port *s)
regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc);
- if (pllcfg)
- max310x_wait_pll(s);
-
- dev_dbg(s->port.dev, "Reference clock set to %lu Hz\n", bestfreq);
+ /* Wait for crystal */
+ if (pllcfg && !(s->pdata->driver_flags & MAX310X_EXT_CLK))
+ msleep(10);
return (int)bestfreq;
}
-static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen)
+static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
{
- unsigned int sts = 0, ch = 0, flag;
+ unsigned int sts, ch, flag;
- if (unlikely(rxlen >= MAX310X_FIFO_SIZE)) {
- dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen);
+ if (unlikely(rxlen >= port->fifosize)) {
+ dev_warn_ratelimited(port->dev,
+ "Port %i: Possible RX FIFO overrun\n",
+ port->line);
+ port->icount.buf_overrun++;
/* Ensure sanity of RX level */
- rxlen = MAX310X_FIFO_SIZE;
+ rxlen = port->fifosize;
}
- dev_dbg(s->port.dev, "RX Len = %u\n", rxlen);
-
while (rxlen--) {
- regmap_read(s->regmap, MAX310X_RHR_REG, &ch);
- regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &sts);
+ ch = max310x_port_read(port, MAX310X_RHR_REG);
+ sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT |
MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT;
- s->port.icount.rx++;
+ port->icount.rx++;
flag = TTY_NORMAL;
if (unlikely(sts)) {
if (sts & MAX310X_LSR_RXBRK_BIT) {
- s->port.icount.brk++;
- if (uart_handle_break(&s->port))
+ port->icount.brk++;
+ if (uart_handle_break(port))
continue;
} else if (sts & MAX310X_LSR_RXPAR_BIT)
- s->port.icount.parity++;
+ port->icount.parity++;
else if (sts & MAX310X_LSR_FRERR_BIT)
- s->port.icount.frame++;
+ port->icount.frame++;
else if (sts & MAX310X_LSR_RXOVR_BIT)
- s->port.icount.overrun++;
+ port->icount.overrun++;
- sts &= s->port.read_status_mask;
+ sts &= port->read_status_mask;
if (sts & MAX310X_LSR_RXBRK_BIT)
flag = TTY_BREAK;
else if (sts & MAX310X_LSR_RXPAR_BIT)
@@ -502,129 +649,129 @@ static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen)
flag = TTY_OVERRUN;
}
- if (uart_handle_sysrq_char(s->port, ch))
+ if (uart_handle_sysrq_char(port, ch))
continue;
- if (sts & s->port.ignore_status_mask)
+ if (sts & port->ignore_status_mask)
continue;
- uart_insert_char(&s->port, sts, MAX310X_LSR_RXOVR_BIT,
- ch, flag);
+ uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, ch, flag);
}
- tty_flip_buffer_push(&s->port.state->port);
+ tty_flip_buffer_push(&port->state->port);
}
-static void max310x_handle_tx(struct max310x_port *s)
+static void max310x_handle_tx(struct uart_port *port)
{
- struct circ_buf *xmit = &s->port.state->xmit;
- unsigned int txlen = 0, to_send;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int txlen, to_send;
- if (unlikely(s->port.x_char)) {
- regmap_write(s->regmap, MAX310X_THR_REG, s->port.x_char);
- s->port.icount.tx++;
- s->port.x_char = 0;
+ if (unlikely(port->x_char)) {
+ max310x_port_write(port, MAX310X_THR_REG, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
return;
}
- if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port))
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
return;
/* Get length of data pending in circular buffer */
to_send = uart_circ_chars_pending(xmit);
if (likely(to_send)) {
/* Limit to size of TX FIFO */
- regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &txlen);
- txlen = MAX310X_FIFO_SIZE - txlen;
+ txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
+ txlen = port->fifosize - txlen;
to_send = (to_send > txlen) ? txlen : to_send;
- dev_dbg(s->port.dev, "TX Len = %u\n", to_send);
-
/* Add data to send */
- s->port.icount.tx += to_send;
+ port->icount.tx += to_send;
while (to_send--) {
- regmap_write(s->regmap, MAX310X_THR_REG,
- xmit->buf[xmit->tail]);
+ max310x_port_write(port, MAX310X_THR_REG,
+ xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
};
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&s->port);
+ uart_write_wakeup(port);
}
-static irqreturn_t max310x_ist(int irq, void *dev_id)
+static void max310x_port_irq(struct max310x_port *s, int portno)
{
- struct max310x_port *s = (struct max310x_port *)dev_id;
- unsigned int ists = 0, lsr = 0, rxlen = 0;
+ struct uart_port *port = &s->p[portno].port;
- mutex_lock(&s->max310x_mutex);
+ do {
+ unsigned int ists, lsr, rxlen;
- for (;;) {
/* Read IRQ status & RX FIFO level */
- regmap_read(s->regmap, MAX310X_IRQSTS_REG, &ists);
- regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &lsr);
- regmap_read(s->regmap, MAX310X_RXFIFOLVL_REG, &rxlen);
- if (!ists && !(lsr & MAX310X_LSR_RXTO_BIT) && !rxlen)
+ ists = max310x_port_read(port, MAX310X_IRQSTS_REG);
+ rxlen = max310x_port_read(port, MAX310X_RXFIFOLVL_REG);
+ if (!ists && !rxlen)
break;
- dev_dbg(s->port.dev, "IRQ status: 0x%02x\n", ists);
-
- if (rxlen)
- max310x_handle_rx(s, rxlen);
- if (ists & MAX310X_IRQ_TX)
- max310x_handle_tx(s);
- if (ists & MAX310X_IRQ_CTS_BIT)
- uart_handle_cts_change(&s->port,
+ if (ists & MAX310X_IRQ_CTS_BIT) {
+ lsr = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
+ uart_handle_cts_change(port,
!!(lsr & MAX310X_LSR_CTS_BIT));
- }
+ }
+ if (rxlen)
+ max310x_handle_rx(port, rxlen);
+ if (ists & MAX310X_IRQ_TXEMPTY_BIT) {
+ mutex_lock(&s->mutex);
+ max310x_handle_tx(port);
+ mutex_unlock(&s->mutex);
+ }
+ } while (1);
+}
+
+static irqreturn_t max310x_ist(int irq, void *dev_id)
+{
+ struct max310x_port *s = (struct max310x_port *)dev_id;
- mutex_unlock(&s->max310x_mutex);
+ if (s->uart.nr > 1) {
+ do {
+ unsigned int val = ~0;
+
+ WARN_ON_ONCE(regmap_read(s->regmap,
+ MAX310X_GLOBALIRQ_REG, &val));
+ val = ((1 << s->uart.nr) - 1) & ~val;
+ if (!val)
+ break;
+ max310x_port_irq(s, fls(val) - 1);
+ } while (1);
+ } else
+ max310x_port_irq(s, 0);
return IRQ_HANDLED;
}
static void max310x_wq_proc(struct work_struct *ws)
{
- struct max310x_port *s = container_of(ws, struct max310x_port, tx_work);
+ struct max310x_one *one = container_of(ws, struct max310x_one, tx_work);
+ struct max310x_port *s = dev_get_drvdata(one->port.dev);
- mutex_lock(&s->max310x_mutex);
- max310x_handle_tx(s);
- mutex_unlock(&s->max310x_mutex);
+ mutex_lock(&s->mutex);
+ max310x_handle_tx(&one->port);
+ mutex_unlock(&s->mutex);
}
static void max310x_start_tx(struct uart_port *port)
{
- struct max310x_port *s = container_of(port, struct max310x_port, port);
-
- queue_work(s->wq, &s->tx_work);
-}
-
-static void max310x_stop_tx(struct uart_port *port)
-{
- /* Do nothing */
-}
+ struct max310x_one *one = container_of(port, struct max310x_one, port);
-static void max310x_stop_rx(struct uart_port *port)
-{
- /* Do nothing */
+ if (!work_pending(&one->tx_work))
+ schedule_work(&one->tx_work);
}
static unsigned int max310x_tx_empty(struct uart_port *port)
{
- unsigned int val = 0;
- struct max310x_port *s = container_of(port, struct max310x_port, port);
-
- mutex_lock(&s->max310x_mutex);
- regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &val);
- mutex_unlock(&s->max310x_mutex);
+ unsigned int lvl, sts;
- return val ? 0 : TIOCSER_TEMT;
-}
+ lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
+ sts = max310x_port_read(port, MAX310X_IRQSTS_REG);
-static void max310x_enable_ms(struct uart_port *port)
-{
- /* Modem status not supported */
+ return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0;
}
static unsigned int max310x_get_mctrl(struct uart_port *port)
@@ -644,28 +791,20 @@ static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl)
static void max310x_break_ctl(struct uart_port *port, int break_state)
{
- struct max310x_port *s = container_of(port, struct max310x_port, port);
-
- mutex_lock(&s->max310x_mutex);
- regmap_update_bits(s->regmap, MAX310X_LCR_REG,
- MAX310X_LCR_TXBREAK_BIT,
- break_state ? MAX310X_LCR_TXBREAK_BIT : 0);
- mutex_unlock(&s->max310x_mutex);
+ max310x_port_update(port, MAX310X_LCR_REG,
+ MAX310X_LCR_TXBREAK_BIT,
+ break_state ? MAX310X_LCR_TXBREAK_BIT : 0);
}
static void max310x_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
- struct max310x_port *s = container_of(port, struct max310x_port, port);
unsigned int lcr, flow = 0;
int baud;
- mutex_lock(&s->max310x_mutex);
-
/* Mask termios capabilities we don't support */
termios->c_cflag &= ~CMSPAR;
- termios->c_iflag &= ~IXANY;
/* Word size */
switch (termios->c_cflag & CSIZE) {
@@ -696,7 +835,7 @@ static void max310x_set_termios(struct uart_port *port,
lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */
/* Update LCR register */
- regmap_write(s->regmap, MAX310X_LCR_REG, lcr);
+ max310x_port_write(port, MAX310X_LCR_REG, lcr);
/* Set read status mask */
port->read_status_mask = MAX310X_LSR_RXOVR_BIT;
@@ -717,8 +856,8 @@ static void max310x_set_termios(struct uart_port *port,
MAX310X_LSR_RXBRK_BIT;
/* Configure flow control */
- regmap_write(s->regmap, MAX310X_XON1_REG, termios->c_cc[VSTART]);
- regmap_write(s->regmap, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]);
+ max310x_port_write(port, MAX310X_XON1_REG, termios->c_cc[VSTART]);
+ max310x_port_write(port, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]);
if (termios->c_cflag & CRTSCTS)
flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT |
MAX310X_FLOWCTRL_AUTORTS_BIT;
@@ -728,7 +867,7 @@ static void max310x_set_termios(struct uart_port *port,
if (termios->c_iflag & IXOFF)
flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT |
MAX310X_FLOWCTRL_SWFLOWEN_BIT;
- regmap_write(s->regmap, MAX310X_FLOWCTRL_REG, flow);
+ max310x_port_write(port, MAX310X_FLOWCTRL_REG, flow);
/* Get baud rate generator configuration */
baud = uart_get_baud_rate(port, termios, old,
@@ -736,36 +875,30 @@ static void max310x_set_termios(struct uart_port *port,
port->uartclk / 4);
/* Setup baudrate generator */
- max310x_set_baud(s, baud);
+ max310x_set_baud(port, baud);
/* Update timeout according to new baud rate */
uart_update_timeout(port, termios->c_cflag, baud);
-
- mutex_unlock(&s->max310x_mutex);
}
static int max310x_startup(struct uart_port *port)
{
unsigned int val, line = port->line;
- struct max310x_port *s = container_of(port, struct max310x_port, port);
-
- if (s->pdata->suspend)
- s->pdata->suspend(0);
+ struct max310x_port *s = dev_get_drvdata(port->dev);
- mutex_lock(&s->max310x_mutex);
+ s->devtype->power(port, 1);
/* Configure baud rate, 9600 as default */
- max310x_set_baud(s, 9600);
+ max310x_set_baud(port, 9600);
/* Configure LCR register, 8N1 mode by default */
- val = MAX310X_LCR_WORD_LEN_8;
- regmap_write(s->regmap, MAX310X_LCR_REG, val);
+ max310x_port_write(port, MAX310X_LCR_REG, MAX310X_LCR_WORD_LEN_8);
/* Configure MODE1 register */
- regmap_update_bits(s->regmap, MAX310X_MODE1_REG,
- MAX310X_MODE1_TRNSCVCTRL_BIT,
- (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL)
- ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0);
+ max310x_port_update(port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_TRNSCVCTRL_BIT,
+ (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL)
+ ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0);
/* Configure MODE2 register */
val = MAX310X_MODE2_RXEMPTINV_BIT;
@@ -776,63 +909,40 @@ static int max310x_startup(struct uart_port *port)
/* Reset FIFOs */
val |= MAX310X_MODE2_FIFORST_BIT;
- regmap_write(s->regmap, MAX310X_MODE2_REG, val);
-
- /* Configure FIFO trigger level register */
- /* RX FIFO trigger for 16 words, TX FIFO trigger for 64 words */
- val = MAX310X_FIFOTRIGLVL_RX(16) | MAX310X_FIFOTRIGLVL_TX(64);
- regmap_write(s->regmap, MAX310X_FIFOTRIGLVL_REG, val);
+ max310x_port_write(port, MAX310X_MODE2_REG, val);
+ max310x_port_update(port, MAX310X_MODE2_REG,
+ MAX310X_MODE2_FIFORST_BIT, 0);
/* Configure flow control levels */
/* Flow control halt level 96, resume level 48 */
- val = MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96);
- regmap_write(s->regmap, MAX310X_FLOWLVL_REG, val);
-
- /* Clear timeout register */
- regmap_write(s->regmap, MAX310X_RXTO_REG, 0);
-
- /* Configure LSR interrupt enable register */
- /* Enable RX timeout interrupt */
- val = MAX310X_LSR_RXTO_BIT;
- regmap_write(s->regmap, MAX310X_LSR_IRQEN_REG, val);
+ max310x_port_write(port, MAX310X_FLOWLVL_REG,
+ MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96));
- /* Clear FIFO reset */
- regmap_update_bits(s->regmap, MAX310X_MODE2_REG,
- MAX310X_MODE2_FIFORST_BIT, 0);
+ /* Clear IRQ status register */
+ max310x_port_read(port, MAX310X_IRQSTS_REG);
- /* Clear IRQ status register by reading it */
- regmap_read(s->regmap, MAX310X_IRQSTS_REG, &val);
-
- /* Configure interrupt enable register */
- /* Enable CTS change interrupt */
- val = MAX310X_IRQ_CTS_BIT;
- /* Enable RX, TX interrupts */
- val |= MAX310X_IRQ_RX | MAX310X_IRQ_TX;
- regmap_write(s->regmap, MAX310X_IRQEN_REG, val);
-
- mutex_unlock(&s->max310x_mutex);
+ /* Enable RX, TX, CTS change interrupts */
+ val = MAX310X_IRQ_RXEMPTY_BIT | MAX310X_IRQ_TXEMPTY_BIT;
+ max310x_port_write(port, MAX310X_IRQEN_REG, val | MAX310X_IRQ_CTS_BIT);
return 0;
}
static void max310x_shutdown(struct uart_port *port)
{
- struct max310x_port *s = container_of(port, struct max310x_port, port);
+ struct max310x_port *s = dev_get_drvdata(port->dev);
/* Disable all interrupts */
- mutex_lock(&s->max310x_mutex);
- regmap_write(s->regmap, MAX310X_IRQEN_REG, 0);
- mutex_unlock(&s->max310x_mutex);
+ max310x_port_write(port, MAX310X_IRQEN_REG, 0);
- if (s->pdata->suspend)
- s->pdata->suspend(1);
+ s->devtype->power(port, 0);
}
static const char *max310x_type(struct uart_port *port)
{
- struct max310x_port *s = container_of(port, struct max310x_port, port);
+ struct max310x_port *s = dev_get_drvdata(port->dev);
- return (port->type == PORT_MAX310X) ? s->name : NULL;
+ return (port->type == PORT_MAX310X) ? s->devtype->name : NULL;
}
static int max310x_request_port(struct uart_port *port)
@@ -841,134 +951,100 @@ static int max310x_request_port(struct uart_port *port)
return 0;
}
-static void max310x_release_port(struct uart_port *port)
-{
- /* Do nothing */
-}
-
static void max310x_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE)
port->type = PORT_MAX310X;
}
-static int max310x_verify_port(struct uart_port *port, struct serial_struct *ser)
+static int max310x_verify_port(struct uart_port *port, struct serial_struct *s)
{
- if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX310X))
- return 0;
- if (ser->irq == port->irq)
- return 0;
+ if ((s->type != PORT_UNKNOWN) && (s->type != PORT_MAX310X))
+ return -EINVAL;
+ if (s->irq != port->irq)
+ return -EINVAL;
- return -EINVAL;
+ return 0;
}
-static struct uart_ops max310x_ops = {
+static void max310x_null_void(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static const struct uart_ops max310x_ops = {
.tx_empty = max310x_tx_empty,
.set_mctrl = max310x_set_mctrl,
.get_mctrl = max310x_get_mctrl,
- .stop_tx = max310x_stop_tx,
+ .stop_tx = max310x_null_void,
.start_tx = max310x_start_tx,
- .stop_rx = max310x_stop_rx,
- .enable_ms = max310x_enable_ms,
+ .stop_rx = max310x_null_void,
+ .enable_ms = max310x_null_void,
.break_ctl = max310x_break_ctl,
.startup = max310x_startup,
.shutdown = max310x_shutdown,
.set_termios = max310x_set_termios,
.type = max310x_type,
.request_port = max310x_request_port,
- .release_port = max310x_release_port,
+ .release_port = max310x_null_void,
.config_port = max310x_config_port,
.verify_port = max310x_verify_port,
};
-#ifdef CONFIG_PM_SLEEP
-
-static int max310x_suspend(struct device *dev)
+static int __maybe_unused max310x_suspend(struct spi_device *spi,
+ pm_message_t state)
{
- int ret;
- struct max310x_port *s = dev_get_drvdata(dev);
-
- dev_dbg(dev, "Suspend\n");
-
- ret = uart_suspend_port(&s->uart, &s->port);
-
- mutex_lock(&s->max310x_mutex);
+ struct max310x_port *s = dev_get_drvdata(&spi->dev);
+ int i;
- /* Enable sleep mode */
- regmap_update_bits(s->regmap, MAX310X_MODE1_REG,
- MAX310X_MODE1_FORCESLEEP_BIT,
- MAX310X_MODE1_FORCESLEEP_BIT);
-
- mutex_unlock(&s->max310x_mutex);
-
- if (s->pdata->suspend)
- s->pdata->suspend(1);
+ for (i = 0; i < s->uart.nr; i++) {
+ uart_suspend_port(&s->uart, &s->p[i].port);
+ s->devtype->power(&s->p[i].port, 0);
+ }
- return ret;
+ return 0;
}
-static int max310x_resume(struct device *dev)
+static int __maybe_unused max310x_resume(struct spi_device *spi)
{
- struct max310x_port *s = dev_get_drvdata(dev);
-
- dev_dbg(dev, "Resume\n");
-
- if (s->pdata->suspend)
- s->pdata->suspend(0);
+ struct max310x_port *s = dev_get_drvdata(&spi->dev);
+ int i;
- mutex_lock(&s->max310x_mutex);
-
- /* Disable sleep mode */
- regmap_update_bits(s->regmap, MAX310X_MODE1_REG,
- MAX310X_MODE1_FORCESLEEP_BIT,
- 0);
-
- max310x_wait_pll(s);
-
- mutex_unlock(&s->max310x_mutex);
+ for (i = 0; i < s->uart.nr; i++) {
+ s->devtype->power(&s->p[i].port, 1);
+ uart_resume_port(&s->uart, &s->p[i].port);
+ }
- return uart_resume_port(&s->uart, &s->port);
+ return 0;
}
-static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume);
-#define MAX310X_PM_OPS (&max310x_pm_ops)
-
-#else
-#define MAX310X_PM_OPS NULL
-#endif
-
#ifdef CONFIG_GPIOLIB
static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- unsigned int val = 0;
+ unsigned int val;
struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+ struct uart_port *port = &s->p[offset / 4].port;
- mutex_lock(&s->max310x_mutex);
- regmap_read(s->regmap, MAX310X_GPIODATA_REG, &val);
- mutex_unlock(&s->max310x_mutex);
+ val = max310x_port_read(port, MAX310X_GPIODATA_REG);
- return !!((val >> 4) & (1 << offset));
+ return !!((val >> 4) & (1 << (offset % 4)));
}
static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+ struct uart_port *port = &s->p[offset / 4].port;
- mutex_lock(&s->max310x_mutex);
- regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ?
- 1 << offset : 0);
- mutex_unlock(&s->max310x_mutex);
+ max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4),
+ value ? 1 << (offset % 4) : 0);
}
static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+ struct uart_port *port = &s->p[offset / 4].port;
- mutex_lock(&s->max310x_mutex);
-
- regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, 0);
-
- mutex_unlock(&s->max310x_mutex);
+ max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), 0);
return 0;
}
@@ -977,74 +1053,42 @@ static int max310x_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+ struct uart_port *port = &s->p[offset / 4].port;
- mutex_lock(&s->max310x_mutex);
-
- regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset,
- 1 << offset);
- regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ?
- 1 << offset : 0);
-
- mutex_unlock(&s->max310x_mutex);
+ max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4),
+ value ? 1 << (offset % 4) : 0);
+ max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4),
+ 1 << (offset % 4));
return 0;
}
#endif
-/* Generic platform data */
-static struct max310x_pdata generic_plat_data = {
- .driver_flags = MAX310X_EXT_CLK,
- .uart_flags[0] = MAX310X_ECHO_SUPRESS,
- .frequency = 26000000,
-};
-
-static int max310x_probe(struct spi_device *spi)
+static int max310x_probe(struct device *dev, int is_spi,
+ struct max310x_devtype *devtype, int irq)
{
struct max310x_port *s;
- struct device *dev = &spi->dev;
- int chiptype = spi_get_device_id(spi)->driver_data;
- struct max310x_pdata *pdata = dev->platform_data;
- unsigned int val = 0;
- int ret;
+ struct max310x_pdata *pdata = dev_get_platdata(dev);
+ int i, ret, uartclk;
/* Check for IRQ */
- if (spi->irq <= 0) {
+ if (irq <= 0) {
dev_err(dev, "No IRQ specified\n");
return -ENOTSUPP;
}
+ if (!pdata) {
+ dev_err(dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
/* Alloc port structure */
- s = devm_kzalloc(dev, sizeof(struct max310x_port), GFP_KERNEL);
+ s = devm_kzalloc(dev, sizeof(*s) +
+ sizeof(struct max310x_one) * devtype->nr, GFP_KERNEL);
if (!s) {
dev_err(dev, "Error allocating port structure\n");
return -ENOMEM;
}
- dev_set_drvdata(dev, s);
-
- if (!pdata) {
- dev_warn(dev, "No platform data supplied, using defaults\n");
- pdata = &generic_plat_data;
- }
- s->pdata = pdata;
-
- /* Individual chip settings */
- switch (chiptype) {
- case MAX310X_TYPE_MAX3107:
- s->name = "MAX3107";
- s->nr_gpio = 4;
- s->uart.nr = 1;
- s->regcfg.max_register = 0x1f;
- break;
- case MAX310X_TYPE_MAX3108:
- s->name = "MAX3108";
- s->nr_gpio = 4;
- s->uart.nr = 1;
- s->regcfg.max_register = 0x1e;
- break;
- default:
- dev_err(dev, "Unsupported chip type %i\n", chiptype);
- return -ENOTSUPP;
- }
/* Check input frequency */
if ((pdata->driver_flags & MAX310X_EXT_CLK) &&
@@ -1055,13 +1099,11 @@ static int max310x_probe(struct spi_device *spi)
((pdata->frequency < 1000000) || (pdata->frequency > 4000000)))
goto err_freq;
- mutex_init(&s->max310x_mutex);
+ s->pdata = pdata;
+ s->devtype = devtype;
+ dev_set_drvdata(dev, s);
- /* Setup SPI bus */
- spi->mode = SPI_MODE_0;
- spi->bits_per_word = 8;
- spi->max_speed_hz = 26000000;
- spi_setup(spi);
+ mutex_init(&s->mutex);
/* Setup regmap */
s->regcfg.reg_bits = 8;
@@ -1069,109 +1111,100 @@ static int max310x_probe(struct spi_device *spi)
s->regcfg.read_flag_mask = 0x00;
s->regcfg.write_flag_mask = 0x80;
s->regcfg.cache_type = REGCACHE_RBTREE;
- s->regcfg.writeable_reg = max3107_8_reg_writeable;
+ s->regcfg.writeable_reg = max310x_reg_writeable;
s->regcfg.volatile_reg = max310x_reg_volatile;
s->regcfg.precious_reg = max310x_reg_precious;
- s->regmap = devm_regmap_init_spi(spi, &s->regcfg);
+ s->regcfg.max_register = devtype->nr * 0x20 - 1;
+
+ if (IS_ENABLED(CONFIG_SPI_MASTER) && is_spi) {
+ struct spi_device *spi = to_spi_device(dev);
+
+ s->regmap = devm_regmap_init_spi(spi, &s->regcfg);
+ } else
+ return -ENOTSUPP;
+
if (IS_ERR(s->regmap)) {
- ret = PTR_ERR(s->regmap);
dev_err(dev, "Failed to initialize register map\n");
- goto err_out;
- }
-
- /* Reset chip & check SPI function */
- ret = regmap_write(s->regmap, MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT);
- if (ret) {
- dev_err(dev, "SPI transfer failed\n");
- goto err_out;
- }
- /* Clear chip reset */
- regmap_write(s->regmap, MAX310X_MODE2_REG, 0);
-
- switch (chiptype) {
- case MAX310X_TYPE_MAX3107:
- /* Check REV ID to ensure we are talking to what we expect */
- regmap_read(s->regmap, MAX3107_REVID_REG, &val);
- if (((val & MAX3107_REV_MASK) != MAX3107_REV_ID)) {
- dev_err(dev, "%s ID 0x%02x does not match\n",
- s->name, val);
- ret = -ENODEV;
- goto err_out;
- }
- break;
- case MAX310X_TYPE_MAX3108:
- /* MAX3108 have not REV ID register, we just check default value
- * from clocksource register to make sure everything works.
- */
- regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val);
- if (val != (MAX310X_CLKSRC_EXTCLK_BIT |
- MAX310X_CLKSRC_PLLBYP_BIT)) {
- dev_err(dev, "%s not present\n", s->name);
- ret = -ENODEV;
- goto err_out;
- }
- break;
+ return PTR_ERR(s->regmap);
}
/* Board specific configure */
- if (pdata->init)
- pdata->init();
- if (pdata->suspend)
- pdata->suspend(0);
-
- /* Calculate referecne clock */
- s->uartclk = max310x_set_ref_clk(s);
-
- /* Disable all interrupts */
- regmap_write(s->regmap, MAX310X_IRQEN_REG, 0);
-
- /* Setup MODE1 register */
- val = MAX310X_MODE1_IRQSEL_BIT; /* Enable IRQ pin */
- if (pdata->driver_flags & MAX310X_AUTOSLEEP)
- val = MAX310X_MODE1_AUTOSLEEP_BIT;
- regmap_write(s->regmap, MAX310X_MODE1_REG, val);
-
- /* Setup interrupt */
- ret = devm_request_threaded_irq(dev, spi->irq, NULL, max310x_ist,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- dev_name(dev), s);
- if (ret) {
- dev_err(dev, "Unable to reguest IRQ %i\n", spi->irq);
- goto err_out;
+ if (s->pdata->init)
+ s->pdata->init();
+
+ /* Check device to ensure we are talking to what we expect */
+ ret = devtype->detect(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < devtype->nr; i++) {
+ unsigned int offs = i << 5;
+
+ /* Reset port */
+ regmap_write(s->regmap, MAX310X_MODE2_REG + offs,
+ MAX310X_MODE2_RST_BIT);
+ /* Clear port reset */
+ regmap_write(s->regmap, MAX310X_MODE2_REG + offs, 0);
+
+ /* Wait for port startup */
+ do {
+ regmap_read(s->regmap,
+ MAX310X_BRGDIVLSB_REG + offs, &ret);
+ } while (ret != 0x01);
+
+ regmap_update_bits(s->regmap, MAX310X_MODE1_REG + offs,
+ MAX310X_MODE1_AUTOSLEEP_BIT,
+ MAX310X_MODE1_AUTOSLEEP_BIT);
}
+ uartclk = max310x_set_ref_clk(s);
+ dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk);
+
/* Register UART driver */
s->uart.owner = THIS_MODULE;
- s->uart.driver_name = dev_name(dev);
s->uart.dev_name = "ttyMAX";
s->uart.major = MAX310X_MAJOR;
s->uart.minor = MAX310X_MINOR;
+ s->uart.nr = devtype->nr;
ret = uart_register_driver(&s->uart);
if (ret) {
dev_err(dev, "Registering UART driver failed\n");
- goto err_out;
+ return ret;
}
- /* Initialize workqueue for start TX */
- s->wq = create_freezable_workqueue(dev_name(dev));
- INIT_WORK(&s->tx_work, max310x_wq_proc);
-
- /* Initialize UART port data */
- s->port.line = 0;
- s->port.dev = dev;
- s->port.irq = spi->irq;
- s->port.type = PORT_MAX310X;
- s->port.fifosize = MAX310X_FIFO_SIZE;
- s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE;
- s->port.iotype = UPIO_PORT;
- s->port.membase = (void __iomem *)0xffffffff; /* Bogus value */
- s->port.uartclk = s->uartclk;
- s->port.ops = &max310x_ops;
- uart_add_one_port(&s->uart, &s->port);
+ for (i = 0; i < devtype->nr; i++) {
+ /* Initialize port data */
+ s->p[i].port.line = i;
+ s->p[i].port.dev = dev;
+ s->p[i].port.irq = irq;
+ s->p[i].port.type = PORT_MAX310X;
+ s->p[i].port.fifosize = MAX310X_FIFO_SIZE;
+ s->p[i].port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE |
+ UPF_LOW_LATENCY;
+ s->p[i].port.iotype = UPIO_PORT;
+ s->p[i].port.iobase = i * 0x20;
+ s->p[i].port.membase = (void __iomem *)~0;
+ s->p[i].port.uartclk = uartclk;
+ s->p[i].port.ops = &max310x_ops;
+ /* Disable all interrupts */
+ max310x_port_write(&s->p[i].port, MAX310X_IRQEN_REG, 0);
+ /* Clear IRQ status register */
+ max310x_port_read(&s->p[i].port, MAX310X_IRQSTS_REG);
+ /* Enable IRQ pin */
+ max310x_port_update(&s->p[i].port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_IRQSEL_BIT,
+ MAX310X_MODE1_IRQSEL_BIT);
+ /* Initialize queue for start TX */
+ INIT_WORK(&s->p[i].tx_work, max310x_wq_proc);
+ /* Register port */
+ uart_add_one_port(&s->uart, &s->p[i].port);
+ /* Go to suspend mode */
+ devtype->power(&s->p[i].port, 0);
+ }
#ifdef CONFIG_GPIOLIB
/* Setup GPIO cotroller */
- if (pdata->gpio_base) {
+ if (s->pdata->gpio_base) {
s->gpio.owner = THIS_MODULE;
s->gpio.dev = dev;
s->gpio.label = dev_name(dev);
@@ -1179,86 +1212,107 @@ static int max310x_probe(struct spi_device *spi)
s->gpio.get = max310x_gpio_get;
s->gpio.direction_output= max310x_gpio_direction_output;
s->gpio.set = max310x_gpio_set;
- s->gpio.base = pdata->gpio_base;
- s->gpio.ngpio = s->nr_gpio;
+ s->gpio.base = s->pdata->gpio_base;
+ s->gpio.ngpio = devtype->nr * 4;
s->gpio.can_sleep = 1;
- if (gpiochip_add(&s->gpio)) {
- /* Indicate that we should not call gpiochip_remove */
- s->gpio.base = 0;
- }
+ if (!gpiochip_add(&s->gpio))
+ s->gpio_used = 1;
} else
dev_info(dev, "GPIO support not enabled\n");
#endif
- /* Go to suspend mode */
- if (pdata->suspend)
- pdata->suspend(1);
+ /* Setup interrupt */
+ ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(dev), s);
+ if (ret) {
+ dev_err(dev, "Unable to reguest IRQ %i\n", irq);
+#ifdef CONFIG_GPIOLIB
+ if (s->gpio_used)
+ WARN_ON(gpiochip_remove(&s->gpio));
+#endif
+ }
- return 0;
+ return ret;
err_freq:
dev_err(dev, "Frequency parameter incorrect\n");
- ret = -EINVAL;
-
-err_out:
- dev_set_drvdata(dev, NULL);
-
- return ret;
+ return -EINVAL;
}
-static int max310x_remove(struct spi_device *spi)
+static int max310x_remove(struct device *dev)
{
- struct device *dev = &spi->dev;
struct max310x_port *s = dev_get_drvdata(dev);
- int ret = 0;
-
- dev_dbg(dev, "Removing port\n");
-
- devm_free_irq(dev, s->port.irq, s);
-
- destroy_workqueue(s->wq);
+ int i, ret = 0;
- uart_remove_one_port(&s->uart, &s->port);
+ for (i = 0; i < s->uart.nr; i++) {
+ cancel_work_sync(&s->p[i].tx_work);
+ uart_remove_one_port(&s->uart, &s->p[i].port);
+ s->devtype->power(&s->p[i].port, 0);
+ }
uart_unregister_driver(&s->uart);
#ifdef CONFIG_GPIOLIB
- if (s->pdata->gpio_base) {
+ if (s->gpio_used)
ret = gpiochip_remove(&s->gpio);
- if (ret)
- dev_err(dev, "Failed to remove gpio chip: %d\n", ret);
- }
#endif
- dev_set_drvdata(dev, NULL);
-
- if (s->pdata->suspend)
- s->pdata->suspend(1);
if (s->pdata->exit)
s->pdata->exit();
return ret;
}
+#ifdef CONFIG_SPI_MASTER
+static int max310x_spi_probe(struct spi_device *spi)
+{
+ struct max310x_devtype *devtype =
+ (struct max310x_devtype *)spi_get_device_id(spi)->driver_data;
+ int ret;
+
+ /* Setup SPI bus */
+ spi->bits_per_word = 8;
+ spi->mode = spi->mode ? : SPI_MODE_0;
+ spi->max_speed_hz = spi->max_speed_hz ? : 26000000;
+ ret = spi_setup(spi);
+ if (ret) {
+ dev_err(&spi->dev, "SPI setup failed\n");
+ return ret;
+ }
+
+ return max310x_probe(&spi->dev, 1, devtype, spi->irq);
+}
+
+static int max310x_spi_remove(struct spi_device *spi)
+{
+ return max310x_remove(&spi->dev);
+}
+
+static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume);
+
static const struct spi_device_id max310x_id_table[] = {
- { "max3107", MAX310X_TYPE_MAX3107 },
- { "max3108", MAX310X_TYPE_MAX3108 },
+ { "max3107", (kernel_ulong_t)&max3107_devtype, },
+ { "max3108", (kernel_ulong_t)&max3108_devtype, },
+ { "max3109", (kernel_ulong_t)&max3109_devtype, },
+ { "max14830", (kernel_ulong_t)&max14830_devtype, },
{ }
};
MODULE_DEVICE_TABLE(spi, max310x_id_table);
-static struct spi_driver max310x_driver = {
+static struct spi_driver max310x_uart_driver = {
.driver = {
- .name = "max310x",
+ .name = MAX310X_NAME,
.owner = THIS_MODULE,
- .pm = MAX310X_PM_OPS,
+ .pm = &max310x_pm_ops,
},
- .probe = max310x_probe,
- .remove = max310x_remove,
+ .probe = max310x_spi_probe,
+ .remove = max310x_spi_remove,
.id_table = max310x_id_table,
};
-module_spi_driver(max310x_driver);
+module_spi_driver(max310x_uart_driver);
+#endif
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
MODULE_DESCRIPTION("MAX310X serial driver");
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 2c6cfb3cf032..252d514b47fb 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -45,16 +45,19 @@ struct msm_port {
struct clk *clk;
struct clk *pclk;
unsigned int imr;
- unsigned int *gsbi_base;
+ void __iomem *gsbi_base;
int is_uartdm;
unsigned int old_snap_state;
};
-static inline void wait_for_xmitr(struct uart_port *port, int bits)
+static inline void wait_for_xmitr(struct uart_port *port)
{
- if (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY))
- while ((msm_read(port, UART_ISR) & bits) != bits)
- cpu_relax();
+ while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) {
+ if (msm_read(port, UART_ISR) & UART_ISR_TX_READY)
+ break;
+ udelay(1);
+ }
+ msm_write(port, UART_CR_CMD_RESET_TX_READY, UART_CR);
}
static void msm_stop_tx(struct uart_port *port)
@@ -192,49 +195,63 @@ static void handle_rx(struct uart_port *port)
tty_flip_buffer_push(tport);
}
-static void reset_dm_count(struct uart_port *port)
+static void reset_dm_count(struct uart_port *port, int count)
{
- wait_for_xmitr(port, UART_ISR_TX_READY);
- msm_write(port, 1, UARTDM_NCF_TX);
+ wait_for_xmitr(port);
+ msm_write(port, count, UARTDM_NCF_TX);
+ msm_read(port, UARTDM_NCF_TX);
}
static void handle_tx(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
struct msm_port *msm_port = UART_TO_MSM(port);
- int sent_tx;
+ unsigned int tx_count, num_chars;
+ unsigned int tf_pointer = 0;
+
+ tx_count = uart_circ_chars_pending(xmit);
+ tx_count = min3(tx_count, (unsigned int)UART_XMIT_SIZE - xmit->tail,
+ port->fifosize);
if (port->x_char) {
if (msm_port->is_uartdm)
- reset_dm_count(port);
+ reset_dm_count(port, tx_count + 1);
msm_write(port, port->x_char,
msm_port->is_uartdm ? UARTDM_TF : UART_TF);
port->icount.tx++;
port->x_char = 0;
+ } else if (tx_count && msm_port->is_uartdm) {
+ reset_dm_count(port, tx_count);
}
- if (msm_port->is_uartdm)
- reset_dm_count(port);
+ while (tf_pointer < tx_count) {
+ int i;
+ char buf[4] = { 0 };
+ unsigned int *bf = (unsigned int *)&buf;
- while (msm_read(port, UART_SR) & UART_SR_TX_READY) {
- if (uart_circ_empty(xmit)) {
- /* disable tx interrupts */
- msm_port->imr &= ~UART_IMR_TXLEV;
- msm_write(port, msm_port->imr, UART_IMR);
+ if (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
break;
- }
- msm_write(port, xmit->buf[xmit->tail],
- msm_port->is_uartdm ? UARTDM_TF : UART_TF);
if (msm_port->is_uartdm)
- reset_dm_count(port);
+ num_chars = min(tx_count - tf_pointer, sizeof(buf));
+ else
+ num_chars = 1;
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- sent_tx = 1;
+ for (i = 0; i < num_chars; i++) {
+ buf[i] = xmit->buf[xmit->tail + i];
+ port->icount.tx++;
+ }
+
+ msm_write(port, *bf, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+ xmit->tail = (xmit->tail + num_chars) & (UART_XMIT_SIZE - 1);
+ tf_pointer += num_chars;
}
+ /* disable tx interrupts if nothing more to send */
+ if (uart_circ_empty(xmit))
+ msm_stop_tx(port);
+
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
}
@@ -295,7 +312,7 @@ static void msm_reset(struct uart_port *port)
msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
}
-void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
+static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned int mr;
mr = msm_read(port, UART_MR1);
@@ -318,70 +335,60 @@ static void msm_break_ctl(struct uart_port *port, int break_ctl)
msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR);
}
+struct msm_baud_map {
+ u16 divisor;
+ u8 code;
+ u8 rxstale;
+};
+
+static const struct msm_baud_map *
+msm_find_best_baud(struct uart_port *port, unsigned int baud)
+{
+ unsigned int i, divisor;
+ const struct msm_baud_map *entry;
+ static const struct msm_baud_map table[] = {
+ { 1536, 0x00, 1 },
+ { 768, 0x11, 1 },
+ { 384, 0x22, 1 },
+ { 192, 0x33, 1 },
+ { 96, 0x44, 1 },
+ { 48, 0x55, 1 },
+ { 32, 0x66, 1 },
+ { 24, 0x77, 1 },
+ { 16, 0x88, 1 },
+ { 12, 0x99, 6 },
+ { 8, 0xaa, 6 },
+ { 6, 0xbb, 6 },
+ { 4, 0xcc, 6 },
+ { 3, 0xdd, 8 },
+ { 2, 0xee, 16 },
+ { 1, 0xff, 31 },
+ };
+
+ divisor = uart_get_divisor(port, baud);
+
+ for (i = 0, entry = table; i < ARRAY_SIZE(table); i++, entry++)
+ if (entry->divisor <= divisor)
+ break;
+
+ return entry; /* Default to smallest divider */
+}
+
static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
{
- unsigned int baud_code, rxstale, watermark;
+ unsigned int rxstale, watermark;
struct msm_port *msm_port = UART_TO_MSM(port);
+ const struct msm_baud_map *entry;
- switch (baud) {
- case 300:
- baud_code = UART_CSR_300;
- rxstale = 1;
- break;
- case 600:
- baud_code = UART_CSR_600;
- rxstale = 1;
- break;
- case 1200:
- baud_code = UART_CSR_1200;
- rxstale = 1;
- break;
- case 2400:
- baud_code = UART_CSR_2400;
- rxstale = 1;
- break;
- case 4800:
- baud_code = UART_CSR_4800;
- rxstale = 1;
- break;
- case 9600:
- baud_code = UART_CSR_9600;
- rxstale = 2;
- break;
- case 14400:
- baud_code = UART_CSR_14400;
- rxstale = 3;
- break;
- case 19200:
- baud_code = UART_CSR_19200;
- rxstale = 4;
- break;
- case 28800:
- baud_code = UART_CSR_28800;
- rxstale = 6;
- break;
- case 38400:
- baud_code = UART_CSR_38400;
- rxstale = 8;
- break;
- case 57600:
- baud_code = UART_CSR_57600;
- rxstale = 16;
- break;
- case 115200:
- default:
- baud_code = UART_CSR_115200;
- baud = 115200;
- rxstale = 31;
- break;
- }
+ entry = msm_find_best_baud(port, baud);
if (msm_port->is_uartdm)
msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
- msm_write(port, baud_code, UART_CSR);
+ msm_write(port, entry->code, UART_CSR);
/* RX stale watermark */
+ rxstale = entry->rxstale;
watermark = UART_IPR_STALE_LSB & rxstale;
watermark |= UART_IPR_RXSTALE_LAST;
watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2);
@@ -589,12 +596,10 @@ static void msm_release_port(struct uart_port *port)
port->membase = NULL;
if (msm_port->gsbi_base) {
- iowrite32(GSBI_PROTOCOL_IDLE, msm_port->gsbi_base +
- GSBI_CONTROL);
-
- gsbi_resource = platform_get_resource(pdev,
- IORESOURCE_MEM, 1);
+ writel_relaxed(GSBI_PROTOCOL_IDLE,
+ msm_port->gsbi_base + GSBI_CONTROL);
+ gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (unlikely(!gsbi_resource))
return;
@@ -637,7 +642,7 @@ static int msm_request_port(struct uart_port *port)
if (!request_mem_region(gsbi_resource->start, size,
"msm_serial")) {
ret = -EBUSY;
- goto fail_release_port;
+ goto fail_release_port_membase;
}
msm_port->gsbi_base = ioremap(gsbi_resource->start, size);
@@ -651,6 +656,8 @@ static int msm_request_port(struct uart_port *port)
fail_release_gsbi:
release_mem_region(gsbi_resource->start, size);
+fail_release_port_membase:
+ iounmap(port->membase);
fail_release_port:
release_mem_region(port->mapbase, size);
return ret;
@@ -666,10 +673,9 @@ static void msm_config_port(struct uart_port *port, int flags)
if (ret)
return;
}
-
if (msm_port->is_uartdm)
- iowrite32(GSBI_PROTOCOL_UART, msm_port->gsbi_base +
- GSBI_CONTROL);
+ writel_relaxed(GSBI_PROTOCOL_UART,
+ msm_port->gsbi_base + GSBI_CONTROL);
}
static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
@@ -766,7 +772,7 @@ static void msm_console_putchar(struct uart_port *port, int c)
struct msm_port *msm_port = UART_TO_MSM(port);
if (msm_port->is_uartdm)
- reset_dm_count(port);
+ reset_dm_count(port, 1);
while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
;
diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h
index e4acef5de77e..469fda50ac63 100644
--- a/drivers/tty/serial/msm_serial.h
+++ b/drivers/tty/serial/msm_serial.h
@@ -38,19 +38,7 @@
#define UART_MR2_PARITY_MODE_SPACE 0x3
#define UART_MR2_PARITY_MODE 0x3
-#define UART_CSR 0x0008
-#define UART_CSR_115200 0xFF
-#define UART_CSR_57600 0xEE
-#define UART_CSR_38400 0xDD
-#define UART_CSR_28800 0xCC
-#define UART_CSR_19200 0xBB
-#define UART_CSR_14400 0xAA
-#define UART_CSR_9600 0x99
-#define UART_CSR_4800 0x77
-#define UART_CSR_2400 0x55
-#define UART_CSR_1200 0x44
-#define UART_CSR_600 0x33
-#define UART_CSR_300 0x22
+#define UART_CSR 0x0008
#define UART_TF 0x000C
#define UARTDM_TF 0x0070
@@ -71,6 +59,7 @@
#define UART_CR_CMD_RESET_RFR (14 << 4)
#define UART_CR_CMD_PROTECTION_EN (16 << 4)
#define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4)
+#define UART_CR_CMD_RESET_TX_READY (3 << 8)
#define UART_CR_TX_DISABLE (1 << 3)
#define UART_CR_TX_ENABLE (1 << 2)
#define UART_CR_RX_DISABLE (1 << 1)
@@ -151,6 +140,7 @@ static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port)
msm_write(port, 0xF1, UART_NREG);
msm_write(port, 0x0F, UART_DREG);
msm_write(port, 0x1A, UART_MNDREG);
+ port->uartclk = 1843200;
}
/*
@@ -162,6 +152,7 @@ static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port)
msm_write(port, 0xF6, UART_NREG);
msm_write(port, 0x0F, UART_DREG);
msm_write(port, 0x0A, UART_MNDREG);
+ port->uartclk = 1843200;
}
static inline
@@ -169,7 +160,7 @@ void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port)
{
if (port->uartclk == 19200000)
msm_serial_set_mnd_regs_tcxo(port);
- else
+ else if (port->uartclk == 4800000)
msm_serial_set_mnd_regs_tcxoby4(port);
}
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 4f5f161896a1..a63a20e0d69d 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -32,7 +32,6 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/of_device.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
@@ -1015,7 +1014,6 @@ static int mxs_auart_probe(struct platform_device *pdev)
u32 version;
int ret = 0;
struct resource *r;
- struct pinctrl *pinctrl;
s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL);
if (!s) {
@@ -1029,12 +1027,6 @@ static int mxs_auart_probe(struct platform_device *pdev)
else if (ret < 0)
goto out_free;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- goto out_free;
- }
-
if (of_id) {
pdev->id_entry = of_id->data;
s->devtype = pdev->id_entry->driver_data;
diff --git a/drivers/tty/serial/netx-serial.c b/drivers/tty/serial/netx-serial.c
index b9a40ed70be2..ce04f3f8c547 100644
--- a/drivers/tty/serial/netx-serial.c
+++ b/drivers/tty/serial/netx-serial.c
@@ -693,8 +693,6 @@ static int serial_netx_remove(struct platform_device *pdev)
{
struct netx_port *sport = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
if (sport)
uart_remove_one_port(&netx_reg, &sport->port);
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index b6d172873076..c77bf0cf2684 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -40,7 +40,6 @@
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/gpio.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/serial-omap.h>
#define OMAP_MAX_HSUART_PORTS 6
@@ -52,6 +51,11 @@
#define OMAP_UART_REV_52 0x0502
#define OMAP_UART_REV_63 0x0603
+#define OMAP_UART_TX_WAKEUP_EN BIT(7)
+
+/* Feature flags */
+#define OMAP_UART_WER_HAS_TX_WAKEUP BIT(0)
+
#define UART_ERRATA_i202_MDR1_ACCESS BIT(0)
#define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1)
@@ -137,6 +141,7 @@ struct uart_omap_port {
unsigned char dlh;
unsigned char mdr1;
unsigned char scr;
+ unsigned char wer;
int use_dma;
/*
@@ -151,6 +156,7 @@ struct uart_omap_port {
int context_loss_cnt;
u32 errata;
u8 wakeups_enabled;
+ u32 features;
int DTR_gpio;
int DTR_inverted;
@@ -160,7 +166,6 @@ struct uart_omap_port {
u32 latency;
u32 calc_latency;
struct work_struct qos_work;
- struct pinctrl *pins;
bool is_suspending;
};
@@ -310,7 +315,8 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr)
serial_omap_stop_tx(&up->port);
return;
}
- count = up->port.fifosize / 4;
+ count = up->port.fifosize -
+ (serial_in(up, UART_OMAP_TXFIFO_LVL) & 0xFF);
do {
serial_out(up, UART_TX, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
@@ -479,7 +485,6 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id)
struct uart_omap_port *up = dev_id;
unsigned int iir, lsr;
unsigned int type;
- irqreturn_t ret = IRQ_NONE;
int max_count = 256;
spin_lock(&up->port.lock);
@@ -490,7 +495,6 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id)
if (iir & UART_IIR_NO_INT)
break;
- ret = IRQ_HANDLED;
lsr = serial_in(up, UART_LSR);
/* extract IRQ type from IIR register */
@@ -529,7 +533,7 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id)
pm_runtime_put_autosuspend(up->dev);
up->port_activity = jiffies;
- return ret;
+ return IRQ_HANDLED;
}
static unsigned int serial_omap_tx_empty(struct uart_port *port)
@@ -683,7 +687,11 @@ static int serial_omap_startup(struct uart_port *port)
serial_out(up, UART_IER, up->ier);
/* Enable module level wake up */
- serial_out(up, UART_OMAP_WER, OMAP_UART_WER_MOD_WKUP);
+ up->wer = OMAP_UART_WER_MOD_WKUP;
+ if (up->features & OMAP_UART_WER_HAS_TX_WAKEUP)
+ up->wer |= OMAP_UART_TX_WAKEUP_EN;
+
+ serial_out(up, UART_OMAP_WER, up->wer);
pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev);
@@ -1334,7 +1342,7 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up)
u32 mvr, scheme;
u16 revision, major, minor;
- mvr = serial_in(up, UART_OMAP_MVER);
+ mvr = readl(up->port.membase + (UART_OMAP_MVER << up->port.regshift));
/* Check revision register scheme */
scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT;
@@ -1373,9 +1381,11 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up)
case OMAP_UART_REV_52:
up->errata |= (UART_ERRATA_i202_MDR1_ACCESS |
UART_ERRATA_i291_DMA_FORCEIDLE);
+ up->features |= OMAP_UART_WER_HAS_TX_WAKEUP;
break;
case OMAP_UART_REV_63:
up->errata |= UART_ERRATA_i202_MDR1_ACCESS;
+ up->features |= OMAP_UART_WER_HAS_TX_WAKEUP;
break;
default:
break;
@@ -1402,8 +1412,10 @@ static int serial_omap_probe(struct platform_device *pdev)
struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
int ret;
- if (pdev->dev.of_node)
+ if (pdev->dev.of_node) {
omap_up_info = of_get_uart_port_info(&pdev->dev);
+ pdev->dev.platform_data = omap_up_info;
+ }
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
@@ -1468,13 +1480,6 @@ static int serial_omap_probe(struct platform_device *pdev)
goto err_port_line;
}
- up->pins = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(up->pins)) {
- dev_warn(&pdev->dev, "did not get pins for uart%i error: %li\n",
- up->port.line, PTR_ERR(up->pins));
- up->pins = NULL;
- }
-
sprintf(up->name, "OMAP UART%d", up->port.line);
up->port.mapbase = mem->start;
up->port.membase = devm_ioremap(&pdev->dev, mem->start,
@@ -1501,7 +1506,6 @@ static int serial_omap_probe(struct platform_device *pdev)
INIT_WORK(&up->qos_work, serial_omap_uart_qos_work);
platform_set_drvdata(pdev, up);
- pm_runtime_enable(&pdev->dev);
if (omap_up_info->autosuspend_timeout == 0)
omap_up_info->autosuspend_timeout = -1;
device_init_wakeup(up->dev, true);
@@ -1510,6 +1514,8 @@ static int serial_omap_probe(struct platform_device *pdev)
omap_up_info->autosuspend_timeout);
pm_runtime_irq_safe(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
pm_runtime_get_sync(&pdev->dev);
omap_serial_fill_features_erratas(up);
@@ -1609,6 +1615,7 @@ static void serial_omap_restore_context(struct uart_omap_port *up)
serial_omap_mdr1_errataset(up, up->mdr1);
else
serial_out(up, UART_OMAP_MDR1, up->mdr1);
+ serial_out(up, UART_OMAP_WER, up->wer);
}
static int serial_omap_runtime_suspend(struct device *dev)
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 572d48189de9..271cc733573c 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -373,35 +373,62 @@ static const struct file_operations port_regs_ops = {
};
#endif /* CONFIG_DEBUG_FS */
+static struct dmi_system_id __initdata pch_uart_dmi_table[] = {
+ {
+ .ident = "CM-iTC",
+ {
+ DMI_MATCH(DMI_BOARD_NAME, "CM-iTC"),
+ },
+ (void *)CMITC_UARTCLK,
+ },
+ {
+ .ident = "FRI2",
+ {
+ DMI_MATCH(DMI_BIOS_VERSION, "FRI2"),
+ },
+ (void *)FRI2_64_UARTCLK,
+ },
+ {
+ .ident = "Fish River Island II",
+ {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Fish River Island II"),
+ },
+ (void *)FRI2_48_UARTCLK,
+ },
+ {
+ .ident = "COMe-mTT",
+ {
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-mTT"),
+ },
+ (void *)NTC1_UARTCLK,
+ },
+ {
+ .ident = "nanoETXexpress-TT",
+ {
+ DMI_MATCH(DMI_BOARD_NAME, "nanoETXexpress-TT"),
+ },
+ (void *)NTC1_UARTCLK,
+ },
+ {
+ .ident = "MinnowBoard",
+ {
+ DMI_MATCH(DMI_BOARD_NAME, "MinnowBoard"),
+ },
+ (void *)MINNOW_UARTCLK,
+ },
+};
+
/* Return UART clock, checking for board specific clocks. */
static int pch_uart_get_uartclk(void)
{
- const char *cmp;
+ const struct dmi_system_id *d;
if (user_uartclk)
return user_uartclk;
- cmp = dmi_get_system_info(DMI_BOARD_NAME);
- if (cmp && strstr(cmp, "CM-iTC"))
- return CMITC_UARTCLK;
-
- cmp = dmi_get_system_info(DMI_BIOS_VERSION);
- if (cmp && strnstr(cmp, "FRI2", 4))
- return FRI2_64_UARTCLK;
-
- cmp = dmi_get_system_info(DMI_PRODUCT_NAME);
- if (cmp && strstr(cmp, "Fish River Island II"))
- return FRI2_48_UARTCLK;
-
- /* Kontron COMe-mTT10 (nanoETXexpress-TT) */
- cmp = dmi_get_system_info(DMI_BOARD_NAME);
- if (cmp && (strstr(cmp, "COMe-mTT") ||
- strstr(cmp, "nanoETXexpress-TT")))
- return NTC1_UARTCLK;
-
- cmp = dmi_get_system_info(DMI_BOARD_NAME);
- if (cmp && strstr(cmp, "MinnowBoard"))
- return MINNOW_UARTCLK;
+ d = dmi_first_match(pch_uart_dmi_table);
+ if (d)
+ return (int)d->driver_data;
return DEFAULT_UARTCLK;
}
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index b1785f58b6e3..f87f1a0c8c6e 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -1798,7 +1798,6 @@ static int __exit pmz_detach(struct platform_device *pdev)
uart_remove_one_port(&pmz_uart_reg, &uap->port);
- platform_set_drvdata(pdev, NULL);
uap->port.dev = NULL;
return 0;
diff --git a/drivers/tty/serial/pnx8xxx_uart.c b/drivers/tty/serial/pnx8xxx_uart.c
index 7e277a5384a7..b6b7aca5707a 100644
--- a/drivers/tty/serial/pnx8xxx_uart.c
+++ b/drivers/tty/serial/pnx8xxx_uart.c
@@ -801,8 +801,6 @@ static int pnx8xxx_serial_remove(struct platform_device *pdev)
{
struct pnx8xxx_port *sport = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
if (sport)
uart_remove_one_port(&pnx8xxx_reg, &sport->port);
diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c
index 05f504e0c271..ac8b2f5b2396 100644
--- a/drivers/tty/serial/pxa.c
+++ b/drivers/tty/serial/pxa.c
@@ -945,8 +945,6 @@ static int serial_pxa_remove(struct platform_device *dev)
{
struct uart_pxa_port *sport = platform_get_drvdata(dev);
- platform_set_drvdata(dev, NULL);
-
uart_remove_one_port(&serial_pxa_reg, &sport->port);
clk_unprepare(sport->clk);
diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c
index af6b3e3ad24d..fc23ea19073a 100644
--- a/drivers/tty/serial/sa1100.c
+++ b/drivers/tty/serial/sa1100.c
@@ -864,8 +864,6 @@ static int sa1100_serial_remove(struct platform_device *pdev)
{
struct sa1100_port *sport = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
if (sport)
uart_remove_one_port(&sa1100_reg, &sport->port);
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index c77304155410..98555179fe10 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -997,8 +997,6 @@ static int sccnxp_probe(struct platform_device *pdev)
}
err_out:
- platform_set_drvdata(pdev, NULL);
-
return ret;
}
@@ -1016,7 +1014,6 @@ static int sccnxp_remove(struct platform_device *pdev)
uart_remove_one_port(&s->uart, &s->port[i]);
uart_unregister_driver(&s->uart);
- platform_set_drvdata(pdev, NULL);
if (!IS_ERR(s->regulator))
return regulator_disable(s->regulator);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 28cdd2829139..0f02351c9239 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2095,12 +2095,12 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port)
break;
}
- printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n",
+ printk(KERN_INFO "%s%s%s%d at %s (irq = %d, base_baud = %d) is a %s\n",
port->dev ? dev_name(port->dev) : "",
port->dev ? ": " : "",
drv->dev_name,
drv->tty_driver->name_base + port->line,
- address, port->irq, uart_type(port));
+ address, port->irq, port->uartclk / 16, uart_type(port));
}
static void
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index 1fd564b8194b..67a0d1b8341c 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -717,7 +717,6 @@ port_err:
clk_disable_unprepare(sirfport->clk);
clk_put(sirfport->clk);
clk_err:
- platform_set_drvdata(pdev, NULL);
if (sirfport->hw_flow_ctrl)
pinctrl_put(sirfport->p);
err:
@@ -728,7 +727,7 @@ static int sirfsoc_uart_remove(struct platform_device *pdev)
{
struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
struct uart_port *port = &sirfport->port;
- platform_set_drvdata(pdev, NULL);
+
if (sirfport->hw_flow_ctrl)
pinctrl_put(sirfport->p);
clk_disable_unprepare(sirfport->clk);
diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
new file mode 100644
index 000000000000..183879825695
--- /dev/null
+++ b/drivers/tty/serial/st-asc.c
@@ -0,0 +1,937 @@
+/*
+ * st-asc.c: ST Asynchronous serial controller (ASC) driver
+ *
+ * Copyright (C) 2003-2013 STMicroelectronics (R&D) Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#if defined(CONFIG_SERIAL_ST_ASC_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/io.h>
+#include <linux/irq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/serial_core.h>
+#include <linux/clk.h>
+
+#define DRIVER_NAME "st-asc"
+#define ASC_SERIAL_NAME "ttyAS"
+#define ASC_FIFO_SIZE 16
+#define ASC_MAX_PORTS 8
+
+struct asc_port {
+ struct uart_port port;
+ struct clk *clk;
+ unsigned int hw_flow_control:1;
+ unsigned int force_m1:1;
+};
+
+static struct asc_port asc_ports[ASC_MAX_PORTS];
+static struct uart_driver asc_uart_driver;
+
+/*---- UART Register definitions ------------------------------*/
+
+/* Register offsets */
+
+#define ASC_BAUDRATE 0x00
+#define ASC_TXBUF 0x04
+#define ASC_RXBUF 0x08
+#define ASC_CTL 0x0C
+#define ASC_INTEN 0x10
+#define ASC_STA 0x14
+#define ASC_GUARDTIME 0x18
+#define ASC_TIMEOUT 0x1C
+#define ASC_TXRESET 0x20
+#define ASC_RXRESET 0x24
+#define ASC_RETRIES 0x28
+
+/* ASC_RXBUF */
+#define ASC_RXBUF_PE 0x100
+#define ASC_RXBUF_FE 0x200
+/**
+ * Some of status comes from higher bits of the character and some come from
+ * the status register. Combining both of them in to single status using dummy
+ * bits.
+ */
+#define ASC_RXBUF_DUMMY_RX 0x10000
+#define ASC_RXBUF_DUMMY_BE 0x20000
+#define ASC_RXBUF_DUMMY_OE 0x40000
+
+/* ASC_CTL */
+
+#define ASC_CTL_MODE_MSK 0x0007
+#define ASC_CTL_MODE_8BIT 0x0001
+#define ASC_CTL_MODE_7BIT_PAR 0x0003
+#define ASC_CTL_MODE_9BIT 0x0004
+#define ASC_CTL_MODE_8BIT_WKUP 0x0005
+#define ASC_CTL_MODE_8BIT_PAR 0x0007
+#define ASC_CTL_STOP_MSK 0x0018
+#define ASC_CTL_STOP_HALFBIT 0x0000
+#define ASC_CTL_STOP_1BIT 0x0008
+#define ASC_CTL_STOP_1_HALFBIT 0x0010
+#define ASC_CTL_STOP_2BIT 0x0018
+#define ASC_CTL_PARITYODD 0x0020
+#define ASC_CTL_LOOPBACK 0x0040
+#define ASC_CTL_RUN 0x0080
+#define ASC_CTL_RXENABLE 0x0100
+#define ASC_CTL_SCENABLE 0x0200
+#define ASC_CTL_FIFOENABLE 0x0400
+#define ASC_CTL_CTSENABLE 0x0800
+#define ASC_CTL_BAUDMODE 0x1000
+
+/* ASC_GUARDTIME */
+
+#define ASC_GUARDTIME_MSK 0x00FF
+
+/* ASC_INTEN */
+
+#define ASC_INTEN_RBE 0x0001
+#define ASC_INTEN_TE 0x0002
+#define ASC_INTEN_THE 0x0004
+#define ASC_INTEN_PE 0x0008
+#define ASC_INTEN_FE 0x0010
+#define ASC_INTEN_OE 0x0020
+#define ASC_INTEN_TNE 0x0040
+#define ASC_INTEN_TOI 0x0080
+#define ASC_INTEN_RHF 0x0100
+
+/* ASC_RETRIES */
+
+#define ASC_RETRIES_MSK 0x00FF
+
+/* ASC_RXBUF */
+
+#define ASC_RXBUF_MSK 0x03FF
+
+/* ASC_STA */
+
+#define ASC_STA_RBF 0x0001
+#define ASC_STA_TE 0x0002
+#define ASC_STA_THE 0x0004
+#define ASC_STA_PE 0x0008
+#define ASC_STA_FE 0x0010
+#define ASC_STA_OE 0x0020
+#define ASC_STA_TNE 0x0040
+#define ASC_STA_TOI 0x0080
+#define ASC_STA_RHF 0x0100
+#define ASC_STA_TF 0x0200
+#define ASC_STA_NKD 0x0400
+
+/* ASC_TIMEOUT */
+
+#define ASC_TIMEOUT_MSK 0x00FF
+
+/* ASC_TXBUF */
+
+#define ASC_TXBUF_MSK 0x01FF
+
+/*---- Inline function definitions ---------------------------*/
+
+static inline struct asc_port *to_asc_port(struct uart_port *port)
+{
+ return container_of(port, struct asc_port, port);
+}
+
+static inline u32 asc_in(struct uart_port *port, u32 offset)
+{
+ return readl(port->membase + offset);
+}
+
+static inline void asc_out(struct uart_port *port, u32 offset, u32 value)
+{
+ writel(value, port->membase + offset);
+}
+
+/*
+ * Some simple utility functions to enable and disable interrupts.
+ * Note that these need to be called with interrupts disabled.
+ */
+static inline void asc_disable_tx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_THE;
+ asc_out(port, ASC_INTEN, intenable);
+ (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */
+}
+
+static inline void asc_enable_tx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_THE;
+ asc_out(port, ASC_INTEN, intenable);
+}
+
+static inline void asc_disable_rx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_RBE;
+ asc_out(port, ASC_INTEN, intenable);
+ (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */
+}
+
+static inline void asc_enable_rx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_RBE;
+ asc_out(port, ASC_INTEN, intenable);
+}
+
+static inline u32 asc_txfifo_is_empty(struct uart_port *port)
+{
+ return asc_in(port, ASC_STA) & ASC_STA_TE;
+}
+
+static inline int asc_txfifo_is_full(struct uart_port *port)
+{
+ return asc_in(port, ASC_STA) & ASC_STA_TF;
+}
+
+static inline const char *asc_port_name(struct uart_port *port)
+{
+ return to_platform_device(port->dev)->name;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * This section contains code to support the use of the ASC as a
+ * generic serial port.
+ */
+
+static inline unsigned asc_hw_txroom(struct uart_port *port)
+{
+ u32 status = asc_in(port, ASC_STA);
+
+ if (status & ASC_STA_THE)
+ return port->fifosize / 2;
+ else if (!(status & ASC_STA_TF))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Start transmitting chars.
+ * This is called from both interrupt and task level.
+ * Either way interrupts are disabled.
+ */
+static void asc_transmit_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ int txroom;
+ unsigned char c;
+
+ txroom = asc_hw_txroom(port);
+
+ if ((txroom != 0) && port->x_char) {
+ c = port->x_char;
+ port->x_char = 0;
+ asc_out(port, ASC_TXBUF, c);
+ port->icount.tx++;
+ txroom = asc_hw_txroom(port);
+ }
+
+ if (uart_tx_stopped(port)) {
+ /*
+ * We should try and stop the hardware here, but I
+ * don't think the ASC has any way to do that.
+ */
+ asc_disable_tx_interrupts(port);
+ return;
+ }
+
+ if (uart_circ_empty(xmit)) {
+ asc_disable_tx_interrupts(port);
+ return;
+ }
+
+ if (txroom == 0)
+ return;
+
+ do {
+ c = xmit->buf[xmit->tail];
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ asc_out(port, ASC_TXBUF, c);
+ port->icount.tx++;
+ txroom--;
+ } while ((txroom > 0) && (!uart_circ_empty(xmit)));
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ asc_disable_tx_interrupts(port);
+}
+
+static void asc_receive_chars(struct uart_port *port)
+{
+ struct tty_port *tport = &port->state->port;
+ unsigned long status;
+ unsigned long c = 0;
+ char flag;
+
+ if (port->irq_wake)
+ pm_wakeup_event(tport->tty->dev, 0);
+
+ while ((status = asc_in(port, ASC_STA)) & ASC_STA_RBF) {
+ c = asc_in(port, ASC_RXBUF) | ASC_RXBUF_DUMMY_RX;
+ flag = TTY_NORMAL;
+ port->icount.rx++;
+
+ if ((c & (ASC_RXBUF_FE | ASC_RXBUF_PE)) ||
+ status & ASC_STA_OE) {
+
+ if (c & ASC_RXBUF_FE) {
+ if (c == ASC_RXBUF_FE) {
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ continue;
+ c |= ASC_RXBUF_DUMMY_BE;
+ } else {
+ port->icount.frame++;
+ }
+ } else if (c & ASC_RXBUF_PE) {
+ port->icount.parity++;
+ }
+ /*
+ * Reading any data from the RX FIFO clears the
+ * overflow error condition.
+ */
+ if (status & ASC_STA_OE) {
+ port->icount.overrun++;
+ c |= ASC_RXBUF_DUMMY_OE;
+ }
+
+ c &= port->read_status_mask;
+
+ if (c & ASC_RXBUF_DUMMY_BE)
+ flag = TTY_BREAK;
+ else if (c & ASC_RXBUF_PE)
+ flag = TTY_PARITY;
+ else if (c & ASC_RXBUF_FE)
+ flag = TTY_FRAME;
+ }
+
+ if (uart_handle_sysrq_char(port, c))
+ continue;
+
+ uart_insert_char(port, c, ASC_RXBUF_DUMMY_OE, c & 0xff, flag);
+ }
+
+ /* Tell the rest of the system the news. New characters! */
+ tty_flip_buffer_push(tport);
+}
+
+static irqreturn_t asc_interrupt(int irq, void *ptr)
+{
+ struct uart_port *port = ptr;
+ u32 status;
+
+ spin_lock(&port->lock);
+
+ status = asc_in(port, ASC_STA);
+
+ if (status & ASC_STA_RBF) {
+ /* Receive FIFO not empty */
+ asc_receive_chars(port);
+ }
+
+ if ((status & ASC_STA_THE) &&
+ (asc_in(port, ASC_INTEN) & ASC_INTEN_THE)) {
+ /* Transmitter FIFO at least half empty */
+ asc_transmit_chars(port);
+ }
+
+ spin_unlock(&port->lock);
+
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * UART Functions
+ */
+
+static unsigned int asc_tx_empty(struct uart_port *port)
+{
+ return asc_txfifo_is_empty(port) ? TIOCSER_TEMT : 0;
+}
+
+static void asc_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /*
+ * This routine is used for seting signals of: DTR, DCD, CTS/RTS
+ * We use ASC's hardware for CTS/RTS, so don't need any for that.
+ * Some boards have DTR and DCD implemented using PIO pins,
+ * code to do this should be hooked in here.
+ */
+}
+
+static unsigned int asc_get_mctrl(struct uart_port *port)
+{
+ /*
+ * This routine is used for geting signals of: DTR, DCD, DSR, RI,
+ * and CTS/RTS
+ */
+ return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+/* There are probably characters waiting to be transmitted. */
+static void asc_start_tx(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+
+ if (!uart_circ_empty(xmit))
+ asc_enable_tx_interrupts(port);
+}
+
+/* Transmit stop */
+static void asc_stop_tx(struct uart_port *port)
+{
+ asc_disable_tx_interrupts(port);
+}
+
+/* Receive stop */
+static void asc_stop_rx(struct uart_port *port)
+{
+ asc_disable_rx_interrupts(port);
+}
+
+/* Force modem status interrupts on */
+static void asc_enable_ms(struct uart_port *port)
+{
+ /* Nothing here yet .. */
+}
+
+/* Handle breaks - ignored by us */
+static void asc_break_ctl(struct uart_port *port, int break_state)
+{
+ /* Nothing here yet .. */
+}
+
+/*
+ * Enable port for reception.
+ */
+static int asc_startup(struct uart_port *port)
+{
+ if (request_irq(port->irq, asc_interrupt, IRQF_NO_SUSPEND,
+ asc_port_name(port), port)) {
+ dev_err(port->dev, "cannot allocate irq.\n");
+ return -ENODEV;
+ }
+
+ asc_transmit_chars(port);
+ asc_enable_rx_interrupts(port);
+
+ return 0;
+}
+
+static void asc_shutdown(struct uart_port *port)
+{
+ asc_disable_tx_interrupts(port);
+ asc_disable_rx_interrupts(port);
+ free_irq(port->irq, port);
+}
+
+static void asc_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ struct asc_port *ascport = to_asc_port(port);
+ unsigned long flags = 0;
+ u32 ctl;
+
+ switch (state) {
+ case UART_PM_STATE_ON:
+ clk_prepare_enable(ascport->clk);
+ break;
+ case UART_PM_STATE_OFF:
+ /*
+ * Disable the ASC baud rate generator, which is as close as
+ * we can come to turning it off. Note this is not called with
+ * the port spinlock held.
+ */
+ spin_lock_irqsave(&port->lock, flags);
+ ctl = asc_in(port, ASC_CTL) & ~ASC_CTL_RUN;
+ asc_out(port, ASC_CTL, ctl);
+ spin_unlock_irqrestore(&port->lock, flags);
+ clk_disable_unprepare(ascport->clk);
+ break;
+ }
+}
+
+static void asc_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct asc_port *ascport = to_asc_port(port);
+ unsigned int baud;
+ u32 ctrl_val;
+ tcflag_t cflag;
+ unsigned long flags;
+
+ /* Update termios to reflect hardware capabilities */
+ termios->c_cflag &= ~(CMSPAR |
+ (ascport->hw_flow_control ? 0 : CRTSCTS));
+
+ port->uartclk = clk_get_rate(ascport->clk);
+
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+ cflag = termios->c_cflag;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* read control register */
+ ctrl_val = asc_in(port, ASC_CTL);
+
+ /* stop serial port and reset value */
+ asc_out(port, ASC_CTL, (ctrl_val & ~ASC_CTL_RUN));
+ ctrl_val = ASC_CTL_RXENABLE | ASC_CTL_FIFOENABLE;
+
+ /* reset fifo rx & tx */
+ asc_out(port, ASC_TXRESET, 1);
+ asc_out(port, ASC_RXRESET, 1);
+
+ /* set character length */
+ if ((cflag & CSIZE) == CS7) {
+ ctrl_val |= ASC_CTL_MODE_7BIT_PAR;
+ } else {
+ ctrl_val |= (cflag & PARENB) ? ASC_CTL_MODE_8BIT_PAR :
+ ASC_CTL_MODE_8BIT;
+ }
+
+ /* set stop bit */
+ ctrl_val |= (cflag & CSTOPB) ? ASC_CTL_STOP_2BIT : ASC_CTL_STOP_1BIT;
+
+ /* odd parity */
+ if (cflag & PARODD)
+ ctrl_val |= ASC_CTL_PARITYODD;
+
+ /* hardware flow control */
+ if ((cflag & CRTSCTS))
+ ctrl_val |= ASC_CTL_CTSENABLE;
+
+ if ((baud < 19200) && !ascport->force_m1) {
+ asc_out(port, ASC_BAUDRATE, (port->uartclk / (16 * baud)));
+ } else {
+ /*
+ * MODE 1: recommended for high bit rates (above 19.2K)
+ *
+ * baudrate * 16 * 2^16
+ * ASCBaudRate = ------------------------
+ * inputclock
+ *
+ * However to keep the maths inside 32bits we divide top and
+ * bottom by 64. The +1 is to avoid a divide by zero if the
+ * input clock rate is something unexpected.
+ */
+ u32 counter = (baud * 16384) / ((port->uartclk / 64) + 1);
+ asc_out(port, ASC_BAUDRATE, counter);
+ ctrl_val |= ASC_CTL_BAUDMODE;
+ }
+
+ uart_update_timeout(port, cflag, baud);
+
+ ascport->port.read_status_mask = ASC_RXBUF_DUMMY_OE;
+ if (termios->c_iflag & INPCK)
+ ascport->port.read_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ ascport->port.read_status_mask |= ASC_RXBUF_DUMMY_BE;
+
+ /*
+ * Characters to ignore
+ */
+ ascport->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ ascport->port.ignore_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE;
+ if (termios->c_iflag & IGNBRK) {
+ ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_BE;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_OE;
+ }
+
+ /*
+ * Ignore all characters if CREAD is not set.
+ */
+ if (!(termios->c_cflag & CREAD))
+ ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_RX;
+
+ /* Set the timeout */
+ asc_out(port, ASC_TIMEOUT, 20);
+
+ /* write final value and enable port */
+ asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN));
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *asc_type(struct uart_port *port)
+{
+ return (port->type == PORT_ASC) ? DRIVER_NAME : NULL;
+}
+
+static void asc_release_port(struct uart_port *port)
+{
+}
+
+static int asc_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/*
+ * Called when the port is opened, and UPF_BOOT_AUTOCONF flag is set
+ * Set type field if successful
+ */
+static void asc_config_port(struct uart_port *port, int flags)
+{
+ if ((flags & UART_CONFIG_TYPE))
+ port->type = PORT_ASC;
+}
+
+static int
+asc_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ /* No user changeable parameters */
+ return -EINVAL;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context (i.e. kgdb).
+ */
+
+static int asc_get_poll_char(struct uart_port *port)
+{
+ if (!(asc_in(port, ASC_STA) & ASC_STA_RBF))
+ return NO_POLL_CHAR;
+
+ return asc_in(port, ASC_RXBUF);
+}
+
+static void asc_put_poll_char(struct uart_port *port, unsigned char c)
+{
+ while (asc_txfifo_is_full(port))
+ cpu_relax();
+ asc_out(port, ASC_TXBUF, c);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+/*---------------------------------------------------------------------*/
+
+static struct uart_ops asc_uart_ops = {
+ .tx_empty = asc_tx_empty,
+ .set_mctrl = asc_set_mctrl,
+ .get_mctrl = asc_get_mctrl,
+ .start_tx = asc_start_tx,
+ .stop_tx = asc_stop_tx,
+ .stop_rx = asc_stop_rx,
+ .enable_ms = asc_enable_ms,
+ .break_ctl = asc_break_ctl,
+ .startup = asc_startup,
+ .shutdown = asc_shutdown,
+ .set_termios = asc_set_termios,
+ .type = asc_type,
+ .release_port = asc_release_port,
+ .request_port = asc_request_port,
+ .config_port = asc_config_port,
+ .verify_port = asc_verify_port,
+ .pm = asc_pm,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = asc_get_poll_char,
+ .poll_put_char = asc_put_poll_char,
+#endif /* CONFIG_CONSOLE_POLL */
+};
+
+static int asc_init_port(struct asc_port *ascport,
+ struct platform_device *pdev)
+{
+ struct uart_port *port = &ascport->port;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ dev_err(&pdev->dev, "Unable to get io resource\n");
+ return -ENODEV;
+ }
+
+ port->iotype = UPIO_MEM;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->ops = &asc_uart_ops;
+ port->fifosize = ASC_FIFO_SIZE;
+ port->dev = &pdev->dev;
+ port->mapbase = res->start;
+ port->irq = platform_get_irq(pdev, 0);
+
+ port->membase = devm_request_and_ioremap(&pdev->dev, res);
+ if (!port->membase) {
+ dev_err(&pdev->dev, "Unable to request io memory\n");
+ return -ENODEV;
+ }
+
+ spin_lock_init(&port->lock);
+
+ ascport->clk = devm_clk_get(&pdev->dev, NULL);
+
+ if (WARN_ON(IS_ERR(ascport->clk)))
+ return -EINVAL;
+ /* ensure that clk rate is correct by enabling the clk */
+ clk_prepare_enable(ascport->clk);
+ ascport->port.uartclk = clk_get_rate(ascport->clk);
+ WARN_ON(ascport->port.uartclk == 0);
+ clk_disable_unprepare(ascport->clk);
+
+ return 0;
+}
+
+static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int id;
+
+ if (!np)
+ return NULL;
+
+ id = of_alias_get_id(np, ASC_SERIAL_NAME);
+
+ if (id < 0)
+ id = 0;
+
+ if (WARN_ON(id >= ASC_MAX_PORTS))
+ return NULL;
+
+ asc_ports[id].hw_flow_control = of_property_read_bool(np,
+ "st,hw-flow-control");
+ asc_ports[id].force_m1 = of_property_read_bool(np, "st,force_m1");
+ asc_ports[id].port.line = id;
+ return &asc_ports[id];
+}
+
+static struct of_device_id asc_match[] = {
+ { .compatible = "st,asc", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, asc_match);
+
+static int asc_serial_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct asc_port *ascport;
+
+ ascport = asc_of_get_asc_port(pdev);
+ if (!ascport)
+ return -ENODEV;
+
+ ret = asc_init_port(ascport, pdev);
+ if (ret)
+ return ret;
+
+ ret = uart_add_one_port(&asc_uart_driver, &ascport->port);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, &ascport->port);
+
+ return 0;
+}
+
+static int asc_serial_remove(struct platform_device *pdev)
+{
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ return uart_remove_one_port(&asc_uart_driver, port);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int asc_serial_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ return uart_suspend_port(&asc_uart_driver, port);
+}
+
+static int asc_serial_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ return uart_resume_port(&asc_uart_driver, port);
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_SERIAL_ST_ASC_CONSOLE
+static void asc_console_putchar(struct uart_port *port, int ch)
+{
+ unsigned int timeout = 1000000;
+
+ /* Wait for upto 1 second in case flow control is stopping us. */
+ while (--timeout && asc_txfifo_is_full(port))
+ udelay(1);
+
+ asc_out(port, ASC_TXBUF, ch);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ */
+
+static void asc_console_write(struct console *co, const char *s, unsigned count)
+{
+ struct uart_port *port = &asc_ports[co->index].port;
+ unsigned long flags;
+ unsigned long timeout = 1000000;
+ int locked = 1;
+ u32 intenable;
+
+ local_irq_save(flags);
+ if (port->sysrq)
+ locked = 0; /* asc_interrupt has already claimed the lock */
+ else if (oops_in_progress)
+ locked = spin_trylock(&port->lock);
+ else
+ spin_lock(&port->lock);
+
+ /*
+ * Disable interrupts so we don't get the IRQ line bouncing
+ * up and down while interrupts are disabled.
+ */
+ intenable = asc_in(port, ASC_INTEN);
+ asc_out(port, ASC_INTEN, 0);
+ (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */
+
+ uart_console_write(port, s, count, asc_console_putchar);
+
+ while (--timeout && !asc_txfifo_is_empty(port))
+ udelay(1);
+
+ asc_out(port, ASC_INTEN, intenable);
+
+ if (locked)
+ spin_unlock(&port->lock);
+ local_irq_restore(flags);
+}
+
+static int asc_console_setup(struct console *co, char *options)
+{
+ struct asc_port *ascport;
+ int baud = 9600;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index >= ASC_MAX_PORTS)
+ return -ENODEV;
+
+ ascport = &asc_ports[co->index];
+
+ /*
+ * This driver does not support early console initialization
+ * (use ARM early printk support instead), so we only expect
+ * this to be called during the uart port registration when the
+ * driver gets probed and the port should be mapped at that point.
+ */
+ BUG_ON(ascport->port.mapbase == 0 || ascport->port.membase == NULL);
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(&ascport->port, co, baud, parity, bits, flow);
+}
+
+static struct console asc_console = {
+ .name = ASC_SERIAL_NAME,
+ .device = uart_console_device,
+ .write = asc_console_write,
+ .setup = asc_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &asc_uart_driver,
+};
+
+#define ASC_SERIAL_CONSOLE (&asc_console)
+
+#else
+#define ASC_SERIAL_CONSOLE NULL
+#endif /* CONFIG_SERIAL_ST_ASC_CONSOLE */
+
+static struct uart_driver asc_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = ASC_SERIAL_NAME,
+ .major = 0,
+ .minor = 0,
+ .nr = ASC_MAX_PORTS,
+ .cons = ASC_SERIAL_CONSOLE,
+};
+
+static const struct dev_pm_ops asc_serial_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(asc_serial_suspend, asc_serial_resume)
+};
+
+static struct platform_driver asc_serial_driver = {
+ .probe = asc_serial_probe,
+ .remove = asc_serial_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = &asc_serial_pm_ops,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(asc_match),
+ },
+};
+
+static int __init asc_init(void)
+{
+ int ret;
+ static char banner[] __initdata =
+ KERN_INFO "STMicroelectronics ASC driver initialized\n";
+
+ printk(banner);
+
+ ret = uart_register_driver(&asc_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&asc_serial_driver);
+ if (ret)
+ uart_unregister_driver(&asc_uart_driver);
+
+ return ret;
+}
+
+static void __exit asc_exit(void)
+{
+ platform_driver_unregister(&asc_serial_driver);
+ uart_unregister_driver(&asc_uart_driver);
+}
+
+module_init(asc_init);
+module_exit(asc_exit);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("STMicroelectronics (R&D) Limited");
+MODULE_DESCRIPTION("STMicroelectronics ASC serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 48af43de3467..a90bf0440b4f 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -630,7 +630,6 @@ static int vt8500_serial_remove(struct platform_device *pdev)
{
struct vt8500_port *vt8500_port = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
clk_disable_unprepare(vt8500_port->clk);
uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index 9121c1f7aeef..c043136fbe51 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -18,31 +18,118 @@
#include <linux/module.h>
#include <linux/ratelimit.h>
+
+#define MIN_TTYB_SIZE 256
+#define TTYB_ALIGN_MASK 255
+
+/*
+ * Byte threshold to limit memory consumption for flip buffers.
+ * The actual memory limit is > 2x this amount.
+ */
+#define TTYB_MEM_LIMIT 65536
+
+/*
+ * We default to dicing tty buffer allocations to this many characters
+ * in order to avoid multiple page allocations. We know the size of
+ * tty_buffer itself but it must also be taken into account that the
+ * the buffer is 256 byte aligned. See tty_buffer_find for the allocation
+ * logic this must match
+ */
+
+#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF)
+
+
+/**
+ * tty_buffer_lock_exclusive - gain exclusive access to buffer
+ * tty_buffer_unlock_exclusive - release exclusive access
+ *
+ * @port - tty_port owning the flip buffer
+ *
+ * Guarantees safe use of the line discipline's receive_buf() method by
+ * excluding the buffer work and any pending flush from using the flip
+ * buffer. Data can continue to be added concurrently to the flip buffer
+ * from the driver side.
+ *
+ * On release, the buffer work is restarted if there is data in the
+ * flip buffer
+ */
+
+void tty_buffer_lock_exclusive(struct tty_port *port)
+{
+ struct tty_bufhead *buf = &port->buf;
+
+ atomic_inc(&buf->priority);
+ mutex_lock(&buf->lock);
+}
+
+void tty_buffer_unlock_exclusive(struct tty_port *port)
+{
+ struct tty_bufhead *buf = &port->buf;
+ int restart;
+
+ restart = buf->head->commit != buf->head->read;
+
+ atomic_dec(&buf->priority);
+ mutex_unlock(&buf->lock);
+ if (restart)
+ queue_work(system_unbound_wq, &buf->work);
+}
+
+/**
+ * tty_buffer_space_avail - return unused buffer space
+ * @port - tty_port owning the flip buffer
+ *
+ * Returns the # of bytes which can be written by the driver without
+ * reaching the buffer limit.
+ *
+ * Note: this does not guarantee that memory is available to write
+ * the returned # of bytes (use tty_prepare_flip_string_xxx() to
+ * pre-allocate if memory guarantee is required).
+ */
+
+int tty_buffer_space_avail(struct tty_port *port)
+{
+ int space = TTYB_MEM_LIMIT - atomic_read(&port->buf.memory_used);
+ return max(space, 0);
+}
+
+static void tty_buffer_reset(struct tty_buffer *p, size_t size)
+{
+ p->used = 0;
+ p->size = size;
+ p->next = NULL;
+ p->commit = 0;
+ p->read = 0;
+}
+
/**
* tty_buffer_free_all - free buffers used by a tty
* @tty: tty to free from
*
* Remove all the buffers pending on a tty whether queued with data
* or in the free ring. Must be called when the tty is no longer in use
- *
- * Locking: none
*/
void tty_buffer_free_all(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
- struct tty_buffer *thead;
+ struct tty_buffer *p, *next;
+ struct llist_node *llist;
- while ((thead = buf->head) != NULL) {
- buf->head = thead->next;
- kfree(thead);
- }
- while ((thead = buf->free) != NULL) {
- buf->free = thead->next;
- kfree(thead);
+ while ((p = buf->head) != NULL) {
+ buf->head = p->next;
+ if (p->size > 0)
+ kfree(p);
}
- buf->tail = NULL;
- buf->memory_used = 0;
+ llist = llist_del_all(&buf->free);
+ llist_for_each_entry_safe(p, next, llist, free)
+ kfree(p);
+
+ tty_buffer_reset(&buf->sentinel, 0);
+ buf->head = &buf->sentinel;
+ buf->tail = &buf->sentinel;
+
+ atomic_set(&buf->memory_used, 0);
}
/**
@@ -51,29 +138,39 @@ void tty_buffer_free_all(struct tty_port *port)
* @size: desired size (characters)
*
* Allocate a new tty buffer to hold the desired number of characters.
+ * We round our buffers off in 256 character chunks to get better
+ * allocation behaviour.
* Return NULL if out of memory or the allocation would exceed the
* per device queue
- *
- * Locking: Caller must hold tty->buf.lock
*/
static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size)
{
+ struct llist_node *free;
struct tty_buffer *p;
- if (port->buf.memory_used + size > 65536)
+ /* Round the buffer size out */
+ size = __ALIGN_MASK(size, TTYB_ALIGN_MASK);
+
+ if (size <= MIN_TTYB_SIZE) {
+ free = llist_del_first(&port->buf.free);
+ if (free) {
+ p = llist_entry(free, struct tty_buffer, free);
+ goto found;
+ }
+ }
+
+ /* Should possibly check if this fails for the largest buffer we
+ have queued and recycle that ? */
+ if (atomic_read(&port->buf.memory_used) > TTYB_MEM_LIMIT)
return NULL;
p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
if (p == NULL)
return NULL;
- p->used = 0;
- p->size = size;
- p->next = NULL;
- p->commit = 0;
- p->read = 0;
- p->char_buf_ptr = (char *)(p->data);
- p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
- port->buf.memory_used += size;
+
+found:
+ tty_buffer_reset(p, size);
+ atomic_add(size, &port->buf.memory_used);
return p;
}
@@ -84,8 +181,6 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size)
*
* Free a tty buffer, or add it to the free list according to our
* internal strategy
- *
- * Locking: Caller must hold tty->buf.lock
*/
static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b)
@@ -93,41 +188,12 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b)
struct tty_bufhead *buf = &port->buf;
/* Dumb strategy for now - should keep some stats */
- buf->memory_used -= b->size;
- WARN_ON(buf->memory_used < 0);
+ WARN_ON(atomic_sub_return(b->size, &buf->memory_used) < 0);
- if (b->size >= 512)
+ if (b->size > MIN_TTYB_SIZE)
kfree(b);
- else {
- b->next = buf->free;
- buf->free = b;
- }
-}
-
-/**
- * __tty_buffer_flush - flush full tty buffers
- * @tty: tty to flush
- *
- * flush all the buffers containing receive data. Caller must
- * hold the buffer lock and must have ensured no parallel flush to
- * ldisc is running.
- *
- * Locking: Caller must hold tty->buf.lock
- */
-
-static void __tty_buffer_flush(struct tty_port *port)
-{
- struct tty_bufhead *buf = &port->buf;
- struct tty_buffer *thead;
-
- if (unlikely(buf->head == NULL))
- return;
- while ((thead = buf->head->next) != NULL) {
- tty_buffer_free(port, buf->head);
- buf->head = thead;
- }
- WARN_ON(buf->head != buf->tail);
- buf->head->read = buf->head->commit;
+ else if (b->size > 0)
+ llist_add(&b->free, &buf->free);
}
/**
@@ -138,65 +204,28 @@ static void __tty_buffer_flush(struct tty_port *port)
* being processed by flush_to_ldisc then we defer the processing
* to that function
*
- * Locking: none
+ * Locking: takes buffer lock to ensure single-threaded flip buffer
+ * 'consumer'
*/
void tty_buffer_flush(struct tty_struct *tty)
{
struct tty_port *port = tty->port;
struct tty_bufhead *buf = &port->buf;
- unsigned long flags;
-
- spin_lock_irqsave(&buf->lock, flags);
-
- /* If the data is being pushed to the tty layer then we can't
- process it here. Instead set a flag and the flush_to_ldisc
- path will process the flush request before it exits */
- if (test_bit(TTYP_FLUSHING, &port->iflags)) {
- set_bit(TTYP_FLUSHPENDING, &port->iflags);
- spin_unlock_irqrestore(&buf->lock, flags);
- wait_event(tty->read_wait,
- test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0);
- return;
- } else
- __tty_buffer_flush(port);
- spin_unlock_irqrestore(&buf->lock, flags);
-}
+ struct tty_buffer *next;
-/**
- * tty_buffer_find - find a free tty buffer
- * @tty: tty owning the buffer
- * @size: characters wanted
- *
- * Locate an existing suitable tty buffer or if we are lacking one then
- * allocate a new one. We round our buffers off in 256 character chunks
- * to get better allocation behaviour.
- *
- * Locking: Caller must hold tty->buf.lock
- */
+ atomic_inc(&buf->priority);
-static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size)
-{
- struct tty_buffer **tbh = &port->buf.free;
- while ((*tbh) != NULL) {
- struct tty_buffer *t = *tbh;
- if (t->size >= size) {
- *tbh = t->next;
- t->next = NULL;
- t->used = 0;
- t->commit = 0;
- t->read = 0;
- port->buf.memory_used += t->size;
- return t;
- }
- tbh = &((*tbh)->next);
+ mutex_lock(&buf->lock);
+ while ((next = buf->head->next) != NULL) {
+ tty_buffer_free(port, buf->head);
+ buf->head = next;
}
- /* Round the buffer size out */
- size = (size + 0xFF) & ~0xFF;
- return tty_buffer_alloc(port, size);
- /* Should possibly check if this fails for the largest buffer we
- have queued and recycle that ? */
+ buf->head->read = buf->head->commit;
+ atomic_dec(&buf->priority);
+ mutex_unlock(&buf->lock);
}
+
/**
* tty_buffer_request_room - grow tty buffer if needed
* @tty: tty structure
@@ -204,38 +233,26 @@ static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size)
*
* Make at least size bytes of linear space available for the tty
* buffer. If we fail return the size we managed to find.
- *
- * Locking: Takes port->buf.lock
*/
int tty_buffer_request_room(struct tty_port *port, size_t size)
{
struct tty_bufhead *buf = &port->buf;
struct tty_buffer *b, *n;
int left;
- unsigned long flags;
- spin_lock_irqsave(&buf->lock, flags);
- /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
- remove this conditional if its worth it. This would be invisible
- to the callers */
+
b = buf->tail;
- if (b != NULL)
- left = b->size - b->used;
- else
- left = 0;
+ left = b->size - b->used;
if (left < size) {
/* This is the slow path - looking for new buffers to use */
- if ((n = tty_buffer_find(port, size)) != NULL) {
- if (b != NULL) {
- b->next = n;
- b->commit = b->used;
- } else
- buf->head = n;
+ if ((n = tty_buffer_alloc(port, size)) != NULL) {
buf->tail = n;
+ b->commit = b->used;
+ smp_mb();
+ b->next = n;
} else
size = left;
}
- spin_unlock_irqrestore(&buf->lock, flags);
return size;
}
EXPORT_SYMBOL_GPL(tty_buffer_request_room);
@@ -249,8 +266,6 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room);
*
* Queue a series of bytes to the tty buffering. All the characters
* passed are marked with the supplied flag. Returns the number added.
- *
- * Locking: Called functions may take port->buf.lock
*/
int tty_insert_flip_string_fixed_flag(struct tty_port *port,
@@ -261,12 +276,10 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port,
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(port, goal);
struct tty_buffer *tb = port->buf.tail;
- /* If there is no space then tb may be NULL */
- if (unlikely(space == 0)) {
+ if (unlikely(space == 0))
break;
- }
- memcpy(tb->char_buf_ptr + tb->used, chars, space);
- memset(tb->flag_buf_ptr + tb->used, flag, space);
+ memcpy(char_buf_ptr(tb, tb->used), chars, space);
+ memset(flag_buf_ptr(tb, tb->used), flag, space);
tb->used += space;
copied += space;
chars += space;
@@ -287,8 +300,6 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag);
* Queue a series of bytes to the tty buffering. For each character
* the flags array indicates the status of the character. Returns the
* number added.
- *
- * Locking: Called functions may take port->buf.lock
*/
int tty_insert_flip_string_flags(struct tty_port *port,
@@ -299,12 +310,10 @@ int tty_insert_flip_string_flags(struct tty_port *port,
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(port, goal);
struct tty_buffer *tb = port->buf.tail;
- /* If there is no space then tb may be NULL */
- if (unlikely(space == 0)) {
+ if (unlikely(space == 0))
break;
- }
- memcpy(tb->char_buf_ptr + tb->used, chars, space);
- memcpy(tb->flag_buf_ptr + tb->used, flags, space);
+ memcpy(char_buf_ptr(tb, tb->used), chars, space);
+ memcpy(flag_buf_ptr(tb, tb->used), flags, space);
tb->used += space;
copied += space;
chars += space;
@@ -325,20 +334,14 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags);
* processing by the line discipline.
* Note that this function can only be used when the low_latency flag
* is unset. Otherwise the workqueue won't be flushed.
- *
- * Locking: Takes port->buf.lock
*/
void tty_schedule_flip(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
- unsigned long flags;
WARN_ON(port->low_latency);
- spin_lock_irqsave(&buf->lock, flags);
- if (buf->tail != NULL)
- buf->tail->commit = buf->tail->used;
- spin_unlock_irqrestore(&buf->lock, flags);
+ buf->tail->commit = buf->tail->used;
schedule_work(&buf->work);
}
EXPORT_SYMBOL(tty_schedule_flip);
@@ -354,8 +357,6 @@ EXPORT_SYMBOL(tty_schedule_flip);
* accounted for as ready for normal characters. This is used for drivers
* that need their own block copy routines into the buffer. There is no
* guarantee the buffer is a DMA target!
- *
- * Locking: May call functions taking port->buf.lock
*/
int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars,
@@ -364,8 +365,8 @@ int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars,
int space = tty_buffer_request_room(port, size);
if (likely(space)) {
struct tty_buffer *tb = port->buf.tail;
- *chars = tb->char_buf_ptr + tb->used;
- memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
+ *chars = char_buf_ptr(tb, tb->used);
+ memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space);
tb->used += space;
}
return space;
@@ -384,8 +385,6 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
* accounted for as ready for characters. This is used for drivers
* that need their own block copy routines into the buffer. There is no
* guarantee the buffer is a DMA target!
- *
- * Locking: May call functions taking port->buf.lock
*/
int tty_prepare_flip_string_flags(struct tty_port *port,
@@ -394,8 +393,8 @@ int tty_prepare_flip_string_flags(struct tty_port *port,
int space = tty_buffer_request_room(port, size);
if (likely(space)) {
struct tty_buffer *tb = port->buf.tail;
- *chars = tb->char_buf_ptr + tb->used;
- *flags = tb->flag_buf_ptr + tb->used;
+ *chars = char_buf_ptr(tb, tb->used);
+ *flags = flag_buf_ptr(tb, tb->used);
tb->used += space;
}
return space;
@@ -403,6 +402,23 @@ int tty_prepare_flip_string_flags(struct tty_port *port,
EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
+static int
+receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count)
+{
+ struct tty_ldisc *disc = tty->ldisc;
+ unsigned char *p = char_buf_ptr(head, head->read);
+ char *f = flag_buf_ptr(head, head->read);
+
+ if (disc->ops->receive_buf2)
+ count = disc->ops->receive_buf2(tty, p, f, count);
+ else {
+ count = min_t(int, count, tty->receive_room);
+ if (count)
+ disc->ops->receive_buf(tty, p, f, count);
+ }
+ head->read += count;
+ return count;
+}
/**
* flush_to_ldisc
@@ -411,9 +427,10 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
* This routine is called out of the software interrupt to flush data
* from the buffer chain to the line discipline.
*
- * Locking: holds tty->buf.lock to guard buffer list. Drops the lock
- * while invoking the line discipline receive_buf method. The
- * receive_buf method is single threaded for each tty instance.
+ * The receive_buf method is single threaded for each tty instance.
+ *
+ * Locking: takes buffer lock to ensure single-threaded flip buffer
+ * 'consumer'
*/
static void flush_to_ldisc(struct work_struct *work)
@@ -421,7 +438,6 @@ static void flush_to_ldisc(struct work_struct *work)
struct tty_port *port = container_of(work, struct tty_port, buf.work);
struct tty_bufhead *buf = &port->buf;
struct tty_struct *tty;
- unsigned long flags;
struct tty_ldisc *disc;
tty = port->itty;
@@ -429,52 +445,34 @@ static void flush_to_ldisc(struct work_struct *work)
return;
disc = tty_ldisc_ref(tty);
- if (disc == NULL) /* !TTY_LDISC */
+ if (disc == NULL)
return;
- spin_lock_irqsave(&buf->lock, flags);
-
- if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) {
- struct tty_buffer *head;
- while ((head = buf->head) != NULL) {
- int count;
- char *char_buf;
- unsigned char *flag_buf;
-
- count = head->commit - head->read;
- if (!count) {
- if (head->next == NULL)
- break;
- buf->head = head->next;
- tty_buffer_free(port, head);
- continue;
- }
- if (!tty->receive_room)
- break;
- if (count > tty->receive_room)
- count = tty->receive_room;
- char_buf = head->char_buf_ptr + head->read;
- flag_buf = head->flag_buf_ptr + head->read;
- head->read += count;
- spin_unlock_irqrestore(&buf->lock, flags);
- disc->ops->receive_buf(tty, char_buf,
- flag_buf, count);
- spin_lock_irqsave(&buf->lock, flags);
- /* Ldisc or user is trying to flush the buffers.
- We may have a deferred request to flush the
- input buffer, if so pull the chain under the lock
- and empty the queue */
- if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) {
- __tty_buffer_flush(port);
- clear_bit(TTYP_FLUSHPENDING, &port->iflags);
- wake_up(&tty->read_wait);
+ mutex_lock(&buf->lock);
+
+ while (1) {
+ struct tty_buffer *head = buf->head;
+ int count;
+
+ /* Ldisc or user is trying to gain exclusive access */
+ if (atomic_read(&buf->priority))
+ break;
+
+ count = head->commit - head->read;
+ if (!count) {
+ if (head->next == NULL)
break;
- }
+ buf->head = head->next;
+ tty_buffer_free(port, head);
+ continue;
}
- clear_bit(TTYP_FLUSHING, &port->iflags);
+
+ count = receive_buf(tty, head, count);
+ if (!count)
+ break;
}
- spin_unlock_irqrestore(&buf->lock, flags);
+ mutex_unlock(&buf->lock);
tty_ldisc_deref(disc);
}
@@ -503,19 +501,13 @@ void tty_flush_to_ldisc(struct tty_struct *tty)
*
* In the event of the queue being busy for flipping the work will be
* held off and retried later.
- *
- * Locking: tty buffer lock. Driver locks in low latency mode.
*/
void tty_flip_buffer_push(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
- unsigned long flags;
- spin_lock_irqsave(&buf->lock, flags);
- if (buf->tail != NULL)
- buf->tail->commit = buf->tail->used;
- spin_unlock_irqrestore(&buf->lock, flags);
+ buf->tail->commit = buf->tail->used;
if (port->low_latency)
flush_to_ldisc(&buf->work);
@@ -530,19 +522,18 @@ EXPORT_SYMBOL(tty_flip_buffer_push);
*
* Set up the initial state of the buffer management for a tty device.
* Must be called before the other tty buffer functions are used.
- *
- * Locking: none
*/
void tty_buffer_init(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
- spin_lock_init(&buf->lock);
- buf->head = NULL;
- buf->tail = NULL;
- buf->free = NULL;
- buf->memory_used = 0;
+ mutex_init(&buf->lock);
+ tty_buffer_reset(&buf->sentinel, 0);
+ buf->head = &buf->sentinel;
+ buf->tail = &buf->sentinel;
+ init_llist_head(&buf->free);
+ atomic_set(&buf->memory_used, 0);
+ atomic_set(&buf->priority, 0);
INIT_WORK(&buf->work, flush_to_ldisc);
}
-
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 366af832794b..26bb78c30a00 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -603,8 +603,8 @@ static int tty_signal_session_leader(struct tty_struct *tty, int exit_session)
* BTM
* redirect lock for undoing redirection
* file list lock for manipulating list of ttys
- * tty_ldisc_lock from called functions
- * termios_mutex resetting termios data
+ * tty_ldiscs_lock from called functions
+ * termios_rwsem resetting termios data
* tasklist_lock to walk task list for hangup event
* ->siglock to protect ->signal/->sighand
*/
@@ -664,7 +664,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
spin_lock_irq(&tty->ctrl_lock);
clear_bit(TTY_THROTTLED, &tty->flags);
- clear_bit(TTY_PUSH, &tty->flags);
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
put_pid(tty->session);
put_pid(tty->pgrp);
@@ -1388,8 +1387,7 @@ static int tty_reopen(struct tty_struct *tty)
struct tty_driver *driver = tty->driver;
if (test_bit(TTY_CLOSING, &tty->flags) ||
- test_bit(TTY_HUPPING, &tty->flags) ||
- test_bit(TTY_LDISC_CHANGING, &tty->flags))
+ test_bit(TTY_HUPPING, &tty->flags))
return -EIO;
if (driver->type == TTY_DRIVER_TYPE_PTY &&
@@ -1405,7 +1403,7 @@ static int tty_reopen(struct tty_struct *tty)
}
tty->count++;
- WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
+ WARN_ON(!tty->ldisc);
return 0;
}
@@ -2202,7 +2200,7 @@ static int tty_fasync(int fd, struct file *filp, int on)
* FIXME: does not honour flow control ??
*
* Locking:
- * Called functions take tty_ldisc_lock
+ * Called functions take tty_ldiscs_lock
* current->signal->tty check is safe without locks
*
* FIXME: may race normal receive processing
@@ -2231,7 +2229,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
*
* Copies the kernel idea of the window size into the user buffer.
*
- * Locking: tty->termios_mutex is taken to ensure the winsize data
+ * Locking: tty->winsize_mutex is taken to ensure the winsize data
* is consistent.
*/
@@ -2239,9 +2237,9 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
{
int err;
- mutex_lock(&tty->termios_mutex);
+ mutex_lock(&tty->winsize_mutex);
err = copy_to_user(arg, &tty->winsize, sizeof(*arg));
- mutex_unlock(&tty->termios_mutex);
+ mutex_unlock(&tty->winsize_mutex);
return err ? -EFAULT: 0;
}
@@ -2262,7 +2260,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
unsigned long flags;
/* Lock the tty */
- mutex_lock(&tty->termios_mutex);
+ mutex_lock(&tty->winsize_mutex);
if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
goto done;
/* Get the PID values and reference them so we can
@@ -2277,7 +2275,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
tty->winsize = *ws;
done:
- mutex_unlock(&tty->termios_mutex);
+ mutex_unlock(&tty->winsize_mutex);
return 0;
}
EXPORT_SYMBOL(tty_do_resize);
@@ -3016,8 +3014,10 @@ void initialize_tty_struct(struct tty_struct *tty,
tty->session = NULL;
tty->pgrp = NULL;
mutex_init(&tty->legacy_mutex);
- mutex_init(&tty->termios_mutex);
- mutex_init(&tty->ldisc_mutex);
+ mutex_init(&tty->throttle_mutex);
+ init_rwsem(&tty->termios_rwsem);
+ mutex_init(&tty->winsize_mutex);
+ init_ldsem(&tty->ldisc_sem);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
INIT_WORK(&tty->hangup_work, do_tty_hangup);
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index 3500d4114147..03ba081c5772 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -94,20 +94,20 @@ EXPORT_SYMBOL(tty_driver_flush_buffer);
* @tty: terminal
*
* Indicate that a tty should stop transmitting data down the stack.
- * Takes the termios mutex to protect against parallel throttle/unthrottle
+ * Takes the termios rwsem to protect against parallel throttle/unthrottle
* and also to ensure the driver can consistently reference its own
* termios data at this point when implementing software flow control.
*/
void tty_throttle(struct tty_struct *tty)
{
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
/* check TTY_THROTTLED first so it indicates our state */
if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&
tty->ops->throttle)
tty->ops->throttle(tty);
tty->flow_change = 0;
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
}
EXPORT_SYMBOL(tty_throttle);
@@ -116,7 +116,7 @@ EXPORT_SYMBOL(tty_throttle);
* @tty: terminal
*
* Indicate that a tty may continue transmitting data down the stack.
- * Takes the termios mutex to protect against parallel throttle/unthrottle
+ * Takes the termios rwsem to protect against parallel throttle/unthrottle
* and also to ensure the driver can consistently reference its own
* termios data at this point when implementing software flow control.
*
@@ -126,12 +126,12 @@ EXPORT_SYMBOL(tty_throttle);
void tty_unthrottle(struct tty_struct *tty)
{
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
tty->ops->unthrottle)
tty->ops->unthrottle(tty);
tty->flow_change = 0;
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
}
EXPORT_SYMBOL(tty_unthrottle);
@@ -151,7 +151,7 @@ int tty_throttle_safe(struct tty_struct *tty)
{
int ret = 0;
- mutex_lock(&tty->termios_mutex);
+ mutex_lock(&tty->throttle_mutex);
if (!test_bit(TTY_THROTTLED, &tty->flags)) {
if (tty->flow_change != TTY_THROTTLE_SAFE)
ret = 1;
@@ -161,7 +161,7 @@ int tty_throttle_safe(struct tty_struct *tty)
tty->ops->throttle(tty);
}
}
- mutex_unlock(&tty->termios_mutex);
+ mutex_unlock(&tty->throttle_mutex);
return ret;
}
@@ -182,7 +182,7 @@ int tty_unthrottle_safe(struct tty_struct *tty)
{
int ret = 0;
- mutex_lock(&tty->termios_mutex);
+ mutex_lock(&tty->throttle_mutex);
if (test_bit(TTY_THROTTLED, &tty->flags)) {
if (tty->flow_change != TTY_UNTHROTTLE_SAFE)
ret = 1;
@@ -192,7 +192,7 @@ int tty_unthrottle_safe(struct tty_struct *tty)
tty->ops->unthrottle(tty);
}
}
- mutex_unlock(&tty->termios_mutex);
+ mutex_unlock(&tty->throttle_mutex);
return ret;
}
@@ -468,7 +468,7 @@ EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate);
* @obad: output baud rate
*
* Update the current termios data for the tty with the new speed
- * settings. The caller must hold the termios_mutex for the tty in
+ * settings. The caller must hold the termios_rwsem for the tty in
* question.
*/
@@ -528,7 +528,7 @@ EXPORT_SYMBOL(tty_termios_hw_change);
* is a bit of layering violation here with n_tty in terms of the
* internal knowledge of this function.
*
- * Locking: termios_mutex
+ * Locking: termios_rwsem
*/
int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
@@ -544,7 +544,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
/* FIXME: we need to decide on some locking/ordering semantics
for the set_termios notification eventually */
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
old_termios = tty->termios;
tty->termios = *new_termios;
unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked);
@@ -586,7 +586,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
(ld->ops->set_termios)(tty, &old_termios);
tty_ldisc_deref(ld);
}
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
return 0;
}
EXPORT_SYMBOL_GPL(tty_set_termios);
@@ -601,7 +601,7 @@ EXPORT_SYMBOL_GPL(tty_set_termios);
* functions before using tty_set_termios to do the actual changes.
*
* Locking:
- * Called functions take ldisc and termios_mutex locks
+ * Called functions take ldisc and termios_rwsem locks
*/
static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
@@ -613,9 +613,9 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
if (retval)
return retval;
- mutex_lock(&tty->termios_mutex);
+ down_read(&tty->termios_rwsem);
tmp_termios = tty->termios;
- mutex_unlock(&tty->termios_mutex);
+ up_read(&tty->termios_rwsem);
if (opt & TERMIOS_TERMIO) {
if (user_termio_to_kernel_termios(&tmp_termios,
@@ -667,16 +667,16 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
static void copy_termios(struct tty_struct *tty, struct ktermios *kterm)
{
- mutex_lock(&tty->termios_mutex);
+ down_read(&tty->termios_rwsem);
*kterm = tty->termios;
- mutex_unlock(&tty->termios_mutex);
+ up_read(&tty->termios_rwsem);
}
static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm)
{
- mutex_lock(&tty->termios_mutex);
+ down_read(&tty->termios_rwsem);
*kterm = tty->termios_locked;
- mutex_unlock(&tty->termios_mutex);
+ up_read(&tty->termios_rwsem);
}
static int get_termio(struct tty_struct *tty, struct termio __user *termio)
@@ -723,10 +723,10 @@ static int set_termiox(struct tty_struct *tty, void __user *arg, int opt)
return -ERESTARTSYS;
}
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
if (tty->ops->set_termiox)
tty->ops->set_termiox(tty, &tnew);
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
return 0;
}
@@ -761,13 +761,13 @@ static int get_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
{
struct sgttyb tmp;
- mutex_lock(&tty->termios_mutex);
+ down_read(&tty->termios_rwsem);
tmp.sg_ispeed = tty->termios.c_ispeed;
tmp.sg_ospeed = tty->termios.c_ospeed;
tmp.sg_erase = tty->termios.c_cc[VERASE];
tmp.sg_kill = tty->termios.c_cc[VKILL];
tmp.sg_flags = get_sgflags(tty);
- mutex_unlock(&tty->termios_mutex);
+ up_read(&tty->termios_rwsem);
return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}
@@ -806,7 +806,7 @@ static void set_sgflags(struct ktermios *termios, int flags)
* Updates a terminal from the legacy BSD style terminal information
* structure.
*
- * Locking: termios_mutex
+ * Locking: termios_rwsem
*/
static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
@@ -822,7 +822,7 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
return -EFAULT;
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
termios = tty->termios;
termios.c_cc[VERASE] = tmp.sg_erase;
termios.c_cc[VKILL] = tmp.sg_kill;
@@ -832,7 +832,7 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
tty_termios_encode_baud_rate(&termios, termios.c_ispeed,
termios.c_ospeed);
#endif
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
tty_set_termios(tty, &termios);
return 0;
}
@@ -843,14 +843,14 @@ static int get_tchars(struct tty_struct *tty, struct tchars __user *tchars)
{
struct tchars tmp;
- mutex_lock(&tty->termios_mutex);
+ down_read(&tty->termios_rwsem);
tmp.t_intrc = tty->termios.c_cc[VINTR];
tmp.t_quitc = tty->termios.c_cc[VQUIT];
tmp.t_startc = tty->termios.c_cc[VSTART];
tmp.t_stopc = tty->termios.c_cc[VSTOP];
tmp.t_eofc = tty->termios.c_cc[VEOF];
tmp.t_brkc = tty->termios.c_cc[VEOL2]; /* what is brkc anyway? */
- mutex_unlock(&tty->termios_mutex);
+ up_read(&tty->termios_rwsem);
return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}
@@ -860,14 +860,14 @@ static int set_tchars(struct tty_struct *tty, struct tchars __user *tchars)
if (copy_from_user(&tmp, tchars, sizeof(tmp)))
return -EFAULT;
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
tty->termios.c_cc[VINTR] = tmp.t_intrc;
tty->termios.c_cc[VQUIT] = tmp.t_quitc;
tty->termios.c_cc[VSTART] = tmp.t_startc;
tty->termios.c_cc[VSTOP] = tmp.t_stopc;
tty->termios.c_cc[VEOF] = tmp.t_eofc;
tty->termios.c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
return 0;
}
#endif
@@ -877,7 +877,7 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
{
struct ltchars tmp;
- mutex_lock(&tty->termios_mutex);
+ down_read(&tty->termios_rwsem);
tmp.t_suspc = tty->termios.c_cc[VSUSP];
/* what is dsuspc anyway? */
tmp.t_dsuspc = tty->termios.c_cc[VSUSP];
@@ -886,7 +886,7 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
tmp.t_flushc = tty->termios.c_cc[VEOL2];
tmp.t_werasc = tty->termios.c_cc[VWERASE];
tmp.t_lnextc = tty->termios.c_cc[VLNEXT];
- mutex_unlock(&tty->termios_mutex);
+ up_read(&tty->termios_rwsem);
return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}
@@ -897,7 +897,7 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
if (copy_from_user(&tmp, ltchars, sizeof(tmp)))
return -EFAULT;
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
tty->termios.c_cc[VSUSP] = tmp.t_suspc;
/* what is dsuspc anyway? */
tty->termios.c_cc[VEOL2] = tmp.t_dsuspc;
@@ -906,7 +906,7 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
tty->termios.c_cc[VEOL2] = tmp.t_flushc;
tty->termios.c_cc[VWERASE] = tmp.t_werasc;
tty->termios.c_cc[VLNEXT] = tmp.t_lnextc;
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
return 0;
}
#endif
@@ -946,7 +946,7 @@ static int send_prio_char(struct tty_struct *tty, char ch)
* @arg: enable/disable CLOCAL
*
* Perform a change to the CLOCAL state and call into the driver
- * layer to make it visible. All done with the termios mutex
+ * layer to make it visible. All done with the termios rwsem
*/
static int tty_change_softcar(struct tty_struct *tty, int arg)
@@ -955,7 +955,7 @@ static int tty_change_softcar(struct tty_struct *tty, int arg)
int bit = arg ? CLOCAL : 0;
struct ktermios old;
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
old = tty->termios;
tty->termios.c_cflag &= ~CLOCAL;
tty->termios.c_cflag |= bit;
@@ -963,7 +963,7 @@ static int tty_change_softcar(struct tty_struct *tty, int arg)
tty->ops->set_termios(tty, &old);
if ((tty->termios.c_cflag & CLOCAL) != bit)
ret = -EINVAL;
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
return ret;
}
@@ -1066,9 +1066,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
if (user_termios_to_kernel_termios(&kterm,
(struct termios __user *) arg))
return -EFAULT;
- mutex_lock(&real_tty->termios_mutex);
+ down_write(&real_tty->termios_rwsem);
real_tty->termios_locked = kterm;
- mutex_unlock(&real_tty->termios_mutex);
+ up_write(&real_tty->termios_rwsem);
return 0;
#else
case TIOCGLCKTRMIOS:
@@ -1083,9 +1083,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
if (user_termios_to_kernel_termios_1(&kterm,
(struct termios __user *) arg))
return -EFAULT;
- mutex_lock(&real_tty->termios_mutex);
+ down_write(&real_tty->termios_rwsem);
real_tty->termios_locked = kterm;
- mutex_unlock(&real_tty->termios_mutex);
+ up_write(&real_tty->termios_rwsem);
return ret;
#endif
#ifdef TCGETX
@@ -1093,9 +1093,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
struct termiox ktermx;
if (real_tty->termiox == NULL)
return -EINVAL;
- mutex_lock(&real_tty->termios_mutex);
+ down_read(&real_tty->termios_rwsem);
memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox));
- mutex_unlock(&real_tty->termios_mutex);
+ up_read(&real_tty->termios_rwsem);
if (copy_to_user(p, &ktermx, sizeof(struct termiox)))
ret = -EFAULT;
return ret;
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 1afe192bef6a..6458e11e8e9d 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -31,14 +31,20 @@
#define tty_ldisc_debug(tty, f, args...)
#endif
+/* lockdep nested classes for tty->ldisc_sem */
+enum {
+ LDISC_SEM_NORMAL,
+ LDISC_SEM_OTHER,
+};
+
+
/*
* This guards the refcounted line discipline lists. The lock
* must be taken with irqs off because there are hangup path
* callers who will do ldisc lookups and cannot sleep.
*/
-static DEFINE_RAW_SPINLOCK(tty_ldisc_lock);
-static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
+static DEFINE_RAW_SPINLOCK(tty_ldiscs_lock);
/* Line disc dispatch table */
static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
@@ -52,7 +58,7 @@ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
* from this point onwards.
*
* Locking:
- * takes tty_ldisc_lock to guard against ldisc races
+ * takes tty_ldiscs_lock to guard against ldisc races
*/
int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
@@ -63,11 +69,11 @@ int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
if (disc < N_TTY || disc >= NR_LDISCS)
return -EINVAL;
- raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
+ raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
tty_ldiscs[disc] = new_ldisc;
new_ldisc->num = disc;
new_ldisc->refcount = 0;
- raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
return ret;
}
@@ -82,7 +88,7 @@ EXPORT_SYMBOL(tty_register_ldisc);
* currently in use.
*
* Locking:
- * takes tty_ldisc_lock to guard against ldisc races
+ * takes tty_ldiscs_lock to guard against ldisc races
*/
int tty_unregister_ldisc(int disc)
@@ -93,12 +99,12 @@ int tty_unregister_ldisc(int disc)
if (disc < N_TTY || disc >= NR_LDISCS)
return -EINVAL;
- raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
+ raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
if (tty_ldiscs[disc]->refcount)
ret = -EBUSY;
else
tty_ldiscs[disc] = NULL;
- raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
return ret;
}
@@ -109,7 +115,7 @@ static struct tty_ldisc_ops *get_ldops(int disc)
unsigned long flags;
struct tty_ldisc_ops *ldops, *ret;
- raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
+ raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
ret = ERR_PTR(-EINVAL);
ldops = tty_ldiscs[disc];
if (ldops) {
@@ -119,7 +125,7 @@ static struct tty_ldisc_ops *get_ldops(int disc)
ret = ldops;
}
}
- raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
return ret;
}
@@ -127,10 +133,10 @@ static void put_ldops(struct tty_ldisc_ops *ldops)
{
unsigned long flags;
- raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
+ raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
ldops->refcount--;
module_put(ldops->owner);
- raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
}
/**
@@ -143,10 +149,10 @@ static void put_ldops(struct tty_ldisc_ops *ldops)
* available
*
* Locking:
- * takes tty_ldisc_lock to guard against ldisc races
+ * takes tty_ldiscs_lock to guard against ldisc races
*/
-static struct tty_ldisc *tty_ldisc_get(int disc)
+static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
{
struct tty_ldisc *ld;
struct tty_ldisc_ops *ldops;
@@ -173,8 +179,7 @@ static struct tty_ldisc *tty_ldisc_get(int disc)
}
ld->ops = ldops;
- atomic_set(&ld->users, 1);
- init_waitqueue_head(&ld->wq_idle);
+ ld->tty = tty;
return ld;
}
@@ -186,20 +191,11 @@ static struct tty_ldisc *tty_ldisc_get(int disc)
*/
static inline void tty_ldisc_put(struct tty_ldisc *ld)
{
- unsigned long flags;
-
if (WARN_ON_ONCE(!ld))
return;
- raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
-
- /* unreleased reader reference(s) will cause this WARN */
- WARN_ON(!atomic_dec_and_test(&ld->users));
-
- ld->ops->refcount--;
- module_put(ld->ops->owner);
+ put_ldops(ld->ops);
kfree(ld);
- raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
}
static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos)
@@ -251,34 +247,6 @@ const struct file_operations tty_ldiscs_proc_fops = {
};
/**
- * tty_ldisc_try - internal helper
- * @tty: the tty
- *
- * Make a single attempt to grab and bump the refcount on
- * the tty ldisc. Return 0 on failure or 1 on success. This is
- * used to implement both the waiting and non waiting versions
- * of tty_ldisc_ref
- *
- * Locking: takes tty_ldisc_lock
- */
-
-static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty)
-{
- unsigned long flags;
- struct tty_ldisc *ld;
-
- /* FIXME: this allows reference acquire after TTY_LDISC is cleared */
- raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
- ld = NULL;
- if (test_bit(TTY_LDISC, &tty->flags) && tty->ldisc) {
- ld = tty->ldisc;
- atomic_inc(&ld->users);
- }
- raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- return ld;
-}
-
-/**
* tty_ldisc_ref_wait - wait for the tty ldisc
* @tty: tty device
*
@@ -291,16 +259,15 @@ static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty)
* against a discipline change, such as an existing ldisc reference
* (which we check for)
*
- * Locking: call functions take tty_ldisc_lock
+ * Note: only callable from a file_operations routine (which
+ * guarantees tty->ldisc != NULL when the lock is acquired).
*/
struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
{
- struct tty_ldisc *ld;
-
- /* wait_event is a macro */
- wait_event(tty_ldisc_wait, (ld = tty_ldisc_try(tty)) != NULL);
- return ld;
+ ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT);
+ WARN_ON(!tty->ldisc);
+ return tty->ldisc;
}
EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
@@ -311,13 +278,18 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
* Dereference the line discipline for the terminal and take a
* reference to it. If the line discipline is in flux then
* return NULL. Can be called from IRQ and timer functions.
- *
- * Locking: called functions take tty_ldisc_lock
*/
struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
{
- return tty_ldisc_try(tty);
+ struct tty_ldisc *ld = NULL;
+
+ if (ldsem_down_read_trylock(&tty->ldisc_sem)) {
+ ld = tty->ldisc;
+ if (!ld)
+ ldsem_up_read(&tty->ldisc_sem);
+ }
+ return ld;
}
EXPORT_SYMBOL_GPL(tty_ldisc_ref);
@@ -327,48 +299,91 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref);
*
* Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May
* be called in IRQ context.
- *
- * Locking: takes tty_ldisc_lock
*/
void tty_ldisc_deref(struct tty_ldisc *ld)
{
- unsigned long flags;
+ ldsem_up_read(&ld->tty->ldisc_sem);
+}
+EXPORT_SYMBOL_GPL(tty_ldisc_deref);
- if (WARN_ON_ONCE(!ld))
- return;
- raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
- /*
- * WARNs if one-too-many reader references were released
- * - the last reference must be released with tty_ldisc_put
- */
- WARN_ON(atomic_dec_and_test(&ld->users));
- raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+static inline int __lockfunc
+tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
+{
+ return ldsem_down_write(&tty->ldisc_sem, timeout);
+}
- if (waitqueue_active(&ld->wq_idle))
- wake_up(&ld->wq_idle);
+static inline int __lockfunc
+tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout)
+{
+ return ldsem_down_write_nested(&tty->ldisc_sem,
+ LDISC_SEM_OTHER, timeout);
}
-EXPORT_SYMBOL_GPL(tty_ldisc_deref);
-/**
- * tty_ldisc_enable - allow ldisc use
- * @tty: terminal to activate ldisc on
- *
- * Set the TTY_LDISC flag when the line discipline can be called
- * again. Do necessary wakeups for existing sleepers. Clear the LDISC
- * changing flag to indicate any ldisc change is now over.
- *
- * Note: nobody should set the TTY_LDISC bit except via this function.
- * Clearing directly is allowed.
- */
+static inline void tty_ldisc_unlock(struct tty_struct *tty)
+{
+ return ldsem_up_write(&tty->ldisc_sem);
+}
-static void tty_ldisc_enable(struct tty_struct *tty)
+static int __lockfunc
+tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2,
+ unsigned long timeout)
+{
+ int ret;
+
+ if (tty < tty2) {
+ ret = tty_ldisc_lock(tty, timeout);
+ if (ret) {
+ ret = tty_ldisc_lock_nested(tty2, timeout);
+ if (!ret)
+ tty_ldisc_unlock(tty);
+ }
+ } else {
+ /* if this is possible, it has lots of implications */
+ WARN_ON_ONCE(tty == tty2);
+ if (tty2 && tty != tty2) {
+ ret = tty_ldisc_lock(tty2, timeout);
+ if (ret) {
+ ret = tty_ldisc_lock_nested(tty, timeout);
+ if (!ret)
+ tty_ldisc_unlock(tty2);
+ }
+ } else
+ ret = tty_ldisc_lock(tty, timeout);
+ }
+
+ if (!ret)
+ return -EBUSY;
+
+ set_bit(TTY_LDISC_HALTED, &tty->flags);
+ if (tty2)
+ set_bit(TTY_LDISC_HALTED, &tty2->flags);
+ return 0;
+}
+
+static void __lockfunc
+tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2)
+{
+ tty_ldisc_lock_pair_timeout(tty, tty2, MAX_SCHEDULE_TIMEOUT);
+}
+
+static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty,
+ struct tty_struct *tty2)
+{
+ tty_ldisc_unlock(tty);
+ if (tty2)
+ tty_ldisc_unlock(tty2);
+}
+
+static void __lockfunc tty_ldisc_enable_pair(struct tty_struct *tty,
+ struct tty_struct *tty2)
{
clear_bit(TTY_LDISC_HALTED, &tty->flags);
- set_bit(TTY_LDISC, &tty->flags);
- clear_bit(TTY_LDISC_CHANGING, &tty->flags);
- wake_up(&tty_ldisc_wait);
+ if (tty2)
+ clear_bit(TTY_LDISC_HALTED, &tty2->flags);
+
+ tty_ldisc_unlock_pair(tty, tty2);
}
/**
@@ -400,14 +415,14 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush);
* they are not on hot paths so a little discipline won't do
* any harm.
*
- * Locking: takes termios_mutex
+ * Locking: takes termios_rwsem
*/
static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
{
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
tty->termios.c_line = num;
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
}
/**
@@ -468,14 +483,14 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
int r;
/* There is an outstanding reference here so this is safe */
- old = tty_ldisc_get(old->ops->num);
+ old = tty_ldisc_get(tty, old->ops->num);
WARN_ON(IS_ERR(old));
tty->ldisc = old;
tty_set_termios_ldisc(tty, old->ops->num);
if (tty_ldisc_open(tty, old) < 0) {
tty_ldisc_put(old);
/* This driver is always present */
- new_ldisc = tty_ldisc_get(N_TTY);
+ new_ldisc = tty_ldisc_get(tty, N_TTY);
if (IS_ERR(new_ldisc))
panic("n_tty: get");
tty->ldisc = new_ldisc;
@@ -489,101 +504,6 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
}
/**
- * tty_ldisc_wait_idle - wait for the ldisc to become idle
- * @tty: tty to wait for
- * @timeout: for how long to wait at most
- *
- * Wait for the line discipline to become idle. The discipline must
- * have been halted for this to guarantee it remains idle.
- */
-static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout)
-{
- long ret;
- ret = wait_event_timeout(tty->ldisc->wq_idle,
- atomic_read(&tty->ldisc->users) == 1, timeout);
- return ret > 0 ? 0 : -EBUSY;
-}
-
-/**
- * tty_ldisc_halt - shut down the line discipline
- * @tty: tty device
- * @o_tty: paired pty device (can be NULL)
- * @timeout: # of jiffies to wait for ldisc refs to be released
- *
- * Shut down the line discipline and work queue for this tty device and
- * its paired pty (if exists). Clearing the TTY_LDISC flag ensures
- * no further references can be obtained, while waiting for existing
- * references to be released ensures no more data is fed to the ldisc.
- *
- * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex)
- * in order to make sure any currently executing ldisc work is also
- * flushed.
- */
-
-static int tty_ldisc_halt(struct tty_struct *tty, struct tty_struct *o_tty,
- long timeout)
-{
- int retval;
-
- clear_bit(TTY_LDISC, &tty->flags);
- if (o_tty)
- clear_bit(TTY_LDISC, &o_tty->flags);
-
- retval = tty_ldisc_wait_idle(tty, timeout);
- if (!retval && o_tty)
- retval = tty_ldisc_wait_idle(o_tty, timeout);
- if (retval)
- return retval;
-
- set_bit(TTY_LDISC_HALTED, &tty->flags);
- if (o_tty)
- set_bit(TTY_LDISC_HALTED, &o_tty->flags);
-
- return 0;
-}
-
-/**
- * tty_ldisc_hangup_halt - halt the line discipline for hangup
- * @tty: tty being hung up
- *
- * Shut down the line discipline and work queue for the tty device
- * being hungup. Clear the TTY_LDISC flag to ensure no further
- * references can be obtained and wait for remaining references to be
- * released to ensure no more data is fed to this ldisc.
- * Caller must hold legacy and ->ldisc_mutex.
- *
- * NB: tty_set_ldisc() is prevented from changing the ldisc concurrently
- * with this function by checking the TTY_HUPPING flag.
- */
-static bool tty_ldisc_hangup_halt(struct tty_struct *tty)
-{
- char cur_n[TASK_COMM_LEN], tty_n[64];
- long timeout = 3 * HZ;
-
- clear_bit(TTY_LDISC, &tty->flags);
-
- if (tty->ldisc) { /* Not yet closed */
- tty_unlock(tty);
-
- while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) {
- timeout = MAX_SCHEDULE_TIMEOUT;
- printk_ratelimited(KERN_WARNING
- "%s: waiting (%s) for %s took too long, but we keep waiting...\n",
- __func__, get_task_comm(cur_n, current),
- tty_name(tty, tty_n));
- }
-
- set_bit(TTY_LDISC_HALTED, &tty->flags);
-
- /* must reacquire both locks and preserve lock order */
- mutex_unlock(&tty->ldisc_mutex);
- tty_lock(tty);
- mutex_lock(&tty->ldisc_mutex);
- }
- return !!tty->ldisc;
-}
-
-/**
* tty_set_ldisc - set line discipline
* @tty: the terminal to set
* @ldisc: the line discipline
@@ -592,110 +512,49 @@ static bool tty_ldisc_hangup_halt(struct tty_struct *tty)
* context. The ldisc change logic has to protect itself against any
* overlapping ldisc change (including on the other end of pty pairs),
* the close of one side of a tty/pty pair, and eventually hangup.
- *
- * Locking: takes tty_ldisc_lock, termios_mutex
*/
int tty_set_ldisc(struct tty_struct *tty, int ldisc)
{
int retval;
- struct tty_ldisc *o_ldisc, *new_ldisc;
- struct tty_struct *o_tty;
+ struct tty_ldisc *old_ldisc, *new_ldisc;
+ struct tty_struct *o_tty = tty->link;
- new_ldisc = tty_ldisc_get(ldisc);
+ new_ldisc = tty_ldisc_get(tty, ldisc);
if (IS_ERR(new_ldisc))
return PTR_ERR(new_ldisc);
- tty_lock(tty);
- /*
- * We need to look at the tty locking here for pty/tty pairs
- * when both sides try to change in parallel.
- */
-
- o_tty = tty->link; /* o_tty is the pty side or NULL */
-
+ retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ);
+ if (retval) {
+ tty_ldisc_put(new_ldisc);
+ return retval;
+ }
/*
* Check the no-op case
*/
if (tty->ldisc->ops->num == ldisc) {
- tty_unlock(tty);
+ tty_ldisc_enable_pair(tty, o_tty);
tty_ldisc_put(new_ldisc);
return 0;
}
- mutex_lock(&tty->ldisc_mutex);
-
- /*
- * We could be midstream of another ldisc change which has
- * dropped the lock during processing. If so we need to wait.
- */
-
- while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
- mutex_unlock(&tty->ldisc_mutex);
- tty_unlock(tty);
- wait_event(tty_ldisc_wait,
- test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
- tty_lock(tty);
- mutex_lock(&tty->ldisc_mutex);
- }
-
- set_bit(TTY_LDISC_CHANGING, &tty->flags);
-
- /*
- * No more input please, we are switching. The new ldisc
- * will update this value in the ldisc open function
- */
-
- tty->receive_room = 0;
-
- o_ldisc = tty->ldisc;
-
- tty_unlock(tty);
- /*
- * Make sure we don't change while someone holds a
- * reference to the line discipline. The TTY_LDISC bit
- * prevents anyone taking a reference once it is clear.
- * We need the lock to avoid racing reference takers.
- *
- * We must clear the TTY_LDISC bit here to avoid a livelock
- * with a userspace app continually trying to use the tty in
- * parallel to the change and re-referencing the tty.
- */
-
- retval = tty_ldisc_halt(tty, o_tty, 5 * HZ);
-
- /*
- * Wait for hangup to complete, if pending.
- * We must drop the mutex here in case a hangup is also in process.
- */
-
- mutex_unlock(&tty->ldisc_mutex);
-
- flush_work(&tty->hangup_work);
-
+ old_ldisc = tty->ldisc;
tty_lock(tty);
- mutex_lock(&tty->ldisc_mutex);
-
- /* handle wait idle failure locked */
- if (retval) {
- tty_ldisc_put(new_ldisc);
- goto enable;
- }
- if (test_bit(TTY_HUPPING, &tty->flags)) {
+ if (test_bit(TTY_HUPPING, &tty->flags) ||
+ test_bit(TTY_HUPPED, &tty->flags)) {
/* We were raced by the hangup method. It will have stomped
the ldisc data and closed the ldisc down */
- clear_bit(TTY_LDISC_CHANGING, &tty->flags);
- mutex_unlock(&tty->ldisc_mutex);
+ tty_ldisc_enable_pair(tty, o_tty);
tty_ldisc_put(new_ldisc);
tty_unlock(tty);
return -EIO;
}
- /* Shutdown the current discipline. */
- tty_ldisc_close(tty, o_ldisc);
+ /* Shutdown the old discipline. */
+ tty_ldisc_close(tty, old_ldisc);
/* Now set up the new line discipline. */
tty->ldisc = new_ldisc;
@@ -705,26 +564,24 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
if (retval < 0) {
/* Back to the old one or N_TTY if we can't */
tty_ldisc_put(new_ldisc);
- tty_ldisc_restore(tty, o_ldisc);
+ tty_ldisc_restore(tty, old_ldisc);
}
- /* At this point we hold a reference to the new ldisc and a
- a reference to the old ldisc. If we ended up flipping back
- to the existing ldisc we have two references to it */
-
- if (tty->ldisc->ops->num != o_ldisc->ops->num && tty->ops->set_ldisc)
+ if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc)
tty->ops->set_ldisc(tty);
- tty_ldisc_put(o_ldisc);
+ /* At this point we hold a reference to the new ldisc and a
+ reference to the old ldisc, or we hold two references to
+ the old ldisc (if it was restored as part of error cleanup
+ above). In either case, releasing a single reference from
+ the old ldisc is correct. */
+
+ tty_ldisc_put(old_ldisc);
-enable:
/*
* Allow ldisc referencing to occur again
*/
-
- tty_ldisc_enable(tty);
- if (o_tty)
- tty_ldisc_enable(o_tty);
+ tty_ldisc_enable_pair(tty, o_tty);
/* Restart the work queue in case no characters kick it off. Safe if
already running */
@@ -732,7 +589,6 @@ enable:
if (o_tty)
schedule_work(&o_tty->port->buf.work);
- mutex_unlock(&tty->ldisc_mutex);
tty_unlock(tty);
return retval;
}
@@ -746,11 +602,11 @@ enable:
static void tty_reset_termios(struct tty_struct *tty)
{
- mutex_lock(&tty->termios_mutex);
+ down_write(&tty->termios_rwsem);
tty->termios = tty->driver->init_termios;
tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios);
tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios);
- mutex_unlock(&tty->termios_mutex);
+ up_write(&tty->termios_rwsem);
}
@@ -765,7 +621,7 @@ static void tty_reset_termios(struct tty_struct *tty)
static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
{
- struct tty_ldisc *ld = tty_ldisc_get(ldisc);
+ struct tty_ldisc *ld = tty_ldisc_get(tty, ldisc);
if (IS_ERR(ld))
return -1;
@@ -804,14 +660,8 @@ void tty_ldisc_hangup(struct tty_struct *tty)
tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc);
- /*
- * FIXME! What are the locking issues here? This may me overdoing
- * things... This question is especially important now that we've
- * removed the irqlock.
- */
ld = tty_ldisc_ref(tty);
if (ld != NULL) {
- /* We may have no line discipline at this point */
if (ld->ops->flush_buffer)
ld->ops->flush_buffer(tty);
tty_driver_flush_buffer(tty);
@@ -822,21 +672,22 @@ void tty_ldisc_hangup(struct tty_struct *tty)
ld->ops->hangup(tty);
tty_ldisc_deref(ld);
}
- /*
- * FIXME: Once we trust the LDISC code better we can wait here for
- * ldisc completion and fix the driver call race
- */
+
wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
+
+ tty_unlock(tty);
+
/*
* Shutdown the current line discipline, and reset it to
* N_TTY if need be.
*
* Avoid racing set_ldisc or tty_ldisc_release
*/
- mutex_lock(&tty->ldisc_mutex);
+ tty_ldisc_lock_pair(tty, tty->link);
+ tty_lock(tty);
- if (tty_ldisc_hangup_halt(tty)) {
+ if (tty->ldisc) {
/* At this point we have a halted ldisc; we want to close it and
reopen a new ldisc. We could defer the reopen to the next
@@ -855,9 +706,8 @@ void tty_ldisc_hangup(struct tty_struct *tty)
BUG_ON(tty_ldisc_reinit(tty, N_TTY));
WARN_ON(tty_ldisc_open(tty, tty->ldisc));
}
- tty_ldisc_enable(tty);
}
- mutex_unlock(&tty->ldisc_mutex);
+ tty_ldisc_enable_pair(tty, tty->link);
if (reset)
tty_reset_termios(tty);
@@ -889,15 +739,12 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
tty_ldisc_close(tty, ld);
return retval;
}
- tty_ldisc_enable(o_tty);
}
- tty_ldisc_enable(tty);
return 0;
}
static void tty_ldisc_kill(struct tty_struct *tty)
{
- mutex_lock(&tty->ldisc_mutex);
/*
* Now kill off the ldisc
*/
@@ -908,7 +755,6 @@ static void tty_ldisc_kill(struct tty_struct *tty)
/* Ensure the next open requests the N_TTY ldisc */
tty_set_termios_ldisc(tty, N_TTY);
- mutex_unlock(&tty->ldisc_mutex);
}
/**
@@ -930,15 +776,16 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc);
- tty_ldisc_halt(tty, o_tty, MAX_SCHEDULE_TIMEOUT);
-
+ tty_ldisc_lock_pair(tty, o_tty);
tty_lock_pair(tty, o_tty);
- /* This will need doing differently if we need to lock */
+
tty_ldisc_kill(tty);
if (o_tty)
tty_ldisc_kill(o_tty);
tty_unlock_pair(tty, o_tty);
+ tty_ldisc_unlock_pair(tty, o_tty);
+
/* And the memory resources remaining (buffers, termios) will be
disposed of when the kref hits zero */
@@ -955,7 +802,7 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
void tty_ldisc_init(struct tty_struct *tty)
{
- struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
+ struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
if (IS_ERR(ld))
panic("n_tty: init_tty");
tty->ldisc = ld;
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index a9af1b9ae160..d0e3a4497707 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -132,12 +132,6 @@ static int shift_state = 0;
static unsigned char ledstate = 0xff; /* undefined */
static unsigned char ledioctl;
-static struct ledptr {
- unsigned int *addr;
- unsigned int mask;
- unsigned char valid:1;
-} ledptrs[3];
-
/*
* Notifier list for console keyboard events
*/
@@ -994,24 +988,11 @@ void setledstate(struct kbd_struct *kbd, unsigned int led)
static inline unsigned char getleds(void)
{
struct kbd_struct *kbd = kbd_table + fg_console;
- unsigned char leds;
- int i;
if (kbd->ledmode == LED_SHOW_IOCTL)
return ledioctl;
- leds = kbd->ledflagstate;
-
- if (kbd->ledmode == LED_SHOW_MEM) {
- for (i = 0; i < 3; i++)
- if (ledptrs[i].valid) {
- if (*ledptrs[i].addr & ledptrs[i].mask)
- leds |= (1 << i);
- else
- leds &= ~(1 << i);
- }
- }
- return leds;
+ return kbd->ledflagstate;
}
static int kbd_update_leds_helper(struct input_handle *handle, void *data)
diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c
index 60b7b6926059..ea27804d87af 100644
--- a/drivers/tty/vt/selection.c
+++ b/drivers/tty/vt/selection.c
@@ -24,6 +24,7 @@
#include <linux/selection.h>
#include <linux/tiocl.h>
#include <linux/console.h>
+#include <linux/tty_flip.h>
/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
#define isspace(c) ((c) == ' ')
@@ -346,8 +347,8 @@ int paste_selection(struct tty_struct *tty)
console_unlock();
ld = tty_ldisc_ref_wait(tty);
+ tty_buffer_lock_exclusive(&vc->port);
- /* FIXME: this is completely unsafe */
add_wait_queue(&vc->paste_wait, &wait);
while (sel_buffer && sel_buffer_lth > pasted) {
set_current_state(TASK_INTERRUPTIBLE);
@@ -356,13 +357,14 @@ int paste_selection(struct tty_struct *tty)
continue;
}
count = sel_buffer_lth - pasted;
- count = min(count, tty->receive_room);
- ld->ops->receive_buf(tty, sel_buffer + pasted, NULL, count);
+ count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL,
+ count);
pasted += count;
}
remove_wait_queue(&vc->paste_wait, &wait);
__set_current_state(TASK_RUNNING);
+ tty_buffer_unlock_exclusive(&vc->port);
tty_ldisc_deref(ld);
return 0;
}
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index c677829baa8b..02af6ccefe6a 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -828,7 +828,7 @@ static inline int resize_screen(struct vc_data *vc, int width, int height,
* If the caller passes a tty structure then update the termios winsize
* information and perform any necessary signal handling.
*
- * Caller must hold the console semaphore. Takes the termios mutex and
+ * Caller must hold the console semaphore. Takes the termios rwsem and
* ctrl_lock of the tty IFF a tty is passed.
*/
@@ -972,7 +972,7 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows)
* the actual work.
*
* Takes the console sem and the called methods then take the tty
- * termios_mutex and the tty ctrl_lock in that order.
+ * termios_rwsem and the tty ctrl_lock in that order.
*/
static int vt_resize(struct tty_struct *tty, struct winsize *ws)
{
diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h
index b7c8cdc1d422..cbfb171bbcba 100644
--- a/include/linux/kbd_kern.h
+++ b/include/linux/kbd_kern.h
@@ -36,10 +36,9 @@ struct kbd_struct {
#define VC_CTRLRLOCK KG_CTRLR /* ctrlr lock mode */
unsigned char slockstate; /* for `sticky' Shift, Ctrl, etc. */
- unsigned char ledmode:2; /* one 2-bit value */
+ unsigned char ledmode:1;
#define LED_SHOW_FLAGS 0 /* traditional state */
#define LED_SHOW_IOCTL 1 /* only change leds upon ioctl */
-#define LED_SHOW_MEM 2 /* `heartbeat': peek into memory */
unsigned char ledflagstate:4; /* flags, not lights */
unsigned char default_ledflagstate:4;
diff --git a/include/linux/llist.h b/include/linux/llist.h
index cdaa7f023899..8828a78dec9a 100644
--- a/include/linux/llist.h
+++ b/include/linux/llist.h
@@ -125,6 +125,29 @@ static inline void init_llist_head(struct llist_head *list)
(pos) = llist_entry((pos)->member.next, typeof(*(pos)), member))
/**
+ * llist_for_each_entry_safe - iterate over some deleted entries of lock-less list of given type
+ * safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @node: the first entry of deleted list entries.
+ * @member: the name of the llist_node with the struct.
+ *
+ * In general, some entries of the lock-less list can be traversed
+ * safely only after being removed from list, so start with an entry
+ * instead of list head.
+ *
+ * If being used on entries deleted from lock-less list directly, the
+ * traverse order is from the newest to the oldest added entry. If
+ * you want to traverse from the oldest to the newest, you must
+ * reverse the order by yourself before traversing.
+ */
+#define llist_for_each_entry_safe(pos, n, node, member) \
+ for (pos = llist_entry((node), typeof(*pos), member); \
+ &pos->member != NULL && \
+ (n = llist_entry(pos->member.next, typeof(*n), member), true); \
+ pos = n)
+
+/**
* llist_empty - tests whether a lock-less list is empty
* @head: the list to test
*
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 3bed2e89611b..6dec3d6abe0b 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1311,6 +1311,8 @@
#define PCI_DEVICE_ID_IMS_TT128 0x9128
#define PCI_DEVICE_ID_IMS_TT3D 0x9135
+#define PCI_VENDOR_ID_AMCC 0x10e8
+
#define PCI_VENDOR_ID_INTERG 0x10ea
#define PCI_DEVICE_ID_INTERG_1682 0x1682
#define PCI_DEVICE_ID_INTERG_2000 0x2000
@@ -2256,12 +2258,10 @@
/*
* ADDI-DATA GmbH communication cards <info@addi-data.com>
*/
-#define PCI_VENDOR_ID_ADDIDATA_OLD 0x10E8
#define PCI_VENDOR_ID_ADDIDATA 0x15B8
#define PCI_DEVICE_ID_ADDIDATA_APCI7500 0x7000
#define PCI_DEVICE_ID_ADDIDATA_APCI7420 0x7001
#define PCI_DEVICE_ID_ADDIDATA_APCI7300 0x7002
-#define PCI_DEVICE_ID_ADDIDATA_APCI7800 0x818E
#define PCI_DEVICE_ID_ADDIDATA_APCI7500_2 0x7009
#define PCI_DEVICE_ID_ADDIDATA_APCI7420_2 0x700A
#define PCI_DEVICE_ID_ADDIDATA_APCI7300_2 0x700B
diff --git a/include/linux/platform_data/max310x.h b/include/linux/platform_data/max310x.h
index 91648bf5fc5c..dd11dcd1a184 100644
--- a/include/linux/platform_data/max310x.h
+++ b/include/linux/platform_data/max310x.h
@@ -1,5 +1,5 @@
/*
- * Maxim (Dallas) MAX3107/8 serial driver
+ * Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver
*
* Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
*
@@ -37,14 +37,13 @@
* };
*/
-#define MAX310X_MAX_UARTS 1
+#define MAX310X_MAX_UARTS 4
/* MAX310X platform data structure */
struct max310x_pdata {
/* Flags global to driver */
- const u8 driver_flags:2;
+ const u8 driver_flags;
#define MAX310X_EXT_CLK (0x00000001) /* External clock enable */
-#define MAX310X_AUTOSLEEP (0x00000002) /* Enable AutoSleep mode */
/* Flags global to UART port */
const u8 uart_flags[MAX310X_MAX_UARTS];
#define MAX310X_LOOPBACK (0x00000001) /* Loopback mode enable */
@@ -60,8 +59,6 @@ struct max310x_pdata {
void (*init)(void);
/* Called before finish */
void (*exit)(void);
- /* Suspend callback */
- void (*suspend)(int do_suspend);
};
#endif
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 01ac30efd6a6..64f864651d86 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -10,6 +10,8 @@
#include <linux/mutex.h>
#include <linux/tty_flags.h>
#include <uapi/linux/tty.h>
+#include <linux/rwsem.h>
+#include <linux/llist.h>
@@ -29,9 +31,10 @@
#define __DISABLED_CHAR '\0'
struct tty_buffer {
- struct tty_buffer *next;
- char *char_buf_ptr;
- unsigned char *flag_buf_ptr;
+ union {
+ struct tty_buffer *next;
+ struct llist_node free;
+ };
int used;
int size;
int commit;
@@ -40,25 +43,25 @@ struct tty_buffer {
unsigned long data[0];
};
-/*
- * We default to dicing tty buffer allocations to this many characters
- * in order to avoid multiple page allocations. We know the size of
- * tty_buffer itself but it must also be taken into account that the
- * the buffer is 256 byte aligned. See tty_buffer_find for the allocation
- * logic this must match
- */
-
-#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF)
+static inline unsigned char *char_buf_ptr(struct tty_buffer *b, int ofs)
+{
+ return ((unsigned char *)b->data) + ofs;
+}
+static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs)
+{
+ return (char *)char_buf_ptr(b, ofs) + b->size;
+}
struct tty_bufhead {
- struct work_struct work;
- spinlock_t lock;
struct tty_buffer *head; /* Queue head */
+ struct work_struct work;
+ struct mutex lock;
+ atomic_t priority;
+ struct tty_buffer sentinel;
+ struct llist_head free; /* Free queue head */
+ atomic_t memory_used; /* In-use buffers excluding free list */
struct tty_buffer *tail; /* Active buffer */
- struct tty_buffer *free; /* Free queue head */
- int memory_used; /* Buffer space used excluding
- free queue */
};
/*
* When a break, frame error, or parity error happens, these codes are
@@ -199,9 +202,6 @@ struct tty_port {
wait_queue_head_t close_wait; /* Close waiters */
wait_queue_head_t delta_msr_wait; /* Modem status change */
unsigned long flags; /* TTY flags ASY_*/
- unsigned long iflags; /* TTYP_ internal flags */
-#define TTYP_FLUSHING 1 /* Flushing to ldisc in progress */
-#define TTYP_FLUSHPENDING 2 /* Queued buffer flush pending */
unsigned char console:1, /* port is a console */
low_latency:1; /* direct buffer flush */
struct mutex mutex; /* Locking */
@@ -238,14 +238,16 @@ struct tty_struct {
int index;
/* Protects ldisc changes: Lock tty not pty */
- struct mutex ldisc_mutex;
+ struct ld_semaphore ldisc_sem;
struct tty_ldisc *ldisc;
struct mutex atomic_write_lock;
struct mutex legacy_mutex;
- struct mutex termios_mutex;
+ struct mutex throttle_mutex;
+ struct rw_semaphore termios_rwsem;
+ struct mutex winsize_mutex;
spinlock_t ctrl_lock;
- /* Termios values are protected by the termios mutex */
+ /* Termios values are protected by the termios rwsem */
struct ktermios termios, termios_locked;
struct termiox *termiox; /* May be NULL for unsupported */
char name[64];
@@ -253,7 +255,7 @@ struct tty_struct {
struct pid *session;
unsigned long flags;
int count;
- struct winsize winsize; /* termios mutex */
+ struct winsize winsize; /* winsize_mutex */
unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
unsigned char ctrl_status; /* ctrl_lock */
unsigned int receive_room; /* Bytes free for queue */
@@ -303,10 +305,7 @@ struct tty_file_private {
#define TTY_EXCLUSIVE 3 /* Exclusive open mode */
#define TTY_DEBUG 4 /* Debugging */
#define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */
-#define TTY_PUSH 6 /* n_tty private */
#define TTY_CLOSING 7 /* ->close() in progress */
-#define TTY_LDISC 9 /* Line discipline attached */
-#define TTY_LDISC_CHANGING 10 /* Line discipline changing */
#define TTY_LDISC_OPEN 11 /* Line discipline is open */
#define TTY_PTY_LOCK 16 /* pty private */
#define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */
@@ -559,6 +558,19 @@ extern void tty_ldisc_init(struct tty_struct *tty);
extern void tty_ldisc_deinit(struct tty_struct *tty);
extern void tty_ldisc_begin(void);
+static inline int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p,
+ char *f, int count)
+{
+ if (ld->ops->receive_buf2)
+ count = ld->ops->receive_buf2(ld->tty, p, f, count);
+ else {
+ count = min_t(int, count, ld->tty->receive_room);
+ if (count)
+ ld->ops->receive_buf(ld->tty, p, f, count);
+ }
+ return count;
+}
+
/* n_tty.c */
extern struct tty_ldisc_ops tty_ldisc_N_TTY;
diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h
index e0f252633b47..21ddd7d9ea1f 100644
--- a/include/linux/tty_flip.h
+++ b/include/linux/tty_flip.h
@@ -1,6 +1,7 @@
#ifndef _LINUX_TTY_FLIP_H
#define _LINUX_TTY_FLIP_H
+extern int tty_buffer_space_avail(struct tty_port *port);
extern int tty_buffer_request_room(struct tty_port *port, size_t size);
extern int tty_insert_flip_string_flags(struct tty_port *port,
const unsigned char *chars, const char *flags, size_t size);
@@ -18,8 +19,8 @@ static inline int tty_insert_flip_char(struct tty_port *port,
{
struct tty_buffer *tb = port->buf.tail;
if (tb && tb->used < tb->size) {
- tb->flag_buf_ptr[tb->used] = flag;
- tb->char_buf_ptr[tb->used++] = ch;
+ *flag_buf_ptr(tb, tb->used) = flag;
+ *char_buf_ptr(tb, tb->used++) = ch;
return 1;
}
return tty_insert_flip_string_flags(port, &ch, &flag, 1);
@@ -31,4 +32,7 @@ static inline int tty_insert_flip_string(struct tty_port *port,
return tty_insert_flip_string_fixed_flag(port, chars, TTY_NORMAL, size);
}
+extern void tty_buffer_lock_exclusive(struct tty_port *port);
+extern void tty_buffer_unlock_exclusive(struct tty_port *port);
+
#endif /* _LINUX_TTY_FLIP_H */
diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h
index a1b048999821..f15c898ff462 100644
--- a/include/linux/tty_ldisc.h
+++ b/include/linux/tty_ldisc.h
@@ -109,6 +109,17 @@
*
* Tells the discipline that the DCD pin has changed its status.
* Used exclusively by the N_PPS (Pulse-Per-Second) line discipline.
+ *
+ * int (*receive_buf2)(struct tty_struct *, const unsigned char *cp,
+ * char *fp, int count);
+ *
+ * This function is called by the low-level tty driver to send
+ * characters received by the hardware to the line discpline for
+ * processing. <cp> is a pointer to the buffer of input
+ * character received by the device. <fp> is a pointer to a
+ * pointer of flag bytes which indicate whether a character was
+ * received with a parity error, etc.
+ * If assigned, prefer this function for automatic flow control.
*/
#include <linux/fs.h>
@@ -195,6 +206,8 @@ struct tty_ldisc_ops {
void (*write_wakeup)(struct tty_struct *);
void (*dcd_change)(struct tty_struct *, unsigned int);
void (*fasync)(struct tty_struct *tty, int on);
+ int (*receive_buf2)(struct tty_struct *, const unsigned char *cp,
+ char *fp, int count);
struct module *owner;
@@ -203,8 +216,7 @@ struct tty_ldisc_ops {
struct tty_ldisc {
struct tty_ldisc_ops *ops;
- atomic_t users;
- wait_queue_head_t wq_idle;
+ struct tty_struct *tty;
};
#define TTY_LDISC_MAGIC 0x5403
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 9119cc0977bf..e40ebe124ced 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -232,4 +232,7 @@
/* SH-SCI */
#define PORT_HSCIF 104
+/* ST ASC type numbers */
+#define PORT_ASC 105
+
#endif /* _UAPILINUX_SERIAL_CORE_H */
diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h
index e6322605b138..97c26beae605 100644
--- a/include/uapi/linux/serial_reg.h
+++ b/include/uapi/linux/serial_reg.h
@@ -366,6 +366,7 @@
#define UART_OMAP_MDR1_FIR_MODE 0x05 /* FIR mode */
#define UART_OMAP_MDR1_CIR_MODE 0x06 /* CIR mode */
#define UART_OMAP_MDR1_DISABLE 0x07 /* Disable (default state) */
+#define UART_OMAP_TXFIFO_LVL 0x1A /* TX FIFO fullness */
/*
* These are definitions for the Exar XR17V35X and XR17(C|D)15X