summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/Kconfig9
-rw-r--r--drivers/tty/hvc/Kconfig2
-rw-r--r--drivers/tty/hvc/hvc_console.c2
-rw-r--r--drivers/tty/ipwireless/network.c7
-rw-r--r--drivers/tty/n_gsm.c2
-rw-r--r--drivers/tty/pty.c2
-rw-r--r--drivers/tty/serial/8250/8250.c72
-rw-r--r--drivers/tty/serial/8250/8250.h9
-rw-r--r--drivers/tty/serial/8250/8250_pci.c11
-rw-r--r--drivers/tty/serial/8250/8250_pnp.c31
-rw-r--r--drivers/tty/serial/8250/Kconfig16
-rw-r--r--drivers/tty/serial/8250/Makefile5
-rw-r--r--drivers/tty/serial/Kconfig23
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/amba-pl011.c88
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_core.c23
-rw-r--r--drivers/tty/serial/imx.c8
-rw-r--r--drivers/tty/serial/jsm/jsm_driver.c2
-rw-r--r--drivers/tty/serial/kgdb_nmi.c402
-rw-r--r--drivers/tty/serial/kgdboc.c9
-rw-r--r--drivers/tty/serial/max3100.c14
-rw-r--r--drivers/tty/serial/mpc52xx_uart.c2
-rw-r--r--drivers/tty/serial/mxs-auart.c1
-rw-r--r--drivers/tty/serial/omap-serial.c27
-rw-r--r--drivers/tty/serial/samsung.c49
-rw-r--r--drivers/tty/serial/sccnxp.c17
-rw-r--r--drivers/tty/serial/serial_core.c28
-rw-r--r--drivers/tty/serial/serial_ks8695.c4
-rw-r--r--drivers/tty/serial/vt8500_serial.c58
29 files changed, 799 insertions, 125 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 830cd62..d8e05ee 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -214,8 +214,8 @@ config CYCLADES
If you haven't heard about it, it's safe to say N.
config CYZ_INTR
- bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)"
- depends on EXPERIMENTAL && CYCLADES
+ bool "Cyclades-Z interrupt mode operation"
+ depends on CYCLADES
help
The Cyclades-Z family of multiport cards allows 2 (two) driver op
modes: polling and interrupt. In polling mode, the driver will check
@@ -285,7 +285,7 @@ config SYNCLINK_GT
config NOZOMI
tristate "HSDPA Broadband Wireless Data Card - Globe Trotter"
- depends on PCI && EXPERIMENTAL
+ depends on PCI
help
If you have a HSDPA driver Broadband Wireless Data Card -
Globe Trotter PCMCIA card, say Y here.
@@ -294,7 +294,7 @@ config NOZOMI
will be called nozomi.
config ISI
- tristate "Multi-Tech multiport card support (EXPERIMENTAL)"
+ tristate "Multi-Tech multiport card support"
depends on SERIAL_NONSTANDARD && PCI
select FW_LOADER
help
@@ -317,7 +317,6 @@ config N_HDLC
config N_GSM
tristate "GSM MUX line discipline support (EXPERIMENTAL)"
- depends on EXPERIMENTAL
depends on NET
help
This line discipline provides support for the GSM MUX protocol and
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index 0282a83..f47b734 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -76,7 +76,7 @@ config HVC_XEN_FRONTEND
config HVC_UDBG
bool "udbg based fake hypervisor console"
- depends on PPC && EXPERIMENTAL
+ depends on PPC
select HVC_DRIVER
default n
help
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index 7f80f15..4a65299 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -558,7 +558,7 @@ static int hvc_write_room(struct tty_struct *tty)
struct hvc_struct *hp = tty->driver_data;
if (!hp)
- return -1;
+ return 0;
return hp->outbuf_size - hp->n_outbuf;
}
diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c
index 57c8b48..d2af155 100644
--- a/drivers/tty/ipwireless/network.c
+++ b/drivers/tty/ipwireless/network.c
@@ -274,7 +274,12 @@ static void do_go_online(struct work_struct *work_go_online)
network->xaccm[0] = ~0U;
network->xaccm[3] = 0x60000000U;
network->raccm = ~0U;
- ppp_register_channel(channel);
+ if (ppp_register_channel(channel) < 0) {
+ printk(KERN_ERR IPWIRELESS_PCCARD_NAME
+ ": unable to register PPP channel\n");
+ kfree(channel);
+ return;
+ }
spin_lock_irqsave(&network->lock, flags);
network->ppp_channel = channel;
}
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 3e210a4..1e8e8ce 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -488,7 +488,7 @@ static void gsm_print_packet(const char *hdr, int addr, int cr,
default:
if (!(control & 0x01)) {
pr_cont("I N(S)%d N(R)%d",
- (control & 0x0E) >> 1, (control & 0xE) >> 5);
+ (control & 0x0E) >> 1, (control & 0xE0) >> 5);
} else switch (control & 0x0F) {
case RR:
pr_cont("RR(%d)", (control & 0xE0) >> 5);
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 2bace84..a82b399 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -628,6 +628,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
index = devpts_new_index(inode);
if (index < 0) {
retval = index;
+ mutex_unlock(&devpts_mutex);
goto err_file;
}
@@ -667,7 +668,6 @@ out:
mutex_unlock(&tty_mutex);
devpts_kill_index(inode, index);
err_file:
- mutex_unlock(&devpts_mutex);
tty_free_file(filp);
return retval;
}
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index d4e0b07..3ba4234 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -290,6 +290,9 @@ static const struct serial8250_config uart_config[] = {
UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00,
.flags = UART_CAP_FIFO,
},
+ [PORT_8250_CIR] = {
+ .name = "CIR port"
+ }
};
/* Uart divisor latch read */
@@ -1037,6 +1040,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
unsigned char save_lcr, save_mcr;
struct uart_port *port = &up->port;
unsigned long flags;
+ unsigned int old_capabilities;
if (!port->iobase && !port->mapbase && !port->membase)
return;
@@ -1087,6 +1091,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
/*
* We failed; there's nothing here
*/
+ spin_unlock_irqrestore(&port->lock, flags);
DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
scratch2, scratch3);
goto out;
@@ -1110,6 +1115,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
status1 = serial_in(up, UART_MSR) & 0xF0;
serial_out(up, UART_MCR, save_mcr);
if (status1 != 0x90) {
+ spin_unlock_irqrestore(&port->lock, flags);
DEBUG_AUTOCONF("LOOP test failed (%02x) ",
status1);
goto out;
@@ -1132,8 +1138,6 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(up, UART_IIR) >> 6;
- DEBUG_AUTOCONF("iir=%d ", scratch);
-
switch (scratch) {
case 0:
autoconfig_8250(up);
@@ -1167,19 +1171,13 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
serial_out(up, UART_LCR, save_lcr);
- if (up->capabilities != uart_config[port->type].flags) {
- printk(KERN_WARNING
- "ttyS%d: detected caps %08x should be %08x\n",
- serial_index(port), up->capabilities,
- uart_config[port->type].flags);
- }
-
port->fifosize = uart_config[up->port.type].fifo_size;
+ old_capabilities = up->capabilities;
up->capabilities = uart_config[port->type].flags;
up->tx_loadsz = uart_config[port->type].tx_loadsz;
if (port->type == PORT_UNKNOWN)
- goto out;
+ goto out_lock;
/*
* Reset the UART.
@@ -1196,8 +1194,16 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
else
serial_out(up, UART_IER, 0);
- out:
+out_lock:
spin_unlock_irqrestore(&port->lock, flags);
+ if (up->capabilities != old_capabilities) {
+ printk(KERN_WARNING
+ "ttyS%d: detected caps %08x should be %08x\n",
+ serial_index(port), old_capabilities,
+ up->capabilities);
+ }
+out:
+ DEBUG_AUTOCONF("iir=%d ", scratch);
DEBUG_AUTOCONF("type=%s\n", uart_config[port->type].name);
}
@@ -1897,6 +1903,9 @@ static int serial8250_startup(struct uart_port *port)
unsigned char lsr, iir;
int retval;
+ if (port->type == PORT_8250_CIR)
+ return -ENODEV;
+
port->fifosize = uart_config[up->port.type].fifo_size;
up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
up->capabilities = uart_config[up->port.type].flags;
@@ -2554,7 +2563,10 @@ static int serial8250_request_port(struct uart_port *port)
{
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
- int ret = 0;
+ int ret;
+
+ if (port->type == PORT_8250_CIR)
+ return -ENODEV;
ret = serial8250_request_std_resource(up);
if (ret == 0 && port->type == PORT_RSA) {
@@ -2573,6 +2585,9 @@ static void serial8250_config_port(struct uart_port *port, int flags)
int probeflags = PROBE_ANY;
int ret;
+ if (port->type == PORT_8250_CIR)
+ return;
+
/*
* Find the region that we can probe for. This in turn
* tells us whether we can probe for the type of port.
@@ -2672,6 +2687,9 @@ static void __init serial8250_isa_init_ports(void)
return;
first = 0;
+ if (nr_uarts > UART_NR)
+ nr_uarts = UART_NR;
+
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
struct uart_port *port = &up->port;
@@ -2681,6 +2699,7 @@ static void __init serial8250_isa_init_ports(void)
init_timer(&up->timer);
up->timer.function = serial8250_timeout;
+ up->cur_iotype = 0xFF;
/*
* ALPHA_KLUDGE_MCR needs to be killed.
@@ -2732,13 +2751,9 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
- up->cur_iotype = 0xFF;
- }
- serial8250_isa_init_ports();
-
- for (i = 0; i < nr_uarts; i++) {
- struct uart_8250_port *up = &serial8250_ports[i];
+ if (up->port.dev)
+ continue;
up->port.dev = dev;
@@ -2863,9 +2878,6 @@ static struct console serial8250_console = {
static int __init serial8250_console_init(void)
{
- if (nr_uarts > UART_NR)
- nr_uarts = UART_NR;
-
serial8250_isa_init_ports();
register_console(&serial8250_console);
return 0;
@@ -3147,8 +3159,9 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
mutex_lock(&serial_mutex);
uart = serial8250_find_match_or_unused(&up->port);
- if (uart) {
- uart_remove_one_port(&serial8250_reg, &uart->port);
+ if (uart && uart->port.type != PORT_8250_CIR) {
+ if (uart->port.dev)
+ uart_remove_one_port(&serial8250_reg, &uart->port);
uart->port.iobase = up->port.iobase;
uart->port.membase = up->port.membase;
@@ -3232,8 +3245,7 @@ static int __init serial8250_init(void)
{
int ret;
- if (nr_uarts > UART_NR)
- nr_uarts = UART_NR;
+ serial8250_isa_init_ports();
printk(KERN_INFO "Serial: 8250/16550 driver, "
"%d ports, IRQ sharing %sabled\n", nr_uarts,
@@ -3248,11 +3260,15 @@ static int __init serial8250_init(void)
if (ret)
goto out;
+ ret = serial8250_pnp_init();
+ if (ret)
+ goto unreg_uart_drv;
+
serial8250_isa_devs = platform_device_alloc("serial8250",
PLAT8250_DEV_LEGACY);
if (!serial8250_isa_devs) {
ret = -ENOMEM;
- goto unreg_uart_drv;
+ goto unreg_pnp;
}
ret = platform_device_add(serial8250_isa_devs);
@@ -3268,6 +3284,8 @@ static int __init serial8250_init(void)
platform_device_del(serial8250_isa_devs);
put_dev:
platform_device_put(serial8250_isa_devs);
+unreg_pnp:
+ serial8250_pnp_exit();
unreg_uart_drv:
#ifdef CONFIG_SPARC
sunserial_unregister_minors(&serial8250_reg, UART_NR);
@@ -3292,6 +3310,8 @@ static void __exit serial8250_exit(void)
platform_driver_unregister(&serial8250_isa_driver);
platform_device_unregister(isa_dev);
+ serial8250_pnp_exit();
+
#ifdef CONFIG_SPARC
sunserial_unregister_minors(&serial8250_reg, UART_NR);
#else
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 0c5e908..5a76f9c 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -97,3 +97,12 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value)
#else
#define ALPHA_KLUDGE_MCR 0
#endif
+
+#ifdef CONFIG_SERIAL_8250_PNP
+int serial8250_pnp_init(void);
+void serial8250_pnp_exit(void);
+#else
+static inline int serial8250_pnp_init(void) { return 0; }
+static inline void serial8250_pnp_exit(void) { }
+#endif
+
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index fdab80a..17b7d26 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1181,6 +1181,8 @@ pci_wch_ch353_setup(struct serial_private *priv,
#define PCI_SUBDEVICE_ID_OCTPRO422 0x0208
#define PCI_SUBDEVICE_ID_POCTAL232 0x0308
#define PCI_SUBDEVICE_ID_POCTAL422 0x0408
+#define PCI_SUBDEVICE_ID_SIIG_DUAL_00 0x2500
+#define PCI_SUBDEVICE_ID_SIIG_DUAL_30 0x2530
#define PCI_VENDOR_ID_ADVANTECH 0x13fe
#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66
#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620
@@ -3286,8 +3288,11 @@ static struct pci_device_id serial_pci_tbl[] = {
* For now just used the hex ID 0x950a.
*/
{ PCI_VENDOR_ID_OXSEMI, 0x950a,
- PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0,
- pbn_b0_2_115200 },
+ PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_00,
+ 0, 0, pbn_b0_2_115200 },
+ { PCI_VENDOR_ID_OXSEMI, 0x950a,
+ PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_30,
+ 0, 0, pbn_b0_2_115200 },
{ PCI_VENDOR_ID_OXSEMI, 0x950a,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_2_1130000 },
@@ -4309,7 +4314,7 @@ static void serial8250_io_resume(struct pci_dev *dev)
pciserial_resume_ports(priv);
}
-static struct pci_error_handlers serial8250_err_handler = {
+static const struct pci_error_handlers serial8250_err_handler = {
.error_detected = serial8250_io_error_detected,
.slot_reset = serial8250_io_slot_reset,
.resume = serial8250_io_resume,
diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c
index fde5aa6..f8ee250 100644
--- a/drivers/tty/serial/8250/8250_pnp.c
+++ b/drivers/tty/serial/8250/8250_pnp.c
@@ -1,5 +1,5 @@
/*
- * Probe module for 8250/16550-type ISAPNP serial ports.
+ * Probe for 8250/16550-type ISAPNP serial ports.
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
*
@@ -25,7 +25,7 @@
#include "8250.h"
#define UNKNOWN_DEV 0x3000
-
+#define CIR_PORT 0x0800
static const struct pnp_device_id pnp_dev_table[] = {
/* Archtek America Corp. */
@@ -362,6 +362,9 @@ static const struct pnp_device_id pnp_dev_table[] = {
{ "PNPCXXX", UNKNOWN_DEV },
/* More unknown PnP modems */
{ "PNPDXXX", UNKNOWN_DEV },
+ /* Winbond CIR port, should not be probed. We should keep track
+ of it to prevent the legacy serial driver from probing it */
+ { "WEC1022", CIR_PORT },
{ "", 0 }
};
@@ -409,7 +412,7 @@ static int __devinit check_resources(struct pnp_dev *dev)
* PnP modems, alternatively we must hardcode all modems in pnp_devices[]
* table.
*/
-static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
+static int __devinit serial_pnp_guess_board(struct pnp_dev *dev)
{
if (!(check_name(pnp_dev_name(dev)) ||
(dev->card && check_name(dev->card->name))))
@@ -428,7 +431,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
int ret, line, flags = dev_id->driver_data;
if (flags & UNKNOWN_DEV) {
- ret = serial_pnp_guess_board(dev, &flags);
+ ret = serial_pnp_guess_board(dev);
if (ret < 0)
return ret;
}
@@ -436,7 +439,10 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
memset(&uart, 0, sizeof(uart));
if (pnp_irq_valid(dev, 0))
uart.port.irq = pnp_irq(dev, 0);
- if (pnp_port_valid(dev, 0)) {
+ if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) {
+ uart.port.iobase = pnp_port_start(dev, 2);
+ uart.port.iotype = UPIO_PORT;
+ } else if (pnp_port_valid(dev, 0)) {
uart.port.iobase = pnp_port_start(dev, 0);
uart.port.iotype = UPIO_PORT;
} else if (pnp_mem_valid(dev, 0)) {
@@ -451,6 +457,10 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
"Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n",
uart.port.iobase, uart.port.mapbase, uart.port.irq, uart.port.iotype);
#endif
+ if (flags & CIR_PORT) {
+ uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE;
+ uart.port.type = PORT_8250_CIR;
+ }
uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
@@ -459,7 +469,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
uart.port.dev = &dev->dev;
line = serial8250_register_8250_port(&uart);
- if (line < 0)
+ if (line < 0 || (flags & CIR_PORT))
return -ENODEV;
pnp_set_drvdata(dev, (void *)((long)line + 1));
@@ -507,18 +517,13 @@ static struct pnp_driver serial_pnp_driver = {
.id_table = pnp_dev_table,
};
-static int __init serial8250_pnp_init(void)
+int serial8250_pnp_init(void)
{
return pnp_register_driver(&serial_pnp_driver);
}
-static void __exit serial8250_pnp_exit(void)
+void serial8250_pnp_exit(void)
{
pnp_unregister_driver(&serial_pnp_driver);
}
-module_init(serial8250_pnp_init);
-module_exit(serial8250_pnp_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index a27dd05..f3d283f 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -33,6 +33,14 @@ config SERIAL_8250
Most people will say Y or M here, so that they can use serial mice,
modems and similar devices connecting to the standard serial ports.
+config SERIAL_8250_PNP
+ bool "8250/16550 PNP device support" if EXPERT
+ depends on SERIAL_8250 && PNP
+ default y
+ ---help---
+ This builds standard PNP serial support. You may be able to
+ disable this feature if you only need legacy serial support.
+
config SERIAL_8250_CONSOLE
bool "Console on 8250/16550 and compatible serial port"
depends on SERIAL_8250=y
@@ -85,14 +93,6 @@ config SERIAL_8250_PCI
disable this feature if you only need legacy serial support.
Saves about 9K.
-config SERIAL_8250_PNP
- tristate "8250/16550 PNP device support" if EXPERT
- depends on SERIAL_8250 && PNP
- default SERIAL_8250
- help
- This builds standard PNP serial support. You may be able to
- disable this feature if you only need legacy serial support.
-
config SERIAL_8250_HP300
tristate
depends on SERIAL_8250 && HP300
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index d7533c7..108fe7f 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -2,8 +2,9 @@
# Makefile for the 8250 serial device drivers.
#
-obj-$(CONFIG_SERIAL_8250) += 8250.o
-obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
+obj-$(CONFIG_SERIAL_8250) += 8250_core.o
+8250_core-y := 8250.o
+8250_core-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o
obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o
obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 26907cf..233fbaa 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -141,6 +141,25 @@ config SERIAL_ATMEL_TTYAT
Say Y if you have an external 8250/16C550 UART. If unsure, say N.
+config SERIAL_KGDB_NMI
+ bool "Serial console over KGDB NMI debugger port"
+ depends on KGDB_SERIAL_CONSOLE
+ help
+ This special driver allows you to temporary use NMI debugger port
+ as a normal console (assuming that the port is attached to KGDB).
+
+ Unlike KDB's disable_nmi command, with this driver you are always
+ able to go back to the debugger using KGDB escape sequence ($3#33).
+ This is because this console driver processes the input in NMI
+ context, and thus is able to intercept the magic sequence.
+
+ Note that since the console interprets input and uses polling
+ communication methods, for things like PPP you still must fully
+ detach debugger port from the KGDB NMI (i.e. disable_nmi), and
+ use raw console.
+
+ If unsure, say N.
+
config SERIAL_KS8695
bool "Micrel KS8695 (Centaur) serial port support"
depends on ARCH_KS8695
@@ -693,7 +712,7 @@ config SERIAL_SH_SCI_CONSOLE
config SERIAL_SH_SCI_DMA
bool "DMA support"
- depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL
+ depends on SERIAL_SH_SCI && SH_DMAE
config SERIAL_PNX8XXX
bool "Enable PNX8XXX SoCs' UART Support"
@@ -1304,7 +1323,7 @@ config SERIAL_ALTERA_UART_CONSOLE
config SERIAL_IFX6X60
tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)"
- depends on GPIOLIB && SPI && EXPERIMENTAL
+ depends on GPIOLIB && SPI
help
Support for the IFX6x60 modem devices on Intel MID platforms.
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index ce88667..4f694da 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
+obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index cede938..d7e1ede 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1284,11 +1284,40 @@ static void pl011_break_ctl(struct uart_port *port, int break_state)
}
#ifdef CONFIG_CONSOLE_POLL
+
+static void pl011_quiesce_irqs(struct uart_port *port)
+{
+ struct uart_amba_port *uap = (struct uart_amba_port *)port;
+ unsigned char __iomem *regs = uap->port.membase;
+
+ writew(readw(regs + UART011_MIS), regs + UART011_ICR);
+ /*
+ * There is no way to clear TXIM as this is "ready to transmit IRQ", so
+ * we simply mask it. start_tx() will unmask it.
+ *
+ * Note we can race with start_tx(), and if the race happens, the
+ * polling user might get another interrupt just after we clear it.
+ * But it should be OK and can happen even w/o the race, e.g.
+ * controller immediately got some new data and raised the IRQ.
+ *
+ * And whoever uses polling routines assumes that it manages the device
+ * (including tx queue), so we're also fine with start_tx()'s caller
+ * side.
+ */
+ writew(readw(regs + UART011_IMSC) & ~UART011_TXIM, regs + UART011_IMSC);
+}
+
static int pl011_get_poll_char(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
unsigned int status;
+ /*
+ * The caller might need IRQs lowered, e.g. if used with KDB NMI
+ * debugger.
+ */
+ pl011_quiesce_irqs(port);
+
status = readw(uap->port.membase + UART01x_FR);
if (status & UART01x_FR_RXFE)
return NO_POLL_CHAR;
@@ -1309,10 +1338,9 @@ static void pl011_put_poll_char(struct uart_port *port,
#endif /* CONFIG_CONSOLE_POLL */
-static int pl011_startup(struct uart_port *port)
+static int pl011_hwinit(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
- unsigned int cr;
int retval;
/* Optionaly enable pins to be muxed in and configured */
@@ -1337,6 +1365,37 @@ static int pl011_startup(struct uart_port *port)
UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
/*
+ * Save interrupts enable mask, and enable RX interrupts in case if
+ * the interrupt is used for NMI entry.
+ */
+ uap->im = readw(uap->port.membase + UART011_IMSC);
+ writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC);
+
+ if (uap->port.dev->platform_data) {
+ struct amba_pl011_data *plat;
+
+ plat = uap->port.dev->platform_data;
+ if (plat->init)
+ plat->init();
+ }
+ return 0;
+ out:
+ return retval;
+}
+
+static int pl011_startup(struct uart_port *port)
+{
+ struct uart_amba_port *uap = (struct uart_amba_port *)port;
+ unsigned int cr;
+ int retval;
+
+ retval = pl011_hwinit(port);
+ if (retval)
+ goto clk_dis;
+
+ writew(uap->im, uap->port.membase + UART011_IMSC);
+
+ /*
* Allocate the IRQ
*/
retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
@@ -1395,19 +1454,10 @@ static int pl011_startup(struct uart_port *port)
writew(uap->im, uap->port.membase + UART011_IMSC);
spin_unlock_irq(&uap->port.lock);
- if (uap->port.dev->platform_data) {
- struct amba_pl011_data *plat;
-
- plat = uap->port.dev->platform_data;
- if (plat->init)
- plat->init();
- }
-
return 0;
clk_dis:
clk_disable_unprepare(uap->clk);
- out:
return retval;
}
@@ -1595,13 +1645,26 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
old_cr &= ~ST_UART011_CR_OVSFACT;
}
+ /*
+ * Workaround for the ST Micro oversampling variants to
+ * increase the bitrate slightly, by lowering the divisor,
+ * to avoid delayed sampling of start bit at high speeds,
+ * else we see data corruption.
+ */
+ if (uap->vendor->oversampling) {
+ if ((baud >= 3000000) && (baud < 3250000) && (quot > 1))
+ quot -= 1;
+ else if ((baud > 3250000) && (quot > 2))
+ quot -= 2;
+ }
/* Set baud rate */
writew(quot & 0x3f, port->membase + UART011_FBRD);
writew(quot >> 6, port->membase + UART011_IBRD);
/*
* ----------v----------v----------v----------v-----
- * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
+ * NOTE: lcrh_tx and lcrh_rx MUST BE WRITTEN AFTER
+ * UART011_FBRD & UART011_IBRD.
* ----------^----------^----------^----------^-----
*/
writew(lcr_h, port->membase + uap->lcrh_rx);
@@ -1688,6 +1751,7 @@ static struct uart_ops amba_pl011_pops = {
.config_port = pl011_config_port,
.verify_port = pl011_verify_port,
#ifdef CONFIG_CONSOLE_POLL
+ .poll_init = pl011_hwinit,
.poll_get_char = pl011_get_poll_char,
.poll_put_char = pl011_put_poll_char,
#endif
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
index b418947..d0dd919 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
@@ -71,7 +71,7 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo);
/**************************************************************/
-#define HW_BUF_SPD_THRESHOLD 9600
+#define HW_BUF_SPD_THRESHOLD 2400
/*
* Check, if transmit buffers are processed
@@ -417,6 +417,7 @@ static int cpm_uart_startup(struct uart_port *port)
clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR);
clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX);
}
+ cpm_uart_initbd(pinfo);
cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
}
/* Install interrupt handler. */
@@ -500,16 +501,28 @@ static void cpm_uart_set_termios(struct uart_port *port,
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
smc_t __iomem *smcp = pinfo->smcp;
scc_t __iomem *sccp = pinfo->sccp;
+ int maxidl;
pr_debug("CPM uart[%d]:set_termios\n", port->line);
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
- if (baud <= HW_BUF_SPD_THRESHOLD ||
+ if (baud < HW_BUF_SPD_THRESHOLD ||
(pinfo->port.state && pinfo->port.state->port.tty->low_latency))
pinfo->rx_fifosize = 1;
else
pinfo->rx_fifosize = RX_BUF_SIZE;
+ /* MAXIDL is the timeout after which a receive buffer is closed
+ * when not full if no more characters are received.
+ * We calculate it from the baudrate so that the duration is
+ * always the same at standard rates: about 4ms.
+ */
+ maxidl = baud / 2400;
+ if (maxidl < 1)
+ maxidl = 1;
+ if (maxidl > 0x10)
+ maxidl = 0x10;
+
/* Character length programmed into the mode register is the
* sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
* 1 or 2 stop bits, minus 1.
@@ -610,6 +623,7 @@ static void cpm_uart_set_termios(struct uart_port *port,
* SMC/SCC receiver is disabled.
*/
out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize);
+ out_be16(&pinfo->smcup->smc_maxidl, maxidl);
/* Set the mode register. We want to keep a copy of the
* enables, because we want to put them back if they were
@@ -622,6 +636,7 @@ static void cpm_uart_set_termios(struct uart_port *port,
SMCMR_SM_UART | prev_mode);
} else {
out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
+ out_be16(&pinfo->sccup->scc_maxidl, maxidl);
out_be16(&sccp->scc_psmr, (sbits << 12) | scval);
}
@@ -798,7 +813,7 @@ static void cpm_uart_init_scc(struct uart_cpm_port *pinfo)
cpm_set_scc_fcr(sup);
out_be16(&sup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
- out_be16(&sup->scc_maxidl, pinfo->rx_fifosize);
+ out_be16(&sup->scc_maxidl, 0x10);
out_be16(&sup->scc_brkcr, 1);
out_be16(&sup->scc_parec, 0);
out_be16(&sup->scc_frmec, 0);
@@ -872,7 +887,7 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo)
/* Using idle character time requires some additional tuning. */
out_be16(&up->smc_mrblr, pinfo->rx_fifosize);
- out_be16(&up->smc_maxidl, pinfo->rx_fifosize);
+ out_be16(&up->smc_maxidl, 0x10);
out_be16(&up->smc_brklen, 0);
out_be16(&up->smc_brkec, 0);
out_be16(&up->smc_brkcr, 1);
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 2a093a4..5981912 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1373,8 +1373,7 @@ static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
val |= UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
- if (sport)
- uart_suspend_port(&imx_reg, &sport->port);
+ uart_suspend_port(&imx_reg, &sport->port);
return 0;
}
@@ -1389,8 +1388,7 @@ static int serial_imx_resume(struct platform_device *dev)
val &= ~UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
- if (sport)
- uart_resume_port(&imx_reg, &sport->port);
+ uart_resume_port(&imx_reg, &sport->port);
return 0;
}
@@ -1540,7 +1538,7 @@ static int serial_imx_probe(struct platform_device *pdev)
ret = uart_add_one_port(&imx_reg, &sport->port);
if (ret)
goto deinit;
- platform_set_drvdata(pdev, &sport->port);
+ platform_set_drvdata(pdev, sport);
return 0;
deinit:
diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c
index 7545fe1..5ab3c3b 100644
--- a/drivers/tty/serial/jsm/jsm_driver.c
+++ b/drivers/tty/serial/jsm/jsm_driver.c
@@ -54,7 +54,7 @@ static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev,
static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev);
static void jsm_io_resume(struct pci_dev *pdev);
-static struct pci_error_handlers jsm_err_handler = {
+static const struct pci_error_handlers jsm_err_handler = {
.error_detected = jsm_io_error_detected,
.slot_reset = jsm_io_slot_reset,
.resume = jsm_io_resume,
diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c
new file mode 100644
index 0000000..d185247
--- /dev/null
+++ b/drivers/tty/serial/kgdb_nmi.c
@@ -0,0 +1,402 @@
+/*
+ * KGDB NMI serial console
+ *
+ * Copyright 2010 Google, Inc.
+ * Arve Hjønnevåg <arve@android.com>
+ * Colin Cross <ccross@android.com>
+ * Copyright 2012 Linaro Ltd.
+ * Anton Vorontsov <anton.vorontsov@linaro.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/kernel.h>
+#include <linux/module.h>
+#include <linux/compiler.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/atomic.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
+#include <linux/kfifo.h>
+#include <linux/kgdb.h>
+#include <linux/kdb.h>
+
+static int kgdb_nmi_knock = 1;
+module_param_named(knock, kgdb_nmi_knock, int, 0600);
+MODULE_PARM_DESC(knock, "if set to 1 (default), the special '$3#33' command " \
+ "must be used to enter the debugger; when set to 0, " \
+ "hitting return key is enough to enter the debugger; " \
+ "when set to -1, the debugger is entered immediately " \
+ "upon NMI");
+
+static char *kgdb_nmi_magic = "$3#33";
+module_param_named(magic, kgdb_nmi_magic, charp, 0600);
+MODULE_PARM_DESC(magic, "magic sequence to enter NMI debugger (default $3#33)");
+
+static bool kgdb_nmi_tty_enabled;
+
+static void kgdb_nmi_console_write(struct console *co, const char *s, uint c)
+{
+ int i;
+
+ if (!kgdb_nmi_tty_enabled || atomic_read(&kgdb_active) >= 0)
+ return;
+
+ for (i = 0; i < c; i++)
+ dbg_io_ops->write_char(s[i]);
+}
+
+static struct tty_driver *kgdb_nmi_tty_driver;
+
+static struct tty_driver *kgdb_nmi_console_device(struct console *co, int *idx)
+{
+ *idx = co->index;
+ return kgdb_nmi_tty_driver;
+}
+
+static struct console kgdb_nmi_console = {
+ .name = "ttyNMI",
+ .write = kgdb_nmi_console_write,
+ .device = kgdb_nmi_console_device,
+ .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED,
+ .index = -1,
+};
+
+/*
+ * This is usually the maximum rate on debug ports. We make fifo large enough
+ * to make copy-pasting to the terminal usable.
+ */
+#define KGDB_NMI_BAUD 115200
+#define KGDB_NMI_FIFO_SIZE roundup_pow_of_two(KGDB_NMI_BAUD / 8 / HZ)
+
+struct kgdb_nmi_tty_priv {
+ struct tty_port port;
+ struct tasklet_struct tlet;
+ STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo;
+};
+
+static struct kgdb_nmi_tty_priv *kgdb_nmi_port_to_priv(struct tty_port *port)
+{
+ return container_of(port, struct kgdb_nmi_tty_priv, port);
+}
+
+/*
+ * Our debugging console is polled in a tasklet, so we'll check for input
+ * every tick. In HZ-less mode, we should program the next tick. We have
+ * to use the lowlevel stuff as no locks should be grabbed.
+ */
+#ifdef CONFIG_HIGH_RES_TIMERS
+static void kgdb_tty_poke(void)
+{
+ tick_program_event(ktime_get(), 0);
+}
+#else
+static inline void kgdb_tty_poke(void) {}
+#endif
+
+static struct tty_port *kgdb_nmi_port;
+
+static void kgdb_tty_recv(int ch)
+{
+ struct kgdb_nmi_tty_priv *priv;
+ char c = ch;
+
+ if (!kgdb_nmi_port || ch < 0)
+ return;
+ /*
+ * Can't use port->tty->driver_data as tty might be not there. Tasklet
+ * will check for tty and will get the ref, but here we don't have to
+ * do that, and actually, we can't: we're in NMI context, no locks are
+ * possible.
+ */
+ priv = kgdb_nmi_port_to_priv(kgdb_nmi_port);
+ kfifo_in(&priv->fifo, &c, 1);
+ kgdb_tty_poke();
+}
+
+static int kgdb_nmi_poll_one_knock(void)
+{
+ static int n;
+ int c = -1;
+ const char *magic = kgdb_nmi_magic;
+ size_t m = strlen(magic);
+ bool printch = 0;
+
+ c = dbg_io_ops->read_char();
+ if (c == NO_POLL_CHAR)
+ return c;
+
+ if (!kgdb_nmi_knock && (c == '\r' || c == '\n')) {
+ return 1;
+ } else if (c == magic[n]) {
+ n = (n + 1) % m;
+ if (!n)
+ return 1;
+ printch = 1;
+ } else {
+ n = 0;
+ }
+
+ if (kgdb_nmi_tty_enabled) {
+ kgdb_tty_recv(c);
+ return 0;
+ }
+
+ if (printch) {
+ kdb_printf("%c", c);
+ return 0;
+ }
+
+ kdb_printf("\r%s %s to enter the debugger> %*s",
+ kgdb_nmi_knock ? "Type" : "Hit",
+ kgdb_nmi_knock ? magic : "<return>", (int)m, "");
+ while (m--)
+ kdb_printf("\b");
+ return 0;
+}
+
+/**
+ * kgdb_nmi_poll_knock - Check if it is time to enter the debugger
+ *
+ * "Serial ports are often noisy, especially when muxed over another port (we
+ * often use serial over the headset connector). Noise on the async command
+ * line just causes characters that are ignored, on a command line that blocked
+ * execution noise would be catastrophic." -- Colin Cross
+ *
+ * So, this function implements KGDB/KDB knocking on the serial line: we won't
+ * enter the debugger until we receive a known magic phrase (which is actually
+ * "$3#33", known as "escape to KDB" command. There is also a relaxed variant
+ * of knocking, i.e. just pressing the return key is enough to enter the
+ * debugger. And if knocking is disabled, the function always returns 1.
+ */
+bool kgdb_nmi_poll_knock(void)
+{
+ if (kgdb_nmi_knock < 0)
+ return 1;
+
+ while (1) {
+ int ret;
+
+ ret = kgdb_nmi_poll_one_knock();
+ if (ret == NO_POLL_CHAR)
+ return 0;
+ else if (ret == 1)
+ break;
+ }
+ return 1;
+}
+
+/*
+ * The tasklet is cheap, it does not cause wakeups when reschedules itself,
+ * instead it waits for the next tick.
+ */
+static void kgdb_nmi_tty_receiver(unsigned long data)
+{
+ struct kgdb_nmi_tty_priv *priv = (void *)data;
+ struct tty_struct *tty;
+ char ch;
+
+ tasklet_schedule(&priv->tlet);
+
+ if (likely(!kgdb_nmi_tty_enabled || !kfifo_len(&priv->fifo)))
+ return;
+
+ /* Port is there, but tty might be hung up, check. */
+ tty = tty_port_tty_get(kgdb_nmi_port);
+ if (!tty)
+ return;
+
+ while (kfifo_out(&priv->fifo, &ch, 1))
+ tty_insert_flip_char(priv->port.tty, ch, TTY_NORMAL);
+ tty_flip_buffer_push(priv->port.tty);
+
+ tty_kref_put(tty);
+}
+
+static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ kgdb_nmi_port = port;
+ tasklet_schedule(&priv->tlet);
+ return 0;
+}
+
+static void kgdb_nmi_tty_shutdown(struct tty_port *port)
+{
+ struct kgdb_nmi_tty_priv *priv = port->tty->driver_data;
+
+ tasklet_kill(&priv->tlet);
+ kgdb_nmi_port = NULL;
+}
+
+static const struct tty_port_operations kgdb_nmi_tty_port_ops = {
+ .activate = kgdb_nmi_tty_activate,
+ .shutdown = kgdb_nmi_tty_shutdown,
+};
+
+static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ INIT_KFIFO(priv->fifo);
+ tasklet_init(&priv->tlet, kgdb_nmi_tty_receiver, (unsigned long)priv);
+ tty_port_init(&priv->port);
+ priv->port.ops = &kgdb_nmi_tty_port_ops;
+ tty->driver_data = priv;
+
+ ret = tty_port_install(&priv->port, drv, tty);
+ if (ret) {
+ pr_err("%s: can't install tty port: %d\n", __func__, ret);
+ goto err;
+ }
+ return 0;
+err:
+ kfree(priv);
+ return ret;
+}
+
+static void kgdb_nmi_tty_cleanup(struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ tty->driver_data = NULL;
+ kfree(priv);
+}
+
+static int kgdb_nmi_tty_open(struct tty_struct *tty, struct file *file)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ return tty_port_open(&priv->port, tty, file);
+}
+
+static void kgdb_nmi_tty_close(struct tty_struct *tty, struct file *file)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ tty_port_close(&priv->port, tty, file);
+}
+
+static void kgdb_nmi_tty_hangup(struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ tty_port_hangup(&priv->port);
+}
+
+static int kgdb_nmi_tty_write_room(struct tty_struct *tty)
+{
+ /* Actually, we can handle any amount as we use polled writes. */
+ return 2048;
+}
+
+static int kgdb_nmi_tty_write(struct tty_struct *tty, const unchar *buf, int c)
+{
+ int i;
+
+ for (i = 0; i < c; i++)
+ dbg_io_ops->write_char(buf[i]);
+ return c;
+}
+
+static const struct tty_operations kgdb_nmi_tty_ops = {
+ .open = kgdb_nmi_tty_open,
+ .close = kgdb_nmi_tty_close,
+ .install = kgdb_nmi_tty_install,
+ .cleanup = kgdb_nmi_tty_cleanup,
+ .hangup = kgdb_nmi_tty_hangup,
+ .write_room = kgdb_nmi_tty_write_room,
+ .write = kgdb_nmi_tty_write,
+};
+
+static int kgdb_nmi_enable_console(int argc, const char *argv[])
+{
+ kgdb_nmi_tty_enabled = !(argc == 1 && !strcmp(argv[1], "off"));
+ return 0;
+}
+
+int kgdb_register_nmi_console(void)
+{
+ int ret;
+
+ if (!arch_kgdb_ops.enable_nmi)
+ return 0;
+
+ kgdb_nmi_tty_driver = alloc_tty_driver(1);
+ if (!kgdb_nmi_tty_driver) {
+ pr_err("%s: cannot allocate tty\n", __func__);
+ return -ENOMEM;
+ }
+ kgdb_nmi_tty_driver->driver_name = "ttyNMI";
+ kgdb_nmi_tty_driver->name = "ttyNMI";
+ kgdb_nmi_tty_driver->num = 1;
+ kgdb_nmi_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ kgdb_nmi_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ kgdb_nmi_tty_driver->flags = TTY_DRIVER_REAL_RAW;
+ kgdb_nmi_tty_driver->init_termios = tty_std_termios;
+ tty_termios_encode_baud_rate(&kgdb_nmi_tty_driver->init_termios,
+ KGDB_NMI_BAUD, KGDB_NMI_BAUD);
+ tty_set_operations(kgdb_nmi_tty_driver, &kgdb_nmi_tty_ops);
+
+ ret = tty_register_driver(kgdb_nmi_tty_driver);
+ if (ret) {
+ pr_err("%s: can't register tty driver: %d\n", __func__, ret);
+ goto err_drv_reg;
+ }
+
+ ret = kdb_register("nmi_console", kgdb_nmi_enable_console, "[off]",
+ "switch to Linux NMI console", 0);
+ if (ret) {
+ pr_err("%s: can't register kdb command: %d\n", __func__, ret);
+ goto err_kdb_reg;
+ }
+
+ register_console(&kgdb_nmi_console);
+ arch_kgdb_ops.enable_nmi(1);
+
+ return 0;
+err_kdb_reg:
+ tty_unregister_driver(kgdb_nmi_tty_driver);
+err_drv_reg:
+ put_tty_driver(kgdb_nmi_tty_driver);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(kgdb_register_nmi_console);
+
+int kgdb_unregister_nmi_console(void)
+{
+ int ret;
+
+ if (!arch_kgdb_ops.enable_nmi)
+ return 0;
+ arch_kgdb_ops.enable_nmi(0);
+
+ kdb_unregister("nmi_console");
+
+ ret = unregister_console(&kgdb_nmi_console);
+ if (ret)
+ return ret;
+
+ ret = tty_unregister_driver(kgdb_nmi_tty_driver);
+ if (ret)
+ return ret;
+ put_tty_driver(kgdb_nmi_tty_driver);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kgdb_unregister_nmi_console);
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index 2b42a01..3f63d83 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -145,6 +145,8 @@ __setup("kgdboc=", kgdboc_option_setup);
static void cleanup_kgdboc(void)
{
+ if (kgdb_unregister_nmi_console())
+ return;
kgdboc_unregister_kbd();
if (configured == 1)
kgdb_unregister_io_module(&kgdboc_io_ops);
@@ -198,11 +200,18 @@ do_register:
if (err)
goto noconfig;
+ err = kgdb_register_nmi_console();
+ if (err)
+ goto nmi_con_failed;
+
configured = 1;
return 0;
+nmi_con_failed:
+ kgdb_unregister_io_module(&kgdboc_io_ops);
noconfig:
+ kgdboc_unregister_kbd();
config[0] = 0;
configured = 0;
cleanup_kgdboc();
diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index 46043c2..0f24486 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -827,14 +827,16 @@ static int __devexit max3100_remove(struct spi_device *spi)
/* find out the index for the chip we are removing */
for (i = 0; i < MAX_MAX3100; i++)
- if (max3100s[i] == s)
+ if (max3100s[i] == s) {
+ dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i);
+ uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port);
+ kfree(max3100s[i]);
+ max3100s[i] = NULL;
break;
+ }
- dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i);
- uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port);
- kfree(max3100s[i]);
- max3100s[i] = NULL;
-
+ WARN_ON(i == MAX_MAX3100);
+
/* check if this is the last chip we have */
for (i = 0; i < MAX_MAX3100; i++)
if (max3100s[i]) {
diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index f19d04e..8cf5770 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -598,7 +598,7 @@ static struct psc_ops mpc512x_psc_ops = {
};
#endif
-static struct psc_ops *psc_ops;
+static const struct psc_ops *psc_ops;
/* ======================================================================== */
/* UART operations */
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 6898413..6db3baa 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -781,6 +781,7 @@ out_free_irq:
auart_port[pdev->id] = NULL;
free_irq(s->irq, s);
out_free_clk:
+ put_device(s->dev);
clk_put(s->clk);
out_free:
kfree(s);
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index f175385..ccc2f35 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -334,6 +334,10 @@ static unsigned int check_modem_status(struct uart_omap_port *up)
static void serial_omap_rlsi(struct uart_omap_port *up, unsigned int lsr)
{
unsigned int flag;
+ unsigned char ch = 0;
+
+ if (likely(lsr & UART_LSR_DR))
+ ch = serial_in(up, UART_RX);
up->port.icount.rx++;
flag = TTY_NORMAL;
@@ -1222,10 +1226,8 @@ static int serial_omap_suspend(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- if (up) {
- uart_suspend_port(&serial_omap_reg, &up->port);
- flush_work_sync(&up->qos_work);
- }
+ uart_suspend_port(&serial_omap_reg, &up->port);
+ flush_work_sync(&up->qos_work);
return 0;
}
@@ -1234,8 +1236,8 @@ static int serial_omap_resume(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- if (up)
- uart_resume_port(&serial_omap_reg, &up->port);
+ uart_resume_port(&serial_omap_reg, &up->port);
+
return 0;
}
#endif
@@ -1553,17 +1555,14 @@ static int serial_omap_runtime_suspend(struct device *dev)
static int serial_omap_runtime_resume(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- struct omap_uart_port_info *pdata = dev->platform_data;
- if (up && pdata) {
- u32 loss_cnt = serial_omap_get_context_loss_count(up);
+ u32 loss_cnt = serial_omap_get_context_loss_count(up);
- if (up->context_loss_cnt != loss_cnt)
- serial_omap_restore_context(up);
+ if (up->context_loss_cnt != loss_cnt)
+ serial_omap_restore_context(up);
- up->latency = up->calc_latency;
- schedule_work(&up->qos_work);
- }
+ up->latency = up->calc_latency;
+ schedule_work(&up->qos_work);
return 0;
}
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index bdaa06f..7f04717 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -876,11 +876,24 @@ s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
static struct console s3c24xx_serial_console;
+static int __init s3c24xx_serial_console_init(void)
+{
+ register_console(&s3c24xx_serial_console);
+ return 0;
+}
+console_initcall(s3c24xx_serial_console_init);
+
#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
#else
#define S3C24XX_SERIAL_CONSOLE NULL
#endif
+#ifdef CONFIG_CONSOLE_POLL
+static int s3c24xx_serial_get_poll_char(struct uart_port *port);
+static void s3c24xx_serial_put_poll_char(struct uart_port *port,
+ unsigned char c);
+#endif
+
static struct uart_ops s3c24xx_serial_ops = {
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
@@ -899,6 +912,10 @@ static struct uart_ops s3c24xx_serial_ops = {
.request_port = s3c24xx_serial_request_port,
.config_port = s3c24xx_serial_config_port,
.verify_port = s3c24xx_serial_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = s3c24xx_serial_get_poll_char,
+ .poll_put_char = s3c24xx_serial_put_poll_char,
+#endif
};
static struct uart_driver s3c24xx_uart_drv = {
@@ -1316,6 +1333,36 @@ s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
}
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static int s3c24xx_serial_get_poll_char(struct uart_port *port)
+{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+ unsigned int ufstat;
+
+ ufstat = rd_regl(port, S3C2410_UFSTAT);
+ if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
+ return NO_POLL_CHAR;
+
+ return rd_regb(port, S3C2410_URXH);
+}
+
+static void s3c24xx_serial_put_poll_char(struct uart_port *port,
+ unsigned char c)
+{
+ unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
+
+ while (!s3c24xx_serial_console_txrdy(port, ufcon))
+ cpu_relax();
+ wr_regb(cons_uart, S3C2410_UTXH, c);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
static void
s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
{
@@ -1684,7 +1731,7 @@ static int __init s3c24xx_serial_modinit(void)
ret = uart_register_driver(&s3c24xx_uart_drv);
if (ret < 0) {
pr_err("Failed to register Samsung UART driver\n");
- return -1;
+ return ret;
}
return platform_driver_register(&samsung_serial_driver);
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index 05d767c..b7086d0 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -213,7 +213,7 @@ const struct baud_table baud_std[] = {
{ 0, 0, 0, 0 }
};
-static void sccnxp_set_baud(struct uart_port *port, int baud)
+static int sccnxp_set_baud(struct uart_port *port, int baud)
{
struct sccnxp_port *s = dev_get_drvdata(port->dev);
int div_std, tmp_baud, bestbaud = baud, besterr = -1;
@@ -244,8 +244,11 @@ static void sccnxp_set_baud(struct uart_port *port, int baud)
sccnxp_port_write(port, SCCNXP_ACR_REG, acr | ACR_TIMER_MODE);
sccnxp_port_write(port, SCCNXP_CSR_REG, (csr << 4) | csr);
- dev_dbg(port->dev, "Baudrate desired: %i, calculated: %i\n",
- baud, bestbaud);
+ if (baud != bestbaud)
+ dev_dbg(port->dev, "Baudrate desired: %i, calculated: %i\n",
+ baud, bestbaud);
+
+ return bestbaud;
}
static void sccnxp_enable_irq(struct uart_port *port, int mask)
@@ -528,7 +531,6 @@ static void sccnxp_set_termios(struct uart_port *port,
/* Mask termios capabilities we don't support */
termios->c_cflag &= ~CMSPAR;
- termios->c_iflag &= ~(IXON | IXOFF | IXANY);
/* Disable RX & TX, reset break condition, status and FIFOs */
sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET |
@@ -548,8 +550,8 @@ static void sccnxp_set_termios(struct uart_port *port,
case CS7:
mr1 = MR1_BITS_7;
break;
- default:
case CS8:
+ default:
mr1 = MR1_BITS_8;
break;
}
@@ -587,11 +589,14 @@ static void sccnxp_set_termios(struct uart_port *port,
baud = uart_get_baud_rate(port, termios, old, 50,
(s->flags & SCCNXP_HAVE_MR0) ?
230400 : 38400);
- sccnxp_set_baud(port, baud);
+ baud = sccnxp_set_baud(port, baud);
/* Update timeout according to new baud rate */
uart_update_timeout(port, termios->c_cflag, baud);
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+
/* Enable RX & TX */
sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 046279c..0fcfd98 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2129,6 +2129,7 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
int bits = 8;
int parity = 'n';
int flow = 'n';
+ int ret;
if (!state || !state->uart_port)
return -1;
@@ -2137,6 +2138,22 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
if (!(port->ops->poll_get_char && port->ops->poll_put_char))
return -1;
+ if (port->ops->poll_init) {
+ struct tty_port *tport = &state->port;
+
+ ret = 0;
+ mutex_lock(&tport->mutex);
+ /*
+ * We don't set ASYNCB_INITIALIZED as we only initialized the
+ * hw, e.g. state->xmit is still uninitialized.
+ */
+ if (!test_bit(ASYNCB_INITIALIZED, &tport->flags))
+ ret = port->ops->poll_init(port);
+ mutex_unlock(&tport->mutex);
+ if (ret)
+ return ret;
+ }
+
if (options) {
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(port, NULL, baud, parity, bits, flow);
@@ -2501,9 +2518,12 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
{
struct uart_state *state = uport->state;
struct tty_port *port = &state->port;
- struct tty_ldisc *ld = tty_ldisc_ref(port->tty);
+ struct tty_ldisc *ld = NULL;
struct pps_event_time ts;
+ struct tty_struct *tty = port->tty;
+ if (tty)
+ ld = tty_ldisc_ref(tty);
if (ld && ld->ops->dcd_change)
pps_get_ts(&ts);
@@ -2516,12 +2536,12 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
if (port->flags & ASYNC_CHECK_CD) {
if (status)
wake_up_interruptible(&port->open_wait);
- else if (port->tty)
- tty_hangup(port->tty);
+ else if (tty)
+ tty_hangup(tty);
}
if (ld && ld->ops->dcd_change)
- ld->ops->dcd_change(port->tty, status, &ts);
+ ld->ops->dcd_change(tty, status, &ts);
if (ld)
tty_ldisc_deref(ld);
}
diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c
index 7c13639..9bd004f 100644
--- a/drivers/tty/serial/serial_ks8695.c
+++ b/drivers/tty/serial/serial_ks8695.c
@@ -548,8 +548,8 @@ static struct uart_ops ks8695uart_pops = {
static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = {
{
- .membase = (void *) KS8695_UART_VA,
- .mapbase = KS8695_UART_VA,
+ .membase = KS8695_UART_VA,
+ .mapbase = KS8695_UART_PA,
.iotype = SERIAL_IO_MEM,
.irq = KS8695_IRQ_UART_TX,
.uartclk = KS8695_CLOCK_RATE * 16,
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 2be006f..205d4cf 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -34,6 +34,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
/*
* UART Register offsets
@@ -76,6 +77,8 @@
#define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT)
#define TX_FIFO_INTS (TXFAE | TXFE | TXUDR)
+#define VT8500_MAX_PORTS 6
+
struct vt8500_port {
struct uart_port uart;
char name[16];
@@ -83,6 +86,13 @@ struct vt8500_port {
unsigned int ier;
};
+/*
+ * we use this variable to keep track of which ports
+ * have been allocated as we can't use pdev->id in
+ * devicetree
+ */
+static unsigned long vt8500_ports_in_use;
+
static inline void vt8500_write(struct uart_port *port, unsigned int val,
unsigned int off)
{
@@ -431,7 +441,7 @@ static int vt8500_verify_port(struct uart_port *port,
return 0;
}
-static struct vt8500_port *vt8500_uart_ports[4];
+static struct vt8500_port *vt8500_uart_ports[VT8500_MAX_PORTS];
static struct uart_driver vt8500_uart_driver;
#ifdef CONFIG_SERIAL_VT8500_CONSOLE
@@ -548,7 +558,9 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
{
struct vt8500_port *vt8500_port;
struct resource *mmres, *irqres;
+ struct device_node *np = pdev->dev.of_node;
int ret;
+ int port;
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -559,16 +571,46 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
if (!vt8500_port)
return -ENOMEM;
+ if (np)
+ port = of_alias_get_id(np, "serial");
+ if (port > VT8500_MAX_PORTS)
+ port = -1;
+ else
+ port = -1;
+
+ if (port < 0) {
+ /* calculate the port id */
+ port = find_first_zero_bit(&vt8500_ports_in_use,
+ sizeof(vt8500_ports_in_use));
+ }
+
+ if (port > VT8500_MAX_PORTS)
+ return -ENODEV;
+
+ /* reserve the port id */
+ if (test_and_set_bit(port, &vt8500_ports_in_use)) {
+ /* port already in use - shouldn't really happen */
+ return -EBUSY;
+ }
+
vt8500_port->uart.type = PORT_VT8500;
vt8500_port->uart.iotype = UPIO_MEM;
vt8500_port->uart.mapbase = mmres->start;
vt8500_port->uart.irq = irqres->start;
vt8500_port->uart.fifosize = 16;
vt8500_port->uart.ops = &vt8500_uart_pops;
- vt8500_port->uart.line = pdev->id;
+ vt8500_port->uart.line = port;
vt8500_port->uart.dev = &pdev->dev;
vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
- vt8500_port->uart.uartclk = 24000000;
+
+ vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
+ if (vt8500_port->clk) {
+ vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);
+ } else {
+ /* use the default of 24Mhz if not specified and warn */
+ pr_warn("%s: serial clock source not specified\n", __func__);
+ vt8500_port->uart.uartclk = 24000000;
+ }
snprintf(vt8500_port->name, sizeof(vt8500_port->name),
"VT8500 UART%d", pdev->id);
@@ -579,7 +621,7 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
goto err;
}
- vt8500_uart_ports[pdev->id] = vt8500_port;
+ vt8500_uart_ports[port] = vt8500_port;
uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);
@@ -603,12 +645,18 @@ static int __devexit vt8500_serial_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "via,vt8500-uart", },
+ {}
+};
+
static struct platform_driver vt8500_platform_driver = {
.probe = vt8500_serial_probe,
.remove = __devexit_p(vt8500_serial_remove),
.driver = {
.name = "vt8500_serial",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(wmt_dt_ids),
},
};
@@ -642,4 +690,4 @@ module_exit(vt8500_serial_exit);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
MODULE_DESCRIPTION("Driver for vt8500 serial device");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");