aboutsummaryrefslogtreecommitdiffstats
path: root/tty.work
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2009-07-31 21:31:30 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2009-07-31 21:31:30 -0700
commit288dff4528481dae00119111ac38ba4dfee2cb03 (patch)
tree6dd4614e328555f68a1ae9ecf167f17cb09c85f1 /tty.work
parent2f6934c1a5b93267d2793111f6565a69a5bbb514 (diff)
downloadpatches-288dff4528481dae00119111ac38ba4dfee2cb03.tar.gz
add tty and serial patches to queue
Diffstat (limited to 'tty.work')
-rw-r--r--tty.work/serial-core-port-wait230
-rw-r--r--tty.work/serial-extract-portops99
-rw-r--r--tty.work/serial-f81216-helper365
-rw-r--r--tty.work/serial-use-port-hangup37
-rw-r--r--tty.work/serial-use-tty-close102
-rw-r--r--tty.work/tty-usb-cleanup-open211
6 files changed, 1044 insertions, 0 deletions
diff --git a/tty.work/serial-core-port-wait b/tty.work/serial-core-port-wait
new file mode 100644
index 00000000000000..99813a98358ac7
--- /dev/null
+++ b/tty.work/serial-core-port-wait
@@ -0,0 +1,230 @@
+serial_core: use the tty_port wait_until_ready
+
+From: Alan Cox <alan@linux.intel.com>
+
+This subtly changes the locking. Our count and other fields become locked
+by the tty_port (which is the correct behaviour).
+
+The mutex behaviour on open that serial had is actually very useful so we
+propogate that into the tty_port core code.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+---
+
+ drivers/char/tty_port.c | 41 ++++++++++++++++++-
+ drivers/serial/serial_core.c | 91 ++----------------------------------------
+ include/linux/tty.h | 2 +
+ 3 files changed, 45 insertions(+), 89 deletions(-)
+
+
+diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
+index 0293d25..61b09bd 100644
+--- a/drivers/char/tty_port.c
++++ b/drivers/char/tty_port.c
+@@ -180,7 +180,7 @@ void tty_port_lower_dtr_rts(struct tty_port *port)
+ EXPORT_SYMBOL(tty_port_lower_dtr_rts);
+
+ /**
+- * tty_port_block_til_ready - Waiting logic for tty open
++ * __tty_port_block_til_ready - Waiting logic for tty open
+ * @port: the tty port being opened
+ * @tty: the tty device being bound
+ * @filp: the file pointer of the opener
+@@ -197,9 +197,12 @@ EXPORT_SYMBOL(tty_port_lower_dtr_rts);
+ * do carrier detect and the dtr_rts method if it supports software
+ * management of these lines. Note that the dtr/rts raise is done each
+ * iteration as a hangup may have previously dropped them while we wait.
++ *
++ * Called with the port mutex held. See tty_port_block_til_ready for
++ * normal uses.
+ */
+
+-int tty_port_block_til_ready(struct tty_port *port,
++int __tty_port_block_til_ready(struct tty_port *port,
+ struct tty_struct *tty, struct file *filp)
+ {
+ int do_clocal = 0, retval;
+@@ -265,7 +268,9 @@ int tty_port_block_til_ready(struct tty_port *port,
+ retval = -ERESTARTSYS;
+ break;
+ }
++ mutex_unlock(&port->mutex);
+ schedule();
++ mutex_lock(&port->mutex);
+ }
+ finish_wait(&port->open_wait, &wait);
+
+@@ -281,6 +286,38 @@ int tty_port_block_til_ready(struct tty_port *port,
+ return retval;
+
+ }
++EXPORT_SYMBOL(__tty_port_block_til_ready);
++
++/**
++ * tty_port_block_til_ready - Waiting logic for tty open
++ * @port: the tty port being opened
++ * @tty: the tty device being bound
++ * @filp: the file pointer of the opener
++ *
++ * Implement the core POSIX/SuS tty behaviour when opening a tty device.
++ * Handles:
++ * - hangup (both before and during)
++ * - non blocking open
++ * - rts/dtr/dcd
++ * - signals
++ * - port flags and counts
++ *
++ * The passed tty_port must implement the carrier_raised method if it can
++ * do carrier detect and the dtr_rts method if it supports software
++ * management of these lines. Note that the dtr/rts raise is done each
++ * iteration as a hangup may have previously dropped them while we wait.
++ */
++
++int tty_port_block_til_ready(struct tty_port *port,
++ struct tty_struct *tty, struct file *filp)
++{
++ int retval;
++ mutex_lock(&port->mutex);
++ retval = __tty_port_block_til_ready(port, tty, filp);
++ mutex_unlock(&port->mutex);
++ return retval;
++}
++
+ EXPORT_SYMBOL(tty_port_block_til_ready);
+
+ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp)
+diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
+index 6d2290d..b1d4b91 100644
+--- a/drivers/serial/serial_core.c
++++ b/drivers/serial/serial_core.c
+@@ -1452,86 +1452,6 @@ static int uart_carrier_raised(struct tty_port *port)
+ return 0;
+ }
+
+-/*
+- * Block the open until the port is ready. We must be called with
+- * the per-port semaphore held.
+- */
+-static int
+-uart_block_til_ready(struct file *filp, struct uart_state *state)
+-{
+- DECLARE_WAITQUEUE(wait, current);
+- struct tty_port *port = &state->port;
+-
+- port->blocked_open++;
+- port->count--;
+-
+- add_wait_queue(&port->open_wait, &wait);
+- while (1) {
+- set_current_state(TASK_INTERRUPTIBLE);
+-
+- /*
+- * If we have been hung up, tell userspace/restart open.
+- */
+- if (tty_hung_up_p(filp) || port->tty == NULL)
+- break;
+-
+- /*
+- * If the port has been closed, tell userspace/restart open.
+- */
+- if (!(port->flags & ASYNC_INITIALIZED))
+- break;
+-
+- /*
+- * If non-blocking mode is set, or CLOCAL mode is set,
+- * we don't want to wait for the modem status lines to
+- * indicate that the port is ready.
+- *
+- * Also, if the port is not enabled/configured, we want
+- * to allow the open to succeed here. Note that we will
+- * have set TTY_IO_ERROR for a non-existant port.
+- */
+- if ((filp->f_flags & O_NONBLOCK) ||
+- (port->tty->termios->c_cflag & CLOCAL) ||
+- (port->tty->flags & (1 << TTY_IO_ERROR)))
+- break;
+-
+- /*
+- * Set DTR to allow modem to know we're waiting. Do
+- * not set RTS here - we want to make sure we catch
+- * the data from the modem.
+- */
+- if (port->tty->termios->c_cflag & CBAUD)
+- tty_port_raise_dtr_rts(port);
+-
+- /*
+- * and wait for the carrier to indicate that the
+- * modem is ready for us.
+- */
+-
+- if (tty_port_carrier_raised(port))
+- break;
+-
+- mutex_unlock(&port->mutex);
+- schedule();
+- mutex_lock(&port->mutex);
+-
+- if (signal_pending(current))
+- break;
+- }
+- set_current_state(TASK_RUNNING);
+- remove_wait_queue(&port->open_wait, &wait);
+-
+- port->count++;
+- port->blocked_open--;
+-
+- if (signal_pending(current))
+- return -ERESTARTSYS;
+-
+- if (!port->tty || tty_hung_up_p(filp))
+- return -EAGAIN;
+-
+- return 0;
+-}
+
+ static struct uart_state *uart_get(struct uart_driver *drv, int line)
+ {
+@@ -1590,7 +1510,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
+ goto fail;
+
+ /*
+- * We take the semaphore inside uart_get to guarantee that we won't
++ * We take the mutex inside uart_get to guarantee that we won't
+ * be re-entered while allocating the state structure, or while we
+ * request any IRQs that the driver may need. This also has the nice
+ * side-effect that it delays the action of uart_hangup, so we can
+@@ -1640,18 +1560,15 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
+ * If we succeeded, wait until the port is ready.
+ */
+ if (retval == 0)
+- retval = uart_block_til_ready(filp, state);
++ retval = tty_port_block_til_ready(port, tty, filp);
+ mutex_unlock(&port->mutex);
+
+ /*
+ * If this is the first open to succeed, adjust things to suit.
+ */
+- if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) {
+- set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+-
++ if (retval == 0 &&
++ !test_and_set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags))
+ uart_update_termios(state);
+- }
+-
+ fail:
+ return retval;
+ }
+diff --git a/include/linux/tty.h b/include/linux/tty.h
+index 65a7549..7163493 100644
+--- a/include/linux/tty.h
++++ b/include/linux/tty.h
+@@ -458,6 +458,8 @@ extern int tty_port_carrier_raised(struct tty_port *port);
+ extern void tty_port_raise_dtr_rts(struct tty_port *port);
+ extern void tty_port_lower_dtr_rts(struct tty_port *port);
+ extern void tty_port_hangup(struct tty_port *port);
++extern int __tty_port_block_til_ready(struct tty_port *port,
++ struct tty_struct *tty, struct file *filp);
+ extern int tty_port_block_til_ready(struct tty_port *port,
+ struct tty_struct *tty, struct file *filp);
+ extern int tty_port_close_start(struct tty_port *port,
diff --git a/tty.work/serial-extract-portops b/tty.work/serial-extract-portops
new file mode 100644
index 00000000000000..24e0f589bddc17
--- /dev/null
+++ b/tty.work/serial-extract-portops
@@ -0,0 +1,99 @@
+serial: extract carrier operations
+
+From: Alan Cox <alan@linux.intel.com>
+
+The switch to using the tty port helpers needs some functions separated out
+and provided as tty port helpers. Do the split but don't yet switch to the
+tty_port logic so that we can bisect bugs better
+
+Signed-off-by; Alan Cox <alan@linux.intel.com>
+---
+
+ drivers/serial/serial_core.c | 39 +++++++++++++++++++++++++++++++--------
+ 1 files changed, 31 insertions(+), 8 deletions(-)
+
+
+diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
+index 2514d00..285c051 100644
+--- a/drivers/serial/serial_core.c
++++ b/drivers/serial/serial_core.c
+@@ -1473,6 +1473,28 @@ static void uart_update_termios(struct uart_state *state)
+ }
+ }
+
++static void uart_raise_dtr_rts(struct tty_port *port, int up)
++{
++ struct uart_state *state = container_of(port, struct uart_state, port);
++ struct uart_port *uport = state->uart_port;
++ uart_set_mctrl(uport, up ? TIOCM_DTR : 0);
++}
++
++static int uart_carrier_raised(struct tty_port *port)
++{
++ struct uart_state *state = container_of(port, struct uart_state, port);
++ struct uart_port *uport = state->uart_port;
++ unsigned int mctrl;
++
++ spin_lock_irq(&uport->lock);
++ uport->ops->enable_ms(uport);
++ mctrl = uport->ops->get_mctrl(uport);
++ spin_unlock_irq(&uport->lock);
++ if (mctrl & TIOCM_CAR)
++ return 1;
++ return 0;
++}
++
+ /*
+ * Block the open until the port is ready. We must be called with
+ * the per-port semaphore held.
+@@ -1481,9 +1503,7 @@ static int
+ uart_block_til_ready(struct file *filp, struct uart_state *state)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+- struct uart_port *uport = state->uart_port;
+ struct tty_port *port = &state->port;
+- unsigned int mctrl;
+
+ port->blocked_open++;
+ port->count--;
+@@ -1524,17 +1544,14 @@ uart_block_til_ready(struct file *filp, struct uart_state *state)
+ * the data from the modem.
+ */
+ if (port->tty->termios->c_cflag & CBAUD)
+- uart_set_mctrl(uport, TIOCM_DTR);
++ tty_port_raise_dtr_rts(port);
+
+ /*
+ * and wait for the carrier to indicate that the
+ * modem is ready for us.
+ */
+- spin_lock_irq(&uport->lock);
+- uport->ops->enable_ms(uport);
+- mctrl = uport->ops->get_mctrl(uport);
+- spin_unlock_irq(&uport->lock);
+- if (mctrl & TIOCM_CAR)
++
++ if (tty_port_carrier_raised(port))
+ break;
+
+ mutex_unlock(&port->mutex);
+@@ -2326,6 +2343,11 @@ static const struct tty_operations uart_ops = {
+ #endif
+ };
+
++static const struct tty_port_operations uart_port_ops = {
++ .carrier_raised = uart_carrier_raised,
++ .dtr_rts = uart_raise_dtr_rts,
++};
++
+ /**
+ * uart_register_driver - register a driver with the uart core layer
+ * @drv: low level driver structure
+@@ -2383,6 +2405,7 @@ int uart_register_driver(struct uart_driver *drv)
+ struct tty_port *port = &state->port;
+
+ tty_port_init(port);
++ port->ops = &uart_port_ops;
+ port->close_delay = 500; /* .5 seconds */
+ port->closing_wait = 30000; /* 30 seconds */
+ tasklet_init(&state->tlet, uart_tasklet_action,
diff --git a/tty.work/serial-f81216-helper b/tty.work/serial-f81216-helper
new file mode 100644
index 00000000000000..f9179852abd761
--- /dev/null
+++ b/tty.work/serial-f81216-helper
@@ -0,0 +1,365 @@
+serial: Add a helper driver for the F81216
+
+From: Bruno <bonbons67@internet.lu>
+
+This is a helper driver to restore the F81216 SuperIO hardware on systems
+where the BIOS gets it wrong.
+
+See http://bugzilla.kernel.org/attachment.cgi?id=21784
+
+[Added region request/handling alan@linux.intel.com]
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+---
+
+ drivers/serial/8250_f81216.c | 311 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/serial/Kconfig | 11 +
+ drivers/serial/Makefile | 1
+ 3 files changed, 323 insertions(+)
+ create mode 100644 drivers/serial/8250_f81216.c
+
+
+--- /dev/null
++++ b/drivers/serial/8250_f81216.c
+@@ -0,0 +1,311 @@
++/*
++ * linux/drivers/serial/8250_f81216.c
++ *
++ * Copyright (C) 2009 Bruno Prémont <bonbons@linux-vserver.org>.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/serial_8250.h>
++#include <linux/platform_device.h>
++#include <asm/io.h>
++
++static int io_base = 0x4e;
++static int config_key = 0x77;
++
++struct f81216_uart_state {
++ __u16 io_port;
++ __u8 enabled;
++ __u8 irq_channel;
++ __u8 clk_select;
++};
++struct f81216_state {
++ __u8 clk_source;
++ struct f81216_uart_state uart[4];
++};
++
++static struct platform_device *f81216_device;
++
++#define LDN 0x07 /* Register: Logical device select */
++#define DEVICE_ID 0x20 /* Register: Device ID (2 bytes) */
++#define VENDOR_ID 0x23 /* Register: Vendor ID (2 bytes) */
++#define CLK_SRC 0x25 /* Register: Clock source select */
++#define LDN_ENABLE 0x30 /* Register: Device enable */
++#define LDN_IOPORT 0x60 /* Register: I/O Port select (2 bytes) */
++#define LDN_IRQCHAN 0x70 /* Register: IRQ Channel select */
++#define LDN_CLK 0xf0 /* Register: Clock select */
++
++#define F81216_DEVICE_ID 0x0208
++#define F81216_VENDOR_ID 0x1934
++
++static int superio_inb(int reg)
++{
++ outb(reg, io_base);
++ return inb(io_base+1);
++}
++
++static int superio_inw(int reg)
++{
++ int val;
++ outb(reg++, io_base);
++ val = inb(io_base+1) << 8;
++ outb(reg, io_base);
++ val |= inb(io_base+1);
++ return val;
++}
++
++static void superio_outb(int val, int reg)
++{
++ outb(reg, io_base);
++ outb(val, io_base+1);
++}
++
++static void superio_outw(int val, int reg)
++{
++ outb(reg++, io_base);
++ outb((val >> 8) & 0xff, io_base+1);
++ outb(reg, io_base);
++ outb(val & 0xff, io_base+1);
++}
++
++static void superio_enter(void)
++{
++ outb(config_key, io_base);
++ outb(config_key, io_base);
++}
++
++static void superio_exit(void)
++{
++ outb(0xaa, io_base);
++}
++
++static int f81216_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct f81216_state *data = platform_get_drvdata(dev);
++ int i;
++
++ if (!data)
++ return -ENOMEM;
++
++ superio_enter();
++ data->clk_source = superio_inb(CLK_SRC);
++ dev_printk(KERN_DEBUG, &dev->dev, "Chip status: clk=%x\n",
++ data->clk_source);
++ for (i = 0; i < 4; i++) {
++ superio_outb(i, LDN);
++ data->uart[i].enabled = superio_inb(LDN_ENABLE);
++ data->uart[i].io_port = superio_inw(LDN_IOPORT);
++ data->uart[i].irq_channel = superio_inb(LDN_IRQCHAN);
++ data->uart[i].clk_select = superio_inb(LDN_CLK);
++ dev_printk(KERN_DEBUG, &dev->dev, "UART %d status: ioport=%x,"
++ " irqchan=%x, clk=%x, enable=%x\n", i,
++ data->uart[i].io_port,
++ data->uart[i].irq_channel,
++ data->uart[i].clk_select,
++ data->uart[i].enabled);
++ }
++ superio_exit();
++ return 0;
++}
++
++static int f81216_suspend_late(struct platform_device *dev, pm_message_t state)
++{
++ int i;
++
++ superio_enter();
++ dev_printk(KERN_DEBUG, &dev->dev, "Chip status: clk=%x\n",
++ superio_inb(CLK_SRC));
++ for (i = 0; i < 4; i++) {
++ superio_outb(i, LDN);
++ dev_printk(KERN_DEBUG, &dev->dev, "UART %d status: ioport=%x,"
++ " irqchan=%x, clk=%x, enable=%x\n", i,
++ superio_inw(LDN_IOPORT),
++ superio_inb(LDN_IRQCHAN),
++ superio_inb(LDN_CLK),
++ superio_inb(LDN_ENABLE));
++ }
++ superio_exit();
++ return 0;
++}
++
++static void f81216_restoreb(struct platform_device *dev, int ldn, int offset,
++ int value)
++{
++ int v = superio_inb(offset);
++ if (v == value)
++ return;
++
++ if (ldn >= 0)
++ dev_printk(KERN_DEBUG, &dev->dev, "restoring device %d config "
++ "at offset %x (was %x, writing %x)\n",
++ ldn, offset, v, value);
++ else
++ dev_printk(KERN_DEBUG, &dev->dev, "restoring global config "
++ "at offset %x (was %x, writing %x)\n",
++ offset, v, value);
++ superio_outb(value, offset);
++}
++
++static void f81216_restorew(struct platform_device *dev, int ldn, int offset,
++ int value)
++{
++ int v = superio_inw(offset);
++ if (v == value)
++ return;
++
++ if (ldn >= 0)
++ dev_printk(KERN_DEBUG, &dev->dev, "restoring device %d config "
++ "at offset %x (was %x, writing %x)\n",
++ ldn, offset, v, value);
++ else
++ dev_printk(KERN_DEBUG, &dev->dev, "restoring global config "
++ "at offset %x (was %x, writing %x)\n",
++ offset, v, value);
++ superio_outw(value, offset);
++}
++
++static int f81216_resume_early(struct platform_device *dev)
++{
++ int i;
++
++ superio_enter();
++ dev_printk(KERN_DEBUG, &dev->dev, "Chip status: clk=%x\n",
++ superio_inb(CLK_SRC));
++ for (i = 0; i < 4; i++) {
++ superio_outb(i, LDN);
++ dev_printk(KERN_DEBUG, &dev->dev, "UART %d status: ioport=%x,"
++ " irqchan=%x, clk=%x, enable=%x\n", i,
++ superio_inw(LDN_IOPORT),
++ superio_inb(LDN_IRQCHAN),
++ superio_inb(LDN_CLK),
++ superio_inb(LDN_ENABLE));
++ }
++ superio_exit();
++ return 0;
++}
++
++static int f81216_resume(struct platform_device *dev)
++{
++ struct f81216_state *data = platform_get_drvdata(dev);
++ int i;
++
++ superio_enter();
++ f81216_restoreb(dev, -1, CLK_SRC, data->clk_source);
++ for (i = 0; i < 4; i++) {
++ superio_outb(i, LDN);
++ f81216_restoreb(dev, i, LDN_CLK, data->uart[i].clk_select);
++ f81216_restorew(dev, i, LDN_IOPORT, data->uart[i].io_port);
++ f81216_restoreb(dev, i, LDN_IRQCHAN, data->uart[i].irq_channel);
++ f81216_restoreb(dev, i, LDN_ENABLE, data->uart[i].enabled);
++ }
++ superio_exit();
++ return 0;
++}
++
++static struct platform_driver f81216_driver = {
++ .suspend = f81216_suspend,
++ .suspend_late = f81216_suspend_late,
++ .resume_early = f81216_resume_early,
++ .resume = f81216_resume,
++ .driver = {
++ .name = "f81216",
++ .owner = THIS_MODULE,
++ }
++};
++
++static int __init f81216_init(void)
++{
++ int port, i, ret;
++ struct resource res = {
++ .start = io_base,
++ .end = io_base + 0x01,
++ .name = "f81216",
++ .flags = IORESOURCE_IO
++ };
++
++ if (io_base != 0x2e && io_base != 0x4e)
++ return EINVAL;
++ if (config_key != 0x77 && config_key != 0xa0 && config_key != 0x87 &&
++ config_key != 0x67)
++ return EINVAL;
++ if (request_region(io_base, 2, "f81216 probe") == 0)
++ return -EBUSY;
++ /* determine existance of Fintek 81216 chip */
++ superio_enter();
++ if (superio_inw(DEVICE_ID) != F81216_DEVICE_ID)
++ goto out;
++ if (superio_inw(VENDOR_ID) != F81216_VENDOR_ID)
++ goto out;
++
++ /* count enabled ports - to be extended to register our UARTs */
++ for (i = port = 0; i < 4; i++) {
++ superio_outb(i, LDN);
++ if (superio_inb(LDN_ENABLE) == 0x01)
++ port++;
++ }
++ superio_exit();
++ release_region(io_base, 2);
++
++ printk(KERN_INFO "f81216: Found chip with %d UART ports enabled\n",
++ port);
++
++ ret = platform_driver_register(&f81216_driver);
++ if (ret)
++ return ret;
++
++ f81216_device = platform_device_alloc("f81216", -1);
++ if (f81216_device) {
++ platform_set_drvdata(f81216_device, kzalloc(sizeof(struct f81216_state), GFP_KERNEL));
++ if (!platform_get_drvdata(f81216_device))
++ ret = -ENOMEM;
++ } else
++ ret = -ENOMEM;
++ if (ret)
++ goto fail_platform_device_alloc;
++
++ ret = platform_device_add_resources(f81216_device, &res, 1);
++ if (ret)
++ goto fail_platform_device_add;
++
++ ret = platform_device_add(f81216_device);
++ if (ret)
++ goto fail_platform_device_add;
++
++ return 0;
++
++fail_platform_device_add:
++ platform_device_put(f81216_device);
++
++fail_platform_device_alloc:
++ platform_driver_unregister(&f81216_driver);
++ return ret;
++
++out:
++ superio_exit();
++ release_region(io_base, 2);
++ return -ENODEV;
++}
++
++static void __exit f81216_exit(void)
++{
++ platform_device_unregister(f81216_device);
++ platform_driver_unregister(&f81216_driver);
++}
++
++module_init(f81216_init);
++module_exit(f81216_exit);
++
++module_param(io_base, uint, 0444);
++MODULE_PARM_DESC(io_base, "Configuration IO port [0x4e (default) or 0x2e]");
++
++module_param(config_key, uint, 0444);
++MODULE_PARM_DESC(config_key, "Configuration entry key [0x77 (default), "
++ "0xa0, 0x87 or 0x67]");
++
++MODULE_AUTHOR("Bruno Prémont");
++MODULE_DESCRIPTION("8250 serial probe module for F81216 based ports"
++ " (e.g. IEI Kino 690S1)");
++MODULE_LICENSE("GPL");
+--- a/drivers/serial/Kconfig
++++ b/drivers/serial/Kconfig
+@@ -266,6 +266,17 @@ config SERIAL_8250_AU1X00
+ say Y to this option. The driver can handle up to 4 serial ports,
+ depending on the SOC. If unsure, say N.
+
++config SERIAL_8250_F81216
++ tristate "Manage suspend and restore of F81216 devices"
++ depends on SERIAL_8250 != n
++ help
++ Say Y here if you have a board such as the IEI Kino 69OS1 with an
++ F81216 SuperIO chip which requires Linux to restore the serial port
++ configuration when you suspend and resume. If unsure, say N.
++
++ To compile this driver as a module, choose M here: the module
++ will be called 8250_f81216.
++
+ config SERIAL_8250_RM9K
+ bool "Support for MIPS RM9xxx integrated serial port"
+ depends on SERIAL_8250 != n && SERIAL_RM9000
+--- a/drivers/serial/Makefile
++++ b/drivers/serial/Makefile
+@@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_b
+ obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
+ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
+ obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o
++obj-$(CONFIG_SERIAL_8250_F81216) += 8250_f81216.o
+ obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
+ obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
+ obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
diff --git a/tty.work/serial-use-port-hangup b/tty.work/serial-use-port-hangup
new file mode 100644
index 00000000000000..7609ba68ae2180
--- /dev/null
+++ b/tty.work/serial-use-port-hangup
@@ -0,0 +1,37 @@
+serial: use tty_port_hangup
+
+From: Alan Cox <alan@linux.intel.com>
+
+We can now move the uart hangup code to use the core tty port code.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+---
+
+ drivers/serial/serial_core.c | 7 +------
+ 1 files changed, 1 insertions(+), 6 deletions(-)
+
+
+diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
+index 285c051..4e646c4 100644
+--- a/drivers/serial/serial_core.c
++++ b/drivers/serial/serial_core.c
+@@ -1422,18 +1422,13 @@ static void uart_hangup(struct tty_struct *tty)
+ struct uart_state *state = tty->driver_data;
+ struct tty_port *port = &state->port;
+
+- BUG_ON(!kernel_locked());
+ pr_debug("uart_hangup(%d)\n", state->uart_port->line);
+
+ mutex_lock(&port->mutex);
+ if (port->flags & ASYNC_NORMAL_ACTIVE) {
+ uart_flush_buffer(tty);
+ uart_shutdown(state);
+- port->count = 0;
+- clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+- tty_port_tty_set(port, NULL);
+- wake_up_interruptible(&port->open_wait);
+- wake_up_interruptible(&port->delta_msr_wait);
++ tty_port_hangup(port);
+ }
+ mutex_unlock(&port->mutex);
+ }
diff --git a/tty.work/serial-use-tty-close b/tty.work/serial-use-tty-close
new file mode 100644
index 00000000000000..8161e25945a4ad
--- /dev/null
+++ b/tty.work/serial-use-tty-close
@@ -0,0 +1,102 @@
+serial: use tty_port_close
+
+From: Alan Cox <alan@linux.intel.com>
+
+We can now move to the closing helper but can't yet use the open helper
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+---
+
+ drivers/serial/serial_core.c | 60 ++++++++----------------------------------
+ 1 files changed, 11 insertions(+), 49 deletions(-)
+
+
+diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
+index 4e646c4..6d2290d 100644
+--- a/drivers/serial/serial_core.c
++++ b/drivers/serial/serial_core.c
+@@ -1277,48 +1277,20 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
+
+ mutex_lock(&port->mutex);
+
+- if (tty_hung_up_p(filp))
++ /* FIXME: We can also migrate to drain_delay here */
++ if (tty_port_close_start(port, tty, filp) == 0)
+ goto done;
+
+- if ((tty->count == 1) && (port->count != 1)) {
+- /*
+- * Uh, oh. tty->count is 1, which means that the tty
+- * structure will be freed. port->count should always
+- * be one in these conditions. If it's greater than
+- * one, we've got real problems, since it means the
+- * serial port won't be shutdown.
+- */
+- printk(KERN_ERR "uart_close: bad serial port count; tty->count is 1, "
+- "port->count is %d\n", port->count);
+- port->count = 1;
+- }
+- if (--port->count < 0) {
+- printk(KERN_ERR "uart_close: bad serial port count for %s: %d\n",
+- tty->name, port->count);
+- port->count = 0;
+- }
+- if (port->count)
+- goto done;
+-
+- /*
+- * Now we wait for the transmit buffer to clear; and we notify
+- * the line discipline to only process XON/XOFF characters by
+- * setting tty->closing.
+- */
+- tty->closing = 1;
+-
+- if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+- tty_wait_until_sent(tty, msecs_to_jiffies(port->closing_wait));
+-
+ /*
+ * At this point, we stop accepting input. To do this, we
+- * disable the receive line status interrupts.
++ * disable the receive line status interrupts. This logic probably
++ * ultimately belongs in the tty port layer
+ */
+ if (port->flags & ASYNC_INITIALIZED) {
+ unsigned long flags;
+- spin_lock_irqsave(&port->lock, flags);
++ spin_lock_irqsave(&uport->lock, flags);
+ uport->ops->stop_rx(uport);
+- spin_unlock_irqrestore(&port->lock, flags);
++ spin_unlock_irqrestore(&uport->lock, flags);
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+@@ -1330,24 +1302,14 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
+ uart_shutdown(state);
+ uart_flush_buffer(tty);
+
+- tty_ldisc_flush(tty);
+-
+- tty->closing = 0;
++ tty_port_close_end(port, tty);
+ tty_port_tty_set(port, NULL);
+
+- if (port->blocked_open) {
+- if (port->close_delay)
+- msleep_interruptible(port->close_delay);
+- } else if (!uart_console(uport)) {
+- uart_change_pm(state, 3);
+- }
+-
+- /*
+- * Wake up anyone trying to open this port.
++ /* No users waiting - drop the power state
++ FIXME: should be core tty port code ?
+ */
+- clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+- wake_up_interruptible(&port->open_wait);
+-
++ if(port->blocked_open == 0 && !uart_console(uport))
++ uart_change_pm(state, 3);
+ done:
+ mutex_unlock(&port->mutex);
+ }
diff --git a/tty.work/tty-usb-cleanup-open b/tty.work/tty-usb-cleanup-open
new file mode 100644
index 00000000000000..ae36e9c3bdc8d4
--- /dev/null
+++ b/tty.work/tty-usb-cleanup-open
@@ -0,0 +1,211 @@
+tty: clean up the USB open paths
+
+From: Alan Cox <alan@linux.intel.com>
+
+Extract the first time init logic and make it a helper which does its own
+clean up
+
+Remove the clean up we can just let ->shutdown handle when the device is
+freed up
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+---
+
+ drivers/usb/serial/usb-serial.c | 126 +++++++++++++++++++++-------------------
+ 1 file changed, 68 insertions(+), 58 deletions(-)
+
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -179,7 +179,59 @@ void usb_serial_put(struct usb_serial *s
+ /*****************************************************************************
+ * Driver tty interface functions
+ *****************************************************************************/
+-static int serial_open (struct tty_struct *tty, struct file *filp)
++
++/**
++ * serial_activate - activate a USB serial port
++ * @tty: the tty being activated
++ * @port: the USB port being activated
++ * @filp: File handle
++ *
++ * Called to activate a USB serial port. The caller must hold port->mutex
++ *
++ * On exit either
++ * The port is activated and ASYNC_INITIALIZED is set
++ * - a module reference is held
++ * - an autopm reference is held
++ * or
++ * An error occurred
++ * - all references taken are dropped
++ */
++
++static int serial_activate(struct tty_struct *tty, struct usb_serial_port *port)
++{
++ struct usb_serial *serial = port->serial;
++ int retval;
++ /* lock this module before we call it
++ * this may fail, which means we must bail out,
++ * safe because we are called with BKL held */
++ if (!try_module_get(serial->type->driver.owner))
++ return -ENODEV;
++
++ mutex_lock(&serial->disc_mutex);
++ if (serial->disconnected)
++ retval = -ENODEV;
++ else
++ retval = usb_autopm_get_interface(serial->interface);
++ if (retval)
++ goto bailout_module_put;
++
++ /* only call the device specific open if this
++ * is the first time the port is opened */
++ retval = serial->type->open(tty, port);
++ if (retval)
++ goto bailout_interface_put;
++ mutex_unlock(&serial->disc_mutex);
++ set_bit(ASYNCB_INITIALIZED, &port->port.flags);
++ return 0;
++bailout_interface_put:
++ usb_autopm_put_interface(serial->interface);
++bailout_module_put:
++ module_put(serial->type->driver.owner);
++ mutex_unlock(&serial->disc_mutex);
++ return retval;
++}
++
++static int serial_open(struct tty_struct *tty, struct file *filp)
+ {
+ struct usb_serial *serial;
+ struct usb_serial_port *port;
+@@ -189,12 +241,11 @@ static int serial_open (struct tty_struc
+
+ dbg("%s", __func__);
+
++ tty->driver_data = NULL;
+ /* get the serial object associated with this tty pointer */
+ serial = usb_serial_get_by_index(tty->index);
+- if (!serial) {
+- tty->driver_data = NULL;
++ if (!serial)
+ return -ENODEV;
+- }
+
+ mutex_lock(&serial->disc_mutex);
+ portNumber = tty->index - serial->minor;
+@@ -226,53 +277,17 @@ static int serial_open (struct tty_struc
+ /* If the console is attached, the device is already open */
+ if (port->port.count == 1 && !port->console) {
+ first = 1;
+- /* lock this module before we call it
+- * this may fail, which means we must bail out,
+- * safe because we are called with BKL held */
+- if (!try_module_get(serial->type->driver.owner)) {
+- retval = -ENODEV;
+- goto bailout_mutex_unlock;
+- }
+-
+- mutex_lock(&serial->disc_mutex);
+- if (serial->disconnected)
+- retval = -ENODEV;
+- else
+- retval = usb_autopm_get_interface(serial->interface);
+- if (retval)
+- goto bailout_module_put;
+-
+- /* only call the device specific open if this
+- * is the first time the port is opened */
+- retval = serial->type->open(tty, port);
+- if (retval)
+- goto bailout_interface_put;
+- mutex_unlock(&serial->disc_mutex);
+- set_bit(ASYNCB_INITIALIZED, &port->port.flags);
++ retval = serial_activate(tty, port);
+ }
+ mutex_unlock(&port->mutex);
+ /* Now do the correct tty layer semantics */
+ retval = tty_port_block_til_ready(&port->port, tty, filp);
+- if (retval == 0) {
+- if (!first)
+- usb_serial_put(serial);
+- return 0;
+- }
+- mutex_lock(&port->mutex);
+- if (first == 0)
+- goto bailout_mutex_unlock;
+- /* Undo the initial port actions */
+- mutex_lock(&serial->disc_mutex);
+-bailout_interface_put:
+- usb_autopm_put_interface(serial->interface);
+-bailout_module_put:
+- mutex_unlock(&serial->disc_mutex);
+- module_put(serial->type->driver.owner);
+-bailout_mutex_unlock:
+- port->port.count = 0;
+- tty->driver_data = NULL;
+- tty_port_tty_set(&port->port, NULL);
+- mutex_unlock(&port->mutex);
++ if (!first)
++ usb_serial_put(serial);
++ /* Close will do any needed cleaning up and serial_do_free will
++ finish the job */
++ return retval;
++
+ bailout_port_put:
+ put_device(&port->dev);
+ bailout_serial_put:
+@@ -281,7 +296,7 @@ bailout_serial_put:
+ }
+
+ /**
+- * serial_do_down - shut down hardware
++ * serial_shutdown - shut down hardware
+ * @port: port to shut down
+ *
+ * Shut down a USB port unless it is the console. We never shut down the
+@@ -289,8 +304,10 @@ bailout_serial_put:
+ *
+ * Don't free any resources at this point
+ */
+-static void serial_do_down(struct usb_serial_port *port)
++static void serial_shutdown(struct tty_port *tport)
+ {
++ struct usb_serial_port *port = container_of(tport,
++ struct usb_serial_port, port);
+ struct usb_serial_driver *drv = port->serial->type;
+
+ /* The console is magical, do not hang up the console hardware
+@@ -341,24 +358,16 @@ static void serial_do_free(struct tty_st
+ static void serial_close(struct tty_struct *tty, struct file *filp)
+ {
+ struct usb_serial_port *port = tty->driver_data;
+-
+ if (!port)
+ return;
+
+ dbg("%s - port %d", __func__, port->number);
+-
+- if (tty_port_close_start(&port->port, tty, filp) == 0)
+- return;
+- serial_do_down(port);
+- tty_port_close_end(&port->port, tty);
+- tty_port_tty_set(&port->port, NULL);
+-
++ tty_port_close(&port->port, tty, filp);
+ }
+
+ static void serial_hangup(struct tty_struct *tty)
+ {
+ struct usb_serial_port *port = tty->driver_data;
+- serial_do_down(port);
+ tty_port_hangup(&port->port);
+ /* We must not free port yet - the USB serial layer depends on it's
+ continued existence */
+@@ -713,6 +722,7 @@ static void serial_dtr_rts(struct tty_po
+ static const struct tty_port_operations serial_port_ops = {
+ .carrier_raised = serial_carrier_raised,
+ .dtr_rts = serial_dtr_rts,
++ .shutdown = serial_shutdown,
+ };
+
+ /**