Merge tag 'tty-4.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 18 Aug 2018 17:50:41 +0000 (10:50 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 18 Aug 2018 17:50:41 +0000 (10:50 -0700)
Pull tty/serial driver updates from Greg KH:
 "Here is the big tty and serial driver pull request for 4.19-rc1.

  It's not all that big, just a number of small serial driver updates
  and fixes, along with some better vt handling for unicode characters
  for those using braille terminals.

  All of these patches have been in linux-next for a long time with no
  reported issues"

* tag 'tty-4.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (73 commits)
  tty: serial: 8250: Revert NXP SC16C2552 workaround
  serial: 8250_exar: Read INT0 from slave device, too
  tty: rocket: Fix possible buffer overwrite on register_PCI
  serial: 8250_dw: Add ACPI support for uart on Broadcom SoC
  serial: 8250_dw: always set baud rate in dw8250_set_termios
  dt-bindings: serial: Add binding for uartlite
  tty: serial: uartlite: Add support for suspend and resume
  tty: serial: uartlite: Add clock adaptation
  tty: serial: uartlite: Add structure for private data
  serial: sh-sci: Improve support for separate TEI and DRI interrupts
  serial: sh-sci: Remove SCIx_RZ_SCIFA_REGTYPE
  serial: sh-sci: Allow for compressed SCIF address
  serial: sh-sci: Improve interrupts description
  serial: 8250: Use cached port name directly in messages
  serial: 8250_exar: Drop unused variable in pci_xr17v35x_setup()
  vt: drop unused struct vt_struct
  vt: avoid a VLA in the unicode screen scroll function
  vt: add /dev/vcsu* to devices.txt
  vt: coherence validation code for the unicode screen buffer
  vt: selection: take screen contents from uniscr if available
  ...

44 files changed:
Documentation/admin-guide/devices.txt
Documentation/devicetree/bindings/arm/mediatek.txt
Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt
Documentation/devicetree/bindings/serial/fsl-imx-uart.txt
Documentation/devicetree/bindings/serial/mtk-uart.txt
Documentation/devicetree/bindings/serial/omap_serial.txt
Documentation/devicetree/bindings/serial/renesas,rzn1-uart.txt [new file with mode: 0644]
Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.txt [new file with mode: 0644]
Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
drivers/parport/parport_serial.c
drivers/s390/char/keyboard.c
drivers/tty/pty.c
drivers/tty/rocket.c
drivers/tty/serdev/core.c
drivers/tty/serial/8250/8250_core.c
drivers/tty/serial/8250/8250_dw.c
drivers/tty/serial/8250/8250_exar.c
drivers/tty/serial/8250/8250_of.c
drivers/tty/serial/8250/8250_omap.c
drivers/tty/serial/8250/8250_port.c
drivers/tty/serial/8250/serial_cs.c
drivers/tty/serial/imx.c
drivers/tty/serial/jsm/jsm_tty.c
drivers/tty/serial/max310x.c
drivers/tty/serial/pxa.c
drivers/tty/serial/qcom_geni_serial.c
drivers/tty/serial/serial_core.c
drivers/tty/serial/sh-sci.c
drivers/tty/serial/uartlite.c
drivers/tty/serial/xilinx_uartps.c
drivers/tty/tty_baudrate.c
drivers/tty/tty_io.c
drivers/tty/tty_ldsem.c
drivers/tty/vt/keyboard.c
drivers/tty/vt/selection.c
drivers/tty/vt/vc_screen.c
drivers/tty/vt/vt.c
include/linux/console_struct.h
include/linux/selection.h
include/linux/serial_8250.h
include/linux/serial_core.h
include/linux/tty_ldisc.h
include/uapi/linux/keyboard.h

index 4ec843123cc3bd1f206aa8b2315b3acad11b1a88..1649117e6087d3eb7462c4a73452654d32a29e00 100644 (file)
                they are redirected through the parport multiplex layer.
 
    7 char      Virtual console capture devices
-                 0 = /dev/vcs          Current vc text contents
-                 1 = /dev/vcs1         tty1 text contents
+                 0 = /dev/vcs          Current vc text (glyph) contents
+                 1 = /dev/vcs1         tty1 text (glyph) contents
                    ...
-                63 = /dev/vcs63        tty63 text contents
-               128 = /dev/vcsa         Current vc text/attribute contents
-               129 = /dev/vcsa1        tty1 text/attribute contents
+                63 = /dev/vcs63        tty63 text (glyph) contents
+                64 = /dev/vcsu         Current vc text (unicode) contents
+               65 = /dev/vcsu1         tty1 text (unicode) contents
                    ...
-               191 = /dev/vcsa63       tty63 text/attribute contents
+               127 = /dev/vcsu63       tty63 text (unicode) contents
+               128 = /dev/vcsa         Current vc text/attribute (glyph) contents
+               129 = /dev/vcsa1        tty1 text/attribute (glyph) contents
+                   ...
+               191 = /dev/vcsa63       tty63 text/attribute (glyph) contents
 
                NOTE: These devices permit both read and write access.
 
index 7d21ab37c19cdf79c7ef54b942c7b518d7a57a27..48fac4ec91fc08fa21c5decaef92c045d885e3d3 100644 (file)
@@ -11,6 +11,7 @@ compatible: Must contain one of
    "mediatek,mt6589"
    "mediatek,mt6592"
    "mediatek,mt6755"
+   "mediatek,mt6765"
    "mediatek,mt6795"
    "mediatek,mt6797"
    "mediatek,mt7622"
@@ -41,6 +42,9 @@ Supported boards:
 - Evaluation phone for MT6755(Helio P10):
     Required root node properties:
       - compatible = "mediatek,mt6755-evb", "mediatek,mt6755";
+- Evaluation board for MT6765(Helio P22):
+    Required root node properties:
+      - compatible = "mediatek,mt6765-evb", "mediatek,mt6765";
 - Evaluation board for MT6795(Helio X10):
     Required root node properties:
       - compatible = "mediatek,mt6795-evb", "mediatek,mt6795";
index 6a32922a55b8d3df13f4f7ff5d1ab0c9a061a951..33a98eb44949f1c43c972a7955e4d4d5427f7865 100644 (file)
@@ -11,6 +11,7 @@ Required properties:
        "mediatek,mt7622-sysirq", "mediatek,mt6577-sysirq": for MT7622
        "mediatek,mt6795-sysirq", "mediatek,mt6577-sysirq": for MT6795
        "mediatek,mt6797-sysirq", "mediatek,mt6577-sysirq": for MT6797
+       "mediatek,mt6765-sysirq", "mediatek,mt6577-sysirq": for MT6765
        "mediatek,mt6755-sysirq", "mediatek,mt6577-sysirq": for MT6755
        "mediatek,mt6592-sysirq", "mediatek,mt6577-sysirq": for MT6592
        "mediatek,mt6589-sysirq", "mediatek,mt6577-sysirq": for MT6589
index afcfbc34e24340ab006c0cba6c6de7591888aee6..35957cbf1571e4f674e7e6602807cabad140f78f 100644 (file)
@@ -9,7 +9,11 @@ Optional properties:
 - fsl,dte-mode : Indicate the uart works in DTE mode. The uart works
                   in DCE mode by default.
 - rs485-rts-delay, rs485-rts-active-low, rs485-rx-during-tx,
-  linux,rs485-enabled-at-boot-time: see rs485.txt
+  linux,rs485-enabled-at-boot-time: see rs485.txt. Note that for RS485
+  you must enable either the "uart-has-rtscts" or the "rts-gpios"
+  properties. In case you use "uart-has-rtscts" the signal that controls
+  the transceiver is actually CTS_B, not RTS_B. CTS_B is always output,
+  and RTS_B is input, regardless of dte-mode.
 
 Please check Documentation/devicetree/bindings/serial/serial.txt
 for the complete list of generic properties.
index f73abff3de43bcdb0ecdaabc0f4f23a470d7268d..742cb470595ba4d7e2a3e467328d33a8bf5335b5 100644 (file)
@@ -8,6 +8,7 @@ Required properties:
   * "mediatek,mt6582-uart" for MT6582 compatible UARTS
   * "mediatek,mt6589-uart" for MT6589 compatible UARTS
   * "mediatek,mt6755-uart" for MT6755 compatible UARTS
+  * "mediatek,mt6765-uart" for MT6765 compatible UARTS
   * "mediatek,mt6795-uart" for MT6795 compatible UARTS
   * "mediatek,mt6797-uart" for MT6797 compatible UARTS
   * "mediatek,mt7622-uart" for MT7622 compatible UARTS
index 4b0f05adb22811a99850e36d66be1073259b03c1..c35d5ece11562f3b020f5f3819202aef72b082af 100644 (file)
@@ -1,6 +1,7 @@
 OMAP UART controller
 
 Required properties:
+- compatible : should be "ti,am654-uart" for AM654 controllers
 - compatible : should be "ti,omap2-uart" for OMAP2 controllers
 - compatible : should be "ti,omap3-uart" for OMAP3 controllers
 - compatible : should be "ti,omap4-uart" for OMAP4 controllers
diff --git a/Documentation/devicetree/bindings/serial/renesas,rzn1-uart.txt b/Documentation/devicetree/bindings/serial/renesas,rzn1-uart.txt
new file mode 100644 (file)
index 0000000..8b9e0d4
--- /dev/null
@@ -0,0 +1,10 @@
+Renesas RZ/N1 UART
+
+This controller is based on the Synopsys DesignWare ABP UART and inherits all
+properties defined in snps-dw-apb-uart.txt except for the compatible property.
+
+Required properties:
+- compatible : The device specific string followed by the generic RZ/N1 string.
+   Therefore it must be one of:
+   "renesas,r9a06g032-uart", "renesas,rzn1-uart"
+   "renesas,r9a06g033-uart", "renesas,rzn1-uart"
index 106808b55b6da64d97d8d727673b14445b45bbb5..eaca9da79d83af982a79abe8e1b911f944a64ed1 100644 (file)
@@ -5,6 +5,7 @@ Required properties:
   - compatible: Must contain one or more of the following:
 
     - "renesas,scif-r7s72100" for R7S72100 (RZ/A1H) SCIF compatible UART.
+    - "renesas,scif-r7s9210" for R7S9210 (RZ/A2) SCIF compatible UART.
     - "renesas,scifa-r8a73a4" for R8A73A4 (R-Mobile APE6) SCIFA compatible UART.
     - "renesas,scifb-r8a73a4" for R8A73A4 (R-Mobile APE6) SCIFB compatible UART.
     - "renesas,scifa-r8a7740" for R8A7740 (R-Mobile A1) SCIFA compatible UART.
@@ -72,7 +73,21 @@ Required properties:
     family-specific and/or generic versions.
 
   - reg: Base address and length of the I/O registers used by the UART.
-  - interrupts: Must contain an interrupt-specifier for the SCIx interrupt.
+  - interrupts: Must contain one or more interrupt-specifiers for the SCIx.
+                If a single interrupt is expressed, then all events are
+                multiplexed into this single interrupt.
+
+                If multiple interrupts are provided by the hardware, the order
+                in which the interrupts are listed must match order below. Note
+                that some HW interrupt events may be muxed together resulting
+                in duplicate entries.
+                The interrupt order is as follows:
+                  1. Error (ERI)
+                  2. Receive buffer full (RXI)
+                  3. Transmit buffer empty (TXI)
+                  4. Break (BRI)
+                  5. Data Ready (DRI)
+                  6. Transmit End (TEI)
 
   - clocks: Must contain a phandle and clock-specifier pair for each entry
     in clock-names.
@@ -89,7 +104,7 @@ Required properties:
       - "scif_clk" for the optional external clock source for the frequency
        divider (SCIF_CLK).
 
-Note: Each enabled SCIx UART should have an alias correctly numbered in the
+Note: Each enabled SCIx UART may have an optional "serialN" alias in the
 "aliases" node.
 
 Optional properties:
diff --git a/Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.txt b/Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.txt
new file mode 100644 (file)
index 0000000..c37deb4
--- /dev/null
@@ -0,0 +1,23 @@
+Xilinx Axi Uartlite controller Device Tree Bindings
+---------------------------------------------------------
+
+Required properties:
+- compatible           : Can be either of
+                               "xlnx,xps-uartlite-1.00.a"
+                               "xlnx,opb-uartlite-1.00.b"
+- reg                  : Physical base address and size of the Axi Uartlite
+                         registers map.
+- interrupts           : Should contain the UART controller interrupt.
+
+Optional properties:
+- port-number          : Set Uart port number
+- clock-names          : Should be "s_axi_aclk"
+- clocks               : Input clock specifier. Refer to common clock bindings.
+
+Example:
+serial@800c0000 {
+       compatible = "xlnx,xps-uartlite-1.00.a";
+       reg = <0x0 0x800c0000 0x10000>;
+       interrupts = <0x0 0x6e 0x1>;
+       port-number = <0>;
+};
index 68b7d6207e3d75acd51400da27e5ca292c5026d0..ff92e5a41bedcc15d4126f9a93306f3efe70b1f2 100644 (file)
@@ -46,7 +46,7 @@ Child nodes should conform to I2C bus binding as described in i2c.txt.
 Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
 
 Required properties:
-- compatible:          Must be "qcom,geni-debug-uart".
+- compatible:          Must be "qcom,geni-debug-uart" or "qcom,geni-uart".
 - reg:                         Must contain UART register location and length.
 - interrupts:          Must contain UART core interrupts.
 - clock-names:         Must contain "se".
index ae9e01ef7599a5e9a87ef3e30d724d7c18d6c2cc..461fd8a24278030fb97736736f6258428d49c776 100644 (file)
@@ -58,6 +58,7 @@ enum parport_pc_pci_cards {
        timedia_9079c,
        wch_ch353_1s1p,
        wch_ch353_2s1p,
+       wch_ch382_0s1p,
        wch_ch382_2s1p,
        brainboxes_5s1p,
        sunix_2s1p,
@@ -147,6 +148,7 @@ static struct parport_pc_pci cards[] = {
        /* timedia_9079c */             { 1, { { 2, 3 }, } },
        /* wch_ch353_1s1p*/             { 1, { { 1, -1}, } },
        /* wch_ch353_2s1p*/             { 1, { { 2, -1}, } },
+       /* wch_ch382_0s1p*/             { 1, { { 2, -1}, } },
        /* wch_ch382_2s1p*/             { 1, { { 2, -1}, } },
        /* brainboxes_5s1p */           { 1, { { 3, -1 }, } },
        /* sunix_2s1p */                { 1, { { 3, -1 }, } },
@@ -252,6 +254,7 @@ static struct pci_device_id parport_serial_pci_tbl[] = {
        /* WCH CARDS */
        { 0x4348, 0x5053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p},
        { 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
+       { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382_0s1p},
        { 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p},
 
        /* BrainBoxes PX272/PX306 MIO card */
@@ -494,6 +497,12 @@ static struct pciserial_board pci_parport_serial_boards[] = {
                .base_baud      = 115200,
                .uart_offset    = 8,
        },
+       [wch_ch382_0s1p] = {
+               .flags          = FL_BASE0,
+               .num_ports      = 0,
+               .base_baud      = 115200,
+               .uart_offset    = 8,
+       },
        [wch_ch382_2s1p] = {
                .flags          = FL_BASE0,
                .num_ports      = 2,
index bbb3001b0961f339535cb4f52424d2f008142ca2..567aedc03c76332895e34d0704f83f74aad9de0e 100644 (file)
@@ -39,8 +39,34 @@ static const int kbd_max_vals[] = {
 };
 static const int KBD_NR_TYPES = ARRAY_SIZE(kbd_max_vals);
 
-static unsigned char ret_diacr[NR_DEAD] = {
-       '`', '\'', '^', '~', '"', ','
+static const unsigned char ret_diacr[NR_DEAD] = {
+       '`',    /* dead_grave */
+       '\'',   /* dead_acute */
+       '^',    /* dead_circumflex */
+       '~',    /* dead_tilda */
+       '"',    /* dead_diaeresis */
+       ',',    /* dead_cedilla */
+       '_',    /* dead_macron */
+       'U',    /* dead_breve */
+       '.',    /* dead_abovedot */
+       '*',    /* dead_abovering */
+       '=',    /* dead_doubleacute */
+       'c',    /* dead_caron */
+       'k',    /* dead_ogonek */
+       'i',    /* dead_iota */
+       '#',    /* dead_voiced_sound */
+       'o',    /* dead_semivoiced_sound */
+       '!',    /* dead_belowdot */
+       '?',    /* dead_hook */
+       '+',    /* dead_horn */
+       '-',    /* dead_stroke */
+       ')',    /* dead_abovecomma */
+       '(',    /* dead_abovereversedcomma */
+       ':',    /* dead_doublegrave */
+       'n',    /* dead_invertedbreve */
+       ';',    /* dead_belowcomma */
+       '$',    /* dead_currency */
+       '@',    /* dead_greek */
 };
 
 /*
index b0e2c4847a5d6a51ec70bc88e9ff74052fd1a863..678406e0948b227ab0188d938450b384e4dbae82 100644 (file)
@@ -625,7 +625,7 @@ int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags)
        if (tty->driver != ptm_driver)
                return -EIO;
 
-       fd = get_unused_fd_flags(0);
+       fd = get_unused_fd_flags(flags);
        if (fd < 0) {
                retval = fd;
                goto err;
index bdd17d2aaafd957d81b382eb16e8fa1f814bb004..b121d8f8f3d7d1a9d1dfc4341d61d51a5253a3f1 100644 (file)
@@ -1881,7 +1881,7 @@ static __init int register_PCI(int i, struct pci_dev *dev)
        ByteIO_t UPCIRingInd = 0;
 
        if (!dev || !pci_match_id(rocket_pci_ids, dev) ||
-           pci_enable_device(dev))
+           pci_enable_device(dev) || i >= NUM_BOARDS)
                return 0;
 
        rcktpt_io_addr[i] = pci_resource_start(dev, 0);
index 9e59f4788589c879358ce12507362baec459533d..9db93f500b4e722cfa5df55201f28238d484b9ae 100644 (file)
@@ -13,6 +13,8 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
 #include <linux/serdev.h>
 #include <linux/slab.h>
 
@@ -143,11 +145,28 @@ EXPORT_SYMBOL_GPL(serdev_device_remove);
 int serdev_device_open(struct serdev_device *serdev)
 {
        struct serdev_controller *ctrl = serdev->ctrl;
+       int ret;
 
        if (!ctrl || !ctrl->ops->open)
                return -EINVAL;
 
-       return ctrl->ops->open(ctrl);
+       ret = ctrl->ops->open(ctrl);
+       if (ret)
+               return ret;
+
+       ret = pm_runtime_get_sync(&ctrl->dev);
+       if (ret < 0) {
+               pm_runtime_put_noidle(&ctrl->dev);
+               goto err_close;
+       }
+
+       return 0;
+
+err_close:
+       if (ctrl->ops->close)
+               ctrl->ops->close(ctrl);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(serdev_device_open);
 
@@ -158,6 +177,8 @@ void serdev_device_close(struct serdev_device *serdev)
        if (!ctrl || !ctrl->ops->close)
                return;
 
+       pm_runtime_put(&ctrl->dev);
+
        ctrl->ops->close(ctrl);
 }
 EXPORT_SYMBOL_GPL(serdev_device_close);
@@ -330,8 +351,17 @@ EXPORT_SYMBOL_GPL(serdev_device_set_tiocm);
 static int serdev_drv_probe(struct device *dev)
 {
        const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver);
+       int ret;
+
+       ret = dev_pm_domain_attach(dev, true);
+       if (ret)
+               return ret;
+
+       ret = sdrv->probe(to_serdev_device(dev));
+       if (ret)
+               dev_pm_domain_detach(dev, true);
 
-       return sdrv->probe(to_serdev_device(dev));
+       return ret;
 }
 
 static int serdev_drv_remove(struct device *dev)
@@ -339,6 +369,9 @@ static int serdev_drv_remove(struct device *dev)
        const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver);
        if (sdrv->remove)
                sdrv->remove(to_serdev_device(dev));
+
+       dev_pm_domain_detach(dev, true);
+
        return 0;
 }
 
@@ -416,6 +449,9 @@ struct serdev_controller *serdev_controller_alloc(struct device *parent,
 
        dev_set_name(&ctrl->dev, "serial%d", id);
 
+       pm_runtime_no_callbacks(&ctrl->dev);
+       pm_suspend_ignore_children(&ctrl->dev, true);
+
        dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id);
        return ctrl;
 
@@ -547,20 +583,23 @@ int serdev_controller_add(struct serdev_controller *ctrl)
        if (ret)
                return ret;
 
+       pm_runtime_enable(&ctrl->dev);
+
        ret_of = of_serdev_register_devices(ctrl);
        ret_acpi = acpi_serdev_register_devices(ctrl);
        if (ret_of && ret_acpi) {
                dev_dbg(&ctrl->dev, "no devices registered: of:%d acpi:%d\n",
                        ret_of, ret_acpi);
                ret = -ENODEV;
-               goto out_dev_del;
+               goto err_rpm_disable;
        }
 
        dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n",
                ctrl->nr, &ctrl->dev);
        return 0;
 
-out_dev_del:
+err_rpm_disable:
+       pm_runtime_disable(&ctrl->dev);
        device_del(&ctrl->dev);
        return ret;
 };
@@ -591,6 +630,7 @@ void serdev_controller_remove(struct serdev_controller *ctrl)
 
        dummy = device_for_each_child(&ctrl->dev, NULL,
                                      serdev_remove_device);
+       pm_runtime_disable(&ctrl->dev);
        device_del(&ctrl->dev);
 }
 EXPORT_SYMBOL_GPL(serdev_controller_remove);
index 9342fc2ee7dfe292bee10f607434055504401b28..8fe3d0ed229ed27c9bdb53ade9c356aaed1abc85 100644 (file)
@@ -323,7 +323,7 @@ static int univ8250_setup_irq(struct uart_8250_port *up)
         * the port is opened so this value needs to be preserved.
         */
        if (up->bugs & UART_BUG_THRE) {
-               pr_debug("ttyS%d - using backup timer\n", serial_index(port));
+               pr_debug("%s - using backup timer\n", port->name);
 
                up->timer.function = serial8250_backup_timeout;
                mod_timer(&up->timer, jiffies +
@@ -1023,6 +1023,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
                        uart->port.get_mctrl = up->port.get_mctrl;
                if (up->port.set_mctrl)
                        uart->port.set_mctrl = up->port.set_mctrl;
+               if (up->port.get_divisor)
+                       uart->port.get_divisor = up->port.get_divisor;
+               if (up->port.set_divisor)
+                       uart->port.set_divisor = up->port.set_divisor;
                if (up->port.startup)
                        uart->port.startup = up->port.startup;
                if (up->port.shutdown)
index aff04f1de3a543fc4a9e54eb0c9df478dfff5003..fa8dcb470640f7d97b8a11652d2b418ac2f9d0a1 100644 (file)
@@ -31,6 +31,7 @@
 
 /* Offsets for the DesignWare specific registers */
 #define DW_UART_USR    0x1f /* UART Status Register */
+#define DW_UART_DLF    0xc0 /* Divisor Latch Fraction Register */
 #define DW_UART_CPR    0xf4 /* Component Parameter Register */
 #define DW_UART_UCV    0xf8 /* UART Component Version */
 
@@ -55,6 +56,7 @@
 
 struct dw8250_data {
        u8                      usr_reg;
+       u8                      dlf_size;
        int                     line;
        int                     msr_mask_on;
        int                     msr_mask_off;
@@ -67,6 +69,21 @@ struct dw8250_data {
        unsigned int            uart_16550_compatible:1;
 };
 
+static inline u32 dw8250_readl_ext(struct uart_port *p, int offset)
+{
+       if (p->iotype == UPIO_MEM32BE)
+               return ioread32be(p->membase + offset);
+       return readl(p->membase + offset);
+}
+
+static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg)
+{
+       if (p->iotype == UPIO_MEM32BE)
+               iowrite32be(reg, p->membase + offset);
+       else
+               writel(reg, p->membase + offset);
+}
+
 static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
 {
        struct dw8250_data *d = p->private_data;
@@ -293,7 +310,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
        long rate;
        int ret;
 
-       if (IS_ERR(d->clk) || !old)
+       if (IS_ERR(d->clk))
                goto out;
 
        clk_disable_unprepare(d->clk);
@@ -351,6 +368,37 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param)
        return param == chan->device->dev->parent;
 }
 
+/*
+ * divisor = div(I) + div(F)
+ * "I" means integer, "F" means fractional
+ * quot = div(I) = clk / (16 * baud)
+ * frac = div(F) * 2^dlf_size
+ *
+ * let rem = clk % (16 * baud)
+ * we have: div(F) * (16 * baud) = rem
+ * so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud)
+ */
+static unsigned int dw8250_get_divisor(struct uart_port *p,
+                                      unsigned int baud,
+                                      unsigned int *frac)
+{
+       unsigned int quot, rem, base_baud = baud * 16;
+       struct dw8250_data *d = p->private_data;
+
+       quot = p->uartclk / base_baud;
+       rem = p->uartclk % base_baud;
+       *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud);
+
+       return quot;
+}
+
+static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
+                              unsigned int quot, unsigned int quot_frac)
+{
+       dw8250_writel_ext(p, DW_UART_DLF, quot_frac);
+       serial8250_do_set_divisor(p, baud, quot, quot_frac);
+}
+
 static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
 {
        if (p->dev->of_node) {
@@ -404,20 +452,26 @@ static void dw8250_setup_port(struct uart_port *p)
         * If the Component Version Register returns zero, we know that
         * ADDITIONAL_FEATURES are not enabled. No need to go any further.
         */
-       if (p->iotype == UPIO_MEM32BE)
-               reg = ioread32be(p->membase + DW_UART_UCV);
-       else
-               reg = readl(p->membase + DW_UART_UCV);
+       reg = dw8250_readl_ext(p, DW_UART_UCV);
        if (!reg)
                return;
 
        dev_dbg(p->dev, "Designware UART version %c.%c%c\n",
                (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
 
-       if (p->iotype == UPIO_MEM32BE)
-               reg = ioread32be(p->membase + DW_UART_CPR);
-       else
-               reg = readl(p->membase + DW_UART_CPR);
+       dw8250_writel_ext(p, DW_UART_DLF, ~0U);
+       reg = dw8250_readl_ext(p, DW_UART_DLF);
+       dw8250_writel_ext(p, DW_UART_DLF, 0);
+
+       if (reg) {
+               struct dw8250_data *d = p->private_data;
+
+               d->dlf_size = fls(reg);
+               p->get_divisor = dw8250_get_divisor;
+               p->set_divisor = dw8250_set_divisor;
+       }
+
+       reg = dw8250_readl_ext(p, DW_UART_CPR);
        if (!reg)
                return;
 
@@ -693,6 +747,7 @@ static const struct of_device_id dw8250_of_match[] = {
        { .compatible = "snps,dw-apb-uart" },
        { .compatible = "cavium,octeon-3860-uart" },
        { .compatible = "marvell,armada-38x-uart" },
+       { .compatible = "renesas,rzn1-uart" },
        { /* Sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, dw8250_of_match);
@@ -707,6 +762,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = {
        { "APMC0D08", 0},
        { "AMD0020", 0 },
        { "AMDI0020", 0 },
+       { "BRCM2032", 0 },
        { "HISI0031", 0 },
        { },
 };
index 38af306ca0e817c71f38ce90c833b1f1b0fa7191..0089aa305ef9f931dcba31f4828f299241dea4dc 100644 (file)
@@ -109,11 +109,12 @@ struct exar8250_platform {
  * struct exar8250_board - board information
  * @num_ports: number of serial ports
  * @reg_shift: describes UART register mapping in PCI memory
+ * @setup: quirk run at ->probe() stage
+ * @exit: quirk run at ->remove() stage
  */
 struct exar8250_board {
        unsigned int num_ports;
        unsigned int reg_shift;
-       bool has_slave;
        int     (*setup)(struct exar8250 *, struct pci_dev *,
                         struct uart_8250_port *, int);
        void    (*exit)(struct pci_dev *pcidev);
@@ -272,8 +273,32 @@ static int xr17v35x_register_gpio(struct pci_dev *pcidev,
        return 0;
 }
 
+static int generic_rs485_config(struct uart_port *port,
+                               struct serial_rs485 *rs485)
+{
+       bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED);
+       u8 __iomem *p = port->membase;
+       u8 value;
+
+       value = readb(p + UART_EXAR_FCTR);
+       if (is_rs485)
+               value |= UART_FCTR_EXAR_485;
+       else
+               value &= ~UART_FCTR_EXAR_485;
+
+       writeb(value, p + UART_EXAR_FCTR);
+
+       if (is_rs485)
+               writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR);
+
+       port->rs485 = *rs485;
+
+       return 0;
+}
+
 static const struct exar8250_platform exar8250_default_platform = {
        .register_gpio = xr17v35x_register_gpio,
+       .rs485_config = generic_rs485_config,
 };
 
 static int iot2040_rs485_config(struct uart_port *port,
@@ -306,19 +331,7 @@ static int iot2040_rs485_config(struct uart_port *port,
        value |= mode;
        writeb(value, p + UART_EXAR_MPIOLVL_7_0);
 
-       value = readb(p + UART_EXAR_FCTR);
-       if (is_rs485)
-               value |= UART_FCTR_EXAR_485;
-       else
-               value &= ~UART_FCTR_EXAR_485;
-       writeb(value, p + UART_EXAR_FCTR);
-
-       if (is_rs485)
-               writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR);
-
-       port->rs485 = *rs485;
-
-       return 0;
+       return generic_rs485_config(port, rs485);
 }
 
 static const struct property_entry iot2040_gpio_properties[] = {
@@ -364,7 +377,6 @@ static int
 pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
                   struct uart_8250_port *port, int idx)
 {
-       const struct exar8250_board *board = priv->board;
        const struct exar8250_platform *platform;
        const struct dmi_system_id *dmi_match;
        unsigned int offset = idx * 0x400;
@@ -382,10 +394,10 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
        port->port.rs485_config = platform->rs485_config;
 
        /*
-        * Setup the uart clock for the devices on expansion slot to
+        * Setup the UART clock for the devices on expansion slot to
         * half the clock speed of the main chip (which is 125MHz)
         */
-       if (board->has_slave && idx >= 8)
+       if (idx >= 8)
                port->port.uartclk /= 2;
 
        ret = default_setup(priv, pcidev, idx, offset, port);
@@ -433,7 +445,11 @@ static irqreturn_t exar_misc_handler(int irq, void *data)
        struct exar8250 *priv = data;
 
        /* Clear all PCI interrupts by reading INT0. No effect on IIR */
-       ioread8(priv->virt + UART_EXAR_INT0);
+       readb(priv->virt + UART_EXAR_INT0);
+
+       /* Clear INT0 for Expansion Interface slave ports, too */
+       if (priv->board->num_ports > 8)
+               readb(priv->virt + 0x2000 + UART_EXAR_INT0);
 
        return IRQ_HANDLED;
 }
@@ -590,14 +606,12 @@ static const struct exar8250_board pbn_exar_XR17V35x = {
 
 static const struct exar8250_board pbn_exar_XR17V4358 = {
        .num_ports      = 12,
-       .has_slave      = true,
        .setup          = pci_xr17v35x_setup,
        .exit           = pci_xr17v35x_exit,
 };
 
 static const struct exar8250_board pbn_exar_XR17V8358 = {
        .num_ports      = 16,
-       .has_slave      = true,
        .setup          = pci_xr17v35x_setup,
        .exit           = pci_xr17v35x_exit,
 };
index bfb37f0be22f2e5e732c1b18153d45a137b7ff13..af8beefe9b5c30b1153ab3ac769e73faecb47ee3 100644 (file)
@@ -124,7 +124,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
                                dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n",
                                         prop);
                                ret = -EINVAL;
-                               goto err_dispose;
+                               goto err_unprepare;
                        }
                }
                port->flags |= UPF_IOREMAP;
@@ -144,6 +144,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
                port->line = ret;
 
        port->irq = irq_of_parse_and_map(np, 0);
+       if (!port->irq) {
+               ret = -EPROBE_DEFER;
+               goto err_unprepare;
+       }
 
        info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL);
        if (IS_ERR(info->rst)) {
index 1b337fee07ed186853019a402fca59b06cc5d2ff..a019286f8bb65c5673285bdd5ad06c7b0a63aae8 100644 (file)
@@ -1115,6 +1115,7 @@ static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE;
 static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE;
 
 static const struct of_device_id omap8250_dt_ids[] = {
+       { .compatible = "ti,am654-uart" },
        { .compatible = "ti,omap2-uart" },
        { .compatible = "ti,omap3-uart" },
        { .compatible = "ti,omap4-uart", .data = &omap4_habit, },
index cf541aab2bd0e0b6cf0a502dcfd06f750cd0e44d..3f779d25ec0cdfa10575b153b62dc34c0c5b218b 100644 (file)
@@ -90,8 +90,7 @@ static const struct serial8250_config uart_config[] = {
                .name           = "16550A",
                .fifo_size      = 16,
                .tx_loadsz      = 16,
-               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
-                                 UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
                .rxtrig_bytes   = {1, 4, 8, 14},
                .flags          = UART_CAP_FIFO,
        },
@@ -1211,8 +1210,8 @@ static void autoconfig(struct uart_8250_port *up)
        if (!port->iobase && !port->mapbase && !port->membase)
                return;
 
-       DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ",
-                      serial_index(port), port->iobase, port->membase);
+       DEBUG_AUTOCONF("%s: autoconf (0x%04lx, 0x%p): ",
+                      port->name, port->iobase, port->membase);
 
        /*
         * We really do need global IRQs disabled here - we're going to
@@ -1363,9 +1362,8 @@ out_lock:
                fintek_8250_probe(up);
 
        if (up->capabilities != old_capabilities) {
-               pr_warn("ttyS%d: detected caps %08x should be %08x\n",
-                      serial_index(port), old_capabilities,
-                      up->capabilities);
+               pr_warn("%s: detected caps %08x should be %08x\n",
+                       port->name, old_capabilities, up->capabilities);
        }
 out:
        DEBUG_AUTOCONF("iir=%d ", scratch);
@@ -2212,8 +2210,7 @@ int serial8250_do_startup(struct uart_port *port)
         */
        if (!(port->flags & UPF_BUGGY_UART) &&
            (serial_port_in(port, UART_LSR) == 0xff)) {
-               printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n",
-                                  serial_index(port));
+               pr_info_ratelimited("%s: LSR safety check engaged!\n", port->name);
                retval = -ENODEV;
                goto out;
        }
@@ -2245,8 +2242,8 @@ int serial8250_do_startup(struct uart_port *port)
             (port->type == PORT_ALTR_16550_F128)) && (port->fifosize > 1)) {
                /* Bounds checking of TX threshold (valid 0 to fifosize-2) */
                if ((up->tx_loadsz < 2) || (up->tx_loadsz > port->fifosize)) {
-                       pr_err("ttyS%d TX FIFO Threshold errors, skipping\n",
-                              serial_index(port));
+                       pr_err("%s TX FIFO Threshold errors, skipping\n",
+                              port->name);
                } else {
                        serial_port_out(port, UART_ALTR_AFR,
                                        UART_ALTR_EN_TXFIFO_LW);
@@ -2343,8 +2340,8 @@ int serial8250_do_startup(struct uart_port *port)
        if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
                if (!(up->bugs & UART_BUG_TXEN)) {
                        up->bugs |= UART_BUG_TXEN;
-                       pr_debug("ttyS%d - enabling bad tx status workarounds\n",
-                                serial_index(port));
+                       pr_debug("%s - enabling bad tx status workarounds\n",
+                                port->name);
                }
        } else {
                up->bugs &= ~UART_BUG_TXEN;
@@ -2373,8 +2370,8 @@ dont_test_tx_en:
        if (up->dma) {
                retval = serial8250_request_dma(up);
                if (retval) {
-                       pr_warn_ratelimited("ttyS%d - failed to request DMA\n",
-                                           serial_index(port));
+                       pr_warn_ratelimited("%s - failed to request DMA\n",
+                                           port->name);
                        up->dma = NULL;
                }
        }
@@ -2498,11 +2495,11 @@ static unsigned int npcm_get_divisor(struct uart_8250_port *up,
        return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2;
 }
 
-static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
-                                          unsigned int baud,
-                                          unsigned int *frac)
+static unsigned int serial8250_do_get_divisor(struct uart_port *port,
+                                             unsigned int baud,
+                                             unsigned int *frac)
 {
-       struct uart_port *port = &up->port;
+       struct uart_8250_port *up = up_to_u8250p(port);
        unsigned int quot;
 
        /*
@@ -2532,6 +2529,16 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
        return quot;
 }
 
+static unsigned int serial8250_get_divisor(struct uart_port *port,
+                                          unsigned int baud,
+                                          unsigned int *frac)
+{
+       if (port->get_divisor)
+               return port->get_divisor(port, baud, frac);
+
+       return serial8250_do_get_divisor(port, baud, frac);
+}
+
 static unsigned char serial8250_compute_lcr(struct uart_8250_port *up,
                                            tcflag_t c_cflag)
 {
@@ -2570,8 +2577,8 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up,
        return cval;
 }
 
-static void serial8250_set_divisor(struct uart_port *port, unsigned int baud,
-                           unsigned int quot, unsigned int quot_frac)
+void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
+                              unsigned int quot, unsigned int quot_frac)
 {
        struct uart_8250_port *up = up_to_u8250p(port);
 
@@ -2602,6 +2609,16 @@ static void serial8250_set_divisor(struct uart_port *port, unsigned int baud,
                serial_port_out(port, 0x2, quot_frac);
        }
 }
+EXPORT_SYMBOL_GPL(serial8250_do_set_divisor);
+
+static void serial8250_set_divisor(struct uart_port *port, unsigned int baud,
+                                  unsigned int quot, unsigned int quot_frac)
+{
+       if (port->set_divisor)
+               port->set_divisor(port, baud, quot, quot_frac);
+       else
+               serial8250_do_set_divisor(port, baud, quot, quot_frac);
+}
 
 static unsigned int serial8250_get_baud_rate(struct uart_port *port,
                                             struct ktermios *termios,
@@ -2636,7 +2653,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
        cval = serial8250_compute_lcr(up, termios->c_cflag);
 
        baud = serial8250_get_baud_rate(port, termios, old);
-       quot = serial8250_get_divisor(up, baud, &frac);
+       quot = serial8250_get_divisor(port, baud, &frac);
 
        /*
         * Ok, we're now changing the port state.  Do it with
@@ -3197,7 +3214,7 @@ static void serial8250_console_restore(struct uart_8250_port *up)
                termios.c_cflag = port->state->port.tty->termios.c_cflag;
 
        baud = serial8250_get_baud_rate(port, &termios, NULL);
-       quot = serial8250_get_divisor(up, baud, &frac);
+       quot = serial8250_get_divisor(port, baud, &frac);
 
        serial8250_set_divisor(port, baud, quot, frac);
        serial_port_out(port, UART_LCR, up->lcr);
index 9963a766dcfb4e6d0c0a7ef86799e5b2856ed962..c8186a05a453c011d049ac1f51aca2ec66c0cfc4 100644 (file)
@@ -638,8 +638,10 @@ static int serial_config(struct pcmcia_device *link)
            (link->has_func_id) &&
            (link->socket->pcmcia_pfc == 0) &&
            ((link->func_id == CISTPL_FUNCID_MULTI) ||
-            (link->func_id == CISTPL_FUNCID_SERIAL)))
-               pcmcia_loop_config(link, serial_check_for_multi, info);
+            (link->func_id == CISTPL_FUNCID_SERIAL))) {
+               if (pcmcia_loop_config(link, serial_check_for_multi, info))
+                       goto failed;
+       }
 
        /*
         * Apply any multi-port quirk.
index 4e853570ea800ed42ee71fed9da1d205be1fc24e..239c0fa2e9816805cd134fad697495f7f7929d4b 100644 (file)
@@ -314,7 +314,8 @@ static u32 imx_uart_readl(struct imx_port *sport, u32 offset)
                /*
                 * UCR2_SRST is the only bit in the cached registers that might
                 * differ from the value that was last written. As it only
-                * clears after being set, reread conditionally.
+                * automatically becomes one after being cleared, reread
+                * conditionally.
                 */
                if (!(sport->ucr2 & UCR2_SRST))
                        sport->ucr2 = readl(sport->port.membase + offset);
@@ -1051,7 +1052,7 @@ static void imx_uart_dma_rx_callback(void *data)
        unsigned int r_bytes;
        unsigned int bd_size;
 
-       status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state);
+       status = dmaengine_tx_status(chan, sport->rx_cookie, &state);
 
        if (status == DMA_ERROR) {
                imx_uart_clear_rx_errors(sport);
index b6bd6e15e07bde86062beb2bd7e07b872e28ddc7..689774c073ca4d91ef18cc0246923aa8e4c88772 100644 (file)
@@ -430,7 +430,6 @@ int jsm_uart_port_init(struct jsm_board *brd)
 {
        int i, rc;
        unsigned int line;
-       struct jsm_channel *ch;
 
        if (!brd)
                return -ENXIO;
@@ -444,7 +443,7 @@ int jsm_uart_port_init(struct jsm_board *brd)
        brd->nasync = brd->maxports;
 
        /* Set up channel variables */
-       for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
+       for (i = 0; i < brd->nasync; i++) {
 
                if (!brd->channels[i])
                        continue;
index efe55a1a0615f95e23fe6d00129ff8faca1db33d..3db48fcd6068d853f2c3376c885289222a063d10 100644 (file)
@@ -531,8 +531,8 @@ static int max310x_update_best_err(unsigned long f, long *besterr)
        return 1;
 }
 
-static int max310x_set_ref_clk(struct max310x_port *s, unsigned long freq,
-                              bool xtal)
+static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s,
+                              unsigned long freq, bool xtal)
 {
        unsigned int div, clksrc, pllcfg = 0;
        long besterr = -1;
@@ -588,8 +588,14 @@ static int max310x_set_ref_clk(struct max310x_port *s, unsigned long freq,
        regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc);
 
        /* Wait for crystal */
-       if (pllcfg && xtal)
+       if (xtal) {
+               unsigned int val;
                msleep(10);
+               regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &val);
+               if (!(val & MAX310X_STS_CLKREADY_BIT)) {
+                       dev_warn(dev, "clock is not stable yet\n");
+               }
+       }
 
        return (int)bestfreq;
 }
@@ -1260,7 +1266,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype,
                                   MAX310X_MODE1_AUTOSLEEP_BIT);
        }
 
-       uartclk = max310x_set_ref_clk(s, freq, xtal);
+       uartclk = max310x_set_ref_clk(dev, s, freq, xtal);
        dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk);
 
        mutex_init(&s->mutex);
index eda3c7710d6a745f4c7a1f957d38e8b8e2104e59..4932b674f7efa9535421eedd6e929262b42c7cec 100644 (file)
@@ -887,7 +887,8 @@ static int serial_pxa_probe(struct platform_device *dev)
                goto err_clk;
        if (sport->port.line >= ARRAY_SIZE(serial_pxa_ports)) {
                dev_err(&dev->dev, "serial%d out of range\n", sport->port.line);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err_clk;
        }
        snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);
 
index c62e17c85f577dfae26d809034389c1224d81089..29ec343872466e49a310ad740b67fe2f9a9603d0 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/tty_flip.h>
 
 /* UART specific GENI registers */
+#define SE_UART_LOOPBACK_CFG           0x22c
 #define SE_UART_TX_TRANS_CFG           0x25c
 #define SE_UART_TX_WORD_LEN            0x268
 #define SE_UART_TX_STOP_BIT_LEN                0x26c
@@ -26,6 +27,7 @@
 #define SE_UART_RX_STALE_CNT           0x294
 #define SE_UART_TX_PARITY_CFG          0x2a4
 #define SE_UART_RX_PARITY_CFG          0x2a8
+#define SE_UART_MANUAL_RFR             0x2ac
 
 /* SE_UART_TRANS_CFG */
 #define UART_TX_PAR_EN         BIT(0)
 #define PAR_SPACE              0x10
 #define PAR_MARK               0x11
 
+/* SE_UART_MANUAL_RFR register fields */
+#define UART_MANUAL_RFR_EN     BIT(31)
+#define UART_RFR_NOT_READY     BIT(1)
+#define UART_RFR_READY         BIT(0)
+
 /* UART M_CMD OP codes */
 #define UART_START_TX          0x1
 #define UART_START_BREAK       0x4
 #define STALE_TIMEOUT          16
 #define DEFAULT_BITS_PER_CHAR  10
 #define GENI_UART_CONS_PORTS   1
+#define GENI_UART_PORTS                3
 #define DEF_FIFO_DEPTH_WORDS   16
 #define DEF_TX_WM              2
 #define DEF_FIFO_WIDTH_BITS    32
 #define UART_CONSOLE_RX_WM     2
+#define MAX_LOOPBACK_CFG       3
 
 #ifdef CONFIG_CONSOLE_POLL
 #define RX_BYTES_PW 1
@@ -101,22 +110,81 @@ struct qcom_geni_serial_port {
        unsigned int baud;
        unsigned int tx_bytes_pw;
        unsigned int rx_bytes_pw;
+       u32 *rx_fifo;
+       u32 loopback;
        bool brk;
 };
 
 static const struct uart_ops qcom_geni_console_pops;
+static const struct uart_ops qcom_geni_uart_pops;
 static struct uart_driver qcom_geni_console_driver;
+static struct uart_driver qcom_geni_uart_driver;
 static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop);
+static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop);
 static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
 static void qcom_geni_serial_stop_rx(struct uart_port *uport);
 
 static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
                                        32000000, 48000000, 64000000, 80000000,
-                                       96000000, 100000000};
+                                       96000000, 100000000, 102400000,
+                                       112000000, 120000000, 128000000};
 
 #define to_dev_port(ptr, member) \
                container_of(ptr, struct qcom_geni_serial_port, member)
 
+static struct qcom_geni_serial_port qcom_geni_uart_ports[GENI_UART_PORTS] = {
+       [0] = {
+               .uport = {
+                               .iotype = UPIO_MEM,
+                               .ops = &qcom_geni_uart_pops,
+                               .flags = UPF_BOOT_AUTOCONF,
+                               .line = 0,
+               },
+       },
+       [1] = {
+               .uport = {
+                               .iotype = UPIO_MEM,
+                               .ops = &qcom_geni_uart_pops,
+                               .flags = UPF_BOOT_AUTOCONF,
+                               .line = 1,
+               },
+       },
+       [2] = {
+               .uport = {
+                               .iotype = UPIO_MEM,
+                               .ops = &qcom_geni_uart_pops,
+                               .flags = UPF_BOOT_AUTOCONF,
+                               .line = 2,
+               },
+       },
+};
+
+static ssize_t loopback_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+
+       return snprintf(buf, sizeof(u32), "%d\n", port->loopback);
+}
+
+static ssize_t loopback_store(struct device *dev,
+                               struct device_attribute *attr, const char *buf,
+                               size_t size)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+       u32 loopback;
+
+       if (kstrtoint(buf, 0, &loopback) || loopback > MAX_LOOPBACK_CFG) {
+               dev_err(dev, "Invalid input\n");
+               return -EINVAL;
+       }
+       port->loopback = loopback;
+       return size;
+}
+static DEVICE_ATTR_RW(loopback);
+
 static struct qcom_geni_serial_port qcom_geni_console_port = {
        .uport = {
                .iotype = UPIO_MEM,
@@ -148,14 +216,33 @@ static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
        }
 }
 
-static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport)
+static unsigned int qcom_geni_serial_get_mctrl(struct uart_port *uport)
 {
-       return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+       unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
+       u32 geni_ios;
+
+       if (uart_console(uport) || !uart_cts_enabled(uport)) {
+               mctrl |= TIOCM_CTS;
+       } else {
+               geni_ios = readl_relaxed(uport->membase + SE_GENI_IOS);
+               if (!(geni_ios & IO2_DATA_IN))
+                       mctrl |= TIOCM_CTS;
+       }
+
+       return mctrl;
 }
 
-static void qcom_geni_cons_set_mctrl(struct uart_port *uport,
+static void qcom_geni_serial_set_mctrl(struct uart_port *uport,
                                                        unsigned int mctrl)
 {
+       u32 uart_manual_rfr = 0;
+
+       if (uart_console(uport) || !uart_cts_enabled(uport))
+               return;
+
+       if (!(mctrl & TIOCM_RTS))
+               uart_manual_rfr = UART_MANUAL_RFR_EN | UART_RFR_NOT_READY;
+       writel_relaxed(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR);
 }
 
 static const char *qcom_geni_serial_get_type(struct uart_port *uport)
@@ -163,11 +250,16 @@ static const char *qcom_geni_serial_get_type(struct uart_port *uport)
        return "MSM";
 }
 
-static struct qcom_geni_serial_port *get_port_from_line(int line)
+static struct qcom_geni_serial_port *get_port_from_line(int line, bool console)
 {
-       if (line < 0 || line >= GENI_UART_CONS_PORTS)
+       struct qcom_geni_serial_port *port;
+       int nr_ports = console ? GENI_UART_CONS_PORTS : GENI_UART_PORTS;
+
+       if (line < 0 || line >= nr_ports)
                return ERR_PTR(-ENXIO);
-       return &qcom_geni_console_port;
+
+       port = console ? &qcom_geni_console_port : &qcom_geni_uart_ports[line];
+       return port;
 }
 
 static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
@@ -346,7 +438,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
 
        WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
 
-       port = get_port_from_line(co->index);
+       port = get_port_from_line(co->index, true);
        if (IS_ERR(port))
                return;
 
@@ -420,6 +512,32 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
 
 #endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
 
+static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
+{
+       unsigned char *buf;
+       struct tty_port *tport;
+       struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+       u32 num_bytes_pw = port->tx_fifo_width / BITS_PER_BYTE;
+       u32 words = ALIGN(bytes, num_bytes_pw) / num_bytes_pw;
+       int ret;
+
+       tport = &uport->state->port;
+       ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, port->rx_fifo, words);
+       if (drop)
+               return 0;
+
+       buf = (unsigned char *)port->rx_fifo;
+       ret = tty_insert_flip_string(tport, buf, bytes);
+       if (ret != bytes) {
+               dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n",
+                               __func__, ret, bytes);
+               WARN_ON_ONCE(1);
+       }
+       uport->icount.rx += ret;
+       tty_flip_buffer_push(tport);
+       return ret;
+}
+
 static void qcom_geni_serial_start_tx(struct uart_port *uport)
 {
        u32 irq_en;
@@ -586,6 +704,7 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
        u32 status;
        unsigned int chunk;
        int tail;
+       u32 irq_en;
 
        chunk = uart_circ_chars_pending(xmit);
        status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
@@ -595,6 +714,13 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
                goto out_write_wakeup;
        }
 
+       if (!uart_console(uport)) {
+               irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
+               irq_en &= ~(M_TX_FIFO_WATERMARK_EN);
+               writel_relaxed(0, uport->membase + SE_GENI_TX_WATERMARK_REG);
+               writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+       }
+
        avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
        tail = xmit->tail;
        chunk = min3((size_t)chunk, (size_t)(UART_XMIT_SIZE - tail), avail);
@@ -623,7 +749,8 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
        }
 
        xmit->tail = tail & (UART_XMIT_SIZE - 1);
-       qcom_geni_serial_poll_tx_done(uport);
+       if (uart_console(uport))
+               qcom_geni_serial_poll_tx_done(uport);
 out_write_wakeup:
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(uport);
@@ -710,7 +837,8 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport)
        unsigned long flags;
 
        /* Stop the console before stopping the current tx */
-       console_stop(uport->cons);
+       if (uart_console(uport))
+               console_stop(uport->cons);
 
        free_irq(uport->irq, uport);
        spin_lock_irqsave(&uport->lock, flags);
@@ -731,13 +859,20 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
         * it else we could end up in data loss scenarios.
         */
        port->xfer_mode = GENI_SE_FIFO;
-       qcom_geni_serial_poll_tx_done(uport);
+       if (uart_console(uport))
+               qcom_geni_serial_poll_tx_done(uport);
        geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
                                                false, true, false);
        geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
                                                false, false, true);
        geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
        geni_se_select_mode(&port->se, port->xfer_mode);
+       if (!uart_console(uport)) {
+               port->rx_fifo = devm_kzalloc(uport->dev,
+                       port->rx_fifo_depth * sizeof(u32), GFP_KERNEL);
+               if (!port->rx_fifo)
+                       return -ENOMEM;
+       }
        port->setup = true;
        return 0;
 }
@@ -749,8 +884,13 @@ static int qcom_geni_serial_startup(struct uart_port *uport)
        struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
 
        scnprintf(port->name, sizeof(port->name),
-                 "qcom_serial_geni%d", uport->line);
+                 "qcom_serial_%s%d",
+               (uart_console(uport) ? "console" : "uart"), uport->line);
 
+       if (!uart_console(uport)) {
+               port->tx_bytes_pw = 4;
+               port->rx_bytes_pw = RX_BYTES_PW;
+       }
        proto = geni_se_read_proto(&port->se);
        if (proto != GENI_SE_UART) {
                dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto);
@@ -886,6 +1026,9 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
        if (baud)
                uart_update_timeout(uport, termios->c_cflag, baud);
 
+       if (!uart_console(uport))
+               writel_relaxed(port->loopback,
+                               uport->membase + SE_UART_LOOPBACK_CFG);
        writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
        writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
        writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
@@ -917,7 +1060,7 @@ static int __init qcom_geni_console_setup(struct console *co, char *options)
        if (co->index >= GENI_UART_CONS_PORTS  || co->index < 0)
                return -ENXIO;
 
-       port = get_port_from_line(co->index);
+       port = get_port_from_line(co->index, true);
        if (IS_ERR(port)) {
                pr_err("Invalid line %d\n", co->index);
                return PTR_ERR(port);
@@ -1048,16 +1191,23 @@ static void console_unregister(struct uart_driver *drv)
 }
 #endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
 
-static void qcom_geni_serial_cons_pm(struct uart_port *uport,
+static struct uart_driver qcom_geni_uart_driver = {
+       .owner = THIS_MODULE,
+       .driver_name = "qcom_geni_uart",
+       .dev_name = "ttyHS",
+       .nr =  GENI_UART_PORTS,
+};
+
+static void qcom_geni_serial_pm(struct uart_port *uport,
                unsigned int new_state, unsigned int old_state)
 {
        struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
 
-       if (unlikely(!uart_console(uport)))
-               return;
-
        if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
                geni_se_resources_on(&port->se);
+       else if (!uart_console(uport) && (new_state == UART_PM_STATE_ON &&
+                               old_state == UART_PM_STATE_UNDEFINED))
+               geni_se_resources_on(&port->se);
        else if (new_state == UART_PM_STATE_OFF &&
                        old_state == UART_PM_STATE_ON)
                geni_se_resources_off(&port->se);
@@ -1074,13 +1224,29 @@ static const struct uart_ops qcom_geni_console_pops = {
        .config_port = qcom_geni_serial_config_port,
        .shutdown = qcom_geni_serial_shutdown,
        .type = qcom_geni_serial_get_type,
-       .set_mctrl = qcom_geni_cons_set_mctrl,
-       .get_mctrl = qcom_geni_cons_get_mctrl,
+       .set_mctrl = qcom_geni_serial_set_mctrl,
+       .get_mctrl = qcom_geni_serial_get_mctrl,
 #ifdef CONFIG_CONSOLE_POLL
        .poll_get_char  = qcom_geni_serial_get_char,
        .poll_put_char  = qcom_geni_serial_poll_put_char,
 #endif
-       .pm = qcom_geni_serial_cons_pm,
+       .pm = qcom_geni_serial_pm,
+};
+
+static const struct uart_ops qcom_geni_uart_pops = {
+       .tx_empty = qcom_geni_serial_tx_empty,
+       .stop_tx = qcom_geni_serial_stop_tx,
+       .start_tx = qcom_geni_serial_start_tx,
+       .stop_rx = qcom_geni_serial_stop_rx,
+       .set_termios = qcom_geni_serial_set_termios,
+       .startup = qcom_geni_serial_startup,
+       .request_port = qcom_geni_serial_request_port,
+       .config_port = qcom_geni_serial_config_port,
+       .shutdown = qcom_geni_serial_shutdown,
+       .type = qcom_geni_serial_get_type,
+       .set_mctrl = qcom_geni_serial_set_mctrl,
+       .get_mctrl = qcom_geni_serial_get_mctrl,
+       .pm = qcom_geni_serial_pm,
 };
 
 static int qcom_geni_serial_probe(struct platform_device *pdev)
@@ -1091,13 +1257,23 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
        struct uart_port *uport;
        struct resource *res;
        int irq;
+       bool console = false;
+       struct uart_driver *drv;
 
-       if (pdev->dev.of_node)
-               line = of_alias_get_id(pdev->dev.of_node, "serial");
+       if (of_device_is_compatible(pdev->dev.of_node, "qcom,geni-debug-uart"))
+               console = true;
 
-       if (line < 0 || line >= GENI_UART_CONS_PORTS)
-               return -ENXIO;
-       port = get_port_from_line(line);
+       if (pdev->dev.of_node) {
+               if (console) {
+                       drv = &qcom_geni_console_driver;
+                       line = of_alias_get_id(pdev->dev.of_node, "serial");
+               } else {
+                       drv = &qcom_geni_uart_driver;
+                       line = of_alias_get_id(pdev->dev.of_node, "hsuart");
+               }
+       }
+
+       port = get_port_from_line(line, console);
        if (IS_ERR(port)) {
                dev_err(&pdev->dev, "Invalid line %d\n", line);
                return PTR_ERR(port);
@@ -1134,10 +1310,12 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
        }
        uport->irq = irq;
 
-       uport->private_data = &qcom_geni_console_driver;
+       uport->private_data = drv;
        platform_set_drvdata(pdev, port);
-       port->handle_rx = handle_rx_console;
-       return uart_add_one_port(&qcom_geni_console_driver, uport);
+       port->handle_rx = console ? handle_rx_console : handle_rx_uart;
+       if (!console)
+               device_create_file(uport->dev, &dev_attr_loopback);
+       return uart_add_one_port(drv, uport);
 }
 
 static int qcom_geni_serial_remove(struct platform_device *pdev)
@@ -1154,7 +1332,17 @@ static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev)
        struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
        struct uart_port *uport = &port->uport;
 
-       uart_suspend_port(uport->private_data, uport);
+       if (uart_console(uport)) {
+               uart_suspend_port(uport->private_data, uport);
+       } else {
+               struct uart_state *state = uport->state;
+               /*
+                * If the port is open, deny system suspend.
+                */
+               if (state->pm_state == UART_PM_STATE_ON)
+                       return -EBUSY;
+       }
+
        return 0;
 }
 
@@ -1163,7 +1351,8 @@ static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev)
        struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
        struct uart_port *uport = &port->uport;
 
-       if (console_suspend_enabled && uport->suspended) {
+       if (uart_console(uport) &&
+                       console_suspend_enabled && uport->suspended) {
                uart_resume_port(uport->private_data, uport);
                /*
                 * uart_suspend_port() invokes port shutdown which in turn
@@ -1185,6 +1374,7 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
 
 static const struct of_device_id qcom_geni_serial_match_table[] = {
        { .compatible = "qcom,geni-debug-uart", },
+       { .compatible = "qcom,geni-uart", },
        {}
 };
 MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
@@ -1207,9 +1397,17 @@ static int __init qcom_geni_serial_init(void)
        if (ret)
                return ret;
 
+       ret = uart_register_driver(&qcom_geni_uart_driver);
+       if (ret) {
+               console_unregister(&qcom_geni_console_driver);
+               return ret;
+       }
+
        ret = platform_driver_register(&qcom_geni_serial_platform_driver);
-       if (ret)
+       if (ret) {
                console_unregister(&qcom_geni_console_driver);
+               uart_unregister_driver(&qcom_geni_uart_driver);
+       }
        return ret;
 }
 module_init(qcom_geni_serial_init);
@@ -1218,6 +1416,7 @@ static void __exit qcom_geni_serial_exit(void)
 {
        platform_driver_unregister(&qcom_geni_serial_platform_driver);
        console_unregister(&qcom_geni_console_driver);
+       uart_unregister_driver(&qcom_geni_uart_driver);
 }
 module_exit(qcom_geni_serial_exit);
 
index 9c14a453f73c0e8365610809ba742cdd3085311b..80bb56facfb684423e415e702e334fb97be152b1 100644 (file)
@@ -182,6 +182,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
 {
        struct uart_port *uport = uart_port_check(state);
        unsigned long page;
+       unsigned long flags = 0;
        int retval = 0;
 
        if (uport->type == PORT_UNKNOWN)
@@ -196,15 +197,18 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
         * Initialise and allocate the transmit and temporary
         * buffer.
         */
-       if (!state->xmit.buf) {
-               /* This is protected by the per port mutex */
-               page = get_zeroed_page(GFP_KERNEL);
-               if (!page)
-                       return -ENOMEM;
+       page = get_zeroed_page(GFP_KERNEL);
+       if (!page)
+               return -ENOMEM;
 
+       uart_port_lock(state, flags);
+       if (!state->xmit.buf) {
                state->xmit.buf = (unsigned char *) page;
                uart_circ_clear(&state->xmit);
+       } else {
+               free_page(page);
        }
+       uart_port_unlock(uport, flags);
 
        retval = uport->ops->startup(uport);
        if (retval == 0) {
@@ -263,6 +267,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
 {
        struct uart_port *uport = uart_port_check(state);
        struct tty_port *port = &state->port;
+       unsigned long flags = 0;
 
        /*
         * Set the TTY IO error marker
@@ -295,10 +300,12 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
        /*
         * Free the transmit buffer page.
         */
+       uart_port_lock(state, flags);
        if (state->xmit.buf) {
                free_page((unsigned long)state->xmit.buf);
                state->xmit.buf = NULL;
        }
+       uart_port_unlock(uport, flags);
 }
 
 /**
index c181eb37f98509e6e2a34a7df62be2e5383d79e3..ac4424bf6b136cc43093dec459128637f7da5947 100644 (file)
@@ -65,6 +65,8 @@ enum {
        SCIx_RXI_IRQ,
        SCIx_TXI_IRQ,
        SCIx_BRI_IRQ,
+       SCIx_DRI_IRQ,
+       SCIx_TEI_IRQ,
        SCIx_NR_IRQS,
 
        SCIx_MUX_IRQ = SCIx_NR_IRQS,    /* special case */
@@ -135,6 +137,8 @@ struct sci_port {
        struct dma_chan                 *chan_rx;
 
 #ifdef CONFIG_SERIAL_SH_SCI_DMA
+       struct dma_chan                 *chan_tx_saved;
+       struct dma_chan                 *chan_rx_saved;
        dma_cookie_t                    cookie_tx;
        dma_cookie_t                    cookie_rx[2];
        dma_cookie_t                    active_rx;
@@ -315,15 +319,15 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = {
        [SCIx_SH4_SCIF_REGTYPE] = {
                .regs = {
                        [SCSMR]         = { 0x00, 16 },
-                       [SCBRR]         = { 0x04,  8 },
-                       [SCSCR]         = { 0x08, 16 },
-                       [SCxTDR]        = { 0x0c,  8 },
-                       [SCxSR]         = { 0x10, 16 },
-                       [SCxRDR]        = { 0x14,  8 },
-                       [SCFCR]         = { 0x18, 16 },
-                       [SCFDR]         = { 0x1c, 16 },
-                       [SCSPTR]        = { 0x20, 16 },
-                       [SCLSR]         = { 0x24, 16 },
+                       [SCBRR]         = { 0x02,  8 },
+                       [SCSCR]         = { 0x04, 16 },
+                       [SCxTDR]        = { 0x06,  8 },
+                       [SCxSR]         = { 0x08, 16 },
+                       [SCxRDR]        = { 0x0a,  8 },
+                       [SCFCR]         = { 0x0c, 16 },
+                       [SCFDR]         = { 0x0e, 16 },
+                       [SCSPTR]        = { 0x10, 16 },
+                       [SCLSR]         = { 0x12, 16 },
                },
                .fifosize = 16,
                .overrun_reg = SCLSR,
@@ -1212,25 +1216,16 @@ static int sci_dma_rx_find_active(struct sci_port *s)
        return -1;
 }
 
-static void sci_rx_dma_release(struct sci_port *s, bool enable_pio)
+static void sci_rx_dma_release(struct sci_port *s)
 {
-       struct dma_chan *chan = s->chan_rx;
-       struct uart_port *port = &s->port;
-       unsigned long flags;
+       struct dma_chan *chan = s->chan_rx_saved;
 
-       spin_lock_irqsave(&port->lock, flags);
-       s->chan_rx = NULL;
+       s->chan_rx_saved = s->chan_rx = NULL;
        s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL;
-       spin_unlock_irqrestore(&port->lock, flags);
-       dmaengine_terminate_all(chan);
+       dmaengine_terminate_sync(chan);
        dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0],
                          sg_dma_address(&s->sg_rx[0]));
        dma_release_channel(chan);
-       if (enable_pio) {
-               spin_lock_irqsave(&port->lock, flags);
-               sci_start_rx(port);
-               spin_unlock_irqrestore(&port->lock, flags);
-       }
 }
 
 static void start_hrtimer_us(struct hrtimer *hrt, unsigned long usec)
@@ -1289,33 +1284,31 @@ static void sci_dma_rx_complete(void *arg)
 fail:
        spin_unlock_irqrestore(&port->lock, flags);
        dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n");
-       sci_rx_dma_release(s, true);
+       /* Switch to PIO */
+       spin_lock_irqsave(&port->lock, flags);
+       s->chan_rx = NULL;
+       sci_start_rx(port);
+       spin_unlock_irqrestore(&port->lock, flags);
 }
 
-static void sci_tx_dma_release(struct sci_port *s, bool enable_pio)
+static void sci_tx_dma_release(struct sci_port *s)
 {
-       struct dma_chan *chan = s->chan_tx;
-       struct uart_port *port = &s->port;
-       unsigned long flags;
+       struct dma_chan *chan = s->chan_tx_saved;
 
-       spin_lock_irqsave(&port->lock, flags);
-       s->chan_tx = NULL;
+       cancel_work_sync(&s->work_tx);
+       s->chan_tx_saved = s->chan_tx = NULL;
        s->cookie_tx = -EINVAL;
-       spin_unlock_irqrestore(&port->lock, flags);
-       dmaengine_terminate_all(chan);
+       dmaengine_terminate_sync(chan);
        dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE,
                         DMA_TO_DEVICE);
        dma_release_channel(chan);
-       if (enable_pio) {
-               spin_lock_irqsave(&port->lock, flags);
-               sci_start_tx(port);
-               spin_unlock_irqrestore(&port->lock, flags);
-       }
 }
 
 static void sci_submit_rx(struct sci_port *s)
 {
        struct dma_chan *chan = s->chan_rx;
+       struct uart_port *port = &s->port;
+       unsigned long flags;
        int i;
 
        for (i = 0; i < 2; i++) {
@@ -1343,11 +1336,15 @@ static void sci_submit_rx(struct sci_port *s)
 
 fail:
        if (i)
-               dmaengine_terminate_all(chan);
+               dmaengine_terminate_async(chan);
        for (i = 0; i < 2; i++)
                s->cookie_rx[i] = -EINVAL;
        s->active_rx = -EINVAL;
-       sci_rx_dma_release(s, true);
+       /* Switch to PIO */
+       spin_lock_irqsave(&port->lock, flags);
+       s->chan_rx = NULL;
+       sci_start_rx(port);
+       spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void work_fn_tx(struct work_struct *work)
@@ -1357,6 +1354,7 @@ static void work_fn_tx(struct work_struct *work)
        struct dma_chan *chan = s->chan_tx;
        struct uart_port *port = &s->port;
        struct circ_buf *xmit = &port->state->xmit;
+       unsigned long flags;
        dma_addr_t buf;
 
        /*
@@ -1378,9 +1376,7 @@ static void work_fn_tx(struct work_struct *work)
                                           DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
        if (!desc) {
                dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n");
-               /* switch to PIO */
-               sci_tx_dma_release(s, true);
-               return;
+               goto switch_to_pio;
        }
 
        dma_sync_single_for_device(chan->device->dev, buf, s->tx_dma_len,
@@ -1393,15 +1389,21 @@ static void work_fn_tx(struct work_struct *work)
        s->cookie_tx = dmaengine_submit(desc);
        if (dma_submit_error(s->cookie_tx)) {
                dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n");
-               /* switch to PIO */
-               sci_tx_dma_release(s, true);
-               return;
+               goto switch_to_pio;
        }
 
        dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n",
                __func__, xmit->buf, xmit->tail, xmit->head, s->cookie_tx);
 
        dma_async_issue_pending(chan);
+       return;
+
+switch_to_pio:
+       spin_lock_irqsave(&port->lock, flags);
+       s->chan_tx = NULL;
+       sci_start_tx(port);
+       spin_unlock_irqrestore(&port->lock, flags);
+       return;
 }
 
 static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
@@ -1452,7 +1454,7 @@ static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
        }
 
        /* Handle incomplete DMA receive */
-       dmaengine_terminate_all(s->chan_rx);
+       dmaengine_terminate_async(s->chan_rx);
        read = sg_dma_len(&s->sg_rx[active]) - state.residue;
 
        if (read) {
@@ -1535,7 +1537,6 @@ static void sci_request_dma(struct uart_port *port)
        chan = sci_request_dma_chan(port, DMA_MEM_TO_DEV);
        dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan);
        if (chan) {
-               s->chan_tx = chan;
                /* UART circular tx buffer is an aligned page. */
                s->tx_dma_addr = dma_map_single(chan->device->dev,
                                                port->state->xmit.buf,
@@ -1544,14 +1545,14 @@ static void sci_request_dma(struct uart_port *port)
                if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) {
                        dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n");
                        dma_release_channel(chan);
-                       s->chan_tx = NULL;
                } else {
                        dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n",
                                __func__, UART_XMIT_SIZE,
                                port->state->xmit.buf, &s->tx_dma_addr);
-               }
 
-               INIT_WORK(&s->work_tx, work_fn_tx);
+                       INIT_WORK(&s->work_tx, work_fn_tx);
+                       s->chan_tx_saved = s->chan_tx = chan;
+               }
        }
 
        chan = sci_request_dma_chan(port, DMA_DEV_TO_MEM);
@@ -1561,8 +1562,6 @@ static void sci_request_dma(struct uart_port *port)
                dma_addr_t dma;
                void *buf;
 
-               s->chan_rx = chan;
-
                s->buf_len_rx = 2 * max_t(size_t, 16, port->fifosize);
                buf = dma_alloc_coherent(chan->device->dev, s->buf_len_rx * 2,
                                         &dma, GFP_KERNEL);
@@ -1570,7 +1569,6 @@ static void sci_request_dma(struct uart_port *port)
                        dev_warn(port->dev,
                                 "Failed to allocate Rx dma buffer, using PIO\n");
                        dma_release_channel(chan);
-                       s->chan_rx = NULL;
                        return;
                }
 
@@ -1591,6 +1589,8 @@ static void sci_request_dma(struct uart_port *port)
 
                if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
                        sci_submit_rx(s);
+
+               s->chan_rx_saved = s->chan_rx = chan;
        }
 }
 
@@ -1598,10 +1598,10 @@ static void sci_free_dma(struct uart_port *port)
 {
        struct sci_port *s = to_sci_port(port);
 
-       if (s->chan_tx)
-               sci_tx_dma_release(s, false);
-       if (s->chan_rx)
-               sci_rx_dma_release(s, false);
+       if (s->chan_tx_saved)
+               sci_tx_dma_release(s);
+       if (s->chan_rx_saved)
+               sci_rx_dma_release(s);
 }
 
 static void sci_flush_buffer(struct uart_port *port)
@@ -1683,11 +1683,35 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t sci_br_interrupt(int irq, void *ptr)
+{
+       struct uart_port *port = ptr;
+
+       /* Handle BREAKs */
+       sci_handle_breaks(port);
+       sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port));
+
+       return IRQ_HANDLED;
+}
+
 static irqreturn_t sci_er_interrupt(int irq, void *ptr)
 {
        struct uart_port *port = ptr;
        struct sci_port *s = to_sci_port(port);
 
+       if (s->irqs[SCIx_ERI_IRQ] == s->irqs[SCIx_BRI_IRQ]) {
+               /* Break and Error interrupts are muxed */
+               unsigned short ssr_status = serial_port_in(port, SCxSR);
+
+               /* Break Interrupt */
+               if (ssr_status & SCxSR_BRK(port))
+                       sci_br_interrupt(irq, ptr);
+
+               /* Break only? */
+               if (!(ssr_status & SCxSR_ERRORS(port)))
+                       return IRQ_HANDLED;
+       }
+
        /* Handle errors */
        if (port->type == PORT_SCI) {
                if (sci_handle_errors(port)) {
@@ -1710,17 +1734,6 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr)
        return IRQ_HANDLED;
 }
 
-static irqreturn_t sci_br_interrupt(int irq, void *ptr)
-{
-       struct uart_port *port = ptr;
-
-       /* Handle BREAKs */
-       sci_handle_breaks(port);
-       sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port));
-
-       return IRQ_HANDLED;
-}
-
 static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
 {
        unsigned short ssr_status, scr_status, err_enabled, orer_status = 0;
@@ -1794,6 +1807,16 @@ static const struct sci_irq_desc {
                .handler = sci_br_interrupt,
        },
 
+       [SCIx_DRI_IRQ] = {
+               .desc = "rx ready",
+               .handler = sci_rx_interrupt,
+       },
+
+       [SCIx_TEI_IRQ] = {
+               .desc = "tx end",
+               .handler = sci_tx_interrupt,
+       },
+
        /*
         * Special muxed handler.
         */
@@ -1806,12 +1829,19 @@ static const struct sci_irq_desc {
 static int sci_request_irq(struct sci_port *port)
 {
        struct uart_port *up = &port->port;
-       int i, j, ret = 0;
+       int i, j, w, ret = 0;
 
        for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) {
                const struct sci_irq_desc *desc;
                int irq;
 
+               /* Check if already registered (muxed) */
+               for (w = 0; w < i; w++)
+                       if (port->irqs[w] == port->irqs[i])
+                               w = i + 1;
+               if (w > i)
+                       continue;
+
                if (SCIx_IRQ_IS_MUXED(port)) {
                        i = SCIx_MUX_IRQ;
                        irq = up->irq;
@@ -2092,13 +2122,15 @@ static void sci_shutdown(struct uart_port *port)
        spin_unlock_irqrestore(&port->lock, flags);
 
 #ifdef CONFIG_SERIAL_SH_SCI_DMA
-       if (s->chan_rx) {
+       if (s->chan_rx_saved) {
                dev_dbg(port->dev, "%s(%d) deleting rx_timer\n", __func__,
                        port->line);
                hrtimer_cancel(&s->rx_timer);
        }
 #endif
 
+       if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0)
+               del_timer_sync(&s->rx_fifo_timer);
        sci_free_irq(s);
        sci_free_dma(port);
 }
@@ -2778,7 +2810,7 @@ static int sci_init_single(struct platform_device *dev,
 {
        struct uart_port *port = &sci_port->port;
        const struct resource *res;
-       unsigned int i;
+       unsigned int i, regtype;
        int ret;
 
        sci_port->cfg   = p;
@@ -2799,22 +2831,23 @@ static int sci_init_single(struct platform_device *dev,
 
        /* The SCI generates several interrupts. They can be muxed together or
         * connected to different interrupt lines. In the muxed case only one
-        * interrupt resource is specified. In the non-muxed case three or four
-        * interrupt resources are specified, as the BRI interrupt is optional.
+        * interrupt resource is specified as there is only one interrupt ID.
+        * In the non-muxed case, up to 6 interrupt signals might be generated
+        * from the SCI, however those signals might have their own individual
+        * interrupt ID numbers, or muxed together with another interrupt.
         */
        if (sci_port->irqs[0] < 0)
                return -ENXIO;
 
-       if (sci_port->irqs[1] < 0) {
-               sci_port->irqs[1] = sci_port->irqs[0];
-               sci_port->irqs[2] = sci_port->irqs[0];
-               sci_port->irqs[3] = sci_port->irqs[0];
-       }
+       if (sci_port->irqs[1] < 0)
+               for (i = 1; i < ARRAY_SIZE(sci_port->irqs); i++)
+                       sci_port->irqs[i] = sci_port->irqs[0];
 
        sci_port->params = sci_probe_regmap(p);
        if (unlikely(sci_port->params == NULL))
                return -EINVAL;
 
+       regtype = sci_port->params - sci_port_params;
        switch (p->type) {
        case PORT_SCIFB:
                sci_port->rx_trigger = 48;
@@ -2869,6 +2902,10 @@ static int sci_init_single(struct platform_device *dev,
                        port->regshift = 1;
        }
 
+       if (regtype == SCIx_SH4_SCIF_REGTYPE)
+               if (sci_port->reg_size >= 0x20)
+                       port->regshift = 1;
+
        /*
         * The UART port needs an IRQ value, so we peg this to the RX IRQ
         * for the multi-IRQ ports, which is where we are primarily
index c47db78261891df42cc711396a6f15221673cb4a..98d3eadd2fd0380e467b90f3340020ca1f3ccdd6 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_platform.h>
+#include <linux/clk.h>
 
 #define ULITE_NAME             "ttyUL"
 #define ULITE_MAJOR            204
 #define ULITE_CONTROL_RST_RX   0x02
 #define ULITE_CONTROL_IE       0x10
 
+struct uartlite_data {
+       const struct uartlite_reg_ops *reg_ops;
+       struct clk *clk;
+};
+
 struct uartlite_reg_ops {
        u32 (*in)(void __iomem *addr);
        void (*out)(u32 val, void __iomem *addr);
@@ -91,16 +97,16 @@ static const struct uartlite_reg_ops uartlite_le = {
 
 static inline u32 uart_in32(u32 offset, struct uart_port *port)
 {
-       const struct uartlite_reg_ops *reg_ops = port->private_data;
+       struct uartlite_data *pdata = port->private_data;
 
-       return reg_ops->in(port->membase + offset);
+       return pdata->reg_ops->in(port->membase + offset);
 }
 
 static inline void uart_out32(u32 val, u32 offset, struct uart_port *port)
 {
-       const struct uartlite_reg_ops *reg_ops = port->private_data;
+       struct uartlite_data *pdata = port->private_data;
 
-       reg_ops->out(val, port->membase + offset);
+       pdata->reg_ops->out(val, port->membase + offset);
 }
 
 static struct uart_port ulite_ports[ULITE_NR_UARTS];
@@ -257,8 +263,15 @@ static void ulite_break_ctl(struct uart_port *port, int ctl)
 
 static int ulite_startup(struct uart_port *port)
 {
+       struct uartlite_data *pdata = port->private_data;
        int ret;
 
+       ret = clk_enable(pdata->clk);
+       if (ret) {
+               dev_err(port->dev, "Failed to enable clock\n");
+               return ret;
+       }
+
        ret = request_irq(port->irq, ulite_isr, IRQF_SHARED | IRQF_TRIGGER_RISING,
                          "uartlite", port);
        if (ret)
@@ -273,9 +286,12 @@ static int ulite_startup(struct uart_port *port)
 
 static void ulite_shutdown(struct uart_port *port)
 {
+       struct uartlite_data *pdata = port->private_data;
+
        uart_out32(0, ULITE_CONTROL, port);
        uart_in32(ULITE_CONTROL, port); /* dummy */
        free_irq(port->irq, port);
+       clk_disable(pdata->clk);
 }
 
 static void ulite_set_termios(struct uart_port *port, struct ktermios *termios,
@@ -325,6 +341,7 @@ static void ulite_release_port(struct uart_port *port)
 
 static int ulite_request_port(struct uart_port *port)
 {
+       struct uartlite_data *pdata = port->private_data;
        int ret;
 
        pr_debug("ulite console: port=%p; port->mapbase=%llx\n",
@@ -342,13 +359,13 @@ static int ulite_request_port(struct uart_port *port)
                return -EBUSY;
        }
 
-       port->private_data = (void *)&uartlite_be;
+       pdata->reg_ops = &uartlite_be;
        ret = uart_in32(ULITE_CONTROL, port);
        uart_out32(ULITE_CONTROL_RST_TX, ULITE_CONTROL, port);
        ret = uart_in32(ULITE_STATUS, port);
        /* Endianess detection */
        if ((ret & ULITE_STATUS_TXEMPTY) != ULITE_STATUS_TXEMPTY)
-               port->private_data = (void *)&uartlite_le;
+               pdata->reg_ops = &uartlite_le;
 
        return 0;
 }
@@ -365,6 +382,17 @@ static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser)
        return -EINVAL;
 }
 
+static void ulite_pm(struct uart_port *port, unsigned int state,
+                    unsigned int oldstate)
+{
+       struct uartlite_data *pdata = port->private_data;
+
+       if (!state)
+               clk_enable(pdata->clk);
+       else
+               clk_disable(pdata->clk);
+}
+
 #ifdef CONFIG_CONSOLE_POLL
 static int ulite_get_poll_char(struct uart_port *port)
 {
@@ -400,6 +428,7 @@ static const struct uart_ops ulite_ops = {
        .request_port   = ulite_request_port,
        .config_port    = ulite_config_port,
        .verify_port    = ulite_verify_port,
+       .pm             = ulite_pm,
 #ifdef CONFIG_CONSOLE_POLL
        .poll_get_char  = ulite_get_poll_char,
        .poll_put_char  = ulite_put_poll_char,
@@ -585,10 +614,12 @@ static struct uart_driver ulite_uart_driver = {
  * @id: requested id number.  Pass -1 for automatic port assignment
  * @base: base address of uartlite registers
  * @irq: irq number for uartlite
+ * @pdata: private data for uartlite
  *
  * Returns: 0 on success, <0 otherwise
  */
-static int ulite_assign(struct device *dev, int id, u32 base, int irq)
+static int ulite_assign(struct device *dev, int id, u32 base, int irq,
+                       struct uartlite_data *pdata)
 {
        struct uart_port *port;
        int rc;
@@ -625,6 +656,7 @@ static int ulite_assign(struct device *dev, int id, u32 base, int irq)
        port->dev = dev;
        port->type = PORT_UNKNOWN;
        port->line = id;
+       port->private_data = pdata;
 
        dev_set_drvdata(dev, port);
 
@@ -658,10 +690,44 @@ static int ulite_release(struct device *dev)
        return rc;
 }
 
+/**
+ * ulite_suspend - Stop the device.
+ *
+ * @dev: handle to the device structure.
+ * Return: 0 always.
+ */
+static int __maybe_unused ulite_suspend(struct device *dev)
+{
+       struct uart_port *port = dev_get_drvdata(dev);
+
+       if (port)
+               uart_suspend_port(&ulite_uart_driver, port);
+
+       return 0;
+}
+
+/**
+ * ulite_resume - Resume the device.
+ *
+ * @dev: handle to the device structure.
+ * Return: 0 on success, errno otherwise.
+ */
+static int __maybe_unused ulite_resume(struct device *dev)
+{
+       struct uart_port *port = dev_get_drvdata(dev);
+
+       if (port)
+               uart_resume_port(&ulite_uart_driver, port);
+
+       return 0;
+}
+
 /* ---------------------------------------------------------------------
  * Platform bus binding
  */
 
+static SIMPLE_DEV_PM_OPS(ulite_pm_ops, ulite_suspend, ulite_resume);
+
 #if defined(CONFIG_OF)
 /* Match table for of_platform binding */
 static const struct of_device_id ulite_of_match[] = {
@@ -675,7 +741,8 @@ MODULE_DEVICE_TABLE(of, ulite_of_match);
 static int ulite_probe(struct platform_device *pdev)
 {
        struct resource *res;
-       int irq;
+       struct uartlite_data *pdata;
+       int irq, ret;
        int id = pdev->id;
 #ifdef CONFIG_OF
        const __be32 *prop;
@@ -684,6 +751,10 @@ static int ulite_probe(struct platform_device *pdev)
        if (prop)
                id = be32_to_cpup(prop);
 #endif
+       pdata = devm_kzalloc(&pdev->dev, sizeof(struct uartlite_data),
+                            GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res)
@@ -693,11 +764,33 @@ static int ulite_probe(struct platform_device *pdev)
        if (irq <= 0)
                return -ENXIO;
 
-       return ulite_assign(&pdev->dev, id, res->start, irq);
+       pdata->clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
+       if (IS_ERR(pdata->clk)) {
+               if (PTR_ERR(pdata->clk) != -ENOENT)
+                       return PTR_ERR(pdata->clk);
+
+               /*
+                * Clock framework support is optional, continue on
+                * anyways if we don't find a matching clock.
+                */
+               pdata->clk = NULL;
+       }
+
+       ret = clk_prepare(pdata->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to prepare clock\n");
+               return ret;
+       }
+
+       return ulite_assign(&pdev->dev, id, res->start, irq, pdata);
 }
 
 static int ulite_remove(struct platform_device *pdev)
 {
+       struct uart_port *port = dev_get_drvdata(&pdev->dev);
+       struct uartlite_data *pdata = port->private_data;
+
+       clk_disable_unprepare(pdata->clk);
        return ulite_release(&pdev->dev);
 }
 
@@ -710,6 +803,7 @@ static struct platform_driver ulite_platform_driver = {
        .driver = {
                .name  = "uartlite",
                .of_match_table = of_match_ptr(ulite_of_match),
+               .pm = &ulite_pm_ops,
        },
 };
 
index 8a3e34234e981c24a79e4de1902f1e188b7a19f7..a48f19b1b88f1d48c5c6ae43389eb1e0ef79b9b7 100644 (file)
@@ -167,6 +167,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
 #define CDNS_UART_SR_TXEMPTY   0x00000008 /* TX FIFO empty */
 #define CDNS_UART_SR_TXFULL    0x00000010 /* TX FIFO full */
 #define CDNS_UART_SR_RXTRIG    0x00000001 /* Rx Trigger */
+#define CDNS_UART_SR_TACTIVE   0x00000800 /* TX state machine active */
 
 /* baud dividers min/max values */
 #define CDNS_UART_BDIV_MIN     4
@@ -829,7 +830,7 @@ static int cdns_uart_startup(struct uart_port *port)
         * the receiver.
         */
        status = readl(port->membase + CDNS_UART_CR);
-       status &= CDNS_UART_CR_RX_DIS;
+       status &= ~CDNS_UART_CR_RX_DIS;
        status |= CDNS_UART_CR_RX_EN;
        writel(status, port->membase + CDNS_UART_CR);
 
@@ -1098,16 +1099,6 @@ static const struct uart_ops cdns_uart_ops = {
 };
 
 #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
-/**
- * cdns_uart_console_wait_tx - Wait for the TX to be full
- * @port: Handle to the uart port structure
- */
-static void cdns_uart_console_wait_tx(struct uart_port *port)
-{
-       while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY))
-               barrier();
-}
-
 /**
  * cdns_uart_console_putchar - write the character to the FIFO buffer
  * @port: Handle to the uart port structure
@@ -1115,7 +1106,8 @@ static void cdns_uart_console_wait_tx(struct uart_port *port)
  */
 static void cdns_uart_console_putchar(struct uart_port *port, int ch)
 {
-       cdns_uart_console_wait_tx(port);
+       while (readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)
+               cpu_relax();
        writel(ch, port->membase + CDNS_UART_FIFO);
 }
 
@@ -1206,9 +1198,10 @@ static void cdns_uart_console_write(struct console *co, const char *s,
        writel(ctrl, port->membase + CDNS_UART_CR);
 
        uart_console_write(port, s, count, cdns_uart_console_putchar);
-       cdns_uart_console_wait_tx(port);
-
-       writel(ctrl, port->membase + CDNS_UART_CR);
+       while ((readl(port->membase + CDNS_UART_SR) &
+                       (CDNS_UART_SR_TXEMPTY | CDNS_UART_SR_TACTIVE)) !=
+                       CDNS_UART_SR_TXEMPTY)
+               cpu_relax();
 
        /* restore interrupt state */
        writel(imr, port->membase + CDNS_UART_IER);
index 6ff8cdfc9d2a7a4b29bf2e5d9a279629db757bfa..7576ceace57151a21007847f14dcf091005f09bb 100644 (file)
@@ -100,11 +100,11 @@ speed_t tty_termios_input_baud_rate(struct ktermios *termios)
 
        if (cbaud == B0)
                return tty_termios_baud_rate(termios);
-
+#ifdef BOTHER
        /* Magic token for arbitrary speed via c_ispeed*/
        if (cbaud == BOTHER)
                return termios->c_ispeed;
-
+#endif
        if (cbaud & CBAUDEX) {
                cbaud &= ~CBAUDEX;
 
@@ -114,9 +114,9 @@ speed_t tty_termios_input_baud_rate(struct ktermios *termios)
                        cbaud += 15;
        }
        return baud_table[cbaud];
-#else
+#else  /* IBSHIFT */
        return tty_termios_baud_rate(termios);
-#endif
+#endif /* IBSHIFT */
 }
 EXPORT_SYMBOL(tty_termios_input_baud_rate);
 
@@ -156,19 +156,27 @@ void tty_termios_encode_baud_rate(struct ktermios *termios,
        termios->c_ispeed = ibaud;
        termios->c_ospeed = obaud;
 
+#ifdef IBSHIFT
+       if ((termios->c_cflag >> IBSHIFT) & CBAUD)
+               ibinput = 1;    /* An input speed was specified */
+#endif
 #ifdef BOTHER
        /* If the user asked for a precise weird speed give a precise weird
           answer. If they asked for a Bfoo speed they may have problems
           digesting non-exact replies so fuzz a bit */
 
-       if ((termios->c_cflag & CBAUD) == BOTHER)
+       if ((termios->c_cflag & CBAUD) == BOTHER) {
                oclose = 0;
+               if (!ibinput)
+                       iclose = 0;
+       }
        if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER)
                iclose = 0;
-       if ((termios->c_cflag >> IBSHIFT) & CBAUD)
-               ibinput = 1;    /* An input speed was specified */
 #endif
        termios->c_cflag &= ~CBAUD;
+#ifdef IBSHIFT
+       termios->c_cflag &= ~(CBAUD << IBSHIFT);
+#endif
 
        /*
         *      Our goal is to find a close match to the standard baud rate
index aba59521ad488973d0bee75f88a9d2a0f4f76e00..11c2df904ac93acf78067a7b205daaf6b5c50210 100644 (file)
@@ -814,9 +814,9 @@ void start_tty(struct tty_struct *tty)
 }
 EXPORT_SYMBOL(start_tty);
 
-static void tty_update_time(struct timespec *time)
+static void tty_update_time(struct timespec64 *time)
 {
-       unsigned long sec = get_seconds();
+       time64_t sec = ktime_get_real_seconds();
 
        /*
         * We only care if the two values differ in anything other than the
@@ -867,13 +867,8 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
                i = -EIO;
        tty_ldisc_deref(ld);
 
-       if (i > 0) {
-               struct timespec ts;
-
-               ts = timespec64_to_timespec(inode->i_atime);
-               tty_update_time(&ts);
-               inode->i_atime = timespec_to_timespec64(ts);
-       }
+       if (i > 0)
+               tty_update_time(&inode->i_atime);
 
        return i;
 }
@@ -974,11 +969,7 @@ static inline ssize_t do_tty_write(
                cond_resched();
        }
        if (written) {
-               struct timespec ts;
-
-               ts = timespec64_to_timespec(file_inode(file)->i_mtime);
-               tty_update_time(&ts);
-               file_inode(file)->i_mtime = timespec_to_timespec64(ts);
+               tty_update_time(&file_inode(file)->i_mtime);
                ret = written;
        }
 out:
index 37a91b3df98090794d7599661fe77150cadae71d..0c98d88f795a7bf290f4b472a60024e151feccfd 100644 (file)
@@ -74,28 +74,6 @@ struct ldsem_waiter {
        struct task_struct *task;
 };
 
-static inline long ldsem_atomic_update(long delta, struct ld_semaphore *sem)
-{
-       return atomic_long_add_return(delta, (atomic_long_t *)&sem->count);
-}
-
-/*
- * ldsem_cmpxchg() updates @*old with the last-known sem->count value.
- * Returns 1 if count was successfully changed; @*old will have @new value.
- * Returns 0 if count was not changed; @*old will have most recent sem->count
- */
-static inline int ldsem_cmpxchg(long *old, long new, struct ld_semaphore *sem)
-{
-       long tmp = atomic_long_cmpxchg(&sem->count, *old, new);
-       if (tmp == *old) {
-               *old = new;
-               return 1;
-       } else {
-               *old = tmp;
-               return 0;
-       }
-}
-
 /*
  * Initialize an ldsem:
  */
@@ -109,7 +87,7 @@ void __init_ldsem(struct ld_semaphore *sem, const char *name,
        debug_check_no_locks_freed((void *)sem, sizeof(*sem));
        lockdep_init_map(&sem->dep_map, name, key, 0);
 #endif
-       sem->count = LDSEM_UNLOCKED;
+       atomic_long_set(&sem->count, LDSEM_UNLOCKED);
        sem->wait_readers = 0;
        raw_spin_lock_init(&sem->wait_lock);
        INIT_LIST_HEAD(&sem->read_wait);
@@ -122,16 +100,17 @@ static void __ldsem_wake_readers(struct ld_semaphore *sem)
        struct task_struct *tsk;
        long adjust, count;
 
-       /* Try to grant read locks to all readers on the read wait list.
+       /*
+        * Try to grant read locks to all readers on the read wait list.
         * Note the 'active part' of the count is incremented by
         * the number of readers before waking any processes up.
         */
        adjust = sem->wait_readers * (LDSEM_ACTIVE_BIAS - LDSEM_WAIT_BIAS);
-       count = ldsem_atomic_update(adjust, sem);
+       count = atomic_long_add_return(adjust, &sem->count);
        do {
                if (count > 0)
                        break;
-               if (ldsem_cmpxchg(&count, count - adjust, sem))
+               if (atomic_long_try_cmpxchg(&sem->count, &count, count - adjust))
                        return;
        } while (1);
 
@@ -148,14 +127,15 @@ static void __ldsem_wake_readers(struct ld_semaphore *sem)
 
 static inline int writer_trylock(struct ld_semaphore *sem)
 {
-       /* only wake this writer if the active part of the count can be
+       /*
+        * Only wake this writer if the active part of the count can be
         * transitioned from 0 -> 1
         */
-       long count = ldsem_atomic_update(LDSEM_ACTIVE_BIAS, sem);
+       long count = atomic_long_add_return(LDSEM_ACTIVE_BIAS, &sem->count);
        do {
                if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS)
                        return 1;
-               if (ldsem_cmpxchg(&count, count - LDSEM_ACTIVE_BIAS, sem))
+               if (atomic_long_try_cmpxchg(&sem->count, &count, count - LDSEM_ACTIVE_BIAS))
                        return 0;
        } while (1);
 }
@@ -205,12 +185,16 @@ down_read_failed(struct ld_semaphore *sem, long count, long timeout)
        /* set up my own style of waitqueue */
        raw_spin_lock_irq(&sem->wait_lock);
 
-       /* Try to reverse the lock attempt but if the count has changed
+       /*
+        * Try to reverse the lock attempt but if the count has changed
         * so that reversing fails, check if there are are no waiters,
-        * and early-out if not */
+        * and early-out if not
+        */
        do {
-               if (ldsem_cmpxchg(&count, count + adjust, sem))
+               if (atomic_long_try_cmpxchg(&sem->count, &count, count + adjust)) {
+                       count += adjust;
                        break;
+               }
                if (count > 0) {
                        raw_spin_unlock_irq(&sem->wait_lock);
                        return sem;
@@ -243,12 +227,14 @@ down_read_failed(struct ld_semaphore *sem, long count, long timeout)
        __set_current_state(TASK_RUNNING);
 
        if (!timeout) {
-               /* lock timed out but check if this task was just
+               /*
+                * Lock timed out but check if this task was just
                 * granted lock ownership - if so, pretend there
-                * was no timeout; otherwise, cleanup lock wait */
+                * was no timeout; otherwise, cleanup lock wait.
+                */
                raw_spin_lock_irq(&sem->wait_lock);
                if (waiter.task) {
-                       ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem);
+                       atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count);
                        list_del(&waiter.list);
                        raw_spin_unlock_irq(&sem->wait_lock);
                        put_task_struct(waiter.task);
@@ -273,11 +259,13 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout)
        /* set up my own style of waitqueue */
        raw_spin_lock_irq(&sem->wait_lock);
 
-       /* Try to reverse the lock attempt but if the count has changed
+       /*
+        * Try to reverse the lock attempt but if the count has changed
         * so that reversing fails, check if the lock is now owned,
-        * and early-out if so */
+        * and early-out if so.
+        */
        do {
-               if (ldsem_cmpxchg(&count, count + adjust, sem))
+               if (atomic_long_try_cmpxchg(&sem->count, &count, count + adjust))
                        break;
                if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) {
                        raw_spin_unlock_irq(&sem->wait_lock);
@@ -303,7 +291,7 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout)
        }
 
        if (!locked)
-               ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem);
+               atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count);
        list_del(&waiter.list);
        raw_spin_unlock_irq(&sem->wait_lock);
 
@@ -324,7 +312,7 @@ static int __ldsem_down_read_nested(struct ld_semaphore *sem,
 
        lockdep_acquire_read(sem, subclass, 0, _RET_IP_);
 
-       count = ldsem_atomic_update(LDSEM_READ_BIAS, sem);
+       count = atomic_long_add_return(LDSEM_READ_BIAS, &sem->count);
        if (count <= 0) {
                lock_stat(sem, contended);
                if (!down_read_failed(sem, count, timeout)) {
@@ -343,7 +331,7 @@ static int __ldsem_down_write_nested(struct ld_semaphore *sem,
 
        lockdep_acquire(sem, subclass, 0, _RET_IP_);
 
-       count = ldsem_atomic_update(LDSEM_WRITE_BIAS, sem);
+       count = atomic_long_add_return(LDSEM_WRITE_BIAS, &sem->count);
        if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) {
                lock_stat(sem, contended);
                if (!down_write_failed(sem, count, timeout)) {
@@ -370,10 +358,10 @@ int __sched ldsem_down_read(struct ld_semaphore *sem, long timeout)
  */
 int ldsem_down_read_trylock(struct ld_semaphore *sem)
 {
-       long count = sem->count;
+       long count = atomic_long_read(&sem->count);
 
        while (count >= 0) {
-               if (ldsem_cmpxchg(&count, count + LDSEM_READ_BIAS, sem)) {
+               if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_READ_BIAS)) {
                        lockdep_acquire_read(sem, 0, 1, _RET_IP_);
                        lock_stat(sem, acquired);
                        return 1;
@@ -396,10 +384,10 @@ int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout)
  */
 int ldsem_down_write_trylock(struct ld_semaphore *sem)
 {
-       long count = sem->count;
+       long count = atomic_long_read(&sem->count);
 
        while ((count & LDSEM_ACTIVE_MASK) == 0) {
-               if (ldsem_cmpxchg(&count, count + LDSEM_WRITE_BIAS, sem)) {
+               if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_WRITE_BIAS)) {
                        lockdep_acquire(sem, 0, 1, _RET_IP_);
                        lock_stat(sem, acquired);
                        return 1;
@@ -417,7 +405,7 @@ void ldsem_up_read(struct ld_semaphore *sem)
 
        lockdep_release(sem, 1, _RET_IP_);
 
-       count = ldsem_atomic_update(-LDSEM_READ_BIAS, sem);
+       count = atomic_long_add_return(-LDSEM_READ_BIAS, &sem->count);
        if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0)
                ldsem_wake(sem);
 }
@@ -431,7 +419,7 @@ void ldsem_up_write(struct ld_semaphore *sem)
 
        lockdep_release(sem, 1, _RET_IP_);
 
-       count = ldsem_atomic_update(-LDSEM_WRITE_BIAS, sem);
+       count = atomic_long_add_return(-LDSEM_WRITE_BIAS, &sem->count);
        if (count < 0)
                ldsem_wake(sem);
 }
index de310621b8e777b6a75cea8b41031d52d9ba27d8..88312c6c92cc6d2f9e1c806571a6360ecab5e5b2 100644 (file)
@@ -690,7 +690,35 @@ static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag)
  */
 static void k_dead(struct vc_data *vc, unsigned char value, char up_flag)
 {
-       static const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' };
+       static const unsigned char ret_diacr[NR_DEAD] = {
+               '`',    /* dead_grave */
+               '\'',   /* dead_acute */
+               '^',    /* dead_circumflex */
+               '~',    /* dead_tilda */
+               '"',    /* dead_diaeresis */
+               ',',    /* dead_cedilla */
+               '_',    /* dead_macron */
+               'U',    /* dead_breve */
+               '.',    /* dead_abovedot */
+               '*',    /* dead_abovering */
+               '=',    /* dead_doubleacute */
+               'c',    /* dead_caron */
+               'k',    /* dead_ogonek */
+               'i',    /* dead_iota */
+               '#',    /* dead_voiced_sound */
+               'o',    /* dead_semivoiced_sound */
+               '!',    /* dead_belowdot */
+               '?',    /* dead_hook */
+               '+',    /* dead_horn */
+               '-',    /* dead_stroke */
+               ')',    /* dead_abovecomma */
+               '(',    /* dead_abovereversedcomma */
+               ':',    /* dead_doublegrave */
+               'n',    /* dead_invertedbreve */
+               ';',    /* dead_belowcomma */
+               '$',    /* dead_currency */
+               '@',    /* dead_greek */
+       };
 
        k_deadunicode(vc, ret_diacr[value], up_flag);
 }
index 90ea1cc52b7aac91d6552ce161a16dc4036c3563..07496c711d7dcc1dde58fb1b3db7a29384ef336d 100644 (file)
@@ -57,11 +57,13 @@ static inline void highlight_pointer(const int where)
        complement_pos(sel_cons, where);
 }
 
-static u16
+static u32
 sel_pos(int n)
 {
+       if (use_unicode)
+               return screen_glyph_unicode(sel_cons, n / 2);
        return inverse_translate(sel_cons, screen_glyph(sel_cons, n),
-                               use_unicode);
+                               0);
 }
 
 /**
@@ -90,7 +92,8 @@ static u32 inwordLut[]={
   0x07FFFFFE, /* lowercase         */
 };
 
-static inline int inword(const u16 c) {
+static inline int inword(const u32 c)
+{
        return c > 0x7f || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
 }
 
@@ -116,14 +119,8 @@ static inline int atedge(const int p, int size_row)
        return (!(p % size_row) || !((p + 2) % size_row));
 }
 
-/* constrain v such that v <= u */
-static inline unsigned short limit(const unsigned short v, const unsigned short u)
-{
-       return (v > u) ? u : v;
-}
-
-/* stores the char in UTF8 and returns the number of bytes used (1-3) */
-static int store_utf8(u16 c, char *p)
+/* stores the char in UTF8 and returns the number of bytes used (1-4) */
+static int store_utf8(u32 c, char *p)
 {
        if (c < 0x80) {
                /*  0******* */
@@ -134,13 +131,26 @@ static int store_utf8(u16 c, char *p)
                p[0] = 0xc0 | (c >> 6);
                p[1] = 0x80 | (c & 0x3f);
                return 2;
-       } else {
+       } else if (c < 0x10000) {
                /* 1110**** 10****** 10****** */
                p[0] = 0xe0 | (c >> 12);
                p[1] = 0x80 | ((c >> 6) & 0x3f);
                p[2] = 0x80 | (c & 0x3f);
                return 3;
-       }
+       } else if (c < 0x110000) {
+               /* 11110*** 10****** 10****** 10****** */
+               p[0] = 0xf0 | (c >> 18);
+               p[1] = 0x80 | ((c >> 12) & 0x3f);
+               p[2] = 0x80 | ((c >> 6) & 0x3f);
+               p[3] = 0x80 | (c & 0x3f);
+               return 4;
+       } else {
+               /* outside Unicode, replace with U+FFFD */
+               p[0] = 0xef;
+               p[1] = 0xbf;
+               p[2] = 0xbd;
+               return 3;
+       }
 }
 
 /**
@@ -160,17 +170,17 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
        struct tiocl_selection v;
        char *bp, *obp;
        int i, ps, pe, multiplier;
-       u16 c;
+       u32 c;
        int mode;
 
        poke_blanked_console();
        if (copy_from_user(&v, sel, sizeof(*sel)))
                return -EFAULT;
 
-       v.xs = limit(v.xs - 1, vc->vc_cols - 1);
-       v.ys = limit(v.ys - 1, vc->vc_rows - 1);
-       v.xe = limit(v.xe - 1, vc->vc_cols - 1);
-       v.ye = limit(v.ye - 1, vc->vc_rows - 1);
+       v.xs = min_t(u16, v.xs - 1, vc->vc_cols - 1);
+       v.ys = min_t(u16, v.ys - 1, vc->vc_rows - 1);
+       v.xe = min_t(u16, v.xe - 1, vc->vc_cols - 1);
+       v.ye = min_t(u16, v.ye - 1, vc->vc_rows - 1);
        ps = v.ys * vc->vc_size_row + (v.xs << 1);
        pe = v.ye * vc->vc_size_row + (v.xe << 1);
 
@@ -279,7 +289,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
        sel_end = new_sel_end;
 
        /* Allocate a new buffer before freeing the old one ... */
-       multiplier = use_unicode ? 3 : 1;  /* chars can take up to 3 bytes */
+       multiplier = use_unicode ? 4 : 1;  /* chars can take up to 4 bytes */
        bp = kmalloc_array((sel_end - sel_start) / 2 + 1, multiplier,
                           GFP_KERNEL);
        if (!bp) {
index e4a66e1fd05fb9a91076e83d922e2dd3859935b8..2384ea85ffafed31c9450481125dc7740a4b387c 100644 (file)
  *     Attribute/character pair is in native endianity.
  *            [minor: N+128]
  *
+ * /dev/vcsuN: similar to /dev/vcsaN but using 4-byte unicode values
+ *     instead of 1-byte screen glyph values.
+ *            [minor: N+64]
+ *
+ * /dev/vcsuaN: same idea as /dev/vcsaN for unicode (not yet implemented).
+ *
  * This replaces screendump and part of selection, so that the system
  * administrator can control access using file system permissions.
  *
 
 #define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE)
 
+/*
+ * Our minor space:
+ *
+ *   0 ... 63  glyph mode without attributes
+ *  64 ... 127 unicode mode without attributes
+ * 128 ... 191 glyph mode with attributes
+ * 192 ... 255 unused (reserved for unicode with attributes)
+ *
+ * This relies on MAX_NR_CONSOLES being  <= 63, meaning 63 actual consoles
+ * with minors 0, 64, 128 and 192 being proxies for the foreground console.
+ */
+#if MAX_NR_CONSOLES > 63
+#warning "/dev/vcs* devices may not accommodate more than 63 consoles"
+#endif
+
+#define console(inode)         (iminor(inode) & 63)
+#define use_unicode(inode)     (iminor(inode) & 64)
+#define use_attributes(inode)  (iminor(inode) & 128)
+
+
 struct vcs_poll_data {
        struct notifier_block notifier;
        unsigned int cons_num;
@@ -102,7 +128,7 @@ vcs_poll_data_get(struct file *file)
        poll = kzalloc(sizeof(*poll), GFP_KERNEL);
        if (!poll)
                return NULL;
-       poll->cons_num = iminor(file_inode(file)) & 127;
+       poll->cons_num = console(file_inode(file));
        init_waitqueue_head(&poll->waitq);
        poll->notifier.notifier_call = vcs_notifier;
        if (register_vt_notifier(&poll->notifier) != 0) {
@@ -140,7 +166,7 @@ vcs_poll_data_get(struct file *file)
 static struct vc_data*
 vcs_vc(struct inode *inode, int *viewed)
 {
-       unsigned int currcons = iminor(inode) & 127;
+       unsigned int currcons = console(inode);
 
        WARN_CONSOLE_UNLOCKED();
 
@@ -164,7 +190,6 @@ static int
 vcs_size(struct inode *inode)
 {
        int size;
-       int minor = iminor(inode);
        struct vc_data *vc;
 
        WARN_CONSOLE_UNLOCKED();
@@ -175,8 +200,12 @@ vcs_size(struct inode *inode)
 
        size = vc->vc_rows * vc->vc_cols;
 
-       if (minor & 128)
+       if (use_attributes(inode)) {
+               if (use_unicode(inode))
+                       return -EOPNOTSUPP;
                size = 2*size + HEADER_SIZE;
+       } else if (use_unicode(inode))
+               size *= 4;
        return size;
 }
 
@@ -197,12 +226,10 @@ static ssize_t
 vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
 {
        struct inode *inode = file_inode(file);
-       unsigned int currcons = iminor(inode);
        struct vc_data *vc;
        struct vcs_poll_data *poll;
-       long pos;
-       long attr, read;
-       int col, maxcol, viewed;
+       long pos, read;
+       int attr, uni_mode, row, col, maxcol, viewed;
        unsigned short *org = NULL;
        ssize_t ret;
        char *con_buf;
@@ -218,7 +245,8 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
         */
        console_lock();
 
-       attr = (currcons & 128);
+       uni_mode = use_unicode(inode);
+       attr = use_attributes(inode);
        ret = -ENXIO;
        vc = vcs_vc(inode, &viewed);
        if (!vc)
@@ -227,6 +255,10 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
        ret = -EINVAL;
        if (pos < 0)
                goto unlock_out;
+       /* we enforce 32-bit alignment for pos and count in unicode mode */
+       if (uni_mode && (pos | count) & 3)
+               goto unlock_out;
+
        poll = file->private_data;
        if (count && poll)
                poll->seen_last_update = true;
@@ -266,7 +298,28 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
                con_buf_start = con_buf0 = con_buf;
                orig_count = this_round;
                maxcol = vc->vc_cols;
-               if (!attr) {
+               if (uni_mode) {
+                       unsigned int nr;
+
+                       ret = vc_uniscr_check(vc);
+                       if (ret)
+                               break;
+                       p /= 4;
+                       row = p / vc->vc_cols;
+                       col = p % maxcol;
+                       nr = maxcol - col;
+                       do {
+                               if (nr > this_round/4)
+                                       nr = this_round/4;
+                               vc_uniscr_copy_line(vc, con_buf0, viewed,
+                                                   row, col, nr);
+                               con_buf0 += nr * 4;
+                               this_round -= nr * 4;
+                               row++;
+                               col = 0;
+                               nr = maxcol;
+                       } while (this_round);
+               } else if (!attr) {
                        org = screen_pos(vc, p, viewed);
                        col = p % maxcol;
                        p += maxcol - col;
@@ -375,7 +428,6 @@ static ssize_t
 vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 {
        struct inode *inode = file_inode(file);
-       unsigned int currcons = iminor(inode);
        struct vc_data *vc;
        long pos;
        long attr, size, written;
@@ -396,7 +448,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
         */
        console_lock();
 
-       attr = (currcons & 128);
+       attr = use_attributes(inode);
        ret = -ENXIO;
        vc = vcs_vc(inode, &viewed);
        if (!vc)
@@ -593,9 +645,15 @@ vcs_fasync(int fd, struct file *file, int on)
 static int
 vcs_open(struct inode *inode, struct file *filp)
 {
-       unsigned int currcons = iminor(inode) & 127;
+       unsigned int currcons = console(inode);
+       bool attr = use_attributes(inode);
+       bool uni_mode = use_unicode(inode);
        int ret = 0;
-       
+
+       /* we currently don't support attributes in unicode mode */
+       if (attr && uni_mode)
+               return -EOPNOTSUPP;
+
        console_lock();
        if(currcons && !vc_cons_allocated(currcons-1))
                ret = -ENXIO;
@@ -628,6 +686,8 @@ void vcs_make_sysfs(int index)
 {
        device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL,
                      "vcs%u", index + 1);
+       device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL,
+                     "vcsu%u", index + 1);
        device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL,
                      "vcsa%u", index + 1);
 }
@@ -635,6 +695,7 @@ void vcs_make_sysfs(int index)
 void vcs_remove_sysfs(int index)
 {
        device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1));
+       device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 65));
        device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129));
 }
 
@@ -647,6 +708,7 @@ int __init vcs_init(void)
        vc_class = class_create(THIS_MODULE, "vc");
 
        device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
+       device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu");
        device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
        for (i = 0; i < MIN_NR_CONSOLES; i++)
                vcs_make_sysfs(i);
index 15eb6c829d39c5b108adfca034fa50769763c0bc..5f1183b0b89df3bc672ccf7299dc103dfddbf138 100644 (file)
 #include <linux/kdb.h>
 #include <linux/ctype.h>
 #include <linux/bsearch.h>
+#include <linux/gcd.h>
 
 #define MAX_NR_CON_DRIVER 16
 
@@ -317,6 +318,306 @@ void schedule_console_callback(void)
        schedule_work(&console_work);
 }
 
+/*
+ * Code to manage unicode-based screen buffers
+ */
+
+#ifdef NO_VC_UNI_SCREEN
+/* this disables and optimizes related code away at compile time */
+#define get_vc_uniscr(vc) NULL
+#else
+#define get_vc_uniscr(vc) vc->vc_uni_screen
+#endif
+
+#define VC_UNI_SCREEN_DEBUG 0
+
+typedef uint32_t char32_t;
+
+/*
+ * Our screen buffer is preceded by an array of line pointers so that
+ * scrolling only implies some pointer shuffling.
+ */
+struct uni_screen {
+       char32_t *lines[0];
+};
+
+static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows)
+{
+       struct uni_screen *uniscr;
+       void *p;
+       unsigned int memsize, i;
+
+       /* allocate everything in one go */
+       memsize = cols * rows * sizeof(char32_t);
+       memsize += rows * sizeof(char32_t *);
+       p = kmalloc(memsize, GFP_KERNEL);
+       if (!p)
+               return NULL;
+
+       /* initial line pointers */
+       uniscr = p;
+       p = uniscr->lines + rows;
+       for (i = 0; i < rows; i++) {
+               uniscr->lines[i] = p;
+               p += cols * sizeof(char32_t);
+       }
+       return uniscr;
+}
+
+static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr)
+{
+       kfree(vc->vc_uni_screen);
+       vc->vc_uni_screen = new_uniscr;
+}
+
+static void vc_uniscr_putc(struct vc_data *vc, char32_t uc)
+{
+       struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+       if (uniscr)
+               uniscr->lines[vc->vc_y][vc->vc_x] = uc;
+}
+
+static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
+{
+       struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+       if (uniscr) {
+               char32_t *ln = uniscr->lines[vc->vc_y];
+               unsigned int x = vc->vc_x, cols = vc->vc_cols;
+
+               memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln));
+               memset32(&ln[x], ' ', nr);
+       }
+}
+
+static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
+{
+       struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+       if (uniscr) {
+               char32_t *ln = uniscr->lines[vc->vc_y];
+               unsigned int x = vc->vc_x, cols = vc->vc_cols;
+
+               memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln));
+               memset32(&ln[cols - nr], ' ', nr);
+       }
+}
+
+static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x,
+                                unsigned int nr)
+{
+       struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+       if (uniscr) {
+               char32_t *ln = uniscr->lines[vc->vc_y];
+
+               memset32(&ln[x], ' ', nr);
+       }
+}
+
+static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y,
+                                 unsigned int nr)
+{
+       struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+       if (uniscr) {
+               unsigned int cols = vc->vc_cols;
+
+               while (nr--)
+                       memset32(uniscr->lines[y++], ' ', cols);
+       }
+}
+
+static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
+                            enum con_scroll dir, unsigned int nr)
+{
+       struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+       if (uniscr) {
+               unsigned int i, j, k, sz, d, clear;
+
+               sz = b - t;
+               clear = b - nr;
+               d = nr;
+               if (dir == SM_DOWN) {
+                       clear = t;
+                       d = sz - nr;
+               }
+               for (i = 0; i < gcd(d, sz); i++) {
+                       char32_t *tmp = uniscr->lines[t + i];
+                       j = i;
+                       while (1) {
+                               k = j + d;
+                               if (k >= sz)
+                                       k -= sz;
+                               if (k == i)
+                                       break;
+                               uniscr->lines[t + j] = uniscr->lines[t + k];
+                               j = k;
+                       }
+                       uniscr->lines[t + j] = tmp;
+               }
+               vc_uniscr_clear_lines(vc, clear, nr);
+       }
+}
+
+static void vc_uniscr_copy_area(struct uni_screen *dst,
+                               unsigned int dst_cols,
+                               unsigned int dst_rows,
+                               struct uni_screen *src,
+                               unsigned int src_cols,
+                               unsigned int src_top_row,
+                               unsigned int src_bot_row)
+{
+       unsigned int dst_row = 0;
+
+       if (!dst)
+               return;
+
+       while (src_top_row < src_bot_row) {
+               char32_t *src_line = src->lines[src_top_row];
+               char32_t *dst_line = dst->lines[dst_row];
+
+               memcpy(dst_line, src_line, src_cols * sizeof(char32_t));
+               if (dst_cols - src_cols)
+                       memset32(dst_line + src_cols, ' ', dst_cols - src_cols);
+               src_top_row++;
+               dst_row++;
+       }
+       while (dst_row < dst_rows) {
+               char32_t *dst_line = dst->lines[dst_row];
+
+               memset32(dst_line, ' ', dst_cols);
+               dst_row++;
+       }
+}
+
+/*
+ * Called from vcs_read() to make sure unicode screen retrieval is possible.
+ * This will initialize the unicode screen buffer if not already done.
+ * This returns 0 if OK, or a negative error code otherwise.
+ * In particular, -ENODATA is returned if the console is not in UTF-8 mode.
+ */
+int vc_uniscr_check(struct vc_data *vc)
+{
+       struct uni_screen *uniscr;
+       unsigned short *p;
+       int x, y, mask;
+
+       if (__is_defined(NO_VC_UNI_SCREEN))
+               return -EOPNOTSUPP;
+
+       WARN_CONSOLE_UNLOCKED();
+
+       if (!vc->vc_utf)
+               return -ENODATA;
+
+       if (vc->vc_uni_screen)
+               return 0;
+
+       uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
+       if (!uniscr)
+               return -ENOMEM;
+
+       /*
+        * Let's populate it initially with (imperfect) reverse translation.
+        * This is the next best thing we can do short of having it enabled
+        * from the start even when no users rely on this functionality. True
+        * unicode content will be available after a complete screen refresh.
+        */
+       p = (unsigned short *)vc->vc_origin;
+       mask = vc->vc_hi_font_mask | 0xff;
+       for (y = 0; y < vc->vc_rows; y++) {
+               char32_t *line = uniscr->lines[y];
+               for (x = 0; x < vc->vc_cols; x++) {
+                       u16 glyph = scr_readw(p++) & mask;
+                       line[x] = inverse_translate(vc, glyph, true);
+               }
+       }
+
+       vc->vc_uni_screen = uniscr;
+       return 0;
+}
+
+/*
+ * Called from vcs_read() to get the unicode data from the screen.
+ * This must be preceded by a successful call to vc_uniscr_check() once
+ * the console lock has been taken.
+ */
+void vc_uniscr_copy_line(struct vc_data *vc, void *dest, int viewed,
+                        unsigned int row, unsigned int col, unsigned int nr)
+{
+       struct uni_screen *uniscr = get_vc_uniscr(vc);
+       int offset = row * vc->vc_size_row + col * 2;
+       unsigned long pos;
+
+       BUG_ON(!uniscr);
+
+       pos = (unsigned long)screenpos(vc, offset, viewed);
+       if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
+               /*
+                * Desired position falls in the main screen buffer.
+                * However the actual row/col might be different if
+                * scrollback is active.
+                */
+               row = (pos - vc->vc_origin) / vc->vc_size_row;
+               col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2;
+               memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t));
+       } else {
+               /*
+                * Scrollback is active. For now let's simply backtranslate
+                * the screen glyphs until the unicode screen buffer does
+                * synchronize with console display drivers for a scrollback
+                * buffer of its own.
+                */
+               u16 *p = (u16 *)pos;
+               int mask = vc->vc_hi_font_mask | 0xff;
+               char32_t *uni_buf = dest;
+               while (nr--) {
+                       u16 glyph = scr_readw(p++) & mask;
+                       *uni_buf++ = inverse_translate(vc, glyph, true);
+               }
+       }
+}
+
+/* this is for validation and debugging only */
+static void vc_uniscr_debug_check(struct vc_data *vc)
+{
+       struct uni_screen *uniscr = get_vc_uniscr(vc);
+       unsigned short *p;
+       int x, y, mask;
+
+       if (!VC_UNI_SCREEN_DEBUG || !uniscr)
+               return;
+
+       WARN_CONSOLE_UNLOCKED();
+
+       /*
+        * Make sure our unicode screen translates into the same glyphs
+        * as the actual screen. This is brutal indeed.
+        */
+       p = (unsigned short *)vc->vc_origin;
+       mask = vc->vc_hi_font_mask | 0xff;
+       for (y = 0; y < vc->vc_rows; y++) {
+               char32_t *line = uniscr->lines[y];
+               for (x = 0; x < vc->vc_cols; x++) {
+                       u16 glyph = scr_readw(p++) & mask;
+                       char32_t uc = line[x];
+                       int tc = conv_uni_to_pc(vc, uc);
+                       if (tc == -4)
+                               tc = conv_uni_to_pc(vc, 0xfffd);
+                       if (tc == -4)
+                               tc = conv_uni_to_pc(vc, '?');
+                       if (tc != glyph)
+                               pr_err_ratelimited(
+                                       "%s: mismatch at %d,%d: glyph=%#x tc=%#x\n",
+                                       __func__, x, y, glyph, tc);
+               }
+       }
+}
+
+
 static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
                enum con_scroll dir, unsigned int nr)
 {
@@ -326,6 +627,7 @@ static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
                nr = b - t - 1;
        if (b > vc->vc_rows || t >= b || nr < 1)
                return;
+       vc_uniscr_scroll(vc, t, b, dir, nr);
        if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr))
                return;
 
@@ -533,6 +835,7 @@ static void insert_char(struct vc_data *vc, unsigned int nr)
 {
        unsigned short *p = (unsigned short *) vc->vc_pos;
 
+       vc_uniscr_insert(vc, nr);
        scr_memmovew(p + nr, p, (vc->vc_cols - vc->vc_x - nr) * 2);
        scr_memsetw(p, vc->vc_video_erase_char, nr * 2);
        vc->vc_need_wrap = 0;
@@ -545,6 +848,7 @@ static void delete_char(struct vc_data *vc, unsigned int nr)
 {
        unsigned short *p = (unsigned short *) vc->vc_pos;
 
+       vc_uniscr_delete(vc, nr);
        scr_memcpyw(p, p + nr, (vc->vc_cols - vc->vc_x - nr) * 2);
        scr_memsetw(p + vc->vc_cols - vc->vc_x - nr, vc->vc_video_erase_char,
                        nr * 2);
@@ -845,10 +1149,11 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
 {
        unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
        unsigned long end;
-       unsigned int old_rows, old_row_size;
+       unsigned int old_rows, old_row_size, first_copied_row;
        unsigned int new_cols, new_rows, new_row_size, new_screen_size;
        unsigned int user;
        unsigned short *newscreen;
+       struct uni_screen *new_uniscr = NULL;
 
        WARN_CONSOLE_UNLOCKED();
 
@@ -875,6 +1180,14 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
        if (!newscreen)
                return -ENOMEM;
 
+       if (get_vc_uniscr(vc)) {
+               new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
+               if (!new_uniscr) {
+                       kfree(newscreen);
+                       return -ENOMEM;
+               }
+       }
+
        if (vc == sel_cons)
                clear_selection();
 
@@ -884,6 +1197,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
        err = resize_screen(vc, new_cols, new_rows, user);
        if (err) {
                kfree(newscreen);
+               kfree(new_uniscr);
                return err;
        }
 
@@ -904,18 +1218,24 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
                         * Cursor near the bottom, copy contents from the
                         * bottom of buffer
                         */
-                       old_origin += (old_rows - new_rows) * old_row_size;
+                       first_copied_row = (old_rows - new_rows);
                } else {
                        /*
                         * Cursor is in no man's land, copy 1/2 screenful
                         * from the top and bottom of cursor position
                         */
-                       old_origin += (vc->vc_y - new_rows/2) * old_row_size;
+                       first_copied_row = (vc->vc_y - new_rows/2);
                }
-       }
-
+               old_origin += first_copied_row * old_row_size;
+       } else
+               first_copied_row = 0;
        end = old_origin + old_row_size * min(old_rows, new_rows);
 
+       vc_uniscr_copy_area(new_uniscr, new_cols, new_rows,
+                           get_vc_uniscr(vc), rlth/2, first_copied_row,
+                           min(old_rows, new_rows));
+       vc_uniscr_set(vc, new_uniscr);
+
        update_attr(vc);
 
        while (old_origin < end) {
@@ -1013,6 +1333,7 @@ struct vc_data *vc_deallocate(unsigned int currcons)
                vc->vc_sw->con_deinit(vc);
                put_pid(vc->vt_pid);
                module_put(vc->vc_sw->owner);
+               vc_uniscr_set(vc, NULL);
                kfree(vc->vc_screenbuf);
                vc_cons[currcons].d = NULL;
        }
@@ -1171,15 +1492,22 @@ static void csi_J(struct vc_data *vc, int vpar)
 
        switch (vpar) {
                case 0: /* erase from cursor to end of display */
+                       vc_uniscr_clear_line(vc, vc->vc_x,
+                                            vc->vc_cols - vc->vc_x);
+                       vc_uniscr_clear_lines(vc, vc->vc_y + 1,
+                                             vc->vc_rows - vc->vc_y - 1);
                        count = (vc->vc_scr_end - vc->vc_pos) >> 1;
                        start = (unsigned short *)vc->vc_pos;
                        break;
                case 1: /* erase from start to cursor */
+                       vc_uniscr_clear_line(vc, 0, vc->vc_x + 1);
+                       vc_uniscr_clear_lines(vc, 0, vc->vc_y);
                        count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1;
                        start = (unsigned short *)vc->vc_origin;
                        break;
                case 2: /* erase whole display */
                case 3: /* (and scrollback buffer later) */
+                       vc_uniscr_clear_lines(vc, 0, vc->vc_rows);
                        count = vc->vc_cols * vc->vc_rows;
                        start = (unsigned short *)vc->vc_origin;
                        break;
@@ -1200,25 +1528,27 @@ static void csi_J(struct vc_data *vc, int vpar)
 static void csi_K(struct vc_data *vc, int vpar)
 {
        unsigned int count;
-       unsigned short * start;
+       unsigned short *start = (unsigned short *)vc->vc_pos;
+       int offset;
 
        switch (vpar) {
                case 0: /* erase from cursor to end of line */
+                       offset = 0;
                        count = vc->vc_cols - vc->vc_x;
-                       start = (unsigned short *)vc->vc_pos;
                        break;
                case 1: /* erase from start of line to cursor */
-                       start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1));
+                       offset = -vc->vc_x;
                        count = vc->vc_x + 1;
                        break;
                case 2: /* erase whole line */
-                       start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1));
+                       offset = -vc->vc_x;
                        count = vc->vc_cols;
                        break;
                default:
                        return;
        }
-       scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
+       vc_uniscr_clear_line(vc, vc->vc_x + offset, count);
+       scr_memsetw(start + offset, vc->vc_video_erase_char, 2 * count);
        vc->vc_need_wrap = 0;
        if (con_should_update(vc))
                do_update_region(vc, (unsigned long) start, count);
@@ -1232,6 +1562,7 @@ static void csi_X(struct vc_data *vc, int vpar) /* erase the following vpar posi
                vpar++;
        count = (vpar > vc->vc_cols - vc->vc_x) ? (vc->vc_cols - vc->vc_x) : vpar;
 
+       vc_uniscr_clear_line(vc, vc->vc_x, count);
        scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count);
        if (con_should_update(vc))
                vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, count);
@@ -2188,7 +2519,7 @@ static void con_flush(struct vc_data *vc, unsigned long draw_from,
 /* acquires console_lock */
 static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int count)
 {
-       int c, tc, ok, n = 0, draw_x = -1;
+       int c, next_c, tc, ok, n = 0, draw_x = -1;
        unsigned int currcons;
        unsigned long draw_from = 0, draw_to = 0;
        struct vc_data *vc;
@@ -2382,6 +2713,7 @@ rescan_last_byte:
                                con_flush(vc, draw_from, draw_to, &draw_x);
                        }
 
+                       next_c = c;
                        while (1) {
                                if (vc->vc_need_wrap || vc->vc_decim)
                                        con_flush(vc, draw_from, draw_to,
@@ -2392,6 +2724,7 @@ rescan_last_byte:
                                }
                                if (vc->vc_decim)
                                        insert_char(vc, 1);
+                               vc_uniscr_putc(vc, next_c);
                                scr_writew(himask ?
                                             ((vc_attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
                                             (vc_attr << 8) + tc,
@@ -2412,6 +2745,7 @@ rescan_last_byte:
 
                                tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */
                                if (tc < 0) tc = ' ';
+                               next_c = ' ';
                        }
                        notify_write(vc, c);
 
@@ -2431,6 +2765,7 @@ rescan_last_byte:
                do_con_trol(tty, vc, orig);
        }
        con_flush(vc, draw_from, draw_to, &draw_x);
+       vc_uniscr_debug_check(vc);
        console_conditional_schedule();
        console_unlock();
        notify_update(vc);
@@ -4257,6 +4592,16 @@ u16 screen_glyph(struct vc_data *vc, int offset)
 }
 EXPORT_SYMBOL_GPL(screen_glyph);
 
+u32 screen_glyph_unicode(struct vc_data *vc, int n)
+{
+       struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+       if (uniscr)
+               return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols];
+       return inverse_translate(vc, screen_glyph(vc, n * 2), 1);
+}
+EXPORT_SYMBOL_GPL(screen_glyph_unicode);
+
 /* used by vcs - note the word offset */
 unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed)
 {
index c0ec478ea5bf17df0d7ce0356a99e66728cc77b3..fea64f2692a052bea9064f19c723295ac3232c73 100644 (file)
@@ -17,8 +17,8 @@
 #include <linux/vt.h>
 #include <linux/workqueue.h>
 
-struct vt_struct;
 struct uni_pagedir;
+struct uni_screen;
 
 #define NPAR 16
 
@@ -140,6 +140,7 @@ struct vc_data {
        struct vc_data **vc_display_fg;         /* [!] Ptr to var holding fg console for this display */
        struct uni_pagedir *vc_uni_pagedir;
        struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */
+       struct uni_screen *vc_uni_screen;       /* unicode screen content */
        bool vc_panic_force_write; /* when oops/panic this VC can accept forced output/blanking */
        /* additional information is in vt_kern.h */
 };
@@ -148,7 +149,7 @@ struct vc {
        struct vc_data *d;
        struct work_struct SAK_work;
 
-       /* might add  scrmem, vt_struct, kbd  at some time,
+       /* might add  scrmem, kbd  at some time,
           to have everything in one place - the disadvantage
           would be that vc_cons etc can no longer be static */
 };
index 5b278ce99d8d0666ac77bc913133fd3933c1546b..a8f5b97b216f5380803c41fb1f138a405767da07 100644 (file)
@@ -32,6 +32,7 @@ extern unsigned char default_blu[];
 
 extern unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed);
 extern u16 screen_glyph(struct vc_data *vc, int offset);
+extern u32 screen_glyph_unicode(struct vc_data *vc, int offset);
 extern void complement_pos(struct vc_data *vc, int offset);
 extern void invert_screen(struct vc_data *vc, int offset, int count, int shift);
 
@@ -42,4 +43,9 @@ extern u16 vcs_scr_readw(struct vc_data *vc, const u16 *org);
 extern void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org);
 extern void vcs_scr_updated(struct vc_data *vc);
 
+extern int vc_uniscr_check(struct vc_data *vc);
+extern void vc_uniscr_copy_line(struct vc_data *vc, void *dest, int viewed,
+                               unsigned int row, unsigned int col,
+                               unsigned int nr);
+
 #endif
index 76b9db71e489bf63f89b6c3cc5fd99689d36412f..18e21427bce434f0337f76872d89a0b764bb36f1 100644 (file)
@@ -160,6 +160,9 @@ extern void serial8250_do_shutdown(struct uart_port *port);
 extern void serial8250_do_pm(struct uart_port *port, unsigned int state,
                             unsigned int oldstate);
 extern void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl);
+extern void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
+                                     unsigned int quot,
+                                     unsigned int quot_frac);
 extern int fsl8250_handle_irq(struct uart_port *port);
 int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
 unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr);
index 06ea4eeb09abd759dec1bc9740d89220f579584f..406edae44ca3070151c36ed04a72c7dda2e24305 100644 (file)
@@ -127,6 +127,13 @@ struct uart_port {
                                             struct ktermios *);
        unsigned int            (*get_mctrl)(struct uart_port *);
        void                    (*set_mctrl)(struct uart_port *, unsigned int);
+       unsigned int            (*get_divisor)(struct uart_port *,
+                                              unsigned int baud,
+                                              unsigned int *frac);
+       void                    (*set_divisor)(struct uart_port *,
+                                              unsigned int baud,
+                                              unsigned int quot,
+                                              unsigned int quot_frac);
        int                     (*startup)(struct uart_port *port);
        void                    (*shutdown)(struct uart_port *port);
        void                    (*throttle)(struct uart_port *port);
index 1ef64d4ad887ea50e7fcd88707d97b033c985660..840894ca3fc02aeadcf22c4e41b04a3e3b09b36d 100644 (file)
 
 #include <linux/fs.h>
 #include <linux/wait.h>
-
+#include <linux/atomic.h>
 
 /*
  * the semaphore definition
  */
 struct ld_semaphore {
-       long                    count;
+       atomic_long_t           count;
        raw_spinlock_t          wait_lock;
        unsigned int            wait_readers;
        struct list_head        read_wait;
index ab4108c8318618af1e156ebf62f16f97f0d0ea52..4846716e7c5c8c8d34f24011f18a3c14f1084cf2 100644 (file)
 #define K_DTILDE       K(KT_DEAD,3)
 #define K_DDIERE       K(KT_DEAD,4)
 #define K_DCEDIL       K(KT_DEAD,5)
+#define K_DMACRON      K(KT_DEAD,6)
+#define K_DBREVE       K(KT_DEAD,7)
+#define K_DABDOT       K(KT_DEAD,8)
+#define K_DABRING      K(KT_DEAD,9)
+#define K_DDBACUTE     K(KT_DEAD,10)
+#define K_DCARON       K(KT_DEAD,11)
+#define K_DOGONEK      K(KT_DEAD,12)
+#define K_DIOTA                K(KT_DEAD,13)
+#define K_DVOICED      K(KT_DEAD,14)
+#define K_DSEMVOICED   K(KT_DEAD,15)
+#define K_DBEDOT       K(KT_DEAD,16)
+#define K_DHOOK                K(KT_DEAD,17)
+#define K_DHORN                K(KT_DEAD,18)
+#define K_DSTROKE      K(KT_DEAD,19)
+#define K_DABCOMMA     K(KT_DEAD,20)
+#define K_DABREVCOMMA  K(KT_DEAD,21)
+#define K_DDBGRAVE     K(KT_DEAD,22)
+#define K_DINVBREVE    K(KT_DEAD,23)
+#define K_DBECOMMA     K(KT_DEAD,24)
+#define K_DCURRENCY    K(KT_DEAD,25)
+#define K_DGREEK       K(KT_DEAD,26)
 
-#define NR_DEAD                6
+#define NR_DEAD                27
 
 #define K_DOWN         K(KT_CUR,0)
 #define K_LEFT         K(KT_CUR,1)