Merge 3.11-rc4 into tty-next
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 4 Aug 2013 21:41:03 +0000 (05:41 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 4 Aug 2013 21:41:03 +0000 (05:41 +0800)
We want the tty fixes in here as well.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
77 files changed:
Documentation/devicetree/bindings/tty/serial/atmel-usart.txt
Documentation/devicetree/bindings/tty/serial/st-asc.txt [new file with mode: 0644]
arch/mips/sni/a20r.c
drivers/net/irda/irtty-sir.c
drivers/staging/comedi/comedidev.h
drivers/staging/dgrp/dgrp_tty.c
drivers/tty/hvc/hvc_console.c
drivers/tty/hvc/hvc_console.h
drivers/tty/hvc/hvc_iucv.c
drivers/tty/n_gsm.c
drivers/tty/n_tty.c
drivers/tty/pty.c
drivers/tty/serial/8250/8250_core.c
drivers/tty/serial/8250/8250_em.c
drivers/tty/serial/8250/8250_pci.c
drivers/tty/serial/8250/Kconfig
drivers/tty/serial/Kconfig
drivers/tty/serial/Makefile
drivers/tty/serial/altera_jtaguart.c
drivers/tty/serial/altera_uart.c
drivers/tty/serial/amba-pl010.c
drivers/tty/serial/amba-pl011.c
drivers/tty/serial/ar933x_uart.c
drivers/tty/serial/arc_uart.c
drivers/tty/serial/atmel_serial.c
drivers/tty/serial/bcm63xx_uart.c
drivers/tty/serial/bfin_sport_uart.c
drivers/tty/serial/bfin_uart.c
drivers/tty/serial/clps711x.c
drivers/tty/serial/efm32-uart.c
drivers/tty/serial/fsl_lpuart.c
drivers/tty/serial/ifx6x60.c
drivers/tty/serial/imx.c
drivers/tty/serial/max3100.c
drivers/tty/serial/max310x.c
drivers/tty/serial/mcf.c
drivers/tty/serial/mpsc.c
drivers/tty/serial/msm_serial.c
drivers/tty/serial/msm_serial.h
drivers/tty/serial/msm_serial_hs.c
drivers/tty/serial/mxs-auart.c
drivers/tty/serial/netx-serial.c
drivers/tty/serial/omap-serial.c
drivers/tty/serial/pch_uart.c
drivers/tty/serial/pmac_zilog.c
drivers/tty/serial/pnx8xxx_uart.c
drivers/tty/serial/pxa.c
drivers/tty/serial/sa1100.c
drivers/tty/serial/samsung.c
drivers/tty/serial/sc26xx.c
drivers/tty/serial/sccnxp.c
drivers/tty/serial/serial_core.c
drivers/tty/serial/serial_txx9.c
drivers/tty/serial/sh-sci.c
drivers/tty/serial/sirfsoc_uart.c
drivers/tty/serial/st-asc.c [new file with mode: 0644]
drivers/tty/serial/vr41xx_siu.c
drivers/tty/serial/vt8500_serial.c
drivers/tty/synclink.c
drivers/tty/tty_buffer.c
drivers/tty/tty_io.c
drivers/tty/tty_ioctl.c
drivers/tty/tty_ldisc.c
drivers/tty/vt/keyboard.c
drivers/tty/vt/selection.c
drivers/tty/vt/vt.c
include/linux/atmel_serial.h
include/linux/kbd_kern.h
include/linux/llist.h
include/linux/pci_ids.h
include/linux/platform_data/max310x.h
include/linux/platform_data/serial-sccnxp.h
include/linux/tty.h
include/linux/tty_flip.h
include/linux/tty_ldisc.h
include/uapi/linux/serial_core.h
include/uapi/linux/serial_reg.h

index a49d9a1d4ccf8f054cc1faed657f32095d85a1c3..2191dcb9f1da986d008a87688ea0af693ea3bf28 100644 (file)
@@ -10,13 +10,18 @@ Required properties:
 Optional properties:
 - atmel,use-dma-rx: use of PDC or DMA for receiving data
 - atmel,use-dma-tx: use of PDC or DMA for transmitting data
+- add dma bindings for dma transfer:
+       - dmas: DMA specifier, consisting of a phandle to DMA controller node,
+               memory peripheral interface and USART DMA channel ID, FIFO configuration.
+               Refer to dma.txt and atmel-dma.txt for details.
+       - dma-names: "rx" for RX channel, "tx" for TX channel.
 
 <chip> compatible description:
 - at91rm9200:  legacy USART support
 - at91sam9260: generic USART implementation for SAM9 SoCs
 
 Example:
-
+- use PDC:
        usart0: serial@fff8c000 {
                compatible = "atmel,at91sam9260-usart";
                reg = <0xfff8c000 0x4000>;
@@ -25,3 +30,14 @@ Example:
                atmel,use-dma-tx;
        };
 
+- use DMA:
+       usart0: serial@f001c000 {
+               compatible = "atmel,at91sam9260-usart";
+               reg = <0xf001c000 0x100>;
+               interrupts = <12 4 5>;
+               atmel,use-dma-rx;
+               atmel,use-dma-tx;
+               dmas = <&dma0 2 0x3>,
+                      <&dma0 2 0x204>;
+               dma-names = "tx", "rx";
+       };
diff --git a/Documentation/devicetree/bindings/tty/serial/st-asc.txt b/Documentation/devicetree/bindings/tty/serial/st-asc.txt
new file mode 100644 (file)
index 0000000..75d877f
--- /dev/null
@@ -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>;
+};
index dd0ab982d77ec4ed769b29544e9bcacc6206f2fb..f9407e17047624e046c696235ae05d8e53e8780f 100644 (file)
@@ -122,7 +122,6 @@ static struct resource sc26xx_rsrc[] = {
 
 static struct sccnxp_pdata sccnxp_data = {
        .reg_shift      = 2,
-       .frequency      = 3686400,
        .mctrl_cfg[0]   = MCTRL_SIG(DTR_OP, LINE_OP7) |
                          MCTRL_SIG(RTS_OP, LINE_OP3) |
                          MCTRL_SIG(DSR_IP, LINE_IP5) |
index a41267197839304be5ad0deaa00b93f59e0713e7..177441afeb9680de599905bf75e8c1e10a9c7e0b 100644 (file)
@@ -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);
 }
 
 /*****************************************************************/
index b75915f30f48492c99060bb23a805cc6721fe0b3..9f4f73fa652c8bd60a400e22c5a6ef04fe78dae5 100644 (file)
@@ -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
index 654f6010b473d7d93e817bf58cf7b9c4a2e0bd58..0d52de3729c6c1b2e4a72a37ee136bd98a69ab41 100644 (file)
@@ -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;
                }
 
index eb255e807c0662250887beea86a2fb73081a6deb..9eba119bcdd3488d7d7541448a2371497c4d70f4 100644 (file)
@@ -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);
 
index 674d23cb919afaeac34d5bc0c7bc8bbbd38c9c1f..91310198082736e0a5bffb75192b71aa1649764a 100644 (file)
@@ -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) */
index 9d47f50c2755a37835ec04662a194db631dba08a..fd17a9b804b8ab8608097661b824e27c5bb1cd02 100644 (file)
@@ -655,6 +655,49 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
        spin_unlock_bh(&priv->lock);
 }
 
+/**
+ * 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)
@@ -662,15 +705,15 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
  *             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 */
index 642239015b46bbe9e18844f186a31296a343d5f9..c0f76da553042db2f3eadb91cfb9ff08bc60e43d 100644 (file)
@@ -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) {
index 4bf0fc0843d73bc61cd99d0cc5898177fe0dc39c..dd8ae0cad1d5353d88c44e9b5bb55edb09bbd33f 100644 (file)
@@ -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 */
 #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,
 };
 
 /**
index abfd9908978188ffe3bfd7a9c3aa3e8c6700c5d9..25c9bc7837229abc2fb527732386111fb21194f0 100644 (file)
@@ -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;
 }
 
index 86c00b1c55836b3c52801c9d79bccef489c728d2..570df9d2a5d2f46afc72a23d1e373f2c5aa4f3ba 100644 (file)
@@ -3062,7 +3062,7 @@ void serial8250_resume_port(int line)
  */
 static int serial8250_probe(struct platform_device *dev)
 {
-       struct plat_serial8250_port *p = dev->dev.platform_data;
+       struct plat_serial8250_port *p = dev_get_platdata(&dev->dev);
        struct uart_8250_port uart;
        int ret, i, irqflag = 0;
 
index 916cc19fbbda4ae8d52ff612484950a8669b178e..5f3bba12c159eb4ceaef924495929f19eb5b4247 100644 (file)
@@ -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;
 }
 
index c52948b368d81a1dbd6ea274ffa3058f1128ea37..c810da7c7a8843d452ced857d64863993365b464 100644 (file)
@@ -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 },
index a1ba94d648859ecc8c722877ff38fffaf5ce1c96..f3b306efaa591d518b1b894253e0714ea3cba5c6 100644 (file)
@@ -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
index 1456673bcca09b58cc4d1e7c27fd31569b8439bf..f13624807d124dfc3ae466a6ce5409be227f4d44 100644 (file)
@@ -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
index cf650f0cd6e4175adb72d9139c278dc33ec7b4fa..47b679c547e9a86d78a0a13114739422f11ae036 100644 (file)
@@ -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
index c6bdb943726b0494a9deab39a5be591edb76cf9a..2299a34225d452ee948bc89cb18cbaf72d8029a6 100644 (file)
@@ -408,7 +408,8 @@ static struct uart_driver altera_jtaguart_driver = {
 
 static int altera_jtaguart_probe(struct platform_device *pdev)
 {
-       struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data;
+       struct altera_jtaguart_platform_uart *platp =
+                       dev_get_platdata(&pdev->dev);
        struct uart_port *port;
        struct resource *res_irq, *res_mem;
        int i = pdev->id;
index 1d46966e2a65cc742c8d8920fe8004d4bcad3a48..fa638db9a98c8f4d2985c5907ba3aed4fdc59c4f 100644 (file)
@@ -534,7 +534,7 @@ static int altera_uart_get_of_uartclk(struct platform_device *pdev,
 
 static int altera_uart_probe(struct platform_device *pdev)
 {
-       struct altera_uart_platform_uart *platp = pdev->dev.platform_data;
+       struct altera_uart_platform_uart *platp = dev_get_platdata(&pdev->dev);
        struct uart_port *port;
        struct resource *res_mem;
        struct resource *res_irq;
index c3684051952778b568cbe73177196d12a7ef7f38..8b90f0b6dfdfe7838c43863350d93f9fccc41dae 100644 (file)
@@ -721,7 +721,7 @@ static int pl010_probe(struct amba_device *dev, const struct amba_id *id)
        uap->port.flags = UPF_BOOT_AUTOCONF;
        uap->port.line = i;
        uap->dev = dev;
-       uap->data = dev->dev.platform_data;
+       uap->data = dev_get_platdata(&dev->dev);
 
        amba_ports[i] = uap;
 
index 28b35ad9c6cd361818d94c0cde6ee2aa987979c0..eb38fb851b4ec5596fca6c8ede7e6fb4d471e7a8 100644 (file)
@@ -265,7 +265,7 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg,
 static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap)
 {
        /* DMA is the sole user of the platform data right now */
-       struct amba_pl011_data *plat = uap->port.dev->platform_data;
+       struct amba_pl011_data *plat = dev_get_platdata(uap->port.dev);
        struct dma_slave_config tx_conf = {
                .dst_addr = uap->port.mapbase + UART01x_DR,
                .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
@@ -1497,10 +1497,10 @@ static int pl011_hwinit(struct uart_port *port)
        uap->im = readw(uap->port.membase + UART011_IMSC);
        writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC);
 
-       if (uap->port.dev->platform_data) {
+       if (dev_get_platdata(uap->port.dev)) {
                struct amba_pl011_data *plat;
 
-               plat = uap->port.dev->platform_data;
+               plat = dev_get_platdata(uap->port.dev);
                if (plat->init)
                        plat->init();
        }
@@ -1645,10 +1645,10 @@ static void pl011_shutdown(struct uart_port *port)
        /* Optionally let pins go into sleep states */
        pinctrl_pm_select_sleep_state(port->dev);
 
-       if (uap->port.dev->platform_data) {
+       if (dev_get_platdata(uap->port.dev)) {
                struct amba_pl011_data *plat;
 
-               plat = uap->port.dev->platform_data;
+               plat = dev_get_platdata(uap->port.dev);
                if (plat->exit)
                        plat->exit();
        }
@@ -2002,10 +2002,10 @@ static int __init pl011_console_setup(struct console *co, char *options)
        if (ret)
                return ret;
 
-       if (uap->port.dev->platform_data) {
+       if (dev_get_platdata(uap->port.dev)) {
                struct amba_pl011_data *plat;
 
-               plat = uap->port.dev->platform_data;
+               plat = dev_get_platdata(uap->port.dev);
                if (plat->init)
                        plat->init();
        }
index 27f20c57abede4ec6874ec4f1de5654b6d618da9..7aa9062153e8cc4c2c0ba0565ab2ff79efbd7092 100644 (file)
@@ -629,7 +629,7 @@ static int ar933x_uart_probe(struct platform_device *pdev)
        int id;
        int ret;
 
-       pdata = pdev->dev.platform_data;
+       pdata = dev_get_platdata(&pdev->dev);
        if (!pdata)
                return -EINVAL;
 
@@ -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);
index 22f280aa4f2c521b5829a6ea5e72b8c05f2265d9..37abf6ca2f3484a70dea6af2fee36360bcfb554a 100644 (file)
@@ -209,9 +209,9 @@ static void arc_serial_start_tx(struct uart_port *port)
        arc_serial_tx_chars(uart);
 }
 
-static void arc_serial_rx_chars(struct arc_uart_port *uart)
+static void arc_serial_rx_chars(struct arc_uart_port *uart, unsigned int status)
 {
-       unsigned int status, ch, flg = 0;
+       unsigned int ch, flg = 0;
 
        /*
         * UART has 4 deep RX-FIFO. Driver's recongnition of this fact
@@ -222,11 +222,11 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart)
         * before RX-EMPTY=0, implies some sort of buffering going on in the
         * controller, which is indeed the Rx-FIFO.
         */
-       while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)) {
-
-               ch = UART_GET_DATA(uart);
-               uart->port.icount.rx++;
-
+       do {
+               /*
+                * This could be an Rx Intr for err (no data),
+                * so check err and clear that Intr first
+                */
                if (unlikely(status & (RXOERR | RXFERR))) {
                        if (status & RXOERR) {
                                uart->port.icount.overrun++;
@@ -242,14 +242,17 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart)
                } else
                        flg = TTY_NORMAL;
 
-               if (unlikely(uart_handle_sysrq_char(&uart->port, ch)))
-                       goto done;
+               if (status & RXEMPTY)
+                       continue;
 
-               uart_insert_char(&uart->port, status, RXOERR, ch, flg);
+               ch = UART_GET_DATA(uart);
+               uart->port.icount.rx++;
+
+               if (!(uart_handle_sysrq_char(&uart->port, ch)))
+                       uart_insert_char(&uart->port, status, RXOERR, ch, flg);
 
-done:
                tty_flip_buffer_push(&uart->port.state->port);
-       }
+       } while (!((status = UART_GET_STATUS(uart)) & RXEMPTY));
 }
 
 /*
@@ -292,11 +295,11 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id)
         * notifications from the UART Controller.
         * To demultiplex between the two, we check the relevant bits
         */
-       if ((status & RXIENB) && !(status & RXEMPTY)) {
+       if (status & RXIENB) {
 
                /* already in ISR, no need of xx_irqsave */
                spin_lock(&uart->port.lock);
-               arc_serial_rx_chars(uart);
+               arc_serial_rx_chars(uart, status);
                spin_unlock(&uart->port.lock);
        }
 
@@ -528,7 +531,7 @@ arc_uart_init_one(struct platform_device *pdev, int dev_id)
        unsigned long *plat_data;
        struct arc_uart_port *uart = &arc_uart_ports[dev_id];
 
-       plat_data = ((unsigned long *)(pdev->dev.platform_data));
+       plat_data = (unsigned long *)dev_get_platdata(&pdev->dev);
        if (!plat_data)
                return -ENODEV;
 
index 691265faebbe608c555573513d4735be18c7d8e8..d067285a2d203765d38d36535d588854988f75c2 100644 (file)
@@ -39,8 +39,8 @@
 #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 <linux/timer.h>
 
 #include <asm/io.h>
 #include <asm/ioctls.h>
@@ -98,6 +98,7 @@ static void atmel_stop_rx(struct uart_port *port);
 #define UART_PUT_BRGR(port,v)  __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
 #define UART_PUT_RTOR(port,v)  __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
 #define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
+#define UART_GET_IP_NAME(port) __raw_readl((port)->membase + ATMEL_US_NAME)
 
  /* PDC registers */
 #define UART_PUT_PTCR(port,v)  __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -140,13 +141,25 @@ struct atmel_uart_port {
        u32                     backup_imr;     /* IMR saved during suspend */
        int                     break_active;   /* break being received */
 
-       short                   use_dma_rx;     /* enable PDC receiver */
+       bool                    use_dma_rx;     /* enable DMA receiver */
+       bool                    use_pdc_rx;     /* enable PDC receiver */
        short                   pdc_rx_idx;     /* current PDC RX buffer */
        struct atmel_dma_buffer pdc_rx[2];      /* PDC receier */
 
-       short                   use_dma_tx;     /* enable PDC transmitter */
+       bool                    use_dma_tx;     /* enable DMA transmitter */
+       bool                    use_pdc_tx;     /* enable PDC transmitter */
        struct atmel_dma_buffer pdc_tx;         /* PDC transmitter */
 
+       spinlock_t                      lock_tx;        /* port lock */
+       spinlock_t                      lock_rx;        /* port lock */
+       struct dma_chan                 *chan_tx;
+       struct dma_chan                 *chan_rx;
+       struct dma_async_tx_descriptor  *desc_tx;
+       struct dma_async_tx_descriptor  *desc_rx;
+       dma_cookie_t                    cookie_tx;
+       dma_cookie_t                    cookie_rx;
+       struct scatterlist              sg_tx;
+       struct scatterlist              sg_rx;
        struct tasklet_struct   tasklet;
        unsigned int            irq_status;
        unsigned int            irq_status_prev;
@@ -155,6 +168,14 @@ struct atmel_uart_port {
 
        struct serial_rs485     rs485;          /* rs485 settings */
        unsigned int            tx_done_mask;
+       bool                    is_usart;       /* usart or uart */
+       struct timer_list       uart_timer;     /* uart timer */
+       int (*prepare_rx)(struct uart_port *port);
+       int (*prepare_tx)(struct uart_port *port);
+       void (*schedule_rx)(struct uart_port *port);
+       void (*schedule_tx)(struct uart_port *port);
+       void (*release_rx)(struct uart_port *port);
+       void (*release_tx)(struct uart_port *port);
 };
 
 static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -181,31 +202,45 @@ to_atmel_uart_port(struct uart_port *uart)
 }
 
 #ifdef CONFIG_SERIAL_ATMEL_PDC
-static bool atmel_use_dma_rx(struct uart_port *port)
+static bool atmel_use_pdc_rx(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
-       return atmel_port->use_dma_rx;
+       return atmel_port->use_pdc_rx;
 }
 
-static bool atmel_use_dma_tx(struct uart_port *port)
+static bool atmel_use_pdc_tx(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
-       return atmel_port->use_dma_tx;
+       return atmel_port->use_pdc_tx;
 }
 #else
-static bool atmel_use_dma_rx(struct uart_port *port)
+static bool atmel_use_pdc_rx(struct uart_port *port)
 {
        return false;
 }
 
-static bool atmel_use_dma_tx(struct uart_port *port)
+static bool atmel_use_pdc_tx(struct uart_port *port)
 {
        return false;
 }
 #endif
 
+static bool atmel_use_dma_tx(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+       return atmel_port->use_dma_tx;
+}
+
+static bool atmel_use_dma_rx(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+       return atmel_port->use_dma_rx;
+}
+
 /* Enable or disable the rs485 support */
 void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
 {
@@ -233,7 +268,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
                mode |= ATMEL_US_USMODE_RS485;
        } else {
                dev_dbg(port->dev, "Setting UART to RS232\n");
-               if (atmel_use_dma_tx(port))
+               if (atmel_use_pdc_tx(port))
                        atmel_port->tx_done_mask = ATMEL_US_ENDTX |
                                ATMEL_US_TXBUFE;
                else
@@ -345,7 +380,7 @@ static void atmel_stop_tx(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
-       if (atmel_use_dma_tx(port)) {
+       if (atmel_use_pdc_tx(port)) {
                /* disable PDC transmit */
                UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
        }
@@ -364,7 +399,7 @@ static void atmel_start_tx(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
-       if (atmel_use_dma_tx(port)) {
+       if (atmel_use_pdc_tx(port)) {
                if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
                        /* The transmitter is already running.  Yes, we
                           really need this.*/
@@ -390,7 +425,7 @@ static void atmel_start_rx(struct uart_port *port)
 
        UART_PUT_CR(port, ATMEL_US_RXEN);
 
-       if (atmel_use_dma_rx(port)) {
+       if (atmel_use_pdc_rx(port)) {
                /* enable PDC controller */
                UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
                        port->read_status_mask);
@@ -407,7 +442,7 @@ static void atmel_stop_rx(struct uart_port *port)
 {
        UART_PUT_CR(port, ATMEL_US_RXDIS);
 
-       if (atmel_use_dma_rx(port)) {
+       if (atmel_use_pdc_rx(port)) {
                /* disable PDC receive */
                UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
                UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
@@ -564,6 +599,372 @@ static void atmel_tx_chars(struct uart_port *port)
                UART_PUT_IER(port, atmel_port->tx_done_mask);
 }
 
+static void atmel_complete_tx_dma(void *arg)
+{
+       struct atmel_uart_port *atmel_port = arg;
+       struct uart_port *port = &atmel_port->uart;
+       struct circ_buf *xmit = &port->state->xmit;
+       struct dma_chan *chan = atmel_port->chan_tx;
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       if (chan)
+               dmaengine_terminate_all(chan);
+       xmit->tail += sg_dma_len(&atmel_port->sg_tx);
+       xmit->tail &= UART_XMIT_SIZE - 1;
+
+       port->icount.tx += sg_dma_len(&atmel_port->sg_tx);
+
+       spin_lock_irq(&atmel_port->lock_tx);
+       async_tx_ack(atmel_port->desc_tx);
+       atmel_port->cookie_tx = -EINVAL;
+       atmel_port->desc_tx = NULL;
+       spin_unlock_irq(&atmel_port->lock_tx);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+
+       /* Do we really need this? */
+       if (!uart_circ_empty(xmit))
+               tasklet_schedule(&atmel_port->tasklet);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void atmel_release_tx_dma(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct dma_chan *chan = atmel_port->chan_tx;
+
+       if (chan) {
+               dmaengine_terminate_all(chan);
+               dma_release_channel(chan);
+               dma_unmap_sg(port->dev, &atmel_port->sg_tx, 1,
+                               DMA_MEM_TO_DEV);
+       }
+
+       atmel_port->desc_tx = NULL;
+       atmel_port->chan_tx = NULL;
+       atmel_port->cookie_tx = -EINVAL;
+}
+
+/*
+ * Called from tasklet with TXRDY interrupt is disabled.
+ */
+static void atmel_tx_dma(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct circ_buf *xmit = &port->state->xmit;
+       struct dma_chan *chan = atmel_port->chan_tx;
+       struct dma_async_tx_descriptor *desc;
+       struct scatterlist *sg = &atmel_port->sg_tx;
+
+       /* Make sure we have an idle channel */
+       if (atmel_port->desc_tx != NULL)
+               return;
+
+       if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+               /*
+                * DMA is idle now.
+                * Port xmit buffer is already mapped,
+                * and it is one page... Just adjust
+                * offsets and lengths. Since it is a circular buffer,
+                * we have to transmit till the end, and then the rest.
+                * Take the port lock to get a
+                * consistent xmit buffer state.
+                */
+               sg->offset = xmit->tail & (UART_XMIT_SIZE - 1);
+               sg_dma_address(sg) = (sg_dma_address(sg) &
+                                       ~(UART_XMIT_SIZE - 1))
+                                       + sg->offset;
+               sg_dma_len(sg) = CIRC_CNT_TO_END(xmit->head,
+                                               xmit->tail,
+                                               UART_XMIT_SIZE);
+               BUG_ON(!sg_dma_len(sg));
+
+               desc = dmaengine_prep_slave_sg(chan,
+                                               sg,
+                                               1,
+                                               DMA_MEM_TO_DEV,
+                                               DMA_PREP_INTERRUPT |
+                                               DMA_CTRL_ACK);
+               if (!desc) {
+                       dev_err(port->dev, "Failed to send via dma!\n");
+                       return;
+               }
+
+               dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV);
+
+               atmel_port->desc_tx = desc;
+               desc->callback = atmel_complete_tx_dma;
+               desc->callback_param = atmel_port;
+               atmel_port->cookie_tx = dmaengine_submit(desc);
+
+       } else {
+               if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+                       /* DMA done, stop TX, start RX for RS485 */
+                       atmel_start_rx(port);
+               }
+       }
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+}
+
+static int atmel_prepare_tx_dma(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       dma_cap_mask_t          mask;
+       struct dma_slave_config config;
+       int ret, nent;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       atmel_port->chan_tx = dma_request_slave_channel(port->dev, "tx");
+       if (atmel_port->chan_tx == NULL)
+               goto chan_err;
+       dev_info(port->dev, "using %s for tx DMA transfers\n",
+               dma_chan_name(atmel_port->chan_tx));
+
+       spin_lock_init(&atmel_port->lock_tx);
+       sg_init_table(&atmel_port->sg_tx, 1);
+       /* UART circular tx buffer is an aligned page. */
+       BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
+       sg_set_page(&atmel_port->sg_tx,
+                       virt_to_page(port->state->xmit.buf),
+                       UART_XMIT_SIZE,
+                       (int)port->state->xmit.buf & ~PAGE_MASK);
+       nent = dma_map_sg(port->dev,
+                               &atmel_port->sg_tx,
+                               1,
+                               DMA_MEM_TO_DEV);
+
+       if (!nent) {
+               dev_dbg(port->dev, "need to release resource of dma\n");
+               goto chan_err;
+       } else {
+               dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__,
+                       sg_dma_len(&atmel_port->sg_tx),
+                       port->state->xmit.buf,
+                       sg_dma_address(&atmel_port->sg_tx));
+       }
+
+       /* Configure the slave DMA */
+       memset(&config, 0, sizeof(config));
+       config.direction = DMA_MEM_TO_DEV;
+       config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       config.dst_addr = port->mapbase + ATMEL_US_THR;
+
+       ret = dmaengine_device_control(atmel_port->chan_tx,
+                                       DMA_SLAVE_CONFIG,
+                                       (unsigned long)&config);
+       if (ret) {
+               dev_err(port->dev, "DMA tx slave configuration failed\n");
+               goto chan_err;
+       }
+
+       return 0;
+
+chan_err:
+       dev_err(port->dev, "TX channel not available, switch to pio\n");
+       atmel_port->use_dma_tx = 0;
+       if (atmel_port->chan_tx)
+               atmel_release_tx_dma(port);
+       return -EINVAL;
+}
+
+static void atmel_flip_buffer_rx_dma(struct uart_port *port,
+                                       char *buf, size_t count)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct tty_port *tport = &port->state->port;
+
+       dma_sync_sg_for_cpu(port->dev,
+                               &atmel_port->sg_rx,
+                               1,
+                               DMA_DEV_TO_MEM);
+
+       tty_insert_flip_string(tport, buf, count);
+
+       dma_sync_sg_for_device(port->dev,
+                               &atmel_port->sg_rx,
+                               1,
+                               DMA_DEV_TO_MEM);
+       /*
+        * Drop the lock here since it might end up calling
+        * uart_start(), which takes the lock.
+        */
+       spin_unlock(&port->lock);
+       tty_flip_buffer_push(tport);
+       spin_lock(&port->lock);
+}
+
+static void atmel_complete_rx_dma(void *arg)
+{
+       struct uart_port *port = arg;
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+       tasklet_schedule(&atmel_port->tasklet);
+}
+
+static void atmel_release_rx_dma(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct dma_chan *chan = atmel_port->chan_rx;
+
+       if (chan) {
+               dmaengine_terminate_all(chan);
+               dma_release_channel(chan);
+               dma_unmap_sg(port->dev, &atmel_port->sg_rx, 1,
+                               DMA_DEV_TO_MEM);
+       }
+
+       atmel_port->desc_rx = NULL;
+       atmel_port->chan_rx = NULL;
+       atmel_port->cookie_rx = -EINVAL;
+
+       if (!atmel_port->is_usart)
+               del_timer_sync(&atmel_port->uart_timer);
+}
+
+static void atmel_rx_from_dma(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct circ_buf *ring = &atmel_port->rx_ring;
+       struct dma_chan *chan = atmel_port->chan_rx;
+       struct dma_tx_state state;
+       enum dma_status dmastat;
+       size_t pending, count;
+
+
+       /* Reset the UART timeout early so that we don't miss one */
+       UART_PUT_CR(port, ATMEL_US_STTTO);
+       dmastat = dmaengine_tx_status(chan,
+                               atmel_port->cookie_rx,
+                               &state);
+       /* Restart a new tasklet if DMA status is error */
+       if (dmastat == DMA_ERROR) {
+               dev_dbg(port->dev, "Get residue error, restart tasklet\n");
+               UART_PUT_IER(port, ATMEL_US_TIMEOUT);
+               tasklet_schedule(&atmel_port->tasklet);
+               return;
+       }
+       /* current transfer size should no larger than dma buffer */
+       pending = sg_dma_len(&atmel_port->sg_rx) - state.residue;
+       BUG_ON(pending > sg_dma_len(&atmel_port->sg_rx));
+
+       /*
+        * This will take the chars we have so far,
+        * ring->head will record the transfer size, only new bytes come
+        * will insert into the framework.
+        */
+       if (pending > ring->head) {
+               count = pending - ring->head;
+
+               atmel_flip_buffer_rx_dma(port, ring->buf + ring->head, count);
+
+               ring->head += count;
+               if (ring->head == sg_dma_len(&atmel_port->sg_rx))
+                       ring->head = 0;
+
+               port->icount.rx += count;
+       }
+
+       UART_PUT_IER(port, ATMEL_US_TIMEOUT);
+}
+
+static int atmel_prepare_rx_dma(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct dma_async_tx_descriptor *desc;
+       dma_cap_mask_t          mask;
+       struct dma_slave_config config;
+       struct circ_buf         *ring;
+       int ret, nent;
+
+       ring = &atmel_port->rx_ring;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_CYCLIC, mask);
+
+       atmel_port->chan_rx = dma_request_slave_channel(port->dev, "rx");
+       if (atmel_port->chan_rx == NULL)
+               goto chan_err;
+       dev_info(port->dev, "using %s for rx DMA transfers\n",
+               dma_chan_name(atmel_port->chan_rx));
+
+       spin_lock_init(&atmel_port->lock_rx);
+       sg_init_table(&atmel_port->sg_rx, 1);
+       /* UART circular rx buffer is an aligned page. */
+       BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
+       sg_set_page(&atmel_port->sg_rx,
+                       virt_to_page(ring->buf),
+                       ATMEL_SERIAL_RINGSIZE,
+                       (int)ring->buf & ~PAGE_MASK);
+                       nent = dma_map_sg(port->dev,
+                                       &atmel_port->sg_rx,
+                                       1,
+                                       DMA_DEV_TO_MEM);
+
+       if (!nent) {
+               dev_dbg(port->dev, "need to release resource of dma\n");
+               goto chan_err;
+       } else {
+               dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__,
+                       sg_dma_len(&atmel_port->sg_rx),
+                       ring->buf,
+                       sg_dma_address(&atmel_port->sg_rx));
+       }
+
+       /* Configure the slave DMA */
+       memset(&config, 0, sizeof(config));
+       config.direction = DMA_DEV_TO_MEM;
+       config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       config.src_addr = port->mapbase + ATMEL_US_RHR;
+
+       ret = dmaengine_device_control(atmel_port->chan_rx,
+                                       DMA_SLAVE_CONFIG,
+                                       (unsigned long)&config);
+       if (ret) {
+               dev_err(port->dev, "DMA rx slave configuration failed\n");
+               goto chan_err;
+       }
+       /*
+        * Prepare a cyclic dma transfer, assign 2 descriptors,
+        * each one is half ring buffer size
+        */
+       desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx,
+                               sg_dma_address(&atmel_port->sg_rx),
+                               sg_dma_len(&atmel_port->sg_rx),
+                               sg_dma_len(&atmel_port->sg_rx)/2,
+                               DMA_DEV_TO_MEM,
+                               DMA_PREP_INTERRUPT);
+       desc->callback = atmel_complete_rx_dma;
+       desc->callback_param = port;
+       atmel_port->desc_rx = desc;
+       atmel_port->cookie_rx = dmaengine_submit(desc);
+
+       return 0;
+
+chan_err:
+       dev_err(port->dev, "RX channel not available, switch to pio\n");
+       atmel_port->use_dma_rx = 0;
+       if (atmel_port->chan_rx)
+               atmel_release_rx_dma(port);
+       return -EINVAL;
+}
+
+static void atmel_uart_timer_callback(unsigned long data)
+{
+       struct uart_port *port = (void *)data;
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+       tasklet_schedule(&atmel_port->tasklet);
+       mod_timer(&atmel_port->uart_timer, jiffies + uart_poll_timeout(port));
+}
+
 /*
  * receive interrupt handler.
  */
@@ -572,7 +973,7 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
-       if (atmel_use_dma_rx(port)) {
+       if (atmel_use_pdc_rx(port)) {
                /*
                 * PDC receive. Just schedule the tasklet and let it
                 * figure out the details.
@@ -591,6 +992,13 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending)
                        atmel_pdc_rxerr(port, pending);
        }
 
+       if (atmel_use_dma_rx(port)) {
+               if (pending & ATMEL_US_TIMEOUT) {
+                       UART_PUT_IDR(port, ATMEL_US_TIMEOUT);
+                       tasklet_schedule(&atmel_port->tasklet);
+               }
+       }
+
        /* Interrupt receive */
        if (pending & ATMEL_US_RXRDY)
                atmel_rx_chars(port);
@@ -658,10 +1066,21 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
        return pass_counter ? IRQ_HANDLED : IRQ_NONE;
 }
 
+static void atmel_release_tx_pdc(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+
+       dma_unmap_single(port->dev,
+                        pdc->dma_addr,
+                        pdc->dma_size,
+                        DMA_TO_DEVICE);
+}
+
 /*
  * Called from tasklet with ENDTX and TXBUFE interrupts disabled.
  */
-static void atmel_tx_dma(struct uart_port *port)
+static void atmel_tx_pdc(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
        struct circ_buf *xmit = &port->state->xmit;
@@ -710,6 +1129,23 @@ static void atmel_tx_dma(struct uart_port *port)
                uart_write_wakeup(port);
 }
 
+static int atmel_prepare_tx_pdc(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+       struct circ_buf *xmit = &port->state->xmit;
+
+       pdc->buf = xmit->buf;
+       pdc->dma_addr = dma_map_single(port->dev,
+                                       pdc->buf,
+                                       UART_XMIT_SIZE,
+                                       DMA_TO_DEVICE);
+       pdc->dma_size = UART_XMIT_SIZE;
+       pdc->ofs = 0;
+
+       return 0;
+}
+
 static void atmel_rx_from_ring(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
@@ -778,7 +1214,26 @@ static void atmel_rx_from_ring(struct uart_port *port)
        spin_lock(&port->lock);
 }
 
-static void atmel_rx_from_dma(struct uart_port *port)
+static void atmel_release_rx_pdc(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+
+               dma_unmap_single(port->dev,
+                                pdc->dma_addr,
+                                pdc->dma_size,
+                                DMA_FROM_DEVICE);
+               kfree(pdc->buf);
+       }
+
+       if (!atmel_port->is_usart)
+               del_timer_sync(&atmel_port->uart_timer);
+}
+
+static void atmel_rx_from_pdc(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
        struct tty_port *tport = &port->state->port;
@@ -855,6 +1310,45 @@ static void atmel_rx_from_dma(struct uart_port *port)
        UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
 }
 
+static int atmel_prepare_rx_pdc(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+
+               pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
+               if (pdc->buf == NULL) {
+                       if (i != 0) {
+                               dma_unmap_single(port->dev,
+                                       atmel_port->pdc_rx[0].dma_addr,
+                                       PDC_BUFFER_SIZE,
+                                       DMA_FROM_DEVICE);
+                               kfree(atmel_port->pdc_rx[0].buf);
+                       }
+                       atmel_port->use_pdc_rx = 0;
+                       return -ENOMEM;
+               }
+               pdc->dma_addr = dma_map_single(port->dev,
+                                               pdc->buf,
+                                               PDC_BUFFER_SIZE,
+                                               DMA_FROM_DEVICE);
+               pdc->dma_size = PDC_BUFFER_SIZE;
+               pdc->ofs = 0;
+       }
+
+       atmel_port->pdc_rx_idx = 0;
+
+       UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
+       UART_PUT_RCR(port, PDC_BUFFER_SIZE);
+
+       UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
+       UART_PUT_RNCR(port, PDC_BUFFER_SIZE);
+
+       return 0;
+}
+
 /*
  * tasklet handling tty stuff outside the interrupt handler.
  */
@@ -868,10 +1362,7 @@ static void atmel_tasklet_func(unsigned long data)
        /* The interrupt handler does not take the lock */
        spin_lock(&port->lock);
 
-       if (atmel_use_dma_tx(port))
-               atmel_tx_dma(port);
-       else
-               atmel_tx_chars(port);
+       atmel_port->schedule_tx(port);
 
        status = atmel_port->irq_status;
        status_change = status ^ atmel_port->irq_status_prev;
@@ -893,19 +1384,152 @@ static void atmel_tasklet_func(unsigned long data)
                atmel_port->irq_status_prev = status;
        }
 
-       if (atmel_use_dma_rx(port))
-               atmel_rx_from_dma(port);
-       else
-               atmel_rx_from_ring(port);
+       atmel_port->schedule_rx(port);
 
        spin_unlock(&port->lock);
 }
 
+static int atmel_init_property(struct atmel_uart_port *atmel_port,
+                               struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
+
+       if (np) {
+               /* DMA/PDC usage specification */
+               if (of_get_property(np, "atmel,use-dma-rx", NULL)) {
+                       if (of_get_property(np, "dmas", NULL)) {
+                               atmel_port->use_dma_rx  = true;
+                               atmel_port->use_pdc_rx  = false;
+                       } else {
+                               atmel_port->use_dma_rx  = false;
+                               atmel_port->use_pdc_rx  = true;
+                       }
+               } else {
+                       atmel_port->use_dma_rx  = false;
+                       atmel_port->use_pdc_rx  = false;
+               }
+
+               if (of_get_property(np, "atmel,use-dma-tx", NULL)) {
+                       if (of_get_property(np, "dmas", NULL)) {
+                               atmel_port->use_dma_tx  = true;
+                               atmel_port->use_pdc_tx  = false;
+                       } else {
+                               atmel_port->use_dma_tx  = false;
+                               atmel_port->use_pdc_tx  = true;
+                       }
+               } else {
+                       atmel_port->use_dma_tx  = false;
+                       atmel_port->use_pdc_tx  = false;
+               }
+
+       } else {
+               atmel_port->use_pdc_rx  = pdata->use_dma_rx;
+               atmel_port->use_pdc_tx  = pdata->use_dma_tx;
+               atmel_port->use_dma_rx  = false;
+               atmel_port->use_dma_tx  = false;
+       }
+
+       return 0;
+}
+
+static void atmel_init_rs485(struct atmel_uart_port *atmel_port,
+                               struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
+
+       if (np) {
+               u32 rs485_delay[2];
+               /* rs485 properties */
+               if (of_property_read_u32_array(np, "rs485-rts-delay",
+                                       rs485_delay, 2) == 0) {
+                       struct serial_rs485 *rs485conf = &atmel_port->rs485;
+
+                       rs485conf->delay_rts_before_send = rs485_delay[0];
+                       rs485conf->delay_rts_after_send = rs485_delay[1];
+                       rs485conf->flags = 0;
+
+               if (of_get_property(np, "rs485-rx-during-tx", NULL))
+                       rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+               if (of_get_property(np, "linux,rs485-enabled-at-boot-time",
+                                                               NULL))
+                       rs485conf->flags |= SER_RS485_ENABLED;
+               }
+       } else {
+               atmel_port->rs485       = pdata->rs485;
+       }
+
+}
+
+static void atmel_set_ops(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+       if (atmel_use_dma_rx(port)) {
+               atmel_port->prepare_rx = &atmel_prepare_rx_dma;
+               atmel_port->schedule_rx = &atmel_rx_from_dma;
+               atmel_port->release_rx = &atmel_release_rx_dma;
+       } else if (atmel_use_pdc_rx(port)) {
+               atmel_port->prepare_rx = &atmel_prepare_rx_pdc;
+               atmel_port->schedule_rx = &atmel_rx_from_pdc;
+               atmel_port->release_rx = &atmel_release_rx_pdc;
+       } else {
+               atmel_port->prepare_rx = NULL;
+               atmel_port->schedule_rx = &atmel_rx_from_ring;
+               atmel_port->release_rx = NULL;
+       }
+
+       if (atmel_use_dma_tx(port)) {
+               atmel_port->prepare_tx = &atmel_prepare_tx_dma;
+               atmel_port->schedule_tx = &atmel_tx_dma;
+               atmel_port->release_tx = &atmel_release_tx_dma;
+       } else if (atmel_use_pdc_tx(port)) {
+               atmel_port->prepare_tx = &atmel_prepare_tx_pdc;
+               atmel_port->schedule_tx = &atmel_tx_pdc;
+               atmel_port->release_tx = &atmel_release_tx_pdc;
+       } else {
+               atmel_port->prepare_tx = NULL;
+               atmel_port->schedule_tx = &atmel_tx_chars;
+               atmel_port->release_tx = NULL;
+       }
+}
+
+/*
+ * Get ip name usart or uart
+ */
+static int atmel_get_ip_name(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       int name = UART_GET_IP_NAME(port);
+       int usart, uart;
+       /* usart and uart ascii */
+       usart = 0x55534152;
+       uart = 0x44424755;
+
+       atmel_port->is_usart = false;
+
+       if (name == usart) {
+               dev_dbg(port->dev, "This is usart\n");
+               atmel_port->is_usart = true;
+       } else if (name == uart) {
+               dev_dbg(port->dev, "This is uart\n");
+               atmel_port->is_usart = false;
+       } else {
+               dev_err(port->dev, "Not supported ip name, set to uart\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 /*
  * Perform initialization and enable port for reception
  */
 static int atmel_startup(struct uart_port *port)
 {
+       struct platform_device *pdev = to_platform_device(port->dev);
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
        struct tty_struct *tty = port->state->port.tty;
        int retval;
@@ -930,53 +1554,19 @@ static int atmel_startup(struct uart_port *port)
        /*
         * Initialize DMA (if necessary)
         */
-       if (atmel_use_dma_rx(port)) {
-               int i;
-
-               for (i = 0; i < 2; i++) {
-                       struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
-
-                       pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
-                       if (pdc->buf == NULL) {
-                               if (i != 0) {
-                                       dma_unmap_single(port->dev,
-                                               atmel_port->pdc_rx[0].dma_addr,
-                                               PDC_BUFFER_SIZE,
-                                               DMA_FROM_DEVICE);
-                                       kfree(atmel_port->pdc_rx[0].buf);
-                               }
-                               free_irq(port->irq, port);
-                               return -ENOMEM;
-                       }
-                       pdc->dma_addr = dma_map_single(port->dev,
-                                                      pdc->buf,
-                                                      PDC_BUFFER_SIZE,
-                                                      DMA_FROM_DEVICE);
-                       pdc->dma_size = PDC_BUFFER_SIZE;
-                       pdc->ofs = 0;
-               }
+       atmel_init_property(atmel_port, pdev);
 
-               atmel_port->pdc_rx_idx = 0;
-
-               UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
-               UART_PUT_RCR(port, PDC_BUFFER_SIZE);
-
-               UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
-               UART_PUT_RNCR(port, PDC_BUFFER_SIZE);
+       if (atmel_port->prepare_rx) {
+               retval = atmel_port->prepare_rx(port);
+               if (retval < 0)
+                       atmel_set_ops(port);
        }
-       if (atmel_use_dma_tx(port)) {
-               struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
-               struct circ_buf *xmit = &port->state->xmit;
 
-               pdc->buf = xmit->buf;
-               pdc->dma_addr = dma_map_single(port->dev,
-                                              pdc->buf,
-                                              UART_XMIT_SIZE,
-                                              DMA_TO_DEVICE);
-               pdc->dma_size = UART_XMIT_SIZE;
-               pdc->ofs = 0;
+       if (atmel_port->prepare_tx) {
+               retval = atmel_port->prepare_tx(port);
+               if (retval < 0)
+                       atmel_set_ops(port);
        }
-
        /*
         * If there is a specific "open" function (to register
         * control line interrupts)
@@ -1000,14 +1590,38 @@ static int atmel_startup(struct uart_port *port)
        /* enable xmit & rcvr */
        UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
 
-       if (atmel_use_dma_rx(port)) {
+       if (atmel_use_pdc_rx(port)) {
                /* set UART timeout */
-               UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
-               UART_PUT_CR(port, ATMEL_US_STTTO);
-
-               UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
+               if (!atmel_port->is_usart) {
+                       setup_timer(&atmel_port->uart_timer,
+                                       atmel_uart_timer_callback,
+                                       (unsigned long)port);
+                       mod_timer(&atmel_port->uart_timer,
+                                       jiffies + uart_poll_timeout(port));
+               /* set USART timeout */
+               } else {
+                       UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
+                       UART_PUT_CR(port, ATMEL_US_STTTO);
+
+                       UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
+               }
                /* enable PDC controller */
                UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
+       } else if (atmel_use_dma_rx(port)) {
+               /* set UART timeout */
+               if (!atmel_port->is_usart) {
+                       setup_timer(&atmel_port->uart_timer,
+                                       atmel_uart_timer_callback,
+                                       (unsigned long)port);
+                       mod_timer(&atmel_port->uart_timer,
+                                       jiffies + uart_poll_timeout(port));
+               /* set USART timeout */
+               } else {
+                       UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
+                       UART_PUT_CR(port, ATMEL_US_STTTO);
+
+                       UART_PUT_IER(port, ATMEL_US_TIMEOUT);
+               }
        } else {
                /* enable receive only */
                UART_PUT_IER(port, ATMEL_US_RXRDY);
@@ -1031,27 +1645,10 @@ static void atmel_shutdown(struct uart_port *port)
        /*
         * Shut-down the DMA.
         */
-       if (atmel_use_dma_rx(port)) {
-               int i;
-
-               for (i = 0; i < 2; i++) {
-                       struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
-
-                       dma_unmap_single(port->dev,
-                                        pdc->dma_addr,
-                                        pdc->dma_size,
-                                        DMA_FROM_DEVICE);
-                       kfree(pdc->buf);
-               }
-       }
-       if (atmel_use_dma_tx(port)) {
-               struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
-
-               dma_unmap_single(port->dev,
-                                pdc->dma_addr,
-                                pdc->dma_size,
-                                DMA_TO_DEVICE);
-       }
+       if (atmel_port->release_rx)
+               atmel_port->release_rx(port);
+       if (atmel_port->release_tx)
+               atmel_port->release_tx(port);
 
        /*
         * Disable all interrupts, port and break condition.
@@ -1080,7 +1677,7 @@ static void atmel_flush_buffer(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
-       if (atmel_use_dma_tx(port)) {
+       if (atmel_use_pdc_tx(port)) {
                UART_PUT_TCR(port, 0);
                atmel_port->pdc_tx.ofs = 0;
        }
@@ -1193,7 +1790,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
        if (termios->c_iflag & (BRKINT | PARMRK))
                port->read_status_mask |= ATMEL_US_RXBRK;
 
-       if (atmel_use_dma_rx(port))
+       if (atmel_use_pdc_rx(port))
                /* need to enable error interrupts */
                UART_PUT_IER(port, port->read_status_mask);
 
@@ -1423,38 +2020,6 @@ static struct uart_ops atmel_pops = {
 #endif
 };
 
-static void atmel_of_init_port(struct atmel_uart_port *atmel_port,
-                                        struct device_node *np)
-{
-       u32 rs485_delay[2];
-
-       /* DMA/PDC usage specification */
-       if (of_get_property(np, "atmel,use-dma-rx", NULL))
-               atmel_port->use_dma_rx  = 1;
-       else
-               atmel_port->use_dma_rx  = 0;
-       if (of_get_property(np, "atmel,use-dma-tx", NULL))
-               atmel_port->use_dma_tx  = 1;
-       else
-               atmel_port->use_dma_tx  = 0;
-
-       /* rs485 properties */
-       if (of_property_read_u32_array(np, "rs485-rts-delay",
-                                           rs485_delay, 2) == 0) {
-               struct serial_rs485 *rs485conf = &atmel_port->rs485;
-
-               rs485conf->delay_rts_before_send = rs485_delay[0];
-               rs485conf->delay_rts_after_send = rs485_delay[1];
-               rs485conf->flags = 0;
-
-               if (of_get_property(np, "rs485-rx-during-tx", NULL))
-                       rs485conf->flags |= SER_RS485_RX_DURING_TX;
-
-               if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL))
-                       rs485conf->flags |= SER_RS485_ENABLED;
-       }
-}
-
 /*
  * Configure the port from the platform device resource info.
  */
@@ -1463,15 +2028,12 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
 {
        int ret;
        struct uart_port *port = &atmel_port->uart;
-       struct atmel_uart_data *pdata = pdev->dev.platform_data;
+       struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
 
-       if (pdev->dev.of_node) {
-               atmel_of_init_port(atmel_port, pdev->dev.of_node);
-       } else {
-               atmel_port->use_dma_rx  = pdata->use_dma_rx;
-               atmel_port->use_dma_tx  = pdata->use_dma_tx;
-               atmel_port->rs485       = pdata->rs485;
-       }
+       if (!atmel_init_property(atmel_port, pdev))
+               atmel_set_ops(port);
+
+       atmel_init_rs485(atmel_port, pdev);
 
        port->iotype            = UPIO_MEM;
        port->flags             = UPF_BOOT_AUTOCONF;
@@ -1516,7 +2078,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
        /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
        if (atmel_port->rs485.flags & SER_RS485_ENABLED)
                atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
-       else if (atmel_use_dma_tx(port)) {
+       else if (atmel_use_pdc_tx(port)) {
                port->fifosize = PDC_BUFFER_SIZE;
                atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
        } else {
@@ -1664,7 +2226,7 @@ static int __init atmel_console_init(void)
        int ret;
        if (atmel_default_console_device) {
                struct atmel_uart_data *pdata =
-                       atmel_default_console_device->dev.platform_data;
+                       dev_get_platdata(&atmel_default_console_device->dev);
                int id = pdata->num;
                struct atmel_uart_port *port = &atmel_ports[id];
 
@@ -1772,10 +2334,9 @@ static int atmel_serial_probe(struct platform_device *pdev)
 {
        struct atmel_uart_port *port;
        struct device_node *np = pdev->dev.of_node;
-       struct atmel_uart_data *pdata = pdev->dev.platform_data;
+       struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
        void *data;
        int ret = -ENODEV;
-       struct pinctrl *pinctrl;
 
        BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
 
@@ -1809,13 +2370,7 @@ 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)) {
+       if (!atmel_use_pdc_rx(&port->uart)) {
                ret = -ENOMEM;
                data = kmalloc(sizeof(struct atmel_uart_char)
                                * ATMEL_SERIAL_RINGSIZE, GFP_KERNEL);
@@ -1847,6 +2402,13 @@ static int atmel_serial_probe(struct platform_device *pdev)
                UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
        }
 
+       /*
+        * Get port name of usart or uart
+        */
+       ret = atmel_get_ip_name(&port->uart);
+       if (ret < 0)
+               goto err_add_port;
+
        return 0;
 
 err_add_port:
@@ -1868,7 +2430,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);
 
index 6fa2ae77fffde5a2de2fd14f8568da7fc55f8d1e..d14ba5aa2ec7e0582f88a930fa576b83eb5ee241 100644 (file)
@@ -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;
index 487c173b0f72f107f1c671175908e47936f98186..424ed36c26f1c4ec9652b2cd77c91162307d7de1 100644 (file)
@@ -766,7 +766,8 @@ static int sport_uart_probe(struct platform_device *pdev)
                }
 
                ret = peripheral_request_list(
-                       (unsigned short *)pdev->dev.platform_data, DRV_NAME);
+                       (unsigned short *)dev_get_platdata(&pdev->dev),
+                       DRV_NAME);
                if (ret) {
                        dev_err(&pdev->dev,
                                "Fail to request SPORT peripherals\n");
@@ -843,7 +844,7 @@ out_error_unmap:
                iounmap(sport->port.membase);
 out_error_free_peripherals:
                peripheral_free_list(
-                       (unsigned short *)pdev->dev.platform_data);
+                       (unsigned short *)dev_get_platdata(&pdev->dev));
 out_error_free_mem:
                kfree(sport);
                bfin_sport_uart_ports[pdev->id] = NULL;
@@ -863,7 +864,7 @@ static int sport_uart_remove(struct platform_device *pdev)
                uart_remove_one_port(&sport_uart_reg, &sport->port);
                iounmap(sport->port.membase);
                peripheral_free_list(
-                       (unsigned short *)pdev->dev.platform_data);
+                       (unsigned short *)dev_get_platdata(&pdev->dev));
                kfree(sport);
                bfin_sport_uart_ports[pdev->id] = NULL;
        }
index 26a3be7ced7d92df93616f2a50363970a93de67b..378f3c383992bd991f93e993a60909011f0eefe0 100644 (file)
 # 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
@@ -1244,7 +1240,8 @@ static int bfin_serial_probe(struct platform_device *pdev)
                         */
 #endif
                ret = peripheral_request_list(
-                       (unsigned short *)pdev->dev.platform_data, DRIVER_NAME);
+                       (unsigned short *)dev_get_platdata(&pdev->dev),
+                       DRIVER_NAME);
                if (ret) {
                        dev_err(&pdev->dev,
                                "fail to request bfin serial peripherals\n");
@@ -1362,7 +1359,7 @@ out_error_unmap:
                iounmap(uart->port.membase);
 out_error_free_peripherals:
                peripheral_free_list(
-                       (unsigned short *)pdev->dev.platform_data);
+                       (unsigned short *)dev_get_platdata(&pdev->dev));
 out_error_free_mem:
                kfree(uart);
                bfin_serial_ports[pdev->id] = NULL;
@@ -1381,7 +1378,7 @@ static int bfin_serial_remove(struct platform_device *pdev)
                uart_remove_one_port(&bfin_serial_reg, &uart->port);
                iounmap(uart->port.membase);
                peripheral_free_list(
-                       (unsigned short *)pdev->dev.platform_data);
+                       (unsigned short *)dev_get_platdata(&pdev->dev));
                kfree(uart);
                bfin_serial_ports[pdev->id] = NULL;
        }
@@ -1436,7 +1433,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev)
        }
 
        ret = peripheral_request_list(
-               (unsigned short *)pdev->dev.platform_data, DRIVER_NAME);
+               (unsigned short *)dev_get_platdata(&pdev->dev), DRIVER_NAME);
        if (ret) {
                dev_err(&pdev->dev,
                                "fail to request bfin serial peripherals\n");
@@ -1467,7 +1464,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev)
 
 out_error_free_peripherals:
        peripheral_free_list(
-               (unsigned short *)pdev->dev.platform_data);
+               (unsigned short *)dev_get_platdata(&pdev->dev));
 
        return ret;
 }
index bfb17968c8dbbe86ad91902c94f02a824c0a81c3..7e4e4088471cea4cbd6a67c49917b5af2540edc2 100644 (file)
@@ -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;
 }
index 7d199c8e1a750949ccb0edae4d2c730eea790bb9..98adaa1c054f60f0f9b13e5f5d11fcc3a7173498 100644 (file)
@@ -698,6 +698,7 @@ static int efm32_uart_probe(struct platform_device *pdev)
 {
        struct efm32_uart_port *efm_port;
        struct resource *res;
+       unsigned int line;
        int ret;
 
        efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
@@ -750,18 +751,21 @@ static int efm32_uart_probe(struct platform_device *pdev)
 
                if (pdata)
                        efm_port->pdata = *pdata;
-       }
+       } else if (ret < 0)
+               goto err_probe_dt;
+
+       line = efm_port->port.line;
 
-       if (efm_port->port.line >= 0 &&
-                       efm_port->port.line < ARRAY_SIZE(efm32_uart_ports))
-               efm32_uart_ports[efm_port->port.line] = efm_port;
+       if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports))
+               efm32_uart_ports[line] = efm_port;
 
        ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port);
        if (ret) {
                dev_dbg(&pdev->dev, "failed to add port: %d\n", ret);
 
-               if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
-                       efm32_uart_ports[pdev->id] = NULL;
+               if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports))
+                       efm32_uart_ports[line] = NULL;
+err_probe_dt:
 err_get_rxirq:
 err_too_small:
 err_get_base:
@@ -777,20 +781,19 @@ err_get_base:
 static int efm32_uart_remove(struct platform_device *pdev)
 {
        struct efm32_uart_port *efm_port = platform_get_drvdata(pdev);
-
-       platform_set_drvdata(pdev, NULL);
+       unsigned int line = efm_port->port.line;
 
        uart_remove_one_port(&efm32_uart_reg, &efm_port->port);
 
-       if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
-               efm32_uart_ports[pdev->id] = NULL;
+       if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports))
+               efm32_uart_ports[line] = NULL;
 
        kfree(efm_port);
 
        return 0;
 }
 
-static struct of_device_id efm32_uart_dt_ids[] = {
+static const struct of_device_id efm32_uart_dt_ids[] = {
        {
                .compatible = "efm32,uart",
        }, {
index 263cfaabe9e23deb6142f8514f87605386de51bc..8978dc9a58b748c9da1a4f65d2c1ecaffbf8e523 100644 (file)
@@ -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)
index 8b1534c424afc38eb022d26d7ba70b25e6b722d4..af286e6713eb914ccf30f334b69706a6510b1b97 100644 (file)
@@ -1008,7 +1008,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
                return -ENODEV;
        }
 
-       pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data;
+       pl_data = (struct ifx_modem_platform_data *)dev_get_platdata(&spi->dev);
        if (!pl_data) {
                dev_err(&spi->dev, "missing platform data!");
                return -ENODEV;
index 415cec62073fbc61d9770e6a8c613a42f2ba6865..a5f32c7c6c66c6ef8bc889c157b8b2d2d35ba5a2 100644 (file)
 #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 */
 #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 */
 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[] = {
@@ -241,6 +263,9 @@ 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 */
        }
@@ -248,6 +273,7 @@ static struct platform_device_id imx_uart_devtype[] = {
 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);
@@ -833,7 +1222,7 @@ static int imx_startup(struct uart_port *port)
 
        if (USE_IRDA(sport)) {
                struct imxuart_platform_data *pdata;
-               pdata = sport->port.dev->platform_data;
+               pdata = dev_get_platdata(sport->port.dev);
                sport->irda_inv_rx = pdata->irda_inv_rx;
                sport->irda_inv_tx = pdata->irda_inv_tx;
                sport->trcv_delay = pdata->transceiver_delay;
@@ -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);
@@ -867,7 +1265,7 @@ static void imx_shutdown(struct uart_port *port)
 
        if (USE_IRDA(sport)) {
                struct imxuart_platform_data *pdata;
-               pdata = sport->port.dev->platform_data;
+               pdata = dev_get_platdata(sport->port.dev);
                if (pdata->irda_enable)
                        pdata->irda_enable(0);
        }
@@ -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;
@@ -1485,7 +1925,7 @@ static inline int serial_imx_probe_dt(struct imx_port *sport,
 static void serial_imx_probe_pdata(struct imx_port *sport,
                struct platform_device *pdev)
 {
-       struct imxuart_platform_data *pdata = pdev->dev.platform_data;
+       struct imxuart_platform_data *pdata = dev_get_platdata(&pdev->dev);
 
        sport->port.line = pdev->id;
        sport->devdata = (struct imx_uart_data  *) pdev->id_entry->driver_data;
@@ -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,18 +1996,15 @@ 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;
 
-       pdata = pdev->dev.platform_data;
+       pdata = dev_get_platdata(&pdev->dev);
        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;
 }
 
@@ -1603,9 +2024,7 @@ static int serial_imx_remove(struct platform_device *pdev)
        struct imxuart_platform_data *pdata;
        struct imx_port *sport = platform_get_drvdata(pdev);
 
-       pdata = pdev->dev.platform_data;
-
-       platform_set_drvdata(pdev, NULL);
+       pdata = dev_get_platdata(&pdev->dev);
 
        uart_remove_one_port(&imx_reg, &sport->port);
 
index 35866d5872ad1c82bdc487909cd359a84dabb790..79f9a9eff5455e561d3cdb98e57d0aee16281d38 100644 (file)
@@ -779,7 +779,7 @@ static int max3100_probe(struct spi_device *spi)
        max3100s[i]->irq = spi->irq;
        spin_lock_init(&max3100s[i]->conf_lock);
        spi_set_drvdata(spi, max3100s[i]);
-       pdata = spi->dev.platform_data;
+       pdata = dev_get_platdata(&spi->dev);
        max3100s[i]->crystal = pdata->crystal;
        max3100s[i]->loopback = pdata->loopback;
        max3100s[i]->poll_time = pdata->poll_time * HZ / 1000;
index 8941e64189425a4b41e6ce00d7b9f0a75ab022d0..b2e707aa603a52daeff7762bf81a7e82397fc831 100644 (file)
@@ -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>
  *  (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>
 #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 */
 #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 */
 #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);
+       unsigned int lvl, sts;
 
-       mutex_lock(&s->max310x_mutex);
-       regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &val);
-       mutex_unlock(&s->max310x_mutex);
+       lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
+       sts = max310x_port_read(port, MAX310X_IRQSTS_REG);
 
-       return val ? 0 : TIOCSER_TEMT;
-}
-
-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);
+       max310x_port_write(port, MAX310X_FLOWLVL_REG,
+                          MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96));
 
-       /* Configure LSR interrupt enable register */
-       /* Enable RX timeout interrupt */
-       val = MAX310X_LSR_RXTO_BIT;
-       regmap_write(s->regmap, MAX310X_LSR_IRQEN_REG, val);
+       /* Clear IRQ status register */
+       max310x_port_read(port, MAX310X_IRQSTS_REG);
 
-       /* Clear FIFO reset */
-       regmap_update_bits(s->regmap, MAX310X_MODE2_REG,
-                          MAX310X_MODE2_FIFORST_BIT, 0);
-
-       /* 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,99 @@ 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 device *dev)
 {
-       int ret;
        struct max310x_port *s = dev_get_drvdata(dev);
+       int i;
 
-       dev_dbg(dev, "Suspend\n");
-
-       ret = uart_suspend_port(&s->uart, &s->port);
-
-       mutex_lock(&s->max310x_mutex);
-
-       /* 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 device *dev)
 {
        struct max310x_port *s = dev_get_drvdata(dev);
+       int i;
 
-       dev_dbg(dev, "Resume\n");
-
-       if (s->pdata->suspend)
-               s->pdata->suspend(0);
-
-       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 +1052,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 +1098,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 +1110,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 +1211,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");
index 65be0c00c4bff3f802549a23dfe34515a5a03b54..a2e97c96e1597388413bb38c15ade5d3793e2345 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/serial_core.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
+#include <linux/platform_device.h>
 #include <asm/coldfire.h>
 #include <asm/mcfsim.h>
 #include <asm/mcfuart.h>
@@ -644,7 +645,7 @@ static struct uart_driver mcf_driver = {
 
 static int mcf_probe(struct platform_device *pdev)
 {
-       struct mcf_platform_uart *platp = pdev->dev.platform_data;
+       struct mcf_platform_uart *platp = dev_get_platdata(&pdev->dev);
        struct uart_port *port;
        int i;
 
index bc24f4931670d1e70d9bda922a2d3d0341636e0f..fba52578d02f34495c977b183cf0e3b44185f7e9 100644 (file)
@@ -1884,7 +1884,7 @@ static int mpsc_shared_drv_probe(struct platform_device *dev)
        if (dev->id == 0) {
                if (!(rc = mpsc_shared_map_regs(dev))) {
                        pdata = (struct mpsc_shared_pdata *)
-                               dev->dev.platform_data;
+                               dev_get_platdata(&dev->dev);
 
                        mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val;
                        mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val;
@@ -2025,7 +2025,7 @@ static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi,
 {
        struct mpsc_pdata       *pdata;
 
-       pdata = (struct mpsc_pdata *)pd->dev.platform_data;
+       pdata = (struct mpsc_pdata *)dev_get_platdata(&pd->dev);
 
        pi->port.uartclk = pdata->brg_clk_freq;
        pi->port.iotype = UPIO_MEM;
index 2c6cfb3cf0327789929aa6ce3f38040a03e99327..252d514b47fb46b93c1ce16efd4b683d2fe30f51 100644 (file)
@@ -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))
                ;
index e4acef5de77ef82f01d45add56d8646d8ba2f50d..469fda50ac63e9a4e832626239ec11786192fbb7 100644 (file)
 #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);
 }
 
index 4ca2f64861e6ce531546f996a5a1bf3718af0344..48e94961a9e572e96bc8e553ea5e420296d2b1a8 100644 (file)
@@ -1618,7 +1618,7 @@ static int msm_hs_probe(struct platform_device *pdev)
        struct msm_hs_port *msm_uport;
        struct resource *resource;
        const struct msm_serial_hs_platform_data *pdata =
-                                               pdev->dev.platform_data;
+                                               dev_get_platdata(&pdev->dev);
 
        if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
                printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
index f85b8e6d0346fc472fbc835b0451a706ce46aab3..736e95c1ac4ea48947791bb112094ed87e81173f 100644 (file)
@@ -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>
@@ -1021,7 +1020,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) {
@@ -1035,12 +1033,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;
index b9a40ed70be2de379ce98087025447c5652b5ec5..ce04f3f8c547ab93b46856a85db2c908039967c4 100644 (file)
@@ -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);
 
index b6d17287307636dd34afeb3e97fc4a491efe437c..c7517063eb9baec20a95d0cb71604848d3b8a4d0 100644 (file)
@@ -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
 #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;
 };
 
@@ -195,7 +200,7 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up)
 
 static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
 {
-       struct omap_uart_port_info *pdata = up->dev->platform_data;
+       struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
 
        if (!pdata || !pdata->get_context_loss_count)
                return -EINVAL;
@@ -205,7 +210,7 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
 
 static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
 {
-       struct omap_uart_port_info *pdata = up->dev->platform_data;
+       struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
 
        if (!pdata || !pdata->enable_wakeup)
                return;
@@ -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;
@@ -1399,11 +1409,13 @@ static int serial_omap_probe(struct platform_device *pdev)
 {
        struct uart_omap_port   *up;
        struct resource         *mem, *irq;
-       struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
+       struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev);
        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)
index 572d48189de910c632ed12e93b4f603bceb55172..5040c517f3b8b9699a42b467b1d841b72b662480 100644 (file)
@@ -232,7 +232,7 @@ struct eg20t_port {
        unsigned int iobase;
        struct pci_dev *pdev;
        int fifo_size;
-       int uartclk;
+       unsigned int uartclk;
        int start_tx;
        int start_rx;
        int tx_empty;
@@ -373,35 +373,62 @@ static const struct file_operations port_regs_ops = {
 };
 #endif /* CONFIG_DEBUG_FS */
 
+static struct dmi_system_id 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)
+static unsigned 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 (unsigned long)d->driver_data;
 
        return DEFAULT_UARTCLK;
 }
@@ -422,7 +449,7 @@ static void pch_uart_hal_disable_interrupt(struct eg20t_port *priv,
        iowrite8(ier, priv->membase + UART_IER);
 }
 
-static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud,
+static int pch_uart_hal_set_line(struct eg20t_port *priv, unsigned int baud,
                                 unsigned int parity, unsigned int bits,
                                 unsigned int stb)
 {
@@ -457,7 +484,7 @@ static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud,
        lcr |= bits;
        lcr |= stb;
 
-       dev_dbg(priv->port.dev, "%s:baud = %d, div = %04x, lcr = %02x (%lu)\n",
+       dev_dbg(priv->port.dev, "%s:baud = %u, div = %04x, lcr = %02x (%lu)\n",
                 __func__, baud, div, lcr, jiffies);
        iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR);
        iowrite8(dll, priv->membase + PCH_UART_DLL);
@@ -1363,9 +1390,8 @@ static void pch_uart_shutdown(struct uart_port *port)
 static void pch_uart_set_termios(struct uart_port *port,
                                 struct ktermios *termios, struct ktermios *old)
 {
-       int baud;
        int rtn;
-       unsigned int parity, bits, stb;
+       unsigned int baud, parity, bits, stb;
        struct eg20t_port *priv;
        unsigned long flags;
 
index b1785f58b6e37e8e70f7242a38f40ae1b71f6b84..f87f1a0c8c6ee577860e37e7a95038e61776e1cb 100644 (file)
@@ -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;
index 7e277a5384a7679d22a64dd92234d3b5c9e17c43..b6b7aca5707a115132ff28df501352cc40bb4652 100644 (file)
@@ -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);
 
index 05f504e0c27144b04b3b56b075256e6a20f5d4b9..ac8b2f5b2396473c32b011f4f4e7c788c4145508 100644 (file)
@@ -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);
index af6b3e3ad24d18cc48c00736570c8c758403adad..fc23ea19073a40f58850ed60d847d1d9e1c4a4ac 100644 (file)
@@ -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);
 
index 376079b9bd75f77bac213cfcb8e419740bc12a4d..019b9154d7d45514667f16ad6608a196894e9179 100644 (file)
@@ -1250,8 +1250,8 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
 
        ourport->baudclk = ERR_PTR(-EINVAL);
        ourport->info = ourport->drv_data->info;
-       ourport->cfg = (pdev->dev.platform_data) ?
-                       (struct s3c2410_uartcfg *)pdev->dev.platform_data :
+       ourport->cfg = (dev_get_platdata(&pdev->dev)) ?
+                       (struct s3c2410_uartcfg *)dev_get_platdata(&pdev->dev) :
                        ourport->drv_data->def_cfg;
 
        ourport->port.fifosize = (ourport->info->fifosize) ?
index 4b1434d53e9d6ddbe9c478f0631379c527abf31a..887b4f7707490362e9647b767dfe1fbc7fed07b4 100644 (file)
@@ -637,7 +637,7 @@ static int sc26xx_probe(struct platform_device *dev)
 {
        struct resource *res;
        struct uart_sc26xx_port *up;
-       unsigned int *sc26xx_data = dev->dev.platform_data;
+       unsigned int *sc26xx_data = dev_get_platdata(&dev->dev);
        int err;
 
        res = platform_get_resource(dev, IORESOURCE_MEM, 0);
index c77304155410169a1a3c040400cc4f23bf38b788..49e9bbfe6cab525454c5b4337b741cbd5fcafcd0 100644 (file)
@@ -15,6 +15,7 @@
 #define SUPPORT_SYSRQ
 #endif
 
+#include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/device.h>
 #define MCTRL_IBIT(cfg, sig)           ((((cfg) >> (sig)) & 0xf) - LINE_IP0)
 #define MCTRL_OBIT(cfg, sig)           ((((cfg) >> (sig)) & 0xf) - LINE_OP0)
 
-/* Supported chip types */
-enum {
-       SCCNXP_TYPE_SC2681      = 2681,
-       SCCNXP_TYPE_SC2691      = 2691,
-       SCCNXP_TYPE_SC2692      = 2692,
-       SCCNXP_TYPE_SC2891      = 2891,
-       SCCNXP_TYPE_SC2892      = 2892,
-       SCCNXP_TYPE_SC28202     = 28202,
-       SCCNXP_TYPE_SC68681     = 68681,
-       SCCNXP_TYPE_SC68692     = 68692,
+#define SCCNXP_HAVE_IO         0x00000001
+#define SCCNXP_HAVE_MR0                0x00000002
+
+struct sccnxp_chip {
+       const char              *name;
+       unsigned int            nr;
+       unsigned long           freq_min;
+       unsigned long           freq_std;
+       unsigned long           freq_max;
+       unsigned int            flags;
+       unsigned int            fifosize;
 };
 
 struct sccnxp_port {
@@ -111,16 +113,10 @@ struct sccnxp_port {
        struct uart_port        port[SCCNXP_MAX_UARTS];
        bool                    opened[SCCNXP_MAX_UARTS];
 
-       const char              *name;
        int                     irq;
-
        u8                      imr;
-       u8                      addr_mask;
-       int                     freq_std;
 
-       int                     flags;
-#define SCCNXP_HAVE_IO         0x00000001
-#define SCCNXP_HAVE_MR0                0x00000002
+       struct sccnxp_chip      *chip;
 
 #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
        struct console          console;
@@ -136,29 +132,94 @@ struct sccnxp_port {
        struct regulator        *regulator;
 };
 
-static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift)
-{
-       return readb(base + (reg << shift));
-}
+static const struct sccnxp_chip sc2681 = {
+       .name           = "SC2681",
+       .nr             = 2,
+       .freq_min       = 1000000,
+       .freq_std       = 3686400,
+       .freq_max       = 4000000,
+       .flags          = SCCNXP_HAVE_IO,
+       .fifosize       = 3,
+};
 
-static inline void sccnxp_raw_write(void __iomem *base, u8 reg, u8 shift, u8 v)
-{
-       writeb(v, base + (reg << shift));
-}
+static const struct sccnxp_chip sc2691 = {
+       .name           = "SC2691",
+       .nr             = 1,
+       .freq_min       = 1000000,
+       .freq_std       = 3686400,
+       .freq_max       = 4000000,
+       .flags          = 0,
+       .fifosize       = 3,
+};
+
+static const struct sccnxp_chip sc2692 = {
+       .name           = "SC2692",
+       .nr             = 2,
+       .freq_min       = 1000000,
+       .freq_std       = 3686400,
+       .freq_max       = 4000000,
+       .flags          = SCCNXP_HAVE_IO,
+       .fifosize       = 3,
+};
+
+static const struct sccnxp_chip sc2891 = {
+       .name           = "SC2891",
+       .nr             = 1,
+       .freq_min       = 100000,
+       .freq_std       = 3686400,
+       .freq_max       = 8000000,
+       .flags          = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
+       .fifosize       = 16,
+};
+
+static const struct sccnxp_chip sc2892 = {
+       .name           = "SC2892",
+       .nr             = 2,
+       .freq_min       = 100000,
+       .freq_std       = 3686400,
+       .freq_max       = 8000000,
+       .flags          = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
+       .fifosize       = 16,
+};
+
+static const struct sccnxp_chip sc28202 = {
+       .name           = "SC28202",
+       .nr             = 2,
+       .freq_min       = 1000000,
+       .freq_std       = 14745600,
+       .freq_max       = 50000000,
+       .flags          = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
+       .fifosize       = 256,
+};
+
+static const struct sccnxp_chip sc68681 = {
+       .name           = "SC68681",
+       .nr             = 2,
+       .freq_min       = 1000000,
+       .freq_std       = 3686400,
+       .freq_max       = 4000000,
+       .flags          = SCCNXP_HAVE_IO,
+       .fifosize       = 3,
+};
+
+static const struct sccnxp_chip sc68692 = {
+       .name           = "SC68692",
+       .nr             = 2,
+       .freq_min       = 1000000,
+       .freq_std       = 3686400,
+       .freq_max       = 4000000,
+       .flags          = SCCNXP_HAVE_IO,
+       .fifosize       = 3,
+};
 
 static inline u8 sccnxp_read(struct uart_port *port, u8 reg)
 {
-       struct sccnxp_port *s = dev_get_drvdata(port->dev);
-
-       return sccnxp_raw_read(port->membase, reg & s->addr_mask,
-                              port->regshift);
+       return readb(port->membase + (reg << port->regshift));
 }
 
 static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
 {
-       struct sccnxp_port *s = dev_get_drvdata(port->dev);
-
-       sccnxp_raw_write(port->membase, reg & s->addr_mask, port->regshift, v);
+       writeb(v, port->membase + (reg << port->regshift));
 }
 
 static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg)
@@ -224,13 +285,14 @@ static int sccnxp_set_baud(struct uart_port *port, int baud)
 {
        struct sccnxp_port *s = dev_get_drvdata(port->dev);
        int div_std, tmp_baud, bestbaud = baud, besterr = -1;
+       struct sccnxp_chip *chip = s->chip;
        u8 i, acr = 0, csr = 0, mr0 = 0;
 
        /* Find best baud from table */
        for (i = 0; baud_std[i].baud && besterr; i++) {
-               if (baud_std[i].mr0 && !(s->flags & SCCNXP_HAVE_MR0))
+               if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0))
                        continue;
-               div_std = DIV_ROUND_CLOSEST(s->freq_std, baud_std[i].baud);
+               div_std = DIV_ROUND_CLOSEST(chip->freq_std, baud_std[i].baud);
                tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std);
                if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) {
                        acr = baud_std[i].acr;
@@ -240,7 +302,7 @@ static int sccnxp_set_baud(struct uart_port *port, int baud)
                }
        }
 
-       if (s->flags & SCCNXP_HAVE_MR0) {
+       if (chip->flags & SCCNXP_HAVE_MR0) {
                /* Enable FIFO, set half level for TX */
                mr0 |= MR0_FIFO | MR0_TXLVL;
                /* Update MR0 */
@@ -363,7 +425,7 @@ static void sccnxp_handle_tx(struct uart_port *port)
                        sccnxp_disable_irq(port, IMR_TXRDY);
 
                        /* Set direction to input */
-                       if (s->flags & SCCNXP_HAVE_IO)
+                       if (s->chip->flags & SCCNXP_HAVE_IO)
                                sccnxp_set_bit(port, DIR_OP, 0);
                }
                return;
@@ -437,7 +499,7 @@ static void sccnxp_start_tx(struct uart_port *port)
        spin_lock_irqsave(&s->lock, flags);
 
        /* Set direction to output */
-       if (s->flags & SCCNXP_HAVE_IO)
+       if (s->chip->flags & SCCNXP_HAVE_IO)
                sccnxp_set_bit(port, DIR_OP, 1);
 
        sccnxp_enable_irq(port, IMR_TXRDY);
@@ -483,7 +545,7 @@ static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl)
        struct sccnxp_port *s = dev_get_drvdata(port->dev);
        unsigned long flags;
 
-       if (!(s->flags & SCCNXP_HAVE_IO))
+       if (!(s->chip->flags & SCCNXP_HAVE_IO))
                return;
 
        spin_lock_irqsave(&s->lock, flags);
@@ -501,7 +563,7 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port)
        struct sccnxp_port *s = dev_get_drvdata(port->dev);
        unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
 
-       if (!(s->flags & SCCNXP_HAVE_IO))
+       if (!(s->chip->flags & SCCNXP_HAVE_IO))
                return mctrl;
 
        spin_lock_irqsave(&s->lock, flags);
@@ -617,7 +679,7 @@ static void sccnxp_set_termios(struct uart_port *port,
 
        /* Setup baudrate */
        baud = uart_get_baud_rate(port, termios, old, 50,
-                                 (s->flags & SCCNXP_HAVE_MR0) ?
+                                 (s->chip->flags & SCCNXP_HAVE_MR0) ?
                                  230400 : 38400);
        baud = sccnxp_set_baud(port, baud);
 
@@ -641,7 +703,7 @@ static int sccnxp_startup(struct uart_port *port)
 
        spin_lock_irqsave(&s->lock, flags);
 
-       if (s->flags & SCCNXP_HAVE_IO) {
+       if (s->chip->flags & SCCNXP_HAVE_IO) {
                /* Outputs are controlled manually */
                sccnxp_write(port, SCCNXP_OPCR_REG, 0);
        }
@@ -681,7 +743,7 @@ static void sccnxp_shutdown(struct uart_port *port)
        sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE);
 
        /* Leave direction to input */
-       if (s->flags & SCCNXP_HAVE_IO)
+       if (s->chip->flags & SCCNXP_HAVE_IO)
                sccnxp_set_bit(port, DIR_OP, 0);
 
        spin_unlock_irqrestore(&s->lock, flags);
@@ -691,7 +753,7 @@ static const char *sccnxp_type(struct uart_port *port)
 {
        struct sccnxp_port *s = dev_get_drvdata(port->dev);
 
-       return (port->type == PORT_SC26XX) ? s->name : NULL;
+       return (port->type == PORT_SC26XX) ? s->chip->name : NULL;
 }
 
 static void sccnxp_release_port(struct uart_port *port)
@@ -778,19 +840,31 @@ static int sccnxp_console_setup(struct console *co, char *options)
 }
 #endif
 
+static const struct platform_device_id sccnxp_id_table[] = {
+       { .name = "sc2681",     .driver_data = (kernel_ulong_t)&sc2681, },
+       { .name = "sc2691",     .driver_data = (kernel_ulong_t)&sc2691, },
+       { .name = "sc2692",     .driver_data = (kernel_ulong_t)&sc2692, },
+       { .name = "sc2891",     .driver_data = (kernel_ulong_t)&sc2891, },
+       { .name = "sc2892",     .driver_data = (kernel_ulong_t)&sc2892, },
+       { .name = "sc28202",    .driver_data = (kernel_ulong_t)&sc28202, },
+       { .name = "sc68681",    .driver_data = (kernel_ulong_t)&sc68681, },
+       { .name = "sc68692",    .driver_data = (kernel_ulong_t)&sc68692, },
+       { }
+};
+MODULE_DEVICE_TABLE(platform, sccnxp_id_table);
+
 static int sccnxp_probe(struct platform_device *pdev)
 {
        struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       int chiptype = pdev->id_entry->driver_data;
        struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev);
-       int i, ret, fifosize, freq_min, freq_max;
+       int i, ret, uartclk;
        struct sccnxp_port *s;
        void __iomem *membase;
+       struct clk *clk;
 
-       if (!res) {
-               dev_err(&pdev->dev, "Missing memory resource data\n");
-               return -EADDRNOTAVAIL;
-       }
+       membase = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(membase))
+               return PTR_ERR(membase);
 
        s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL);
        if (!s) {
@@ -801,99 +875,38 @@ static int sccnxp_probe(struct platform_device *pdev)
 
        spin_lock_init(&s->lock);
 
-       /* Individual chip settings */
-       switch (chiptype) {
-       case SCCNXP_TYPE_SC2681:
-               s->name         = "SC2681";
-               s->uart.nr      = 2;
-               s->freq_std     = 3686400;
-               s->addr_mask    = 0x0f;
-               s->flags        = SCCNXP_HAVE_IO;
-               fifosize        = 3;
-               freq_min        = 1000000;
-               freq_max        = 4000000;
-               break;
-       case SCCNXP_TYPE_SC2691:
-               s->name         = "SC2691";
-               s->uart.nr      = 1;
-               s->freq_std     = 3686400;
-               s->addr_mask    = 0x07;
-               s->flags        = 0;
-               fifosize        = 3;
-               freq_min        = 1000000;
-               freq_max        = 4000000;
-               break;
-       case SCCNXP_TYPE_SC2692:
-               s->name         = "SC2692";
-               s->uart.nr      = 2;
-               s->freq_std     = 3686400;
-               s->addr_mask    = 0x0f;
-               s->flags        = SCCNXP_HAVE_IO;
-               fifosize        = 3;
-               freq_min        = 1000000;
-               freq_max        = 4000000;
-               break;
-       case SCCNXP_TYPE_SC2891:
-               s->name         = "SC2891";
-               s->uart.nr      = 1;
-               s->freq_std     = 3686400;
-               s->addr_mask    = 0x0f;
-               s->flags        = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
-               fifosize        = 16;
-               freq_min        = 100000;
-               freq_max        = 8000000;
-               break;
-       case SCCNXP_TYPE_SC2892:
-               s->name         = "SC2892";
-               s->uart.nr      = 2;
-               s->freq_std     = 3686400;
-               s->addr_mask    = 0x0f;
-               s->flags        = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
-               fifosize        = 16;
-               freq_min        = 100000;
-               freq_max        = 8000000;
-               break;
-       case SCCNXP_TYPE_SC28202:
-               s->name         = "SC28202";
-               s->uart.nr      = 2;
-               s->freq_std     = 14745600;
-               s->addr_mask    = 0x7f;
-               s->flags        = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
-               fifosize        = 256;
-               freq_min        = 1000000;
-               freq_max        = 50000000;
-               break;
-       case SCCNXP_TYPE_SC68681:
-               s->name         = "SC68681";
-               s->uart.nr      = 2;
-               s->freq_std     = 3686400;
-               s->addr_mask    = 0x0f;
-               s->flags        = SCCNXP_HAVE_IO;
-               fifosize        = 3;
-               freq_min        = 1000000;
-               freq_max        = 4000000;
-               break;
-       case SCCNXP_TYPE_SC68692:
-               s->name         = "SC68692";
-               s->uart.nr      = 2;
-               s->freq_std     = 3686400;
-               s->addr_mask    = 0x0f;
-               s->flags        = SCCNXP_HAVE_IO;
-               fifosize        = 3;
-               freq_min        = 1000000;
-               freq_max        = 4000000;
-               break;
-       default:
-               dev_err(&pdev->dev, "Unsupported chip type %i\n", chiptype);
-               ret = -ENOTSUPP;
+       s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data;
+
+       s->regulator = devm_regulator_get(&pdev->dev, "vcc");
+       if (!IS_ERR(s->regulator)) {
+               ret = regulator_enable(s->regulator);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "Failed to enable regulator: %i\n", ret);
+                       return ret;
+               }
+       } else if (PTR_ERR(s->regulator) == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
+       clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk)) {
+               if (PTR_ERR(clk) == -EPROBE_DEFER) {
+                       ret = -EPROBE_DEFER;
+                       goto err_out;
+               }
+               dev_notice(&pdev->dev, "Using default clock frequency\n");
+               uartclk = s->chip->freq_std;
+       } else
+               uartclk = clk_get_rate(clk);
+
+       /* Check input frequency */
+       if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) {
+               dev_err(&pdev->dev, "Frequency out of bounds\n");
+               ret = -EINVAL;
                goto err_out;
        }
 
-       if (!pdata) {
-               dev_warn(&pdev->dev,
-                        "No platform data supplied, using defaults\n");
-               s->pdata.frequency = s->freq_std;
-       } else
+       if (pdata)
                memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata));
 
        if (s->pdata.poll_time_us) {
@@ -911,34 +924,11 @@ static int sccnxp_probe(struct platform_device *pdev)
                }
        }
 
-       /* Check input frequency */
-       if ((s->pdata.frequency < freq_min) ||
-           (s->pdata.frequency > freq_max)) {
-               dev_err(&pdev->dev, "Frequency out of bounds\n");
-               ret = -EINVAL;
-               goto err_out;
-       }
-
-       s->regulator = devm_regulator_get(&pdev->dev, "VCC");
-       if (!IS_ERR(s->regulator)) {
-               ret = regulator_enable(s->regulator);
-               if (ret) {
-                       dev_err(&pdev->dev,
-                               "Failed to enable regulator: %i\n", ret);
-                       return ret;
-               }
-       }
-
-       membase = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(membase)) {
-               ret = PTR_ERR(membase);
-               goto err_out;
-       }
-
        s->uart.owner           = THIS_MODULE;
        s->uart.dev_name        = "ttySC";
        s->uart.major           = SCCNXP_MAJOR;
        s->uart.minor           = SCCNXP_MINOR;
+       s->uart.nr              = s->chip->nr;
 #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
        s->uart.cons            = &s->console;
        s->uart.cons->device    = uart_console_device;
@@ -960,17 +950,17 @@ static int sccnxp_probe(struct platform_device *pdev)
                s->port[i].dev          = &pdev->dev;
                s->port[i].irq          = s->irq;
                s->port[i].type         = PORT_SC26XX;
-               s->port[i].fifosize     = fifosize;
+               s->port[i].fifosize     = s->chip->fifosize;
                s->port[i].flags        = UPF_SKIP_TEST | UPF_FIXED_TYPE;
                s->port[i].iotype       = UPIO_MEM;
                s->port[i].mapbase      = res->start;
                s->port[i].membase      = membase;
                s->port[i].regshift     = s->pdata.reg_shift;
-               s->port[i].uartclk      = s->pdata.frequency;
+               s->port[i].uartclk      = uartclk;
                s->port[i].ops          = &sccnxp_ops;
                uart_add_one_port(&s->uart, &s->port[i]);
                /* Set direction to input */
-               if (s->flags & SCCNXP_HAVE_IO)
+               if (s->chip->flags & SCCNXP_HAVE_IO)
                        sccnxp_set_bit(&s->port[i], DIR_OP, 0);
        }
 
@@ -997,7 +987,8 @@ static int sccnxp_probe(struct platform_device *pdev)
        }
 
 err_out:
-       platform_set_drvdata(pdev, NULL);
+       if (!IS_ERR(s->regulator))
+               return regulator_disable(s->regulator);
 
        return ret;
 }
@@ -1016,7 +1007,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);
@@ -1024,19 +1014,6 @@ static int sccnxp_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct platform_device_id sccnxp_id_table[] = {
-       { "sc2681",     SCCNXP_TYPE_SC2681 },
-       { "sc2691",     SCCNXP_TYPE_SC2691 },
-       { "sc2692",     SCCNXP_TYPE_SC2692 },
-       { "sc2891",     SCCNXP_TYPE_SC2891 },
-       { "sc2892",     SCCNXP_TYPE_SC2892 },
-       { "sc28202",    SCCNXP_TYPE_SC28202 },
-       { "sc68681",    SCCNXP_TYPE_SC68681 },
-       { "sc68692",    SCCNXP_TYPE_SC68692 },
-       { },
-};
-MODULE_DEVICE_TABLE(platform, sccnxp_id_table);
-
 static struct platform_driver sccnxp_uart_driver = {
        .driver = {
                .name   = SCCNXP_NAME,
index 28cdd2829139270bbd5dd2b9624481c6bd32b23c..0f02351c9239b2221dbc3820b7896388a83a71a8 100644 (file)
@@ -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
index fe48a0c2b4cab88c4b1bdb4d3ef09aa93dd0d4b2..440a962412da9251c10aa60ee6cf25cf2624474a 100644 (file)
@@ -1097,7 +1097,7 @@ static void serial_txx9_unregister_port(int line)
  */
 static int serial_txx9_probe(struct platform_device *dev)
 {
-       struct uart_port *p = dev->dev.platform_data;
+       struct uart_port *p = dev_get_platdata(&dev->dev);
        struct uart_port port;
        int ret, i;
 
index 7477e0ea5cdb886607e023ae06ce8cc92bb2edde..537750261aaa2fe5a5e2de8fdb3e6ee0adf8fe4d 100644 (file)
@@ -2380,7 +2380,7 @@ static char early_serial_buf[32];
 
 static int sci_probe_earlyprintk(struct platform_device *pdev)
 {
-       struct plat_sci_port *cfg = pdev->dev.platform_data;
+       struct plat_sci_port *cfg = dev_get_platdata(&pdev->dev);
 
        if (early_serial_console.data)
                return -EEXIST;
@@ -2469,7 +2469,7 @@ static int sci_probe_single(struct platform_device *dev,
 
 static int sci_probe(struct platform_device *dev)
 {
-       struct plat_sci_port *p = dev->dev.platform_data;
+       struct plat_sci_port *p = dev_get_platdata(&dev->dev);
        struct sci_port *sp = &sci_ports[dev->id];
        int ret;
 
index 1fd564b8194b7affe59be4ae0d3b827829c7ba8a..67a0d1b8341c9a91e2f75bf22b796398bc552951 100644 (file)
@@ -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 (file)
index 0000000..2ff4b1b
--- /dev/null
@@ -0,0 +1,939 @@
+/*
+ * 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];
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id asc_match[] = {
+       { .compatible = "st,asc", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, asc_match);
+#endif
+
+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");
index f655997f44af21328db8dfcd750402ef6f10a6d7..a63c14bc9a24a2be5c59bd95d1f0643bc3ed8f9b 100644 (file)
@@ -705,7 +705,7 @@ static int siu_init_ports(struct platform_device *pdev)
 {
        struct uart_port *port;
        struct resource *res;
-       int *type = pdev->dev.platform_data;
+       int *type = dev_get_platdata(&pdev->dev);
        int i;
 
        if (!type)
index 48af43de34677d17343cc7e95b99f54cb5d821a9..a90bf0440b4ff8c42a4e6c57cb609beba2562a8b 100644 (file)
@@ -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);
 
index 8eaf1ab8addb1d30faae7b93a28e532fad6a1cf6..e1ce141bad5e2995afb98ee3fd9719c1558f03f8 100644 (file)
@@ -577,22 +577,22 @@ struct mgsl_struct {
 
 #define SICR_RXC_ACTIVE                        BIT15
 #define SICR_RXC_INACTIVE              BIT14
-#define SICR_RXC                       (BIT15+BIT14)
+#define SICR_RXC                       (BIT15|BIT14)
 #define SICR_TXC_ACTIVE                        BIT13
 #define SICR_TXC_INACTIVE              BIT12
-#define SICR_TXC                       (BIT13+BIT12)
+#define SICR_TXC                       (BIT13|BIT12)
 #define SICR_RI_ACTIVE                 BIT11
 #define SICR_RI_INACTIVE               BIT10
-#define SICR_RI                                (BIT11+BIT10)
+#define SICR_RI                                (BIT11|BIT10)
 #define SICR_DSR_ACTIVE                        BIT9
 #define SICR_DSR_INACTIVE              BIT8
-#define SICR_DSR                       (BIT9+BIT8)
+#define SICR_DSR                       (BIT9|BIT8)
 #define SICR_DCD_ACTIVE                        BIT7
 #define SICR_DCD_INACTIVE              BIT6
-#define SICR_DCD                       (BIT7+BIT6)
+#define SICR_DCD                       (BIT7|BIT6)
 #define SICR_CTS_ACTIVE                        BIT5
 #define SICR_CTS_INACTIVE              BIT4
-#define SICR_CTS                       (BIT5+BIT4)
+#define SICR_CTS                       (BIT5|BIT4)
 #define SICR_RCC_UNDERFLOW             BIT3
 #define SICR_DPLL_NO_SYNC              BIT2
 #define SICR_BRG1_ZERO                 BIT1
@@ -1161,7 +1161,7 @@ static void mgsl_isr_receive_status( struct mgsl_struct *info )
 {
        u16 status = usc_InReg( info, RCSR );
 
-       if ( debug_level >= DEBUG_LEVEL_ISR )   
+       if ( debug_level >= DEBUG_LEVEL_ISR )
                printk("%s(%d):mgsl_isr_receive_status status=%04X\n",
                        __FILE__,__LINE__,status);
                        
@@ -1181,7 +1181,7 @@ static void mgsl_isr_receive_status( struct mgsl_struct *info )
                        (usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED));
        }
 
-       if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) {
+       if (status & (RXSTATUS_EXITED_HUNT | RXSTATUS_IDLE_RECEIVED)) {
                if (status & RXSTATUS_EXITED_HUNT)
                        info->icount.exithunt++;
                if (status & RXSTATUS_IDLE_RECEIVED)
@@ -1463,21 +1463,21 @@ static void mgsl_isr_receive_data( struct mgsl_struct *info )
 
                /* get the status of the received byte */
                status = usc_InReg(info, RCSR);
-               if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR +
-                               RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) )
+               if ( status & (RXSTATUS_FRAMING_ERROR | RXSTATUS_PARITY_ERROR |
+                               RXSTATUS_OVERRUN | RXSTATUS_BREAK_RECEIVED) )
                        usc_UnlatchRxstatusBits(info,RXSTATUS_ALL);
                
                icount->rx++;
                
                flag = 0;
-               if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR +
-                               RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) {
-                       printk("rxerr=%04X\n",status);                                  
+               if ( status & (RXSTATUS_FRAMING_ERROR | RXSTATUS_PARITY_ERROR |
+                               RXSTATUS_OVERRUN | RXSTATUS_BREAK_RECEIVED) ) {
+                       printk("rxerr=%04X\n",status);
                        /* update error statistics */
                        if ( status & RXSTATUS_BREAK_RECEIVED ) {
-                               status &= ~(RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR);
+                               status &= ~(RXSTATUS_FRAMING_ERROR | RXSTATUS_PARITY_ERROR);
                                icount->brk++;
-                       } else if (status & RXSTATUS_PARITY_ERROR) 
+                       } else if (status & RXSTATUS_PARITY_ERROR)
                                icount->parity++;
                        else if (status & RXSTATUS_FRAMING_ERROR)
                                icount->frame++;
@@ -1488,7 +1488,7 @@ static void mgsl_isr_receive_data( struct mgsl_struct *info )
                                icount->overrun++;
                        }
 
-                       /* discard char if tty control flags say so */                                  
+                       /* discard char if tty control flags say so */
                        if (status & info->ignore_status_mask)
                                continue;
                                
@@ -1545,8 +1545,8 @@ static void mgsl_isr_misc( struct mgsl_struct *info )
                usc_EnableReceiver(info,DISABLE_UNCONDITIONAL);
                usc_DmaCmd(info, DmaCmd_ResetRxChannel);
                usc_UnlatchRxstatusBits(info, RXSTATUS_ALL);
-               usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS);
-               usc_DisableInterrupts(info, RECEIVE_DATA + RECEIVE_STATUS);
+               usc_ClearIrqPendingBits(info, RECEIVE_DATA | RECEIVE_STATUS);
+               usc_DisableInterrupts(info, RECEIVE_DATA | RECEIVE_STATUS);
 
                /* schedule BH handler to restart receiver */
                info->pending_bh |= BH_RECEIVE;
@@ -1595,7 +1595,7 @@ static void mgsl_isr_receive_dma( struct mgsl_struct *info )
        u16 status;
        
        /* clear interrupt pending and IUS bit for Rx DMA IRQ */
-       usc_OutDmaReg( info, CDIR, BIT9+BIT1 );
+       usc_OutDmaReg( info, CDIR, BIT9 | BIT1 );
 
        /* Read the receive DMA status to identify interrupt type. */
        /* This also clears the status bits. */
@@ -1639,7 +1639,7 @@ static void mgsl_isr_transmit_dma( struct mgsl_struct *info )
        u16 status;
 
        /* clear interrupt pending and IUS bit for Tx DMA IRQ */
-       usc_OutDmaReg(info, CDIR, BIT8+BIT0 );
+       usc_OutDmaReg(info, CDIR, BIT8 | BIT0 );
 
        /* Read the transmit DMA status to identify interrupt type. */
        /* This also clears the status bits. */
@@ -1832,8 +1832,8 @@ static void shutdown(struct mgsl_struct * info)
        usc_DisableMasterIrqBit(info);
        usc_stop_receiver(info);
        usc_stop_transmitter(info);
-       usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS +
-               TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC );
+       usc_DisableInterrupts(info,RECEIVE_DATA | RECEIVE_STATUS |
+               TRANSMIT_DATA | TRANSMIT_STATUS | IO_PIN | MISC );
        usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE);
 
        /* Disable DMAEN (Port 7, Bit 14) */
@@ -1886,7 +1886,7 @@ static void mgsl_program_hw(struct mgsl_struct *info)
        info->ri_chkcount = 0;
        info->dsr_chkcount = 0;
 
-       usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI);          
+       usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI);
        usc_EnableInterrupts(info, IO_PIN);
        usc_get_serial_signals(info);
                
@@ -2773,7 +2773,7 @@ static int mgsl_wait_event(struct mgsl_struct * info, int __user * mask_ptr)
                if (!waitqueue_active(&info->event_wait_q)) {
                        /* disable enable exit hunt mode/idle rcvd IRQs */
                        usc_OutReg(info, RICR, usc_InReg(info,RICR) &
-                               ~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED));
+                               ~(RXSTATUS_EXITED_HUNT | RXSTATUS_IDLE_RECEIVED));
                }
                spin_unlock_irqrestore(&info->irq_spinlock,flags);
        }
@@ -3092,7 +3092,7 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp)
                printk("%s(%d):mgsl_close(%s) entry, count=%d\n",
                         __FILE__,__LINE__, info->device_name, info->port.count);
 
-       if (tty_port_close_start(&info->port, tty, filp) == 0)                   
+       if (tty_port_close_start(&info->port, tty, filp) == 0)
                goto cleanup;
 
        mutex_lock(&info->port.mutex);
@@ -4297,7 +4297,7 @@ static struct mgsl_struct* mgsl_allocate_device(void)
                spin_lock_init(&info->irq_spinlock);
                spin_lock_init(&info->netlock);
                memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
-               info->idle_mode = HDLC_TXIDLE_FLAGS;            
+               info->idle_mode = HDLC_TXIDLE_FLAGS;
                info->num_tx_dma_buffers = 1;
                info->num_tx_holding_buffers = 0;
        }
@@ -4722,7 +4722,7 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info )
                else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG )
                        RegValue |= BIT15;
                else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC )
-                       RegValue |= BIT15 + BIT14;
+                       RegValue |= BIT15 | BIT14;
                }
 
                if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE )
@@ -4763,11 +4763,11 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info )
        switch ( info->params.encoding ) {
        case HDLC_ENCODING_NRZB:               RegValue |= BIT13; break;
        case HDLC_ENCODING_NRZI_MARK:          RegValue |= BIT14; break;
-       case HDLC_ENCODING_NRZI_SPACE:         RegValue |= BIT14 + BIT13; break;
+       case HDLC_ENCODING_NRZI_SPACE:         RegValue |= BIT14 | BIT13; break;
        case HDLC_ENCODING_BIPHASE_MARK:       RegValue |= BIT15; break;
-       case HDLC_ENCODING_BIPHASE_SPACE:      RegValue |= BIT15 + BIT13; break;
-       case HDLC_ENCODING_BIPHASE_LEVEL:      RegValue |= BIT15 + BIT14; break;
-       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break;
+       case HDLC_ENCODING_BIPHASE_SPACE:      RegValue |= BIT15 | BIT13; break;
+       case HDLC_ENCODING_BIPHASE_LEVEL:      RegValue |= BIT15 | BIT14; break;
+       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 | BIT14 | BIT13; break;
        }
 
        if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT )
@@ -4838,15 +4838,15 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info )
        switch ( info->params.encoding ) {
        case HDLC_ENCODING_NRZB:               RegValue |= BIT13; break;
        case HDLC_ENCODING_NRZI_MARK:          RegValue |= BIT14; break;
-       case HDLC_ENCODING_NRZI_SPACE:         RegValue |= BIT14 + BIT13; break;
+       case HDLC_ENCODING_NRZI_SPACE:         RegValue |= BIT14 | BIT13; break;
        case HDLC_ENCODING_BIPHASE_MARK:       RegValue |= BIT15; break;
-       case HDLC_ENCODING_BIPHASE_SPACE:      RegValue |= BIT15 + BIT13; break;
-       case HDLC_ENCODING_BIPHASE_LEVEL:      RegValue |= BIT15 + BIT14; break;
-       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break;
+       case HDLC_ENCODING_BIPHASE_SPACE:      RegValue |= BIT15 | BIT13; break;
+       case HDLC_ENCODING_BIPHASE_LEVEL:      RegValue |= BIT15 | BIT14; break;
+       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 | BIT14 | BIT13; break;
        }
 
        if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT )
-               RegValue |= BIT9 + BIT8;
+               RegValue |= BIT9 | BIT8;
        else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT )
                RegValue |= ( BIT12 | BIT10 | BIT9 | BIT8);
 
@@ -4957,7 +4957,7 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info )
 
        RegValue = 0x0000;
 
-       if ( info->params.flags & (HDLC_FLAG_RXC_DPLL + HDLC_FLAG_TXC_DPLL) ) {
+       if ( info->params.flags & (HDLC_FLAG_RXC_DPLL | HDLC_FLAG_TXC_DPLL) ) {
                u32 XtalSpeed;
                u32 DpllDivisor;
                u16 Tc;
@@ -5019,7 +5019,7 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info )
                case HDLC_ENCODING_BIPHASE_MARK:
                case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT9; break;
                case HDLC_ENCODING_BIPHASE_LEVEL:
-               case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 + BIT8; break;
+               case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 | BIT8; break;
                }
        }
 
@@ -5056,8 +5056,8 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info )
        /* enable Master Interrupt Enable bit (MIE) */
        usc_EnableMasterIrqBit( info );
 
-       usc_ClearIrqPendingBits( info, RECEIVE_STATUS + RECEIVE_DATA +
-                               TRANSMIT_STATUS + TRANSMIT_DATA + MISC);
+       usc_ClearIrqPendingBits( info, RECEIVE_STATUS | RECEIVE_DATA |
+                               TRANSMIT_STATUS | TRANSMIT_DATA | MISC);
 
        /* arm RCC underflow interrupt */
        usc_OutReg(info, SICR, (u16)(usc_InReg(info,SICR) | BIT3));
@@ -5175,14 +5175,14 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info )
        switch ( info->params.preamble_length ) {
        case HDLC_PREAMBLE_LENGTH_16BITS: RegValue |= BIT10; break;
        case HDLC_PREAMBLE_LENGTH_32BITS: RegValue |= BIT11; break;
-       case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 + BIT10; break;
+       case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 | BIT10; break;
        }
 
        switch ( info->params.preamble ) {
-       case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 + BIT12; break;
+       case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 | BIT12; break;
        case HDLC_PREAMBLE_PATTERN_ONES:  RegValue |= BIT8; break;
        case HDLC_PREAMBLE_PATTERN_10:    RegValue |= BIT9; break;
-       case HDLC_PREAMBLE_PATTERN_01:    RegValue |= BIT9 + BIT8; break;
+       case HDLC_PREAMBLE_PATTERN_01:    RegValue |= BIT9 | BIT8; break;
        }
 
        usc_OutReg( info, CCR, RegValue );
@@ -5221,7 +5221,7 @@ static void usc_enable_loopback(struct mgsl_struct *info, int enable)
 {
        if (enable) {
                /* blank external TXD output */
-               usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7+BIT6));
+               usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7 | BIT6));
        
                /* Clock mode Control Register (CMCR)
                 *
@@ -5260,7 +5260,7 @@ static void usc_enable_loopback(struct mgsl_struct *info, int enable)
                outw( 0x0300, info->io_base + CCAR );
        } else {
                /* enable external TXD output */
-               usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7+BIT6));
+               usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7 | BIT6));
        
                /* clear Internal Data loopback mode */
                info->loopback_bits = 0;
@@ -5447,13 +5447,13 @@ static void usc_process_rxoverrun_sync( struct mgsl_struct *info )
                usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) );
 
                usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
-               usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
+               usc_ClearIrqPendingBits( info, RECEIVE_DATA | RECEIVE_STATUS );
                usc_EnableInterrupts( info, RECEIVE_STATUS );
 
                /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */
                /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */
 
-               usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 );
+               usc_OutDmaReg( info, RDIAR, BIT3 | BIT2 );
                usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) );
                usc_DmaCmd( info, DmaCmd_InitRxChannel );
                if ( info->params.flags & HDLC_FLAG_AUTO_DCD )
@@ -5488,8 +5488,8 @@ static void usc_stop_receiver( struct mgsl_struct *info )
        usc_DmaCmd( info, DmaCmd_ResetRxChannel );
 
        usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
-       usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
-       usc_DisableInterrupts( info, RECEIVE_DATA + RECEIVE_STATUS );
+       usc_ClearIrqPendingBits( info, RECEIVE_DATA | RECEIVE_STATUS );
+       usc_DisableInterrupts( info, RECEIVE_DATA | RECEIVE_STATUS );
 
        usc_EnableReceiver(info,DISABLE_UNCONDITIONAL);
 
@@ -5536,13 +5536,13 @@ static void usc_start_receiver( struct mgsl_struct *info )
                usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) );
 
                usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
-               usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
+               usc_ClearIrqPendingBits( info, RECEIVE_DATA | RECEIVE_STATUS );
                usc_EnableInterrupts( info, RECEIVE_STATUS );
 
                /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */
                /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */
 
-               usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 );
+               usc_OutDmaReg( info, RDIAR, BIT3 | BIT2 );
                usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) );
                usc_DmaCmd( info, DmaCmd_InitRxChannel );
                if ( info->params.flags & HDLC_FLAG_AUTO_DCD )
@@ -5551,7 +5551,7 @@ static void usc_start_receiver( struct mgsl_struct *info )
                        usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
        } else {
                usc_UnlatchRxstatusBits(info, RXSTATUS_ALL);
-               usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS);
+               usc_ClearIrqPendingBits(info, RECEIVE_DATA | RECEIVE_STATUS);
                usc_EnableInterrupts(info, RECEIVE_DATA);
 
                usc_RTCmd( info, RTCmd_PurgeRxFifo );
@@ -5925,7 +5925,7 @@ static void usc_set_async_mode( struct mgsl_struct *info )
        RegValue = 0;
 
        if ( info->params.data_bits != 8 )
-               RegValue |= BIT4+BIT3+BIT2;
+               RegValue |= BIT4 | BIT3 | BIT2;
 
        if ( info->params.parity != ASYNC_PARITY_NONE ) {
                RegValue |= BIT5;
@@ -5982,7 +5982,7 @@ static void usc_set_async_mode( struct mgsl_struct *info )
        RegValue = 0;
 
        if ( info->params.data_bits != 8 )
-               RegValue |= BIT4+BIT3+BIT2;
+               RegValue |= BIT4 | BIT3 | BIT2;
 
        if ( info->params.parity != ASYNC_PARITY_NONE ) {
                RegValue |= BIT5;
@@ -6129,7 +6129,7 @@ static void usc_loopback_frame( struct mgsl_struct *info )
                                                        
        /* WAIT FOR RECEIVE COMPLETE */
        for (i=0 ; i<1000 ; i++)
-               if (usc_InReg( info, RCSR ) & (BIT8 + BIT4 + BIT3 + BIT1))
+               if (usc_InReg( info, RCSR ) & (BIT8 | BIT4 | BIT3 | BIT1))
                        break;
 
        /* clear Internal Data loopback mode */
@@ -6579,8 +6579,8 @@ static bool mgsl_get_rx_frame(struct mgsl_struct *info)
        
        status = info->rx_buffer_list[EndIndex].status;
 
-       if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN +
-                       RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) {
+       if ( status & (RXSTATUS_SHORT_FRAME | RXSTATUS_OVERRUN |
+                       RXSTATUS_CRC_ERROR | RXSTATUS_ABORT) ) {
                if ( status & RXSTATUS_SHORT_FRAME )
                        info->icount.rxshort++;
                else if ( status & RXSTATUS_ABORT )
@@ -6762,8 +6762,8 @@ static bool mgsl_get_raw_rx_frame(struct mgsl_struct *info)
 
                status = info->rx_buffer_list[CurrentIndex].status;
 
-               if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN +
-                               RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) {
+               if ( status & (RXSTATUS_SHORT_FRAME | RXSTATUS_OVERRUN |
+                               RXSTATUS_CRC_ERROR | RXSTATUS_ABORT) ) {
                        if ( status & RXSTATUS_SHORT_FRAME )
                                info->icount.rxshort++;
                        else if ( status & RXSTATUS_ABORT )
@@ -6899,7 +6899,7 @@ static void mgsl_load_tx_dma_buffer(struct mgsl_struct *info,
                /* set CMR:13 to start transmit when
                 * next GoAhead (abort) is received
                 */
-               info->cmr_value |= BIT13;                         
+               info->cmr_value |= BIT13;
        }
                
        /* begin loading the frame in the next available tx dma
@@ -7278,7 +7278,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info )
                
                spin_unlock_irqrestore(&info->irq_spinlock,flags);
 
-                                               
+
                /******************************/
                /* WAIT FOR TRANSMIT COMPLETE */
                /******************************/
@@ -7292,7 +7292,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info )
                status = usc_InReg( info, TCSR );
                spin_unlock_irqrestore(&info->irq_spinlock,flags);
 
-               while ( !(status & (BIT6+BIT5+BIT4+BIT2+BIT1)) ) {
+               while ( !(status & (BIT6 | BIT5 | BIT4 | BIT2 | BIT1)) ) {
                        if (time_after(jiffies, EndTime)) {
                                rc = false;
                                break;
@@ -7307,7 +7307,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info )
 
        if ( rc ){
                /* CHECK FOR TRANSMIT ERRORS */
-               if ( status & (BIT5 + BIT1) ) 
+               if ( status & (BIT5 | BIT1) )
                        rc = false;
        }
 
@@ -7333,7 +7333,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info )
                /* CHECK FOR RECEIVE ERRORS */
                status = info->rx_buffer_list[0].status;
 
-               if ( status & (BIT8 + BIT3 + BIT1) ) {
+               if ( status & (BIT8 | BIT3 | BIT1) ) {
                        /* receive error has occurred */
                        rc = false;
                } else {
@@ -7605,7 +7605,7 @@ static void usc_loopmode_send_done( struct mgsl_struct * info )
 {
        info->loopmode_send_done_requested = false;
        /* clear CMR:13 to 0 to start echoing RxData to TxData */
-       info->cmr_value &= ~BIT13;                        
+       info->cmr_value &= ~BIT13;
        usc_OutReg(info, CMR, info->cmr_value);
 }
 
index 9121c1f7aeefef4b029501b114a5799f3195ca15..c043136fbe5198cf0a7a8704254fc28763ecd0b9 100644 (file)
 #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);
 }
-
index 366af832794b54db8ab9b8b7c7f524bdfe11f464..a9355ce1c6d586cef3c9cd62641a141bfa91c245 100644 (file)
@@ -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
  */
@@ -629,6 +629,11 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
 
        tty_lock(tty);
 
+       if (test_bit(TTY_HUPPED, &tty->flags)) {
+               tty_unlock(tty);
+               return;
+       }
+
        /* some functions below drop BTM, so we need this bit */
        set_bit(TTY_HUPPING, &tty->flags);
 
@@ -664,7 +669,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 +1392,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 +1408,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 +2205,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 +2234,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 +2242,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 +2265,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 +2280,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 +3019,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);
index 3500d41141472394f364b51952f599f31990bd19..03ba081c577251a0947466b79f23b36b359b51c1 100644 (file)
@@ -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;
index 1afe192bef6a7e8a795c91a3cf38a26615b6f2a1..6458e11e8e9d656daef35b0a5aaddc21dcfd1804 100644 (file)
 #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)
@@ -250,34 +246,6 @@ const struct file_operations tty_ldiscs_proc_fops = {
        .release        = seq_release,
 };
 
-/**
- *     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;
@@ -488,101 +503,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
@@ -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;
index a9af1b9ae160ec1b9bd47d4e9c1dfcabb27a2db9..d0e3a44977074526979a92e638c620aabaf661cb 100644 (file)
@@ -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)
index 60b7b69260592e9b413b599dcf572117f8487450..ea27804d87af9ec2485b1043f8ad97b30c05713f 100644 (file)
@@ -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;
 }
index c677829baa8b0d54adfefe1a9276c62ed9f3f03d..02af6ccefe6aca0299efcd86a886303c28e6057e 100644 (file)
@@ -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)
 {
index fd6833764d72b3bf77788741c6e85309b5aa6a72..be201ca2990ce1a0363608b39f2dd210097c57a2 100644 (file)
 #define ATMEL_US_NER           0x44                    /* Number of Errors Register */
 #define ATMEL_US_IF            0x4c                    /* IrDA Filter Register */
 
+#define ATMEL_US_NAME          0xf0                    /* Ip Name */
+
 #endif
index b7c8cdc1d4223b4565d7c1ede7e10229621cca97..cbfb171bbcbaaf169726b00a2013683e3ec94a1b 100644 (file)
@@ -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;
index cdaa7f0238993655e251fb933a01a69b8bd1fbad..8828a78dec9a2b3ed30d4c91f872405a8594f51a 100644 (file)
@@ -124,6 +124,29 @@ static inline void init_llist_head(struct llist_head *list)
             &(pos)->member != NULL;                                    \
             (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
index 3bed2e89611bf2d6a5d41fe2c38ebbb98d4dc7f4..6dec3d6abe0baea5527be38b412506652b0ef143 100644 (file)
 #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
 /*
  * 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
index 91648bf5fc5c29310a232bec16dd1623018720ea..dd11dcd1a1845ae8572d9ecb9babfd5b5ef6956a 100644 (file)
@@ -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>
  *
  * };
  */
 
-#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
index bdc510d032452fc53b03b146c4421bdf4709ab71..af0c8c3b89ae8f576cab1896acd2a43b5f27ff64 100644 (file)
@@ -60,7 +60,6 @@
  * };
  *
  * static struct sccnxp_pdata sc2892_info = {
- *     .frequency      = 3686400,
  *     .mctrl_cfg[0]   = MCTRL_SIG(DIR_OP, LINE_OP0),
  *     .mctrl_cfg[1]   = MCTRL_SIG(DIR_OP, LINE_OP1),
  * };
@@ -78,8 +77,6 @@
 
 /* SCCNXP platform data structure */
 struct sccnxp_pdata {
-       /* Frequency (extrenal clock or crystal) */
-       int                     frequency;
        /* Shift for A0 line */
        const u8                reg_shift;
        /* Modem control lines configuration */
index 01ac30efd6a6fbe649a97185c745e48357a47579..64f864651d8696aa5cf8a93b931c78c06a23bfdc 100644 (file)
@@ -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>
 
 
 
 #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;
index e0f252633b4745940edef0c4539381de4bb129a5..21ddd7d9ea1fe0303e5910b7de3019b9851e8bda 100644 (file)
@@ -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 */
index a1b0489998212d8db2bf831145da0bb87f4ee594..f15c898ff462006fb0bec91d5922ecbf24267d8c 100644 (file)
  *
  *     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
index 9119cc0977bf42a36f2ae8aa51123c99b0b5f9d7..e40ebe124cedff75cae654adb6658d0d0bfe3ea6 100644 (file)
 /* SH-SCI */
 #define PORT_HSCIF     104
 
+/* ST ASC type numbers */
+#define PORT_ASC       105
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
index e6322605b1380e64ed45208d6935729f0bfb9c08..97c26beae605aacff96ab44662f4d1f79a926ba8 100644 (file)
 #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