diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-08 06:52:11 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-08 06:52:11 -0400 |
commit | 683a52a10148e929fb4844f9237f059a47c0b01b (patch) | |
tree | a64af13ef46a08ff12d1974725cd70d768750fb6 /drivers/tty | |
parent | b9af5643fdc5365a23f9752a1181fef374425217 (diff) | |
parent | 69784fa539efa477f39b5a849c27d5e630e0db27 (diff) | |
download | op-kernel-dev-683a52a10148e929fb4844f9237f059a47c0b01b.zip op-kernel-dev-683a52a10148e929fb4844f9237f059a47c0b01b.tar.gz |
Merge tag 'tty-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial driver updates from Greg KH:
"Here's the big tty/serial driver patchset for 3.18-rc1.
Lots of little things in here, some good work from Peter Hurley on the
tty core, and in lots of drivers. There are also lots of other driver
updates in here as well, full details in the changelogs.
All have been in the linux-next tree for a while"
* tag 'tty-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (99 commits)
Revert "serial/core: Initialize the console pm state"
tty: serial: 8250: use 32bit variable for rpm_tx_active
tty: serial: msm: Add earlycon support
serial/core: Initialize the console pm state
serial: asc: Conditionally use readl_relaxed (COMPILE_TEST)
serial: of-serial: add PM suspend/resume support
m68k: AMIGA_BUILTIN_SERIAL should depend on TTY
asm/uapi: Add definition of TIOC[SG]RS485
tty/metag_da: Add console_poll module parameter
serial: 8250_pci: remove rts_n override from Baytrail quirk
serial: cadence: Add generic earlycon support
serial: imx: change the wait even to interruptiable
serial: imx: terminate the RX DMA when the UART is suspending
serial: imx: fix throttle/unthrottle callbacks for hardware assisted flow control
serial: 8250: Add Quark X1000 to 8250_pci.c
tty: omap-serial: pull out calculation from baud_is_mode16
tty: omap-serial: fix division by zero
xen_hvc: no reason to write the type key on xenstore
tty: serial: 8250_core: remove UART_IER_RDI in serial8250_stop_rx()
tty: serial: 8250_core: use the ->line argument as a hint in serial8250_find_match_or_unused()
...
Diffstat (limited to 'drivers/tty')
45 files changed, 2183 insertions, 488 deletions
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 2dc2831..2967f03 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -402,9 +402,6 @@ static int xencons_connect_backend(struct xenbus_device *dev, evtchn); if (ret) goto error_xenbus; - ret = xenbus_printf(xbt, dev->nodename, "type", "ioemu"); - if (ret) - goto error_xenbus; ret = xenbus_transaction_end(xbt, 0); if (ret) { if (ret == -EAGAIN) diff --git a/drivers/tty/metag_da.c b/drivers/tty/metag_da.c index 7332e2c..3774600 100644 --- a/drivers/tty/metag_da.c +++ b/drivers/tty/metag_da.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/kthread.h> +#include <linux/moduleparam.h> #include <linux/mutex.h> #include <linux/sched.h> #include <linux/serial.h> @@ -70,6 +71,15 @@ static struct tty_driver *channel_driver; static struct timer_list put_timer; static struct task_struct *dashtty_thread; +/* + * The console_poll parameter determines whether the console channel should be + * polled for input. + * By default the console channel isn't polled at all, in order to avoid the + * overhead, but that means it isn't possible to have a login on /dev/console. + */ +static bool console_poll; +module_param(console_poll, bool, S_IRUGO); + #define RX_BUF_SIZE 1024 enum { @@ -353,7 +363,7 @@ static int dashtty_port_activate(struct tty_port *port, struct tty_struct *tty) * possible to have a login on /dev/console. * */ - if (dport != &dashtty_ports[CONSOLE_CHANNEL]) + if (console_poll || dport != &dashtty_ports[CONSOLE_CHANNEL]) if (atomic_inc_return(&num_channels_need_poll) == 1) add_poll_timer(&poll_timer); @@ -372,7 +382,7 @@ static void dashtty_port_shutdown(struct tty_port *port) unsigned int count; /* stop reading */ - if (dport != &dashtty_ports[CONSOLE_CHANNEL]) + if (console_poll || dport != &dashtty_ports[CONSOLE_CHANNEL]) if (atomic_dec_and_test(&num_channels_need_poll)) del_timer_sync(&poll_timer); diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 1deaca4..14c54e0 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -1096,7 +1096,7 @@ static int __init moxa_init(void) continue; } - printk(KERN_INFO "MOXA isa board found at 0x%.8lu and " + printk(KERN_INFO "MOXA isa board found at 0x%.8lx and " "ready (%u ports, firmware loaded)\n", baseaddr[i], brd->numPorts); diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index f44f1ba..89c4cee 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1166,7 +1166,7 @@ static void n_tty_receive_break(struct tty_struct *tty) } put_tty_queue('\0', ldata); if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible(&tty->read_wait); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); } /** @@ -1226,7 +1226,7 @@ static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) } else put_tty_queue(c, ldata); if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible(&tty->read_wait); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); } static void @@ -1378,7 +1378,7 @@ handle_newline: ldata->canon_head = ldata->read_head; kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible(&tty->read_wait); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); return 0; } } @@ -1679,7 +1679,7 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible(&tty->read_wait); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); } } diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index cd04293..74885af 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -523,7 +523,7 @@ static u32 write_mem32(void __iomem *mem_addr_start, const u32 *buf, } /* Setup pointers to different channels and also setup buffer sizes. */ -static void setup_memory(struct nozomi *dc) +static void nozomi_setup_memory(struct nozomi *dc) { void __iomem *offset = dc->base_addr + dc->config_table.dl_start; /* The length reported is including the length field of 4 bytes, @@ -671,7 +671,7 @@ static int nozomi_read_config_table(struct nozomi *dc) int i; DBG1("Second phase, configuring card"); - setup_memory(dc); + nozomi_setup_memory(dc); dc->port[PORT_MDM].toggle_ul = dc->config_table.toggle.mdm_ul; dc->port[PORT_MDM].toggle_dl = dc->config_table.toggle.mdm_dl; @@ -705,7 +705,7 @@ static int nozomi_read_config_table(struct nozomi *dc) dc->config_table.version); /* Here we should disable all I/O over F32. */ - setup_memory(dc); + nozomi_setup_memory(dc); /* * We should send ALL channel pair tokens back along diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 9bbdb1d..7c4447a 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -24,6 +24,7 @@ #include <linux/devpts_fs.h> #include <linux/slab.h> #include <linux/mutex.h> +#include <linux/poll.h> #ifdef CONFIG_UNIX98_PTYS @@ -313,6 +314,42 @@ done: } /** + * pty_start - start() handler + * pty_stop - stop() handler + * @tty: tty being flow-controlled + * + * Propagates the TIOCPKT status to the master pty. + * + * NB: only the master pty can be in packet mode so only the slave + * needs start()/stop() handlers + */ +static void pty_start(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (tty->link && tty->link->packet) { + tty->ctrl_status &= ~TIOCPKT_STOP; + tty->ctrl_status |= TIOCPKT_START; + wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); + } + spin_unlock_irqrestore(&tty->ctrl_lock, flags); +} + +static void pty_stop(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (tty->link && tty->link->packet) { + tty->ctrl_status &= ~TIOCPKT_START; + tty->ctrl_status |= TIOCPKT_STOP; + wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); + } + spin_unlock_irqrestore(&tty->ctrl_lock, flags); +} + +/** * pty_common_install - set up the pty pair * @driver: the pty driver * @tty: the tty being instantiated @@ -471,6 +508,8 @@ static const struct tty_operations slave_pty_ops_bsd = { .set_termios = pty_set_termios, .cleanup = pty_cleanup, .resize = pty_resize, + .start = pty_start, + .stop = pty_stop, .remove = pty_remove }; @@ -646,6 +685,8 @@ static const struct tty_operations pty_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, + .start = pty_start, + .stop = pty_stop, .shutdown = pty_unix98_shutdown, .cleanup = pty_cleanup, }; diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 1b08c91..1bcb4b2 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -72,6 +72,7 @@ struct serial8250_config { #define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ #define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */ #define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */ +#define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */ #define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ @@ -112,6 +113,8 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) up->dl_write(up, value); } +struct uart_8250_port *serial8250_get_port(int line); + #if defined(__alpha__) && !defined(CONFIG_PCI) /* * Digital did something really horribly wrong with the OUT1 and OUT2 diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index bd67294..ca5cfdc 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -37,6 +37,8 @@ #include <linux/nmi.h> #include <linux/mutex.h> #include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/pm_runtime.h> #ifdef CONFIG_SPARC #include <linux/sunserialcore.h> #endif @@ -539,6 +541,53 @@ void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) } EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); +static void serial8250_rpm_get(struct uart_8250_port *p) +{ + if (!(p->capabilities & UART_CAP_RPM)) + return; + pm_runtime_get_sync(p->port.dev); +} + +static void serial8250_rpm_put(struct uart_8250_port *p) +{ + if (!(p->capabilities & UART_CAP_RPM)) + return; + pm_runtime_mark_last_busy(p->port.dev); + pm_runtime_put_autosuspend(p->port.dev); +} + +/* + * This two wrapper ensure, that enable_runtime_pm_tx() can be called more than + * once and disable_runtime_pm_tx() will still disable RPM because the fifo is + * empty and the HW can idle again. + */ +static void serial8250_rpm_get_tx(struct uart_8250_port *p) +{ + unsigned char rpm_active; + + if (!(p->capabilities & UART_CAP_RPM)) + return; + + rpm_active = xchg(&p->rpm_tx_active, 1); + if (rpm_active) + return; + pm_runtime_get_sync(p->port.dev); +} + +static void serial8250_rpm_put_tx(struct uart_8250_port *p) +{ + unsigned char rpm_active; + + if (!(p->capabilities & UART_CAP_RPM)) + return; + + rpm_active = xchg(&p->rpm_tx_active, 0); + if (!rpm_active) + return; + pm_runtime_mark_last_busy(p->port.dev); + pm_runtime_put_autosuspend(p->port.dev); +} + /* * IER sleep support. UARTs which have EFRs need the "extended * capability" bit enabled. Note that on XR16C850s, we need to @@ -553,10 +602,11 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) * offset but the UART channel may only write to the corresponding * bit. */ + serial8250_rpm_get(p); if ((p->port.type == PORT_XR17V35X) || (p->port.type == PORT_XR17D15X)) { serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0); - return; + goto out; } if (p->capabilities & UART_CAP_SLEEP) { @@ -572,6 +622,8 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) serial_out(p, UART_LCR, 0); } } +out: + serial8250_rpm_put(p); } #ifdef CONFIG_SERIAL_8250_RSA @@ -1272,6 +1324,7 @@ static inline void __stop_tx(struct uart_8250_port *p) if (p->ier & UART_IER_THRI) { p->ier &= ~UART_IER_THRI; serial_out(p, UART_IER, p->ier); + serial8250_rpm_put_tx(p); } } @@ -1279,6 +1332,7 @@ static void serial8250_stop_tx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get(up); __stop_tx(up); /* @@ -1288,12 +1342,14 @@ static void serial8250_stop_tx(struct uart_port *port) up->acr |= UART_ACR_TXDIS; serial_icr_write(up, UART_ACR, up->acr); } + serial8250_rpm_put(up); } static void serial8250_start_tx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get_tx(up); if (up->dma && !serial8250_tx_dma(up)) { return; } else if (!(up->ier & UART_IER_THRI)) { @@ -1318,13 +1374,27 @@ static void serial8250_start_tx(struct uart_port *port) } } +static void serial8250_throttle(struct uart_port *port) +{ + port->throttle(port); +} + +static void serial8250_unthrottle(struct uart_port *port) +{ + port->unthrottle(port); +} + static void serial8250_stop_rx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); - up->ier &= ~UART_IER_RLSI; + serial8250_rpm_get(up); + + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); up->port.read_status_mask &= ~UART_LSR_DR; serial_port_out(port, UART_IER, up->ier); + + serial8250_rpm_put(up); } static void serial8250_enable_ms(struct uart_port *port) @@ -1336,7 +1406,10 @@ static void serial8250_enable_ms(struct uart_port *port) return; up->ier |= UART_IER_MSI; + + serial8250_rpm_get(up); serial_port_out(port, UART_IER, up->ier); + serial8250_rpm_put(up); } /* @@ -1458,11 +1531,17 @@ void serial8250_tx_chars(struct uart_8250_port *up) DEBUG_INTR("THRE..."); - if (uart_circ_empty(xmit)) + /* + * With RPM enabled, we have to wait once the FIFO is empty before the + * HW can go idle. So we get here once again with empty FIFO and disable + * the interrupt and RPM in __stop_tx() + */ + if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)) __stop_tx(up); } EXPORT_SYMBOL_GPL(serial8250_tx_chars); +/* Caller holds uart port lock */ unsigned int serial8250_modem_status(struct uart_8250_port *up) { struct uart_port *port = &up->port; @@ -1525,9 +1604,17 @@ EXPORT_SYMBOL_GPL(serial8250_handle_irq); static int serial8250_default_handle_irq(struct uart_port *port) { - unsigned int iir = serial_port_in(port, UART_IIR); + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir; + int ret; + + serial8250_rpm_get(up); - return serial8250_handle_irq(port, iir); + iir = serial_port_in(port, UART_IIR); + ret = serial8250_handle_irq(port, iir); + + serial8250_rpm_put(up); + return ret; } /* @@ -1784,11 +1871,15 @@ static unsigned int serial8250_tx_empty(struct uart_port *port) unsigned long flags; unsigned int lsr; + serial8250_rpm_get(up); + spin_lock_irqsave(&port->lock, flags); lsr = serial_port_in(port, UART_LSR); up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); + return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; } @@ -1798,7 +1889,9 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) unsigned int status; unsigned int ret; + serial8250_rpm_get(up); status = serial8250_modem_status(up); + serial8250_rpm_put(up); ret = 0; if (status & UART_MSR_DCD) @@ -1838,6 +1931,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; + serial8250_rpm_get(up); spin_lock_irqsave(&port->lock, flags); if (break_state == -1) up->lcr |= UART_LCR_SBC; @@ -1845,6 +1939,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) up->lcr &= ~UART_LCR_SBC; serial_port_out(port, UART_LCR, up->lcr); spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); } /* @@ -1889,12 +1984,23 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) static int serial8250_get_poll_char(struct uart_port *port) { - unsigned char lsr = serial_port_in(port, UART_LSR); + struct uart_8250_port *up = up_to_u8250p(port); + unsigned char lsr; + int status; + + serial8250_rpm_get(up); + + lsr = serial_port_in(port, UART_LSR); - if (!(lsr & UART_LSR_DR)) - return NO_POLL_CHAR; + if (!(lsr & UART_LSR_DR)) { + status = NO_POLL_CHAR; + goto out; + } - return serial_port_in(port, UART_RX); + status = serial_port_in(port, UART_RX); +out: + serial8250_rpm_put(up); + return status; } @@ -1904,6 +2010,7 @@ static void serial8250_put_poll_char(struct uart_port *port, unsigned int ier; struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get(up); /* * First save the IER then disable the interrupts */ @@ -1925,11 +2032,12 @@ static void serial8250_put_poll_char(struct uart_port *port, */ wait_for_xmitr(up, BOTH_EMPTY); serial_port_out(port, UART_IER, ier); + serial8250_rpm_put(up); } #endif /* CONFIG_CONSOLE_POLL */ -static int serial8250_startup(struct uart_port *port) +int serial8250_do_startup(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; @@ -1950,6 +2058,7 @@ static int serial8250_startup(struct uart_port *port) if (port->iotype != up->cur_iotype) set_io_from_upio(port); + serial8250_rpm_get(up); if (port->type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0; @@ -1970,7 +2079,6 @@ static int serial8250_startup(struct uart_port *port) */ enable_rsa(up); #endif - /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) @@ -1980,8 +2088,8 @@ static int serial8250_startup(struct uart_port *port) /* * Clear the interrupt registers. */ - serial_port_in(port, UART_LSR); - serial_port_in(port, UART_RX); + if (serial_port_in(port, UART_LSR) & UART_LSR_DR) + serial_port_in(port, UART_RX); serial_port_in(port, UART_IIR); serial_port_in(port, UART_MSR); @@ -1994,7 +2102,8 @@ static int serial8250_startup(struct uart_port *port) (serial_port_in(port, UART_LSR) == 0xff)) { printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n", serial_index(port)); - return -ENODEV; + retval = -ENODEV; + goto out; } /* @@ -2079,7 +2188,7 @@ static int serial8250_startup(struct uart_port *port) } else { retval = serial_link_irq_chain(up); if (retval) - return retval; + goto out; } /* @@ -2141,8 +2250,8 @@ dont_test_tx_en: * saved flags to avoid getting false values from polling * routines or the previous session. */ - serial_port_in(port, UART_LSR); - serial_port_in(port, UART_RX); + if (serial_port_in(port, UART_LSR) & UART_LSR_DR) + serial_port_in(port, UART_RX); serial_port_in(port, UART_IIR); serial_port_in(port, UART_MSR); up->lsr_saved_flags = 0; @@ -2177,15 +2286,26 @@ dont_test_tx_en: outb_p(0x80, icp); inb_p(icp); } + retval = 0; +out: + serial8250_rpm_put(up); + return retval; +} +EXPORT_SYMBOL_GPL(serial8250_do_startup); - return 0; +static int serial8250_startup(struct uart_port *port) +{ + if (port->startup) + return port->startup(port); + return serial8250_do_startup(port); } -static void serial8250_shutdown(struct uart_port *port) +void serial8250_do_shutdown(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; + serial8250_rpm_get(up); /* * Disable interrupts from this port */ @@ -2224,13 +2344,24 @@ static void serial8250_shutdown(struct uart_port *port) * Read data port to reset things, and then unlink from * the IRQ chain. */ - serial_port_in(port, UART_RX); + if (serial_port_in(port, UART_LSR) & UART_LSR_DR) + serial_port_in(port, UART_RX); + serial8250_rpm_put(up); del_timer_sync(&up->timer); up->timer.function = serial8250_timeout; if (port->irq) serial_unlink_irq_chain(up); } +EXPORT_SYMBOL_GPL(serial8250_do_shutdown); + +static void serial8250_shutdown(struct uart_port *port) +{ + if (port->shutdown) + port->shutdown(port); + else + serial8250_do_shutdown(port); +} static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) { @@ -2319,11 +2450,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, * the trigger, or the MCR RTS bit is cleared. In the case where * the remote UART is not using CTS auto flow control, we must * have sufficient FIFO entries for the latency of the remote - * UART to respond. IOW, at least 32 bytes of FIFO. Also enable - * AFE if hw flow control is supported + * UART to respond. IOW, at least 32 bytes of FIFO. */ - if ((up->capabilities & UART_CAP_AFE && (port->fifosize >= 32)) || - (port->flags & UPF_HARD_FLOW)) { + if (up->capabilities & UART_CAP_AFE && port->fifosize >= 32) { up->mcr &= ~UART_MCR_AFE; if (termios->c_cflag & CRTSCTS) up->mcr |= UART_MCR_AFE; @@ -2333,6 +2462,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, * Ok, we're now changing the port state. Do it with * interrupts disabled. */ + serial8250_rpm_get(up); spin_lock_irqsave(&port->lock, flags); /* @@ -2454,6 +2584,8 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, } serial8250_set_mctrl(port, port->mctrl); spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); + /* Don't rewrite B0 */ if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); @@ -2843,6 +2975,42 @@ serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) return 0; } +static int serial8250_ioctl(struct uart_port *port, unsigned int cmd, + unsigned long arg) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + int ret; + struct serial_rs485 rs485_config; + + if (!up->rs485_config) + return -ENOIOCTLCMD; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485_config, (void __user *)arg, + sizeof(rs485_config))) + return -EFAULT; + + ret = up->rs485_config(up, &rs485_config); + if (ret) + return ret; + + memcpy(&up->rs485, &rs485_config, sizeof(rs485_config)); + + return 0; + case TIOCGRS485: + if (copy_to_user((void __user *)arg, &up->rs485, + sizeof(up->rs485))) + return -EFAULT; + return 0; + default: + break; + } + + return -ENOIOCTLCMD; +} + static const char * serial8250_type(struct uart_port *port) { @@ -2859,6 +3027,8 @@ static struct uart_ops serial8250_pops = { .get_mctrl = serial8250_get_mctrl, .stop_tx = serial8250_stop_tx, .start_tx = serial8250_start_tx, + .throttle = serial8250_throttle, + .unthrottle = serial8250_unthrottle, .stop_rx = serial8250_stop_rx, .enable_ms = serial8250_enable_ms, .break_ctl = serial8250_break_ctl, @@ -2872,6 +3042,7 @@ static struct uart_ops serial8250_pops = { .request_port = serial8250_request_port, .config_port = serial8250_config_port, .verify_port = serial8250_verify_port, + .ioctl = serial8250_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = serial8250_get_poll_char, .poll_put_char = serial8250_put_poll_char, @@ -2880,6 +3051,24 @@ static struct uart_ops serial8250_pops = { static struct uart_8250_port serial8250_ports[UART_NR]; +/** + * serial8250_get_port - retrieve struct uart_8250_port + * @line: serial line number + * + * This function retrieves struct uart_8250_port for the specific line. + * This struct *must* *not* be used to perform a 8250 or serial core operation + * which is not accessible otherwise. Its only purpose is to make the struct + * accessible to the runtime-pm callbacks for context suspend/restore. + * The lock assumption made here is none because runtime-pm suspend/resume + * callbacks should not be invoked if there is any operation performed on the + * port. + */ +struct uart_8250_port *serial8250_get_port(int line) +{ + return &serial8250_ports[line]; +} +EXPORT_SYMBOL_GPL(serial8250_get_port); + static void (*serial8250_isa_config)(int port, struct uart_port *up, unsigned short *capabilities); @@ -3007,6 +3196,8 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) touch_nmi_watchdog(); + serial8250_rpm_get(up); + if (port->sysrq || oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags); else @@ -3043,6 +3234,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) if (locked) spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); } static int __init serial8250_console_setup(struct console *co, char *options) @@ -3324,6 +3516,11 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port * if (uart_match_port(&serial8250_ports[i].port, port)) return &serial8250_ports[i]; + /* try line number first if still available */ + i = port->line; + if (i < nr_uarts && serial8250_ports[i].port.type == PORT_UNKNOWN && + serial8250_ports[i].port.iobase == 0) + return &serial8250_ports[i]; /* * We didn't find a matching entry, so look for the first * free entry. We look for one which hasn't been previously @@ -3388,6 +3585,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->port.fifosize = up->port.fifosize; uart->tx_loadsz = up->tx_loadsz; uart->capabilities = up->capabilities; + uart->rs485_config = up->rs485_config; + uart->rs485 = up->rs485; + uart->port.throttle = up->port.throttle; + uart->port.unthrottle = up->port.unthrottle; /* Take tx_loadsz from fifosize if it wasn't set separately */ if (uart->port.fifosize && !uart->tx_loadsz) @@ -3410,6 +3611,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up) /* Possibly override set_termios call */ if (up->port.set_termios) uart->port.set_termios = up->port.set_termios; + if (up->port.startup) + uart->port.startup = up->port.startup; + if (up->port.shutdown) + uart->port.shutdown = up->port.shutdown; if (up->port.pm) uart->port.pm = up->port.pm; if (up->port.handle_break) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c new file mode 100644 index 0000000..1bb28cb --- /dev/null +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -0,0 +1,249 @@ +/* + * Probe for F81216A LPC to 4 UART + * + * Based on drivers/tty/serial/8250_pnp.c, by Russell King, et al + * + * Copyright (C) 2014 Ricardo Ribalda, Qtechnology A/S + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pnp.h> +#include <linux/kernel.h> +#include <linux/serial_core.h> +#include "8250.h" + +#define ADDR_PORT 0x4E +#define DATA_PORT 0x4F +#define ENTRY_KEY 0x77 +#define EXIT_KEY 0xAA +#define CHIP_ID1 0x20 +#define CHIP_ID1_VAL 0x02 +#define CHIP_ID2 0x21 +#define CHIP_ID2_VAL 0x16 +#define VENDOR_ID1 0x23 +#define VENDOR_ID1_VAL 0x19 +#define VENDOR_ID2 0x24 +#define VENDOR_ID2_VAL 0x34 +#define LDN 0x7 + +#define RS485 0xF0 +#define RTS_INVERT BIT(5) +#define RS485_URA BIT(4) +#define RXW4C_IRA BIT(3) +#define TXW4C_IRA BIT(2) + +#define DRIVER_NAME "8250_fintek" + +static int fintek_8250_enter_key(void){ + + if (!request_muxed_region(ADDR_PORT, 2, DRIVER_NAME)) + return -EBUSY; + + outb(ENTRY_KEY, ADDR_PORT); + outb(ENTRY_KEY, ADDR_PORT); + return 0; +} + +static void fintek_8250_exit_key(void){ + + outb(EXIT_KEY, ADDR_PORT); + release_region(ADDR_PORT, 2); +} + +static int fintek_8250_get_index(resource_size_t base_addr) +{ + resource_size_t base[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8}; + int i; + + for (i = 0; i < ARRAY_SIZE(base); i++) + if (base_addr == base[i]) + return i; + + return -ENODEV; +} + +static int fintek_8250_check_id(void) +{ + + outb(CHIP_ID1, ADDR_PORT); + if (inb(DATA_PORT) != CHIP_ID1_VAL) + return -ENODEV; + + outb(CHIP_ID2, ADDR_PORT); + if (inb(DATA_PORT) != CHIP_ID2_VAL) + return -ENODEV; + + outb(VENDOR_ID1, ADDR_PORT); + if (inb(DATA_PORT) != VENDOR_ID1_VAL) + return -ENODEV; + + outb(VENDOR_ID2, ADDR_PORT); + if (inb(DATA_PORT) != VENDOR_ID2_VAL) + return -ENODEV; + + return 0; +} + +static int fintek_8250_rs4850_config(struct uart_8250_port *uart, + struct serial_rs485 *rs485) +{ + uint8_t config = 0; + int index = fintek_8250_get_index(uart->port.iobase); + + if (index < 0) + return -EINVAL; + + if (rs485->flags & SER_RS485_ENABLED) + memset(rs485->padding, 0, sizeof(rs485->padding)); + else + memset(rs485, 0, sizeof(*rs485)); + + rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND; + + if (rs485->delay_rts_before_send) { + rs485->delay_rts_before_send = 1; + config |= TXW4C_IRA; + } + + if (rs485->delay_rts_after_send) { + rs485->delay_rts_after_send = 1; + config |= RXW4C_IRA; + } + + if ((!!(rs485->flags & SER_RS485_RTS_ON_SEND)) == + (!!(rs485->flags & SER_RS485_RTS_AFTER_SEND))) + rs485->flags &= SER_RS485_ENABLED; + else + config |= RS485_URA; + + if (rs485->flags & SER_RS485_RTS_ON_SEND) + config |= RTS_INVERT; + + if (fintek_8250_enter_key()) + return -EBUSY; + + outb(LDN, ADDR_PORT); + outb(index, DATA_PORT); + outb(RS485, ADDR_PORT); + outb(config, DATA_PORT); + fintek_8250_exit_key(); + + return 0; +} + +static int +fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + int line; + struct uart_8250_port uart; + int ret; + + if (!pnp_port_valid(dev, 0)) + return -ENODEV; + + if (fintek_8250_get_index(pnp_port_start(dev, 0)) < 0) + return -ENODEV; + + /* Enable configuration registers*/ + if (fintek_8250_enter_key()) + return -EBUSY; + + /*Check ID*/ + ret = fintek_8250_check_id(); + fintek_8250_exit_key(); + if (ret) + return ret; + + memset(&uart, 0, sizeof(uart)); + if (!pnp_irq_valid(dev, 0)) + return -ENODEV; + uart.port.irq = pnp_irq(dev, 0); + uart.port.iobase = pnp_port_start(dev, 0); + uart.port.iotype = UPIO_PORT; + uart.rs485_config = fintek_8250_rs4850_config; + + uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) + uart.port.flags |= UPF_SHARE_IRQ; + uart.port.uartclk = 1843200; + uart.port.dev = &dev->dev; + + line = serial8250_register_8250_port(&uart); + if (line < 0) + return -ENODEV; + + pnp_set_drvdata(dev, (void *)((long)line + 1)); + return 0; +} + +static void fintek_8250_remove(struct pnp_dev *dev) +{ + long line = (long)pnp_get_drvdata(dev); + + if (line) + serial8250_unregister_port(line - 1); +} + +#ifdef CONFIG_PM +static int fintek_8250_suspend(struct pnp_dev *dev, pm_message_t state) +{ + long line = (long)pnp_get_drvdata(dev); + + if (!line) + return -ENODEV; + serial8250_suspend_port(line - 1); + return 0; +} + +static int fintek_8250_resume(struct pnp_dev *dev) +{ + long line = (long)pnp_get_drvdata(dev); + + if (!line) + return -ENODEV; + serial8250_resume_port(line - 1); + return 0; +} +#else +#define fintek_8250_suspend NULL +#define fintek_8250_resume NULL +#endif /* CONFIG_PM */ + +static const struct pnp_device_id fintek_dev_table[] = { + /* Qtechnology Panel PC / IO1000 */ + { "PNP0501"}, + {} +}; + +MODULE_DEVICE_TABLE(pnp, fintek_dev_table); + +static struct pnp_driver fintek_8250_driver = { + .name = DRIVER_NAME, + .probe = fintek_8250_probe, + .remove = fintek_8250_remove, + .suspend = fintek_8250_suspend, + .resume = fintek_8250_resume, + .id_table = fintek_dev_table, +}; + +static int fintek_8250_init(void) +{ + return pnp_register_driver(&fintek_8250_driver); +} +module_init(fintek_8250_init); + +static void fintek_8250_exit(void) +{ + pnp_unregister_driver(&fintek_8250_driver); +} +module_exit(fintek_8250_exit); + +MODULE_DESCRIPTION("Fintek F812164 module"); +MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c index 5bdaf27..afffe4d 100644 --- a/drivers/tty/serial/8250/8250_hp300.c +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -21,7 +21,7 @@ #include "8250.h" #if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI) -#warning CONFIG_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? +#warning CONFIG_SERIAL_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? #endif #ifdef CONFIG_HPAPCI diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c new file mode 100644 index 0000000..8f37d57 --- /dev/null +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -0,0 +1,294 @@ +/* + * Mediatek 8250 driver. + * + * Copyright (c) 2014 MundoReader S.L. + * Author: Matthias Brugger <matthias.bgg@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/serial_8250.h> +#include <linux/serial_reg.h> + +#include "8250.h" + +#define UART_MTK_HIGHS 0x09 /* Highspeed register */ +#define UART_MTK_SAMPLE_COUNT 0x0a /* Sample count register */ +#define UART_MTK_SAMPLE_POINT 0x0b /* Sample point register */ +#define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */ + +struct mtk8250_data { + int line; + struct clk *uart_clk; +}; + +static void +mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, quot; + + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + serial8250_do_set_termios(port, termios, old); + + /* + * Mediatek UARTs use an extra highspeed register (UART_MTK_HIGHS) + * + * We need to recalcualte the quot register, as the claculation depends + * on the vaule in the highspeed register. + * + * Some baudrates are not supported by the chip, so we use the next + * lower rate supported and update termios c_flag. + * + * If highspeed register is set to 3, we need to specify sample count + * and sample point to increase accuracy. If not, we reset the + * registers to their default values. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); + + if (baud <= 115200) { + serial_port_out(port, UART_MTK_HIGHS, 0x0); + quot = uart_get_divisor(port, baud); + } else if (baud <= 576000) { + serial_port_out(port, UART_MTK_HIGHS, 0x2); + + /* Set to next lower baudrate supported */ + if ((baud == 500000) || (baud == 576000)) + baud = 460800; + quot = DIV_ROUND_CLOSEST(port->uartclk, 4 * baud); + } else { + serial_port_out(port, UART_MTK_HIGHS, 0x3); + + /* Set to highest baudrate supported */ + if (baud >= 1152000) + baud = 921600; + quot = DIV_ROUND_CLOSEST(port->uartclk, 256 * baud); + } + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&port->lock, flags); + + /* set DLAB we have cval saved in up->lcr from the call to the core */ + serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB); + serial_dl_write(up, quot); + + /* reset DLAB */ + serial_port_out(port, UART_LCR, up->lcr); + + if (baud > 460800) { + unsigned int tmp; + + tmp = DIV_ROUND_CLOSEST(port->uartclk, quot * baud); + serial_port_out(port, UART_MTK_SAMPLE_COUNT, tmp - 1); + serial_port_out(port, UART_MTK_SAMPLE_POINT, + (tmp - 2) >> 1); + } else { + serial_port_out(port, UART_MTK_SAMPLE_COUNT, 0x00); + serial_port_out(port, UART_MTK_SAMPLE_POINT, 0xff); + } + + spin_unlock_irqrestore(&port->lock, flags); + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static void +mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) +{ + if (!state) + pm_runtime_get_sync(port->dev); + + serial8250_do_pm(port, state, old); + + if (state) + pm_runtime_put_sync_suspend(port->dev); +} + +static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p, + struct mtk8250_data *data) +{ + int err; + struct device_node *np = pdev->dev.of_node; + + data->uart_clk = of_clk_get(np, 0); + if (IS_ERR(data->uart_clk)) { + dev_warn(&pdev->dev, "Can't get timer clock\n"); + return PTR_ERR(data->uart_clk); + } + + err = clk_prepare_enable(data->uart_clk); + if (err) { + dev_warn(&pdev->dev, "Can't prepare clock\n"); + clk_put(data->uart_clk); + return err; + } + p->uartclk = clk_get_rate(data->uart_clk); + + return 0; +} + +static int mtk8250_probe(struct platform_device *pdev) +{ + struct uart_8250_port uart = {}; + struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + struct mtk8250_data *data; + int err; + + if (!regs || !irq) { + dev_err(&pdev->dev, "no registers/irq defined\n"); + return -EINVAL; + } + + uart.port.membase = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); + if (!uart.port.membase) + return -ENOMEM; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (pdev->dev.of_node) { + err = mtk8250_probe_of(pdev, &uart.port, data); + if (err) + return err; + } else + return -ENODEV; + + spin_lock_init(&uart.port.lock); + uart.port.mapbase = regs->start; + uart.port.irq = irq->start; + uart.port.pm = mtk8250_do_pm; + uart.port.type = PORT_16550; + uart.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; + uart.port.dev = &pdev->dev; + uart.port.iotype = UPIO_MEM32; + uart.port.regshift = 2; + uart.port.private_data = data; + uart.port.set_termios = mtk8250_set_termios; + + /* Disable Rate Fix function */ + writel(0x0, uart.port.membase + + (MTK_UART_RATE_FIX << uart.port.regshift)); + + data->line = serial8250_register_8250_port(&uart); + if (data->line < 0) + return data->line; + + platform_set_drvdata(pdev, data); + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return 0; +} + +static int mtk8250_remove(struct platform_device *pdev) +{ + struct mtk8250_data *data = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&pdev->dev); + + serial8250_unregister_port(data->line); + if (!IS_ERR(data->uart_clk)) { + clk_disable_unprepare(data->uart_clk); + clk_put(data->uart_clk); + } + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk8250_suspend(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + + serial8250_suspend_port(data->line); + + return 0; +} + +static int mtk8250_resume(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + + serial8250_resume_port(data->line); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM_RUNTIME +static int mtk8250_runtime_suspend(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + + if (!IS_ERR(data->uart_clk)) + clk_disable_unprepare(data->uart_clk); + + return 0; +} + +static int mtk8250_runtime_resume(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + + if (!IS_ERR(data->uart_clk)) + clk_prepare_enable(data->uart_clk); + + return 0; +} +#endif + +static const struct dev_pm_ops mtk8250_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mtk8250_suspend, mtk8250_resume) + SET_RUNTIME_PM_OPS(mtk8250_runtime_suspend, mtk8250_runtime_resume, + NULL) +}; + +static const struct of_device_id mtk8250_of_match[] = { + { .compatible = "mediatek,mt6577-uart" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mtk8250_of_match); + +static struct platform_driver mtk8250_platform_driver = { + .driver = { + .name = "mt6577-uart", + .pm = &mtk8250_pm_ops, + .of_match_table = mtk8250_of_match, + }, + .probe = mtk8250_probe, + .remove = mtk8250_remove, +}; +module_platform_driver(mtk8250_platform_driver); + +MODULE_AUTHOR("Matthias Brugger"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Mediatek 8250 serial port driver"); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 61830b1..4f1cd29 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1355,9 +1355,6 @@ ce4100_serial_setup(struct serial_private *priv, #define BYT_PRV_CLK_N_VAL_SHIFT 16 #define BYT_PRV_CLK_UPDATE (1 << 31) -#define BYT_GENERAL_REG 0x808 -#define BYT_GENERAL_DIS_RTS_N_OVERRIDE (1 << 3) - #define BYT_TX_OVF_INT 0x820 #define BYT_TX_OVF_INT_MASK (1 << 1) @@ -1412,16 +1409,6 @@ byt_set_termios(struct uart_port *p, struct ktermios *termios, reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE; writel(reg, p->membase + BYT_PRV_CLK); - /* - * If auto-handshake mechanism is not enabled, - * disable rts_n override - */ - reg = readl(p->membase + BYT_GENERAL_REG); - reg &= ~BYT_GENERAL_DIS_RTS_N_OVERRIDE; - if (termios->c_cflag & CRTSCTS) - reg |= BYT_GENERAL_DIS_RTS_N_OVERRIDE; - writel(reg, p->membase + BYT_GENERAL_REG); - serial8250_do_set_termios(p, termios, old); } @@ -1788,6 +1775,7 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a #define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e +#define PCI_DEVICE_ID_INTEL_QRK_UART 0x0936 #define PCI_VENDOR_ID_SUNIX 0x1fd4 #define PCI_DEVICE_ID_SUNIX_1999 0x1999 @@ -1898,6 +1886,13 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = byt_serial_setup, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_QRK_UART, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, /* * ITE */ @@ -2740,6 +2735,7 @@ enum pci_board_num_t { pbn_ADDIDATA_PCIe_8_3906250, pbn_ce4100_1_115200, pbn_byt, + pbn_qrk, pbn_omegapci, pbn_NETMOS9900_2s_115200, pbn_brcm_trumanage, @@ -3490,6 +3486,12 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 0x80, .reg_shift = 2, }, + [pbn_qrk] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 2764800, + .reg_shift = 2, + }, [pbn_omegapci] = { .flags = FL_BASE0, .num_ports = 8, @@ -5192,6 +5194,12 @@ static struct pci_device_id serial_pci_tbl[] = { pbn_byt }, /* + * Intel Quark x1000 + */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_UART, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_qrk }, + /* * Cronyx Omega PCI */ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA, diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 349ee59..21eca79 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -298,3 +298,18 @@ config SERIAL_8250_RT288X If you have a Ralink RT288x/RT305x SoC based board and want to use the serial port, say Y to this option. The driver can handle up to 2 serial ports. If unsure, say N. + +config SERIAL_8250_FINTEK + tristate "Support for Fintek F81216A LPC to 4 UART" + depends on SERIAL_8250 && PNP + help + Selecting this option will add support for the Fintek F81216A + LPC to 4 UART. This device has some RS485 functionality not available + through the PNP driver. If unsure, say N. + +config SERIAL_8250_MT6577 + bool "Mediatek serial port support" + depends on SERIAL_8250 && ARCH_MEDIATEK + help + If you have a Mediatek based board and want to use the + serial port, say Y to this option. If unsure, say N. diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 36d68d0..5256b89 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -20,3 +20,5 @@ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o +obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o +obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 26cec64..649b784 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -200,10 +200,29 @@ config SERIAL_KS8695_CONSOLE receives all kernel messages and warnings and which allows logins in single user mode). +config SERIAL_MESON + tristate "Meson serial port support" + depends on ARCH_MESON + select SERIAL_CORE + help + This enables the driver for the on-chip UARTs of the Amlogic + MesonX processors. + +config SERIAL_MESON_CONSOLE + bool "Support for console on meson" + depends on SERIAL_MESON=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you wish to use a Amlogic MesonX UART as the + system console (the system console is the device which + receives all kernel messages and warnings and which allows + logins in single user mode) as /dev/ttyAMLx. + config SERIAL_CLPS711X tristate "CLPS711X serial port support" depends on ARCH_CLPS711X || COMPILE_TEST select SERIAL_CORE + select SERIAL_MCTRL_GPIO if GPIOLIB help This enables the driver for the on-chip UARTs of the Cirrus Logic EP711x/EP721x/EP731x processors. @@ -220,7 +239,7 @@ config SERIAL_CLPS711X_CONSOLE config SERIAL_SAMSUNG tristate "Samsung SoC serial support" - depends on PLAT_SAMSUNG + depends on PLAT_SAMSUNG || ARCH_EXYNOS select SERIAL_CORE help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, @@ -1051,6 +1070,7 @@ config SERIAL_MSM_CONSOLE bool "MSM serial console support" depends on SERIAL_MSM=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON config SERIAL_MSM_HS tristate "MSM UART High Speed: Serial Driver" @@ -1410,6 +1430,7 @@ config SERIAL_XILINX_PS_UART_CONSOLE bool "Cadence UART console support" depends on SERIAL_XILINX_PS_UART=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Enable a Cadence UART port to be the system console. diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 0080cc3..9a548ac 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o obj-$(CONFIG_SERIAL_ICOM) += icom.o obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o obj-$(CONFIG_SERIAL_MPSC) += mpsc.o +obj-$(CONFIG_SERIAL_MESON) += meson_uart.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index d22e3d9..932e019 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -462,7 +462,7 @@ static int altera_jtaguart_remove(struct platform_device *pdev) } #ifdef CONFIG_OF -static struct of_device_id altera_jtaguart_match[] = { +static const struct of_device_id altera_jtaguart_match[] = { { .compatible = "ALTR,juart-1.0", }, { .compatible = "altr,juart-1.0", }, {}, diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 6a24323..1cb2cdb 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -610,7 +610,7 @@ static int altera_uart_remove(struct platform_device *pdev) } #ifdef CONFIG_OF -static struct of_device_id altera_uart_match[] = { +static const struct of_device_id altera_uart_match[] = { { .compatible = "ALTR,uart-1.0", }, { .compatible = "altr,uart-1.0", }, {}, diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 8572f2a..02016fc 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -678,7 +678,8 @@ static void pl011_dma_flush_buffer(struct uart_port *port) __releases(&uap->port.lock) __acquires(&uap->port.lock) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); if (!uap->using_tx_dma) return; @@ -1163,7 +1164,8 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) static void pl011_stop_tx(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); uap->im &= ~UART011_TXIM; writew(uap->im, uap->port.membase + UART011_IMSC); @@ -1172,7 +1174,8 @@ static void pl011_stop_tx(struct uart_port *port) static void pl011_start_tx(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); if (!pl011_dma_tx_start(uap)) { uap->im |= UART011_TXIM; @@ -1182,7 +1185,8 @@ static void pl011_start_tx(struct uart_port *port) static void pl011_stop_rx(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM| UART011_PEIM|UART011_BEIM|UART011_OEIM); @@ -1193,7 +1197,8 @@ static void pl011_stop_rx(struct uart_port *port) static void pl011_enable_ms(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); uap->im |= UART011_RIMIM|UART011_CTSMIM|UART011_DCDMIM|UART011_DSRMIM; writew(uap->im, uap->port.membase + UART011_IMSC); @@ -1349,14 +1354,16 @@ static irqreturn_t pl011_int(int irq, void *dev_id) static unsigned int pl011_tx_empty(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int status = readw(uap->port.membase + UART01x_FR); return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; } static unsigned int pl011_get_mctrl(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int result = 0; unsigned int status = readw(uap->port.membase + UART01x_FR); @@ -1374,7 +1381,8 @@ static unsigned int pl011_get_mctrl(struct uart_port *port) static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int cr; cr = readw(uap->port.membase + UART011_CR); @@ -1402,7 +1410,8 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) static void pl011_break_ctl(struct uart_port *port, int break_state) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned long flags; unsigned int lcr_h; @@ -1420,7 +1429,8 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) static void pl011_quiesce_irqs(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned char __iomem *regs = uap->port.membase; writew(readw(regs + UART011_MIS), regs + UART011_ICR); @@ -1442,7 +1452,8 @@ static void pl011_quiesce_irqs(struct uart_port *port) static int pl011_get_poll_char(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int status; /* @@ -1461,7 +1472,8 @@ static int pl011_get_poll_char(struct uart_port *port) static void pl011_put_poll_char(struct uart_port *port, unsigned char ch) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) barrier(); @@ -1473,7 +1485,8 @@ static void pl011_put_poll_char(struct uart_port *port, static int pl011_hwinit(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); int retval; /* Optionaly enable pins to be muxed in and configured */ @@ -1526,7 +1539,8 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h) static int pl011_startup(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int cr, lcr_h, fbrd, ibrd; int retval; @@ -1618,7 +1632,8 @@ static void pl011_shutdown_channel(struct uart_amba_port *uap, static void pl011_shutdown(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int cr; /* @@ -1680,7 +1695,8 @@ static void pl011_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); unsigned int lcr_h, old_cr; unsigned long flags; unsigned int baud, quot, clkdiv; @@ -1822,7 +1838,8 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, static const char *pl011_type(struct uart_port *port) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); return uap->port.type == PORT_AMBA ? uap->type : NULL; } @@ -1900,7 +1917,8 @@ static struct uart_amba_port *amba_ports[UART_NR]; static void pl011_console_putchar(struct uart_port *port, int ch) { - struct uart_amba_port *uap = (struct uart_amba_port *)port; + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) barrier(); diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index 7810aa2..d62d8da 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -33,6 +33,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/serial_core.h> +#include <linux/gpio.h> #include <asm/bfin_sport.h> #include <asm/delay.h> diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index dec0fd7..7da9911 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -108,22 +108,23 @@ static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id) { struct bfin_serial_port *uart = dev_id; - unsigned int status = bfin_serial_get_mctrl(&uart->port); + struct uart_port *uport = &uart->port; + unsigned int status = bfin_serial_get_mctrl(uport); #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - struct tty_struct *tty = uart->port.state->port.tty; UART_CLEAR_SCTS(uart); - if (tty->hw_stopped) { + if (uport->hw_stopped) { if (status) { - tty->hw_stopped = 0; - uart_write_wakeup(&uart->port); + uport->hw_stopped = 0; + uart_write_wakeup(uport); } } else { if (!status) - tty->hw_stopped = 1; + uport->hw_stopped = 1; } +#else + uart_handle_cts_change(uport, status & TIOCM_CTS); #endif - uart_handle_cts_change(&uart->port, status & TIOCM_CTS); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index f5b4c3d..acfe317 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -33,6 +33,8 @@ #include <linux/mfd/syscon.h> #include <linux/mfd/syscon/clps711x.h> +#include "serial_mctrl_gpio.h" + #define UART_CLPS711X_DEVNAME "ttyCL" #define UART_CLPS711X_NR 2 #define UART_CLPS711X_MAJOR 204 @@ -62,7 +64,7 @@ struct clps711x_port { unsigned int tx_enabled; int rx_irq; struct regmap *syscon; - bool use_ms; + struct mctrl_gpios *gpios; }; static struct uart_driver clps711x_uart = { @@ -198,28 +200,17 @@ static unsigned int uart_clps711x_tx_empty(struct uart_port *port) static unsigned int uart_clps711x_get_mctrl(struct uart_port *port) { + unsigned int result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; struct clps711x_port *s = dev_get_drvdata(port->dev); - unsigned int result = 0; - - if (s->use_ms) { - u32 sysflg = 0; - - regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); - if (sysflg & SYSFLG1_DCD) - result |= TIOCM_CAR; - if (sysflg & SYSFLG1_DSR) - result |= TIOCM_DSR; - if (sysflg & SYSFLG1_CTS) - result |= TIOCM_CTS; - } else - result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; - return result; + return mctrl_gpio_get(s->gpios, &result); } static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl) { - /* Do nothing */ + struct clps711x_port *s = dev_get_drvdata(port->dev); + + mctrl_gpio_set(s->gpios, mctrl); } static void uart_clps711x_break_ctl(struct uart_port *port, int break_state) @@ -490,15 +481,10 @@ static int uart_clps711x_probe(struct platform_device *pdev) s->syscon = syscon_regmap_lookup_by_pdevname(syscon_name); if (IS_ERR(s->syscon)) return PTR_ERR(s->syscon); - - s->use_ms = !index; } else { s->syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); if (IS_ERR(s->syscon)) return PTR_ERR(s->syscon); - - if (!index) - s->use_ms = of_property_read_bool(np, "uart-use-ms"); } s->port.line = index; @@ -513,6 +499,8 @@ static int uart_clps711x_probe(struct platform_device *pdev) platform_set_drvdata(pdev, s); + s->gpios = mctrl_gpio_init(&pdev->dev, 0); + ret = uart_add_one_port(&clps711x_uart, &s->port); if (ret) return ret; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 044e86d..8f62a3c 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -80,6 +80,7 @@ #define URXD_FRMERR (1<<12) #define URXD_BRK (1<<11) #define URXD_PRERR (1<<10) +#define URXD_RX_DATA (0xFF<<0) #define UCR1_ADEN (1<<15) /* Auto detect interrupt */ #define UCR1_ADBR (1<<14) /* Auto detect baud rate */ #define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */ @@ -435,12 +436,14 @@ static void imx_stop_rx(struct uart_port *port) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - /* - * We are maybe in the SMP context, so if the DMA TX thread is running - * on other cpu, we have to wait for it to finish. - */ - if (sport->dma_is_enabled && sport->dma_is_rxing) - return; + if (sport->dma_is_enabled && sport->dma_is_rxing) { + if (sport->port.suspended) { + dmaengine_terminate_all(sport->dma_chan_rx); + sport->dma_is_rxing = 0; + } else { + return; + } + } temp = readl(sport->port.membase + UCR2); writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2); @@ -464,9 +467,19 @@ static inline void imx_transmit_buffer(struct imx_port *sport) { struct circ_buf *xmit = &sport->port.state->xmit; + if (sport->port.x_char) { + /* Send next char */ + writel(sport->port.x_char, sport->port.membase + URTX0); + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + imx_stop_tx(&sport->port); + return; + } + while (!uart_circ_empty(xmit) && - !(readl(sport->port.membase + uts_reg(sport)) - & UTS_TXFULL)) { + !(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) { /* send xmit->buf[xmit->tail] * out the port here */ writel(xmit->buf[xmit->tail], sport->port.membase + URTX0); @@ -567,9 +580,6 @@ static void imx_start_tx(struct uart_port *port) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - if (uart_circ_empty(&port->state->xmit)) - return; - if (USE_IRDA(sport)) { /* half duplex in IrDA mode; have to disable receive mode */ temp = readl(sport->port.membase + UCR4); @@ -604,7 +614,10 @@ static void imx_start_tx(struct uart_port *port) } if (sport->dma_is_enabled) { - imx_dma_tx(sport); + /* FIXME: port->x_char must be transmitted if != 0 */ + if (!uart_circ_empty(&port->state->xmit) && + !uart_tx_stopped(port)) + imx_dma_tx(sport); return; } @@ -632,27 +645,10 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id) static irqreturn_t imx_txint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - struct circ_buf *xmit = &sport->port.state->xmit; unsigned long flags; spin_lock_irqsave(&sport->port.lock, flags); - if (sport->port.x_char) { - /* Send next char */ - writel(sport->port.x_char, sport->port.membase + URTX0); - goto out; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - imx_stop_tx(&sport->port); - goto out; - } - imx_transmit_buffer(sport); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); - -out: spin_unlock_irqrestore(&sport->port.lock, flags); return IRQ_HANDLED; } @@ -823,11 +819,9 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; - temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS; - + temp = readl(sport->port.membase + UCR2) & ~(UCR2_CTS | UCR2_CTSC); if (mctrl & TIOCM_RTS) - if (!sport->dma_is_enabled) - temp |= UCR2_CTS; + temp |= UCR2_CTS | UCR2_CTSC; writel(temp, sport->port.membase + UCR2); @@ -1225,9 +1219,18 @@ static void imx_shutdown(struct uart_port *port) unsigned long flags; if (sport->dma_is_enabled) { + int ret; + /* We have to wait for the DMA to finish. */ - wait_event(sport->dma_wait, + ret = wait_event_interruptible(sport->dma_wait, !sport->dma_is_rxing && !sport->dma_is_txing); + if (ret != 0) { + sport->dma_is_rxing = 0; + sport->dma_is_txing = 0; + dmaengine_terminate_all(sport->dma_chan_tx); + dmaengine_terminate_all(sport->dma_chan_rx); + } + imx_stop_tx(port); imx_stop_rx(port); imx_disable_dma(sport); imx_uart_dma_exit(sport); @@ -1506,32 +1509,10 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser) #if defined(CONFIG_CONSOLE_POLL) static int imx_poll_get_char(struct uart_port *port) { - struct imx_port_ucrs old_ucr; - unsigned int status; - unsigned char c; - - /* save control registers */ - imx_port_ucrs_save(port, &old_ucr); - - /* disable interrupts */ - writel(UCR1_UARTEN, port->membase + UCR1); - writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI), - port->membase + UCR2); - writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN), - port->membase + UCR3); - - /* poll */ - do { - status = readl(port->membase + USR2); - } while (~status & USR2_RDR); - - /* read */ - c = readl(port->membase + URXD0); - - /* restore control registers */ - imx_port_ucrs_restore(port, &old_ucr); + if (!(readl(port->membase + USR2) & USR2_RDR)) + return NO_POLL_CHAR; - return c; + return readl(port->membase + URXD0) & URXD_RX_DATA; } static void imx_poll_put_char(struct uart_port *port, unsigned char c) diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 844d5e4..af70134 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -67,6 +67,16 @@ do { \ #define MAXPORTS 8 #define MAX_STOPS_SENT 5 +/* Board ids */ +#define PCI_DEVICE_ID_NEO_4 0x00B0 +#define PCI_DEVICE_ID_NEO_1_422 0x00CC +#define PCI_DEVICE_ID_NEO_1_422_485 0x00CD +#define PCI_DEVICE_ID_NEO_2_422_485 0x00CE +#define PCIE_DEVICE_ID_NEO_8 0x00F0 +#define PCIE_DEVICE_ID_NEO_4 0x00F1 +#define PCIE_DEVICE_ID_NEO_4RJ45 0x00F2 +#define PCIE_DEVICE_ID_NEO_8RJ45 0x00F3 + /* Board type definitions */ #define T_NEO 0000 diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index a47d882..d2885a7 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -93,12 +93,34 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* store the info for the board we've found */ brd->boardnum = adapter_count++; brd->pci_dev = pdev; - if (pdev->device == PCIE_DEVICE_ID_NEO_4_IBM) + + switch (pdev->device) { + + case PCI_DEVICE_ID_NEO_2DB9: + case PCI_DEVICE_ID_NEO_2DB9PRI: + case PCI_DEVICE_ID_NEO_2RJ45: + case PCI_DEVICE_ID_NEO_2RJ45PRI: + case PCI_DEVICE_ID_NEO_2_422_485: + brd->maxports = 2; + break; + + case PCI_DEVICE_ID_NEO_4: + case PCIE_DEVICE_ID_NEO_4: + case PCIE_DEVICE_ID_NEO_4RJ45: + case PCIE_DEVICE_ID_NEO_4_IBM: brd->maxports = 4; - else if (pdev->device == PCI_DEVICE_ID_DIGI_NEO_8) + break; + + case PCI_DEVICE_ID_DIGI_NEO_8: + case PCIE_DEVICE_ID_NEO_8: + case PCIE_DEVICE_ID_NEO_8RJ45: brd->maxports = 8; - else - brd->maxports = 2; + break; + + default: + brd->maxports = 1; + break; + } spin_lock_init(&brd->bd_intr_lock); @@ -209,6 +231,14 @@ static struct pci_device_id jsm_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 }, { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4_IBM), 0, 0, 4 }, { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_NEO_8), 0, 0, 5 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_4), 0, 0, 6 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_1_422), 0, 0, 7 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_1_422_485), 0, 0, 8 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2_422_485), 0, 0, 9 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_8), 0, 0, 10 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4), 0, 0, 11 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4RJ45), 0, 0, 12 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_8RJ45), 0, 0, 13 }, { 0, } }; MODULE_DEVICE_TABLE(pci, jsm_pci_tbl); diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index 6ec7501..129dc5b 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -46,6 +46,8 @@ static atomic_t kgdb_nmi_num_readers = ATOMIC_INIT(0); static int kgdb_nmi_console_setup(struct console *co, char *options) { + arch_kgdb_ops.enable_nmi(1); + /* The NMI console uses the dbg_io_ops to issue console messages. To * avoid duplicate messages during kdb sessions we must inform kdb's * I/O utilities that messages sent to the console will automatically @@ -77,7 +79,7 @@ static struct console kgdb_nmi_console = { .setup = kgdb_nmi_console_setup, .write = kgdb_nmi_console_write, .device = kgdb_nmi_console_device, - .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, + .flags = CON_PRINTBUFFER | CON_ANYTIME, .index = -1, }; @@ -354,7 +356,6 @@ int kgdb_register_nmi_console(void) } register_console(&kgdb_nmi_console); - arch_kgdb_ops.enable_nmi(1); return 0; err_drv_reg: diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c new file mode 100644 index 0000000..15c74975 --- /dev/null +++ b/drivers/tty/serial/meson_uart.c @@ -0,0 +1,634 @@ +/* + * Based on meson_uart.c, by AMLOGIC, INC. + * + * Copyright (C) 2014 Carlo Caione <carlo@caione.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +/* Register offsets */ +#define AML_UART_WFIFO 0x00 +#define AML_UART_RFIFO 0x04 +#define AML_UART_CONTROL 0x08 +#define AML_UART_STATUS 0x0c +#define AML_UART_MISC 0x10 +#define AML_UART_REG5 0x14 + +/* AML_UART_CONTROL bits */ +#define AML_UART_TX_EN BIT(12) +#define AML_UART_RX_EN BIT(13) +#define AML_UART_TX_RST BIT(22) +#define AML_UART_RX_RST BIT(23) +#define AML_UART_CLR_ERR BIT(24) +#define AML_UART_RX_INT_EN BIT(27) +#define AML_UART_TX_INT_EN BIT(28) +#define AML_UART_DATA_LEN_MASK (0x03 << 20) +#define AML_UART_DATA_LEN_8BIT (0x00 << 20) +#define AML_UART_DATA_LEN_7BIT (0x01 << 20) +#define AML_UART_DATA_LEN_6BIT (0x02 << 20) +#define AML_UART_DATA_LEN_5BIT (0x03 << 20) + +/* AML_UART_STATUS bits */ +#define AML_UART_PARITY_ERR BIT(16) +#define AML_UART_FRAME_ERR BIT(17) +#define AML_UART_TX_FIFO_WERR BIT(18) +#define AML_UART_RX_EMPTY BIT(20) +#define AML_UART_TX_FULL BIT(21) +#define AML_UART_TX_EMPTY BIT(22) +#define AML_UART_ERR (AML_UART_PARITY_ERR | \ + AML_UART_FRAME_ERR | \ + AML_UART_TX_FIFO_WERR) + +/* AML_UART_CONTROL bits */ +#define AML_UART_TWO_WIRE_EN BIT(15) +#define AML_UART_PARITY_TYPE BIT(18) +#define AML_UART_PARITY_EN BIT(19) +#define AML_UART_CLEAR_ERR BIT(24) +#define AML_UART_STOP_BIN_LEN_MASK (0x03 << 16) +#define AML_UART_STOP_BIN_1SB (0x00 << 16) +#define AML_UART_STOP_BIN_2SB (0x01 << 16) + +/* AML_UART_MISC bits */ +#define AML_UART_XMIT_IRQ(c) (((c) & 0xff) << 8) +#define AML_UART_RECV_IRQ(c) ((c) & 0xff) + +/* AML_UART_REG5 bits */ +#define AML_UART_BAUD_MASK 0x7fffff +#define AML_UART_BAUD_USE BIT(23) + +#define AML_UART_PORT_NUM 6 +#define AML_UART_DEV_NAME "ttyAML" + + +static struct uart_driver meson_uart_driver; + +static struct uart_port *meson_ports[AML_UART_PORT_NUM]; + +static void meson_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int meson_uart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS; +} + +static unsigned int meson_uart_tx_empty(struct uart_port *port) +{ + u32 val; + + val = readl(port->membase + AML_UART_STATUS); + return (val & AML_UART_TX_EMPTY) ? TIOCSER_TEMT : 0; +} + +static void meson_uart_stop_tx(struct uart_port *port) +{ + u32 val; + + val = readl(port->membase + AML_UART_CONTROL); + val &= ~AML_UART_TX_EN; + writel(val, port->membase + AML_UART_CONTROL); +} + +static void meson_uart_stop_rx(struct uart_port *port) +{ + u32 val; + + val = readl(port->membase + AML_UART_CONTROL); + val &= ~AML_UART_RX_EN; + writel(val, port->membase + AML_UART_CONTROL); +} + +static void meson_uart_shutdown(struct uart_port *port) +{ + unsigned long flags; + u32 val; + + free_irq(port->irq, port); + + spin_lock_irqsave(&port->lock, flags); + + val = readl(port->membase + AML_UART_CONTROL); + val &= ~(AML_UART_RX_EN | AML_UART_TX_EN); + val &= ~(AML_UART_RX_INT_EN | AML_UART_TX_INT_EN); + writel(val, port->membase + AML_UART_CONTROL); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void meson_uart_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + unsigned int ch; + + if (uart_tx_stopped(port)) { + meson_uart_stop_tx(port); + return; + } + + while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL)) { + if (port->x_char) { + writel(port->x_char, port->membase + AML_UART_WFIFO); + port->icount.tx++; + port->x_char = 0; + continue; + } + + if (uart_circ_empty(xmit)) + break; + + ch = xmit->buf[xmit->tail]; + writel(ch, port->membase + AML_UART_WFIFO); + xmit->tail = (xmit->tail+1) & (SERIAL_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static void meson_receive_chars(struct uart_port *port) +{ + struct tty_port *tport = &port->state->port; + char flag; + u32 status, ch, mode; + + do { + flag = TTY_NORMAL; + port->icount.rx++; + status = readl(port->membase + AML_UART_STATUS); + + if (status & AML_UART_ERR) { + if (status & AML_UART_TX_FIFO_WERR) + port->icount.overrun++; + else if (status & AML_UART_FRAME_ERR) + port->icount.frame++; + else if (status & AML_UART_PARITY_ERR) + port->icount.frame++; + + mode = readl(port->membase + AML_UART_CONTROL); + mode |= AML_UART_CLEAR_ERR; + writel(mode, port->membase + AML_UART_CONTROL); + + /* It doesn't clear to 0 automatically */ + mode &= ~AML_UART_CLEAR_ERR; + writel(mode, port->membase + AML_UART_CONTROL); + + status &= port->read_status_mask; + if (status & AML_UART_FRAME_ERR) + flag = TTY_FRAME; + else if (status & AML_UART_PARITY_ERR) + flag = TTY_PARITY; + } + + ch = readl(port->membase + AML_UART_RFIFO); + ch &= 0xff; + + if ((status & port->ignore_status_mask) == 0) + tty_insert_flip_char(tport, ch, flag); + + if (status & AML_UART_TX_FIFO_WERR) + tty_insert_flip_char(tport, 0, TTY_OVERRUN); + + } while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY)); + + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + spin_lock(&port->lock); +} + +static irqreturn_t meson_uart_interrupt(int irq, void *dev_id) +{ + struct uart_port *port = (struct uart_port *)dev_id; + + spin_lock(&port->lock); + + if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY)) + meson_receive_chars(port); + + if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL)) + meson_uart_start_tx(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static const char *meson_uart_type(struct uart_port *port) +{ + return (port->type == PORT_MESON) ? "meson_uart" : NULL; +} + +static int meson_uart_startup(struct uart_port *port) +{ + u32 val; + int ret = 0; + + val = readl(port->membase + AML_UART_CONTROL); + val |= (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR); + writel(val, port->membase + AML_UART_CONTROL); + + val &= ~(AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR); + writel(val, port->membase + AML_UART_CONTROL); + + val |= (AML_UART_RX_EN | AML_UART_TX_EN); + writel(val, port->membase + AML_UART_CONTROL); + + val |= (AML_UART_RX_INT_EN | AML_UART_TX_INT_EN); + writel(val, port->membase + AML_UART_CONTROL); + + val = (AML_UART_RECV_IRQ(1) | AML_UART_XMIT_IRQ(port->fifosize / 2)); + writel(val, port->membase + AML_UART_MISC); + + ret = request_irq(port->irq, meson_uart_interrupt, 0, + meson_uart_type(port), port); + + return ret; +} + +static void meson_uart_change_speed(struct uart_port *port, unsigned long baud) +{ + u32 val; + + while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_EMPTY)) + cpu_relax(); + + val = readl(port->membase + AML_UART_REG5); + val &= ~AML_UART_BAUD_MASK; + val = ((port->uartclk * 10 / (baud * 4) + 5) / 10) - 1; + val |= AML_UART_BAUD_USE; + writel(val, port->membase + AML_UART_REG5); +} + +static void meson_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int cflags, iflags, baud; + unsigned long flags; + u32 val; + + spin_lock_irqsave(&port->lock, flags); + + cflags = termios->c_cflag; + iflags = termios->c_iflag; + + val = readl(port->membase + AML_UART_CONTROL); + + val &= ~AML_UART_DATA_LEN_MASK; + switch (cflags & CSIZE) { + case CS8: + val |= AML_UART_DATA_LEN_8BIT; + break; + case CS7: + val |= AML_UART_DATA_LEN_7BIT; + break; + case CS6: + val |= AML_UART_DATA_LEN_6BIT; + break; + case CS5: + val |= AML_UART_DATA_LEN_5BIT; + break; + } + + if (cflags & PARENB) + val |= AML_UART_PARITY_EN; + else + val &= ~AML_UART_PARITY_EN; + + if (cflags & PARODD) + val |= AML_UART_PARITY_TYPE; + else + val &= ~AML_UART_PARITY_TYPE; + + val &= ~AML_UART_STOP_BIN_LEN_MASK; + if (cflags & CSTOPB) + val |= AML_UART_STOP_BIN_2SB; + else + val &= ~AML_UART_STOP_BIN_1SB; + + if (cflags & CRTSCTS) + val &= ~AML_UART_TWO_WIRE_EN; + else + val |= AML_UART_TWO_WIRE_EN; + + writel(val, port->membase + AML_UART_CONTROL); + + baud = uart_get_baud_rate(port, termios, old, 9600, 115200); + meson_uart_change_speed(port, baud); + + port->read_status_mask = AML_UART_TX_FIFO_WERR; + if (iflags & INPCK) + port->read_status_mask |= AML_UART_PARITY_ERR | + AML_UART_FRAME_ERR; + + port->ignore_status_mask = 0; + if (iflags & IGNPAR) + port->ignore_status_mask |= AML_UART_PARITY_ERR | + AML_UART_FRAME_ERR; + + uart_update_timeout(port, termios->c_cflag, baud); + spin_unlock_irqrestore(&port->lock, flags); +} + +static int meson_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int ret = 0; + + if (port->type != PORT_MESON) + ret = -EINVAL; + if (port->irq != ser->irq) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static void meson_uart_release_port(struct uart_port *port) +{ + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } +} + +static int meson_uart_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *res; + int size; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot obtain I/O memory region"); + return -ENODEV; + } + size = resource_size(res); + + if (!devm_request_mem_region(port->dev, port->mapbase, size, + dev_name(port->dev))) { + dev_err(port->dev, "Memory region busy\n"); + return -EBUSY; + } + + if (port->flags & UPF_IOREMAP) { + port->membase = devm_ioremap_nocache(port->dev, + port->mapbase, + size); + if (port->membase == NULL) + return -ENOMEM; + } + + return 0; +} + +static void meson_uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_MESON; + meson_uart_request_port(port); + } +} + +static struct uart_ops meson_uart_ops = { + .set_mctrl = meson_uart_set_mctrl, + .get_mctrl = meson_uart_get_mctrl, + .tx_empty = meson_uart_tx_empty, + .start_tx = meson_uart_start_tx, + .stop_tx = meson_uart_stop_tx, + .stop_rx = meson_uart_stop_rx, + .startup = meson_uart_startup, + .shutdown = meson_uart_shutdown, + .set_termios = meson_uart_set_termios, + .type = meson_uart_type, + .config_port = meson_uart_config_port, + .request_port = meson_uart_request_port, + .release_port = meson_uart_release_port, + .verify_port = meson_uart_verify_port, +}; + +#ifdef CONFIG_SERIAL_MESON_CONSOLE + +static void meson_console_putchar(struct uart_port *port, int ch) +{ + if (!port->membase) + return; + + while (readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL) + cpu_relax(); + writel(ch, port->membase + AML_UART_WFIFO); +} + +static void meson_serial_console_write(struct console *co, const char *s, + u_int count) +{ + struct uart_port *port; + unsigned long flags; + int locked; + + port = meson_ports[co->index]; + if (!port) + return; + + local_irq_save(flags); + if (port->sysrq) { + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&port->lock); + } else { + spin_lock(&port->lock); + locked = 1; + } + + uart_console_write(port, s, count, meson_console_putchar); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +static int meson_serial_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= AML_UART_PORT_NUM) + return -EINVAL; + + port = meson_ports[co->index]; + if (!port || !port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console meson_serial_console = { + .name = AML_UART_DEV_NAME, + .write = meson_serial_console_write, + .device = uart_console_device, + .setup = meson_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &meson_uart_driver, +}; + +static int __init meson_serial_console_init(void) +{ + register_console(&meson_serial_console); + return 0; +} +console_initcall(meson_serial_console_init); + +#define MESON_SERIAL_CONSOLE (&meson_serial_console) +#else +#define MESON_SERIAL_CONSOLE NULL +#endif + +static struct uart_driver meson_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "meson_uart", + .dev_name = AML_UART_DEV_NAME, + .nr = AML_UART_PORT_NUM, + .cons = MESON_SERIAL_CONSOLE, +}; + +static int meson_uart_probe(struct platform_device *pdev) +{ + struct resource *res_mem, *res_irq; + struct uart_port *port; + struct clk *clk; + int ret = 0; + + if (pdev->dev.of_node) + pdev->id = of_alias_get_id(pdev->dev.of_node, "serial"); + + if (pdev->id < 0 || pdev->id >= AML_UART_PORT_NUM) + return -EINVAL; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) + return -ENODEV; + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_irq) + return -ENODEV; + + if (meson_ports[pdev->id]) { + dev_err(&pdev->dev, "port %d already allocated\n", pdev->id); + return -EBUSY; + } + + port = devm_kzalloc(&pdev->dev, sizeof(struct uart_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + port->uartclk = clk_get_rate(clk); + port->iotype = UPIO_MEM; + port->mapbase = res_mem->start; + port->irq = res_irq->start; + port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_LOW_LATENCY; + port->dev = &pdev->dev; + port->line = pdev->id; + port->type = PORT_MESON; + port->x_char = 0; + port->ops = &meson_uart_ops; + port->fifosize = 64; + + meson_ports[pdev->id] = port; + platform_set_drvdata(pdev, port); + + ret = uart_add_one_port(&meson_uart_driver, port); + if (ret) + meson_ports[pdev->id] = NULL; + + return ret; +} + +static int meson_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + + port = platform_get_drvdata(pdev); + uart_remove_one_port(&meson_uart_driver, port); + meson_ports[pdev->id] = NULL; + + return 0; +} + + +static const struct of_device_id meson_uart_dt_match[] = { + { .compatible = "amlogic,meson-uart" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, meson_uart_dt_match); + +static struct platform_driver meson_uart_platform_driver = { + .probe = meson_uart_probe, + .remove = meson_uart_remove, + .driver = { + .owner = THIS_MODULE, + .name = "meson_uart", + .of_match_table = meson_uart_dt_match, + }, +}; + +static int __init meson_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&meson_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&meson_uart_platform_driver); + if (ret) + uart_unregister_driver(&meson_uart_driver); + + return ret; +} + +static void __exit meson_uart_exit(void) +{ + platform_driver_unregister(&meson_uart_platform_driver); + uart_unregister_driver(&meson_uart_driver); +} + +module_init(meson_uart_init); +module_exit(meson_uart_exit); + +MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); +MODULE_DESCRIPTION("Amlogic Meson serial port driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 97888f4..a5f4e36 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1087,22 +1087,6 @@ mpc52xx_uart_start_tx(struct uart_port *port) } static void -mpc52xx_uart_send_xchar(struct uart_port *port, char ch) -{ - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - - port->x_char = ch; - if (ch) { - /* Make sure tx interrupts are on */ - /* Truly necessary ??? They should be anyway */ - psc_ops->start_tx(port); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void mpc52xx_uart_stop_rx(struct uart_port *port) { /* port->lock taken by caller */ @@ -1361,7 +1345,6 @@ static struct uart_ops mpc52xx_uart_ops = { .get_mctrl = mpc52xx_uart_get_mctrl, .stop_tx = mpc52xx_uart_stop_tx, .start_tx = mpc52xx_uart_start_tx, - .send_xchar = mpc52xx_uart_send_xchar, .stop_rx = mpc52xx_uart_stop_rx, .enable_ms = mpc52xx_uart_enable_ms, .break_ctl = mpc52xx_uart_break_ctl, diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 0da0b54..4b6c783 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -190,11 +190,10 @@ static void handle_rx(struct uart_port *port) /* Mask conditions we're ignorning. */ sr &= port->read_status_mask; - if (sr & UART_SR_RX_BREAK) { + if (sr & UART_SR_RX_BREAK) flag = TTY_BREAK; - } else if (sr & UART_SR_PAR_FRAME_ERR) { + else if (sr & UART_SR_PAR_FRAME_ERR) flag = TTY_FRAME; - } if (!uart_handle_sysrq_char(port, c)) tty_insert_flip_char(tport, c, flag); @@ -315,7 +314,6 @@ static unsigned int msm_get_mctrl(struct uart_port *port) return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS; } - static void msm_reset(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); @@ -336,6 +334,7 @@ static void msm_reset(struct uart_port *port) static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned int mr; + mr = msm_read(port, UART_MR1); if (!(mctrl & TIOCM_RTS)) { @@ -431,7 +430,6 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) return baud; } - static void msm_init_clock(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); @@ -646,6 +644,7 @@ fail_release_port: static void msm_config_port(struct uart_port *port, int flags) { int ret; + if (flags & UART_CONFIG_TYPE) { port->type = PORT_MSM; ret = msm_request_port(port); @@ -678,22 +677,11 @@ static void msm_power(struct uart_port *port, unsigned int state, clk_disable_unprepare(msm_port->pclk); break; default: - printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state); + pr_err("msm_serial: Unknown PM state %d\n", state); } } #ifdef CONFIG_CONSOLE_POLL -static int msm_poll_init(struct uart_port *port) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - /* Enable single character mode on RX FIFO */ - if (msm_port->is_uartdm >= UARTDM_1P4) - msm_write(port, UARTDM_DMEN_RX_SC_ENABLE, UARTDM_DMEN); - - return 0; -} - static int msm_poll_get_char_single(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); @@ -701,11 +689,11 @@ static int msm_poll_get_char_single(struct uart_port *port) if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) return NO_POLL_CHAR; - else - return msm_read(port, rf_reg) & 0xff; + + return msm_read(port, rf_reg) & 0xff; } -static int msm_poll_get_char_dm_1p3(struct uart_port *port) +static int msm_poll_get_char_dm(struct uart_port *port) { int c; static u32 slop; @@ -729,6 +717,10 @@ static int msm_poll_get_char_dm_1p3(struct uart_port *port) slop = msm_read(port, UARTDM_RF); c = sp[0]; count--; + msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR); + msm_write(port, 0xFFFFFF, UARTDM_DMRX); + msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, + UART_CR); } else { c = NO_POLL_CHAR; } @@ -752,8 +744,8 @@ static int msm_poll_get_char(struct uart_port *port) imr = msm_read(port, UART_IMR); msm_write(port, 0, UART_IMR); - if (msm_port->is_uartdm == UARTDM_1P3) - c = msm_poll_get_char_dm_1p3(port); + if (msm_port->is_uartdm) + c = msm_poll_get_char_dm(port); else c = msm_poll_get_char_single(port); @@ -788,8 +780,6 @@ static void msm_poll_put_char(struct uart_port *port, unsigned char c) /* Enable interrupts */ msm_write(port, imr, UART_IMR); - - return; } #endif @@ -812,7 +802,6 @@ static struct uart_ops msm_uart_pops = { .verify_port = msm_verify_port, .pm = msm_power, #ifdef CONFIG_CONSOLE_POLL - .poll_init = msm_poll_init, .poll_get_char = msm_poll_get_char, .poll_put_char = msm_poll_put_char, #endif @@ -856,22 +845,15 @@ static inline struct uart_port *get_port_from_line(unsigned int line) } #ifdef CONFIG_SERIAL_MSM_CONSOLE -static void msm_console_write(struct console *co, const char *s, - unsigned int count) +static void __msm_console_write(struct uart_port *port, const char *s, + unsigned int count, bool is_uartdm) { int i; - struct uart_port *port; - struct msm_port *msm_port; int num_newlines = 0; bool replaced = false; void __iomem *tf; - BUG_ON(co->index < 0 || co->index >= UART_NR); - - port = get_port_from_line(co->index); - msm_port = UART_TO_MSM(port); - - if (msm_port->is_uartdm) + if (is_uartdm) tf = port->membase + UARTDM_TF; else tf = port->membase + UART_TF; @@ -883,7 +865,7 @@ static void msm_console_write(struct console *co, const char *s, count += num_newlines; spin_lock(&port->lock); - if (msm_port->is_uartdm) + if (is_uartdm) reset_dm_count(port, count); i = 0; @@ -892,7 +874,7 @@ static void msm_console_write(struct console *co, const char *s, unsigned int num_chars; char buf[4] = { 0 }; - if (msm_port->is_uartdm) + if (is_uartdm) num_chars = min(count - i, (unsigned int)sizeof(buf)); else num_chars = 1; @@ -921,6 +903,20 @@ static void msm_console_write(struct console *co, const char *s, spin_unlock(&port->lock); } +static void msm_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port; + struct msm_port *msm_port; + + BUG_ON(co->index < 0 || co->index >= UART_NR); + + port = get_port_from_line(co->index); + msm_port = UART_TO_MSM(port); + + __msm_console_write(port, s, count, msm_port->is_uartdm); +} + static int __init msm_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -958,11 +954,54 @@ static int __init msm_console_setup(struct console *co, char *options) msm_write(port, UART_CR_TX_ENABLE, UART_CR); } - printk(KERN_INFO "msm_serial: console setup on port #%d\n", port->line); + pr_info("msm_serial: console setup on port #%d\n", port->line); return uart_set_options(port, co, baud, parity, bits, flow); } +static void +msm_serial_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + __msm_console_write(&dev->port, s, n, false); +} + +static int __init +msm_serial_early_console_setup(struct earlycon_device *device, const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = msm_serial_early_write; + return 0; +} +EARLYCON_DECLARE(msm_serial, msm_serial_early_console_setup); +OF_EARLYCON_DECLARE(msm_serial, "qcom,msm-uart", + msm_serial_early_console_setup); + +static void +msm_serial_early_write_dm(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + __msm_console_write(&dev->port, s, n, true); +} + +static int __init +msm_serial_early_console_setup_dm(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = msm_serial_early_write_dm; + return 0; +} +EARLYCON_DECLARE(msm_serial_dm, msm_serial_early_console_setup_dm); +OF_EARLYCON_DECLARE(msm_serial_dm, "qcom,msm-uartdm", + msm_serial_early_console_setup_dm); + static struct uart_driver msm_uart_driver; static struct console msm_console = { @@ -1013,7 +1052,7 @@ static int msm_serial_probe(struct platform_device *pdev) if (unlikely(pdev->id < 0 || pdev->id >= UART_NR)) return -ENXIO; - printk(KERN_INFO "msm_serial: detected port #%d\n", pdev->id); + dev_info(&pdev->dev, "msm_serial: detected port #%d\n", pdev->id); port = get_port_from_line(pdev->id); port->dev = &pdev->dev; @@ -1038,8 +1077,7 @@ static int msm_serial_probe(struct platform_device *pdev) } port->uartclk = clk_get_rate(msm_port->clk); - printk(KERN_INFO "uartclk = %d\n", port->uartclk); - + dev_info(&pdev->dev, "uartclk = %d\n", port->uartclk); resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!resource)) @@ -1093,7 +1131,7 @@ static int __init msm_serial_init(void) if (unlikely(ret)) uart_unregister_driver(&msm_uart_driver); - printk(KERN_INFO "msm_serial: driver initialized\n"); + pr_info("msm_serial: driver initialized\n"); return ret; } diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index b5c3292..10c2933 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -408,7 +408,7 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS); if (mctrl & TIOCM_RTS) { - if (tty_port_cts_enabled(&u->state->port)) + if (uart_cts_enabled(u)) ctrl |= AUART_CTRL2_RTSEN; else ctrl |= AUART_CTRL2_RTS; diff --git a/drivers/tty/serial/nwpserial.c b/drivers/tty/serial/nwpserial.c index c06366b..5da7622 100644 --- a/drivers/tty/serial/nwpserial.c +++ b/drivers/tty/serial/nwpserial.c @@ -22,6 +22,7 @@ #include <linux/of_platform.h> #include <linux/of_device.h> #include <linux/nwpserial.h> +#include <linux/delay.h> #include <asm/prom.h> #include <asm/dcr.h> diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 68d4455..8bc2563 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -183,10 +183,6 @@ static int of_platform_serial_probe(struct platform_device *ofdev) "auto-flow-control")) port8250.capabilities |= UART_CAP_AFE; - if (of_property_read_bool(ofdev->dev.of_node, - "has-hw-flow-control")) - port8250.port.flags |= UPF_HARD_FLOW; - ret = serial8250_register_8250_port(&port8250); break; } @@ -244,6 +240,32 @@ static int of_platform_serial_remove(struct platform_device *ofdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int of_serial_suspend(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + serial8250_suspend_port(info->line); + if (info->clk) + clk_disable_unprepare(info->clk); + + return 0; +} + +static int of_serial_resume(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + if (info->clk) + clk_prepare_enable(info->clk); + + serial8250_resume_port(info->line); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); + /* * A few common types, add more as needed. */ @@ -275,6 +297,7 @@ static struct platform_driver of_platform_serial_driver = { .name = "of_serial", .owner = THIS_MODULE, .of_match_table = of_platform_serial_table, + .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, .remove = of_platform_serial_remove, diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index d017cec..18c30ca 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -239,6 +239,26 @@ static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) } /* + * Calculate the absolute difference between the desired and actual baud + * rate for the given mode. + */ +static inline int calculate_baud_abs_diff(struct uart_port *port, + unsigned int baud, unsigned int mode) +{ + unsigned int n = port->uartclk / (mode * baud); + int abs_diff; + + if (n == 0) + n = 1; + + abs_diff = baud - (port->uartclk / (mode * n)); + if (abs_diff < 0) + abs_diff = -abs_diff; + + return abs_diff; +} + +/* * serial_omap_baud_is_mode16 - check if baud rate is MODE16X * @port: uart port info * @baud: baudrate for which mode needs to be determined @@ -252,16 +272,10 @@ static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) static bool serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud) { - unsigned int n13 = port->uartclk / (13 * baud); - unsigned int n16 = port->uartclk / (16 * baud); - int baudAbsDiff13 = baud - (port->uartclk / (13 * n13)); - int baudAbsDiff16 = baud - (port->uartclk / (16 * n16)); - if (baudAbsDiff13 < 0) - baudAbsDiff13 = -baudAbsDiff13; - if (baudAbsDiff16 < 0) - baudAbsDiff16 = -baudAbsDiff16; - - return (baudAbsDiff13 >= baudAbsDiff16); + int abs_diff_13 = calculate_baud_abs_diff(port, baud, 13); + int abs_diff_16 = calculate_baud_abs_diff(port, baud, 16); + + return (abs_diff_13 >= abs_diff_16); } /* diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 29a7be4..df3a8c7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -59,6 +59,11 @@ static void uart_change_pm(struct uart_state *state, static void uart_port_shutdown(struct tty_port *port); +static int uart_dcd_enabled(struct uart_port *uport) +{ + return uport->status & UPSTAT_DCD_ENABLE; +} + /* * This routine is used by the interrupt handler to schedule processing in * the software interrupt portion of the driver. @@ -90,7 +95,7 @@ static void __uart_start(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; - if (!tty->stopped && !tty->hw_stopped) + if (!uart_tx_stopped(port)) port->ops->start_tx(port); } @@ -130,7 +135,6 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, int init_hw) { struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; unsigned long page; int retval = 0; @@ -175,17 +179,14 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, if (tty->termios.c_cflag & CBAUD) uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } - /* - * if hw support flow control without software intervention, - * then skip the below check - */ - if (tty_port_cts_enabled(port) && - !(uport->flags & UPF_HARD_FLOW)) { - spin_lock_irq(&uport->lock); - if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) - tty->hw_stopped = 1; - spin_unlock_irq(&uport->lock); - } + + spin_lock_irq(&uport->lock); + if (uart_cts_enabled(uport) && + !(uport->ops->get_mctrl(uport) & TIOCM_CTS)) + uport->hw_stopped = 1; + else + uport->hw_stopped = 0; + spin_unlock_irq(&uport->lock); } /* @@ -439,7 +440,6 @@ EXPORT_SYMBOL(uart_get_divisor); static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios) { - struct tty_port *port = &state->port; struct uart_port *uport = state->uart_port; struct ktermios *termios; @@ -454,17 +454,19 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, uport->ops->set_termios(uport, termios, old_termios); /* - * Set flags based on termios cflag + * Set modem status enables based on termios cflag */ + spin_lock_irq(&uport->lock); if (termios->c_cflag & CRTSCTS) - set_bit(ASYNCB_CTS_FLOW, &port->flags); + uport->status |= UPSTAT_CTS_ENABLE; else - clear_bit(ASYNCB_CTS_FLOW, &port->flags); + uport->status &= ~UPSTAT_CTS_ENABLE; if (termios->c_cflag & CLOCAL) - clear_bit(ASYNCB_CHECK_CD, &port->flags); + uport->status &= ~UPSTAT_DCD_ENABLE; else - set_bit(ASYNCB_CHECK_CD, &port->flags); + uport->status |= UPSTAT_DCD_ENABLE; + spin_unlock_irq(&uport->lock); } static inline int __uart_put_char(struct uart_port *port, @@ -604,12 +606,11 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) if (port->ops->send_xchar) port->ops->send_xchar(port, ch); else { + spin_lock_irqsave(&port->lock, flags); port->x_char = ch; - if (ch) { - spin_lock_irqsave(&port->lock, flags); + if (ch) port->ops->start_tx(port); - spin_unlock_irqrestore(&port->lock, flags); - } + spin_unlock_irqrestore(&port->lock, flags); } } @@ -652,12 +653,8 @@ static void uart_unthrottle(struct tty_struct *tty) mask &= ~port->flags; } - if (mask & UPF_SOFT_FLOW) { - if (port->x_char) - port->x_char = 0; - else - uart_send_xchar(tty, START_CHAR(tty)); - } + if (mask & UPF_SOFT_FLOW) + uart_send_xchar(tty, START_CHAR(tty)); if (mask & UPF_HARD_FLOW) uart_set_mctrl(port, TIOCM_RTS); @@ -892,10 +889,11 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, */ if (uport->flags & UPF_SPD_MASK) { char buf[64]; - printk(KERN_NOTICE - "%s sets custom speed on %s. This " - "is deprecated.\n", current->comm, - tty_name(port->tty, buf)); + + dev_notice(uport->dev, + "%s sets custom speed on %s. This is deprecated.\n", + current->comm, + tty_name(port->tty, buf)); } uart_change_speed(tty, state, NULL); } @@ -952,7 +950,7 @@ static int uart_get_lsr_info(struct tty_struct *tty, */ if (uport->x_char || ((uart_circ_chars_pending(&state->xmit) > 0) && - !tty->stopped && !tty->hw_stopped)) + !uart_tx_stopped(uport))) result &= ~TIOCSER_TEMT; return put_user(result, value); @@ -1251,7 +1249,6 @@ static void uart_set_termios(struct tty_struct *tty, { struct uart_state *state = tty->driver_data; struct uart_port *uport = state->uart_port; - unsigned long flags; unsigned int cflag = tty->termios.c_cflag; unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK; bool sw_changed = false; @@ -1291,34 +1288,33 @@ static void uart_set_termios(struct tty_struct *tty, /* Handle transition away from B0 status */ else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { unsigned int mask = TIOCM_DTR; - if (!(cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) + if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) mask |= TIOCM_RTS; uart_set_mctrl(uport, mask); } /* * If the port is doing h/w assisted flow control, do nothing. - * We assume that tty->hw_stopped has never been set. + * We assume that port->hw_stopped has never been set. */ if (uport->flags & UPF_HARD_FLOW) return; /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { - spin_lock_irqsave(&uport->lock, flags); - tty->hw_stopped = 0; + spin_lock_irq(&uport->lock); + uport->hw_stopped = 0; __uart_start(tty); - spin_unlock_irqrestore(&uport->lock, flags); + spin_unlock_irq(&uport->lock); } /* Handle turning on CRTSCTS */ else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { - spin_lock_irqsave(&uport->lock, flags); + spin_lock_irq(&uport->lock); if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) { - tty->hw_stopped = 1; + uport->hw_stopped = 1; uport->ops->stop_tx(uport); } - spin_unlock_irqrestore(&uport->lock, flags); + spin_unlock_irq(&uport->lock); } } @@ -1975,12 +1971,9 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) for (tries = 3; !ops->tx_empty(uport) && tries; tries--) msleep(10); if (!tries) - printk(KERN_ERR "%s%s%s%d: Unable to drain " - "transmitter\n", - uport->dev ? dev_name(uport->dev) : "", - uport->dev ? ": " : "", - drv->dev_name, - drv->tty_driver->name_base + uport->line); + dev_err(uport->dev, "%s%d: Unable to drain transmitter\n", + drv->dev_name, + drv->tty_driver->name_base + uport->line); if (console_suspend_enabled || !uart_console(uport)) ops->shutdown(uport); @@ -2109,9 +2102,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) break; } - printk(KERN_INFO "%s%s%s%d at %s (irq = %d, base_baud = %d) is a %s\n", - port->dev ? dev_name(port->dev) : "", - port->dev ? ": " : "", + dev_info(port->dev, "%s%d at %s (irq = %d, base_baud = %d) is a %s\n", drv->dev_name, drv->tty_driver->name_base + port->line, address, port->irq, port->uartclk / 16, uart_type(port)); @@ -2640,7 +2631,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) if (likely(!IS_ERR(tty_dev))) { device_set_wakeup_capable(tty_dev, 1); } else { - printk(KERN_ERR "Cannot register tty device on line %d\n", + dev_err(uport->dev, "Cannot register tty device on line %d\n", uport->line); } @@ -2675,7 +2666,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) BUG_ON(in_interrupt()); if (state->uart_port != uport) - printk(KERN_ALERT "Removing wrong port: %p != %p\n", + dev_alert(uport->dev, "Removing wrong port: %p != %p\n", state->uart_port, uport); mutex_lock(&port_mutex); @@ -2757,22 +2748,29 @@ EXPORT_SYMBOL(uart_match_port); * uart_handle_dcd_change - handle a change of carrier detect state * @uport: uart_port structure for the open port * @status: new carrier detect status, nonzero if active + * + * Caller must hold uport->lock */ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) { struct tty_port *port = &uport->state->port; struct tty_struct *tty = port->tty; - struct tty_ldisc *ld = tty ? tty_ldisc_ref(tty) : NULL; + struct tty_ldisc *ld; - if (ld) { - if (ld->ops->dcd_change) - ld->ops->dcd_change(tty, status); - tty_ldisc_deref(ld); + lockdep_assert_held_once(&uport->lock); + + if (tty) { + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->ops->dcd_change) + ld->ops->dcd_change(tty, status); + tty_ldisc_deref(ld); + } } uport->icount.dcd++; - if (port->flags & ASYNC_CHECK_CD) { + if (uart_dcd_enabled(uport)) { if (status) wake_up_interruptible(&port->open_wait); else if (tty) @@ -2785,26 +2783,25 @@ EXPORT_SYMBOL_GPL(uart_handle_dcd_change); * uart_handle_cts_change - handle a change of clear-to-send state * @uport: uart_port structure for the open port * @status: new clear to send status, nonzero if active + * + * Caller must hold uport->lock */ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) { - struct tty_port *port = &uport->state->port; - struct tty_struct *tty = port->tty; + lockdep_assert_held_once(&uport->lock); uport->icount.cts++; - /* skip below code if the hw flow control is supported */ - if (tty_port_cts_enabled(port) && - !(uport->flags & UPF_HARD_FLOW)) { - if (tty->hw_stopped) { + if (uart_cts_enabled(uport)) { + if (uport->hw_stopped) { if (status) { - tty->hw_stopped = 0; + uport->hw_stopped = 0; uport->ops->start_tx(uport); uart_write_wakeup(uport); } } else { if (!status) { - tty->hw_stopped = 1; + uport->hw_stopped = 1; uport->ops->stop_tx(uport); } } diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index bf9560f..a3035f9 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -18,7 +18,7 @@ #include <linux/err.h> #include <linux/device.h> #include <linux/gpio/consumer.h> -#include <uapi/asm-generic/termios.h> +#include <linux/termios.h> #include "serial_mctrl_gpio.h" diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index 8b2d735..a316584 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -151,12 +151,20 @@ static inline struct asc_port *to_asc_port(struct uart_port *port) static inline u32 asc_in(struct uart_port *port, u32 offset) { +#ifdef readl_relaxed + return readl_relaxed(port->membase + offset); +#else return readl(port->membase + offset); +#endif } static inline void asc_out(struct uart_port *port, u32 offset, u32 value) { +#ifdef writel_relaxed + writel_relaxed(value, port->membase + offset); +#else writel(value, port->membase + offset); +#endif } /* diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 20521db..25d43ce 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -268,6 +268,9 @@ static void sunhv_send_xchar(struct uart_port *port, char ch) unsigned long flags; int limit = 10000; + if (ch == __DISABLED_CHAR) + return; + spin_lock_irqsave(&port->lock, flags); while (limit-- > 0) { diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index b9598b2..b339fe4 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -436,7 +436,7 @@ static void sunsab_start_tx(struct uart_port *port) struct circ_buf *xmit = &up->port.state->xmit; int i; - if (uart_circ_empty(xmit)) + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); @@ -468,6 +468,9 @@ static void sunsab_send_xchar(struct uart_port *port, char ch) struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; unsigned long flags; + if (ch == __DISABLED_CHAR) + return; + spin_lock_irqsave(&up->port.lock, flags); sunsab_tec_wait(up); diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 9fc22f4..189f52e 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -665,7 +665,6 @@ static struct platform_driver ulite_platform_driver = { .probe = ulite_probe, .remove = ulite_remove, .driver = { - .owner = THIS_MODULE, .name = "uartlite", .of_match_table = of_match_ptr(ulite_of_match), }, diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c index db0c8a4..d7f9d62 100644 --- a/drivers/tty/serial/vr41xx_siu.c +++ b/drivers/tty/serial/vr41xx_siu.c @@ -847,7 +847,6 @@ void __init vr41xx_siu_early_setup(struct uart_port *port) siu_uart_ports[port->line].type = port->type; siu_uart_ports[port->line].uartclk = SIU_BAUD_BASE * 16; siu_uart_ports[port->line].mapbase = port->mapbase; - siu_uart_ports[port->line].mapbase = port->mapbase; siu_uart_ports[port->line].ops = &siu_uart_ops; } diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 15ad6fc..b2bc9e8 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -33,8 +33,8 @@ #include <linux/serial.h> #include <linux/slab.h> #include <linux/clk.h> -#include <linux/platform_device.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/err.h> /* @@ -78,13 +78,40 @@ #define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT) #define TX_FIFO_INTS (TXFAE | TXFE | TXUDR) +/* + * Line control bits + */ + +#define VT8500_TXEN (1 << 0) /* Enable transmit logic */ +#define VT8500_RXEN (1 << 1) /* Enable receive logic */ +#define VT8500_CS8 (1 << 2) /* 8-bit data length (vs. 7-bit) */ +#define VT8500_CSTOPB (1 << 3) /* 2 stop bits (vs. 1) */ +#define VT8500_PARENB (1 << 4) /* Enable parity */ +#define VT8500_PARODD (1 << 5) /* Odd parity (vs. even) */ +#define VT8500_RTS (1 << 6) /* Ready to send */ +#define VT8500_LOOPBK (1 << 7) /* Enable internal loopback */ +#define VT8500_DMA (1 << 8) /* Enable DMA mode (needs FIFO) */ +#define VT8500_BREAK (1 << 9) /* Initiate break signal */ +#define VT8500_PSLVERR (1 << 10) /* APB error upon empty RX FIFO read */ +#define VT8500_SWRTSCTS (1 << 11) /* Software-controlled RTS/CTS */ + +/* + * Capability flags (driver-internal) + */ + +#define VT8500_HAS_SWRTSCTS_SWITCH (1 << 1) + +#define VT8500_RECOMMENDED_CLK 12000000 +#define VT8500_OVERSAMPLING_DIVISOR 13 #define VT8500_MAX_PORTS 6 struct vt8500_port { struct uart_port uart; char name[16]; struct clk *clk; + unsigned int clk_predivisor; unsigned int ier; + unsigned int vt8500_uart_flags; }; /* @@ -267,31 +294,45 @@ static unsigned int vt8500_get_mctrl(struct uart_port *port) static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl) { + unsigned int lcr = vt8500_read(port, VT8500_URLCR); + + if (mctrl & TIOCM_RTS) + lcr |= VT8500_RTS; + else + lcr &= ~VT8500_RTS; + + vt8500_write(port, lcr, VT8500_URLCR); } static void vt8500_break_ctl(struct uart_port *port, int break_ctl) { if (break_ctl) - vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9), + vt8500_write(port, + vt8500_read(port, VT8500_URLCR) | VT8500_BREAK, VT8500_URLCR); } static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud) { + struct vt8500_port *vt8500_port = + container_of(port, struct vt8500_port, uart); unsigned long div; unsigned int loops = 1000; - div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff); + div = ((vt8500_port->clk_predivisor - 1) & 0xf) << 16; + div |= (uart_get_divisor(port, baud) - 1) & 0x3ff; - if (unlikely((baud < 900) || (baud > 921600))) - div |= 7; - else - div |= (921600 / baud) - 1; + /* Effective baud rate */ + baud = port->uartclk / 16 / ((div & 0x3ff) + 1); while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops) cpu_relax(); + vt8500_write(port, div, VT8500_URDIV); + /* Break signal timing depends on baud rate, update accordingly */ + vt8500_write(port, mult_frac(baud, 4096, 1000000), VT8500_URBKR); + return baud; } @@ -347,31 +388,35 @@ static void vt8500_set_termios(struct uart_port *port, /* calculate parity */ lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR); - lcr &= ~((1 << 5) | (1 << 4)); + lcr &= ~(VT8500_PARENB | VT8500_PARODD); if (termios->c_cflag & PARENB) { - lcr |= (1 << 4); + lcr |= VT8500_PARENB; termios->c_cflag &= ~CMSPAR; if (termios->c_cflag & PARODD) - lcr |= (1 << 5); + lcr |= VT8500_PARODD; } /* calculate bits per char */ - lcr &= ~(1 << 2); + lcr &= ~VT8500_CS8; switch (termios->c_cflag & CSIZE) { case CS7: break; case CS8: default: - lcr |= (1 << 2); + lcr |= VT8500_CS8; termios->c_cflag &= ~CSIZE; termios->c_cflag |= CS8; break; } /* calculate stop bits */ - lcr &= ~(1 << 3); + lcr &= ~VT8500_CSTOPB; if (termios->c_cflag & CSTOPB) - lcr |= (1 << 3); + lcr |= VT8500_CSTOPB; + + lcr &= ~VT8500_SWRTSCTS; + if (vt8500_port->vt8500_uart_flags & VT8500_HAS_SWRTSCTS_SWITCH) + lcr |= VT8500_SWRTSCTS; /* set parity, bits per char, and stop bit */ vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR); @@ -521,6 +566,33 @@ static struct console vt8500_console = { #define VT8500_CONSOLE NULL #endif +#ifdef CONFIG_CONSOLE_POLL +static int vt8500_get_poll_char(struct uart_port *port) +{ + unsigned int status = vt8500_read(port, VT8500_URFIDX); + + if (!(status & 0x1f00)) + return NO_POLL_CHAR; + + return vt8500_read(port, VT8500_RXFIFO) & 0xff; +} + +static void vt8500_put_poll_char(struct uart_port *port, unsigned char c) +{ + unsigned int status, tmout = 10000; + + do { + status = vt8500_read(port, VT8500_URFIDX); + + if (--tmout == 0) + break; + udelay(1); + } while (status & 0x10); + + vt8500_write(port, c, VT8500_TXFIFO); +} +#endif + static struct uart_ops vt8500_uart_pops = { .tx_empty = vt8500_tx_empty, .set_mctrl = vt8500_set_mctrl, @@ -538,6 +610,10 @@ static struct uart_ops vt8500_uart_pops = { .request_port = vt8500_request_port, .config_port = vt8500_config_port, .verify_port = vt8500_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = vt8500_get_poll_char, + .poll_put_char = vt8500_put_poll_char, +#endif }; static struct uart_driver vt8500_uart_driver = { @@ -548,14 +624,31 @@ static struct uart_driver vt8500_uart_driver = { .cons = VT8500_CONSOLE, }; +static unsigned int vt8500_flags; /* none required so far */ +static unsigned int wm8880_flags = VT8500_HAS_SWRTSCTS_SWITCH; + +static const struct of_device_id wmt_dt_ids[] = { + { .compatible = "via,vt8500-uart", .data = &vt8500_flags}, + { .compatible = "wm,wm8880-uart", .data = &wm8880_flags}, + {} +}; + static int vt8500_serial_probe(struct platform_device *pdev) { struct vt8500_port *vt8500_port; struct resource *mmres, *irqres; struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + const unsigned int *flags; int ret; int port; + match = of_match_device(wmt_dt_ids, &pdev->dev); + if (!match) + return -EINVAL; + + flags = match->data; + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!mmres || !irqres) @@ -605,6 +698,11 @@ static int vt8500_serial_probe(struct platform_device *pdev) return ret; } + vt8500_port->vt8500_uart_flags = *flags; + vt8500_port->clk_predivisor = DIV_ROUND_CLOSEST( + clk_get_rate(vt8500_port->clk), + VT8500_RECOMMENDED_CLK + ); vt8500_port->uart.type = PORT_VT8500; vt8500_port->uart.iotype = UPIO_MEM; vt8500_port->uart.mapbase = mmres->start; @@ -615,7 +713,10 @@ static int vt8500_serial_probe(struct platform_device *pdev) vt8500_port->uart.dev = &pdev->dev; vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; - vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk); + /* Serial core uses the magic "16" everywhere - adjust for it */ + vt8500_port->uart.uartclk = 16 * clk_get_rate(vt8500_port->clk) / + vt8500_port->clk_predivisor / + VT8500_OVERSAMPLING_DIVISOR; snprintf(vt8500_port->name, sizeof(vt8500_port->name), "VT8500 UART%d", pdev->id); @@ -639,11 +740,6 @@ static int 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 = vt8500_serial_remove, diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 806e4bc..200c1af 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1051,6 +1051,25 @@ static void cdns_uart_console_putchar(struct uart_port *port, int ch) cdns_uart_writel(ch, CDNS_UART_FIFO_OFFSET); } +static void cdns_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, cdns_uart_console_putchar); +} + +static int __init cdns_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = cdns_early_write; + + return 0; +} +EARLYCON_DECLARE(cdns, cdns_early_console_setup); + /** * cdns_uart_console_write - perform write operation * @co: Console handle @@ -1428,7 +1447,6 @@ static struct platform_driver cdns_uart_platform_driver = { .probe = cdns_uart_probe, .remove = cdns_uart_remove, .driver = { - .owner = THIS_MODULE, .name = CDNS_UART_NAME, .of_match_table = cdns_uart_of_match, .pm = &cdns_uart_dev_pm_ops, diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 8fbad34..2f6f9b5 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -908,8 +908,7 @@ void no_tty(void) * stop_tty - propagate flow control * @tty: tty to stop * - * Perform flow control to the driver. For PTY/TTY pairs we - * must also propagate the TIOCKPKT status. May be called + * Perform flow control to the driver. May be called * on an already stopped device and will not re-call the driver * method. * @@ -919,64 +918,58 @@ void no_tty(void) * but not always. * * Locking: - * Uses the tty control lock internally + * flow_lock */ -void stop_tty(struct tty_struct *tty) +void __stop_tty(struct tty_struct *tty) { - unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->stopped) { - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + if (tty->stopped) return; - } tty->stopped = 1; - if (tty->link && tty->link->packet) { - tty->ctrl_status &= ~TIOCPKT_START; - tty->ctrl_status |= TIOCPKT_STOP; - wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (tty->ops->stop) (tty->ops->stop)(tty); } +void stop_tty(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->flow_lock, flags); + __stop_tty(tty); + spin_unlock_irqrestore(&tty->flow_lock, flags); +} EXPORT_SYMBOL(stop_tty); /** * start_tty - propagate flow control * @tty: tty to start * - * Start a tty that has been stopped if at all possible. Perform - * any necessary wakeups and propagate the TIOCPKT status. If this - * is the tty was previous stopped and is being started then the - * driver start method is invoked and the line discipline woken. + * Start a tty that has been stopped if at all possible. If this + * tty was previous stopped and is now being started, the driver + * start method is invoked and the line discipline woken. * * Locking: - * ctrl_lock + * flow_lock */ -void start_tty(struct tty_struct *tty) +void __start_tty(struct tty_struct *tty) { - unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (!tty->stopped || tty->flow_stopped) { - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + if (!tty->stopped || tty->flow_stopped) return; - } tty->stopped = 0; - if (tty->link && tty->link->packet) { - tty->ctrl_status &= ~TIOCPKT_STOP; - tty->ctrl_status |= TIOCPKT_START; - wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); if (tty->ops->start) (tty->ops->start)(tty); - /* If we have a running line discipline it may need kicking */ tty_wakeup(tty); } +void start_tty(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->flow_lock, flags); + __start_tty(tty); + spin_unlock_irqrestore(&tty->flow_lock, flags); +} EXPORT_SYMBOL(start_tty); /* We limit tty time update visibility to every 8 seconds or so. */ @@ -1030,14 +1023,14 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, return i; } -void tty_write_unlock(struct tty_struct *tty) +static void tty_write_unlock(struct tty_struct *tty) __releases(&tty->atomic_write_lock) { mutex_unlock(&tty->atomic_write_lock); wake_up_interruptible_poll(&tty->write_wait, POLLOUT); } -int tty_write_lock(struct tty_struct *tty, int ndelay) +static int tty_write_lock(struct tty_struct *tty, int ndelay) __acquires(&tty->atomic_write_lock) { if (!mutex_trylock(&tty->atomic_write_lock)) { @@ -1224,6 +1217,35 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf, return tty_write(file, buf, count, ppos); } +/** + * tty_send_xchar - send priority character + * + * Send a high priority character to the tty even if stopped + * + * Locking: none for xchar method, write ordering for write method. + */ + +int tty_send_xchar(struct tty_struct *tty, char ch) +{ + int was_stopped = tty->stopped; + + if (tty->ops->send_xchar) { + tty->ops->send_xchar(tty, ch); + return 0; + } + + if (tty_write_lock(tty, 0) < 0) + return -ERESTARTSYS; + + if (was_stopped) + start_tty(tty); + tty->ops->write(tty, &ch, 1); + if (was_stopped) + stop_tty(tty); + tty_write_unlock(tty); + return 0; +} + static char ptychar[] = "pqrstuvwxyzabcde"; /** @@ -1544,13 +1566,14 @@ static void release_one_tty(struct work_struct *work) struct tty_struct *tty = container_of(work, struct tty_struct, hangup_work); struct tty_driver *driver = tty->driver; + struct module *owner = driver->owner; if (tty->ops->cleanup) tty->ops->cleanup(tty); tty->magic = 0; tty_driver_kref_put(driver); - module_put(driver->owner); + module_put(owner); spin_lock(&tty_files_lock); list_del_init(&tty->tty_files); @@ -3018,6 +3041,7 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) INIT_WORK(&tty->hangup_work, do_tty_hangup); mutex_init(&tty->atomic_write_lock); spin_lock_init(&tty->ctrl_lock); + spin_lock_init(&tty->flow_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 6fd60fe..62380cc 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -402,7 +402,7 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, #ifdef BOTHER /* If the user asked for a precise weird speed give a precise weird - answer. If they asked for a Bfoo speed they many have problems + 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) @@ -912,35 +912,6 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) #endif /** - * send_prio_char - send priority character - * - * Send a high priority character to the tty even if stopped - * - * Locking: none for xchar method, write ordering for write method. - */ - -static int send_prio_char(struct tty_struct *tty, char ch) -{ - int was_stopped = tty->stopped; - - if (tty->ops->send_xchar) { - tty->ops->send_xchar(tty, ch); - return 0; - } - - if (tty_write_lock(tty, 0) < 0) - return -ERESTARTSYS; - - if (was_stopped) - start_tty(tty); - tty->ops->write(tty, &ch, 1); - if (was_stopped) - stop_tty(tty); - tty_write_unlock(tty); - return 0; -} - -/** * tty_change_softcar - carrier change ioctl helper * @tty: tty to update * @arg: enable/disable CLOCAL @@ -1177,29 +1148,37 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, return retval; switch (arg) { case TCOOFF: + spin_lock_irq(&tty->flow_lock); if (!tty->flow_stopped) { tty->flow_stopped = 1; - stop_tty(tty); + __stop_tty(tty); } + spin_unlock_irq(&tty->flow_lock); break; case TCOON: + spin_lock_irq(&tty->flow_lock); if (tty->flow_stopped) { tty->flow_stopped = 0; - start_tty(tty); + __start_tty(tty); } + spin_unlock_irq(&tty->flow_lock); break; case TCIOFF: + down_read(&tty->termios_rwsem); if (STOP_CHAR(tty) != __DISABLED_CHAR) - return send_prio_char(tty, STOP_CHAR(tty)); + retval = tty_send_xchar(tty, STOP_CHAR(tty)); + up_read(&tty->termios_rwsem); break; case TCION: + down_read(&tty->termios_rwsem); if (START_CHAR(tty) != __DISABLED_CHAR) - return send_prio_char(tty, START_CHAR(tty)); + retval = tty_send_xchar(tty, START_CHAR(tty)); + up_read(&tty->termios_rwsem); break; default: return -EINVAL; } - return 0; + return retval; case TCFLSH: retval = tty_check_change(tty); if (retval) diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index d0e3a44..c039cfe 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -268,30 +268,30 @@ EXPORT_SYMBOL(kd_mksound); static int kbd_rate_helper(struct input_handle *handle, void *data) { struct input_dev *dev = handle->dev; - struct kbd_repeat *rep = data; + struct kbd_repeat *rpt = data; if (test_bit(EV_REP, dev->evbit)) { - if (rep[0].delay > 0) + if (rpt[0].delay > 0) input_inject_event(handle, - EV_REP, REP_DELAY, rep[0].delay); - if (rep[0].period > 0) + EV_REP, REP_DELAY, rpt[0].delay); + if (rpt[0].period > 0) input_inject_event(handle, - EV_REP, REP_PERIOD, rep[0].period); + EV_REP, REP_PERIOD, rpt[0].period); - rep[1].delay = dev->rep[REP_DELAY]; - rep[1].period = dev->rep[REP_PERIOD]; + rpt[1].delay = dev->rep[REP_DELAY]; + rpt[1].period = dev->rep[REP_PERIOD]; } return 0; } -int kbd_rate(struct kbd_repeat *rep) +int kbd_rate(struct kbd_repeat *rpt) { - struct kbd_repeat data[2] = { *rep }; + struct kbd_repeat data[2] = { *rpt }; input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper); - *rep = data[1]; /* Copy currently used settings */ + *rpt = data[1]; /* Copy currently used settings */ return 0; } @@ -971,15 +971,15 @@ static unsigned char getledstate(void) return ledstate; } -void setledstate(struct kbd_struct *kbd, unsigned int led) +void setledstate(struct kbd_struct *kb, unsigned int led) { unsigned long flags; spin_lock_irqsave(&led_lock, flags); if (!(led & ~7)) { ledioctl = led; - kbd->ledmode = LED_SHOW_IOCTL; + kb->ledmode = LED_SHOW_IOCTL; } else - kbd->ledmode = LED_SHOW_FLAGS; + kb->ledmode = LED_SHOW_FLAGS; set_leds(); spin_unlock_irqrestore(&led_lock, flags); @@ -987,12 +987,12 @@ void setledstate(struct kbd_struct *kbd, unsigned int led) static inline unsigned char getleds(void) { - struct kbd_struct *kbd = kbd_table + fg_console; + struct kbd_struct *kb = kbd_table + fg_console; - if (kbd->ledmode == LED_SHOW_IOCTL) + if (kb->ledmode == LED_SHOW_IOCTL) return ledioctl; - return kbd->ledflagstate; + return kb->ledflagstate; } static int kbd_update_leds_helper(struct input_handle *handle, void *data) @@ -1018,12 +1018,12 @@ static int kbd_update_leds_helper(struct input_handle *handle, void *data) */ int vt_get_leds(int console, int flag) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; int ret; unsigned long flags; spin_lock_irqsave(&led_lock, flags); - ret = vc_kbd_led(kbd, flag); + ret = vc_kbd_led(kb, flag); spin_unlock_irqrestore(&led_lock, flags); return ret; @@ -1040,8 +1040,8 @@ EXPORT_SYMBOL_GPL(vt_get_leds); */ void vt_set_led_state(int console, int leds) { - struct kbd_struct * kbd = kbd_table + console; - setledstate(kbd, leds); + struct kbd_struct *kb = kbd_table + console; + setledstate(kb, leds); } /** @@ -1059,10 +1059,10 @@ void vt_set_led_state(int console, int leds) */ void vt_kbd_con_start(int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; spin_lock_irqsave(&led_lock, flags); - clr_vc_kbd_led(kbd, VC_SCROLLOCK); + clr_vc_kbd_led(kb, VC_SCROLLOCK); set_leds(); spin_unlock_irqrestore(&led_lock, flags); } @@ -1076,10 +1076,10 @@ void vt_kbd_con_start(int console) */ void vt_kbd_con_stop(int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; spin_lock_irqsave(&led_lock, flags); - set_vc_kbd_led(kbd, VC_SCROLLOCK); + set_vc_kbd_led(kb, VC_SCROLLOCK); set_leds(); spin_unlock_irqrestore(&led_lock, flags); } @@ -1512,15 +1512,14 @@ int __init kbd_init(void) /** * vt_do_diacrit - diacritical table updates * @cmd: ioctl request - * @up: pointer to user data for ioctl + * @udp: pointer to user data for ioctl * @perm: permissions check computed by caller * * Update the diacritical tables atomically and safely. Lock them * against simultaneous keypresses */ -int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) +int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm) { - struct kbdiacrs __user *a = up; unsigned long flags; int asize; int ret = 0; @@ -1528,12 +1527,13 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) switch (cmd) { case KDGKBDIACR: { - struct kbdiacr *diacr; + struct kbdiacrs __user *a = udp; + struct kbdiacr *dia; int i; - diacr = kmalloc(MAX_DIACR * sizeof(struct kbdiacr), + dia = kmalloc(MAX_DIACR * sizeof(struct kbdiacr), GFP_KERNEL); - if (diacr == NULL) + if (!dia) return -ENOMEM; /* Lock the diacriticals table, make a copy and then @@ -1542,26 +1542,26 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) asize = accent_table_size; for (i = 0; i < asize; i++) { - diacr[i].diacr = conv_uni_to_8bit( + dia[i].diacr = conv_uni_to_8bit( accent_table[i].diacr); - diacr[i].base = conv_uni_to_8bit( + dia[i].base = conv_uni_to_8bit( accent_table[i].base); - diacr[i].result = conv_uni_to_8bit( + dia[i].result = conv_uni_to_8bit( accent_table[i].result); } spin_unlock_irqrestore(&kbd_event_lock, flags); if (put_user(asize, &a->kb_cnt)) ret = -EFAULT; - else if (copy_to_user(a->kbdiacr, diacr, + else if (copy_to_user(a->kbdiacr, dia, asize * sizeof(struct kbdiacr))) ret = -EFAULT; - kfree(diacr); + kfree(dia); return ret; } case KDGKBDIACRUC: { - struct kbdiacrsuc __user *a = up; + struct kbdiacrsuc __user *a = udp; void *buf; buf = kmalloc(MAX_DIACR * sizeof(struct kbdiacruc), @@ -1589,8 +1589,8 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) case KDSKBDIACR: { - struct kbdiacrs __user *a = up; - struct kbdiacr *diacr = NULL; + struct kbdiacrs __user *a = udp; + struct kbdiacr *dia = NULL; unsigned int ct; int i; @@ -1602,14 +1602,14 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) return -EINVAL; if (ct) { - diacr = kmalloc(sizeof(struct kbdiacr) * ct, + dia = kmalloc(sizeof(struct kbdiacr) * ct, GFP_KERNEL); - if (diacr == NULL) + if (!dia) return -ENOMEM; - if (copy_from_user(diacr, a->kbdiacr, + if (copy_from_user(dia, a->kbdiacr, sizeof(struct kbdiacr) * ct)) { - kfree(diacr); + kfree(dia); return -EFAULT; } } @@ -1618,20 +1618,20 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) accent_table_size = ct; for (i = 0; i < ct; i++) { accent_table[i].diacr = - conv_8bit_to_uni(diacr[i].diacr); + conv_8bit_to_uni(dia[i].diacr); accent_table[i].base = - conv_8bit_to_uni(diacr[i].base); + conv_8bit_to_uni(dia[i].base); accent_table[i].result = - conv_8bit_to_uni(diacr[i].result); + conv_8bit_to_uni(dia[i].result); } spin_unlock_irqrestore(&kbd_event_lock, flags); - kfree(diacr); + kfree(dia); return 0; } case KDSKBDIACRUC: { - struct kbdiacrsuc __user *a = up; + struct kbdiacrsuc __user *a = udp; unsigned int ct; void *buf = NULL; @@ -1679,28 +1679,28 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) */ int vt_do_kdskbmode(int console, unsigned int arg) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; int ret = 0; unsigned long flags; spin_lock_irqsave(&kbd_event_lock, flags); switch(arg) { case K_RAW: - kbd->kbdmode = VC_RAW; + kb->kbdmode = VC_RAW; break; case K_MEDIUMRAW: - kbd->kbdmode = VC_MEDIUMRAW; + kb->kbdmode = VC_MEDIUMRAW; break; case K_XLATE: - kbd->kbdmode = VC_XLATE; + kb->kbdmode = VC_XLATE; do_compute_shiftstate(); break; case K_UNICODE: - kbd->kbdmode = VC_UNICODE; + kb->kbdmode = VC_UNICODE; do_compute_shiftstate(); break; case K_OFF: - kbd->kbdmode = VC_OFF; + kb->kbdmode = VC_OFF; break; default: ret = -EINVAL; @@ -1719,17 +1719,17 @@ int vt_do_kdskbmode(int console, unsigned int arg) */ int vt_do_kdskbmeta(int console, unsigned int arg) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; int ret = 0; unsigned long flags; spin_lock_irqsave(&kbd_event_lock, flags); switch(arg) { case K_METABIT: - clr_vc_kbd_mode(kbd, VC_META); + clr_vc_kbd_mode(kb, VC_META); break; case K_ESCPREFIX: - set_vc_kbd_mode(kbd, VC_META); + set_vc_kbd_mode(kb, VC_META); break; default: ret = -EINVAL; @@ -1768,7 +1768,7 @@ int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; struct kbentry tmp; ushort *key_map, *new_map, val, ov; unsigned long flags; @@ -1786,7 +1786,7 @@ int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, key_map = key_maps[s]; if (key_map) { val = U(key_map[i]); - if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES) + if (kb->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES) val = K_HOLE; } else val = (i ? K_HOLE : K_NOSUCHMAP); @@ -1814,7 +1814,7 @@ int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, if (KVAL(v) > max_vals[KTYP(v)]) return -EINVAL; } else - if (kbd->kbdmode != VC_UNICODE) + if (kb->kbdmode != VC_UNICODE) return -EINVAL; /* ++Geert: non-PC keyboards may generate keycode zero */ @@ -1985,7 +1985,7 @@ reterr: int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; unsigned char ucval; @@ -1994,7 +1994,7 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) /* don't use them - they will go away without warning */ case KDGKBLED: spin_lock_irqsave(&kbd_event_lock, flags); - ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4); + ucval = kb->ledflagstate | (kb->default_ledflagstate << 4); spin_unlock_irqrestore(&kbd_event_lock, flags); return put_user(ucval, (char __user *)arg); @@ -2004,8 +2004,8 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) if (arg & ~0x77) return -EINVAL; spin_lock_irqsave(&led_lock, flags); - kbd->ledflagstate = (arg & 7); - kbd->default_ledflagstate = ((arg >> 4) & 7); + kb->ledflagstate = (arg & 7); + kb->default_ledflagstate = ((arg >> 4) & 7); set_leds(); spin_unlock_irqrestore(&led_lock, flags); return 0; @@ -2019,7 +2019,7 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) case KDSETLED: if (!perm) return -EPERM; - setledstate(kbd, arg); + setledstate(kb, arg); return 0; } return -ENOIOCTLCMD; @@ -2027,9 +2027,9 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) int vt_do_kdgkbmode(int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; /* This is a spot read so needs no locking */ - switch (kbd->kbdmode) { + switch (kb->kbdmode) { case VC_RAW: return K_RAW; case VC_MEDIUMRAW: @@ -2051,9 +2051,9 @@ int vt_do_kdgkbmode(int console) */ int vt_do_kdgkbmeta(int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; /* Again a spot read so no locking */ - return vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT; + return vc_kbd_mode(kb, VC_META) ? K_ESCPREFIX : K_METABIT; } /** @@ -2092,19 +2092,19 @@ int vt_get_shift_state(void) */ void vt_reset_keyboard(int console) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; spin_lock_irqsave(&kbd_event_lock, flags); - set_vc_kbd_mode(kbd, VC_REPEAT); - clr_vc_kbd_mode(kbd, VC_CKMODE); - clr_vc_kbd_mode(kbd, VC_APPLIC); - clr_vc_kbd_mode(kbd, VC_CRLF); - kbd->lockstate = 0; - kbd->slockstate = 0; + set_vc_kbd_mode(kb, VC_REPEAT); + clr_vc_kbd_mode(kb, VC_CKMODE); + clr_vc_kbd_mode(kb, VC_APPLIC); + clr_vc_kbd_mode(kb, VC_CRLF); + kb->lockstate = 0; + kb->slockstate = 0; spin_lock(&led_lock); - kbd->ledmode = LED_SHOW_FLAGS; - kbd->ledflagstate = kbd->default_ledflagstate; + kb->ledmode = LED_SHOW_FLAGS; + kb->ledflagstate = kb->default_ledflagstate; spin_unlock(&led_lock); /* do not do set_leds here because this causes an endless tasklet loop when the keyboard hasn't been initialized yet */ @@ -2122,8 +2122,8 @@ void vt_reset_keyboard(int console) int vt_get_kbd_mode_bit(int console, int bit) { - struct kbd_struct * kbd = kbd_table + console; - return vc_kbd_mode(kbd, bit); + struct kbd_struct *kb = kbd_table + console; + return vc_kbd_mode(kb, bit); } /** @@ -2137,11 +2137,11 @@ int vt_get_kbd_mode_bit(int console, int bit) void vt_set_kbd_mode_bit(int console, int bit) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; spin_lock_irqsave(&kbd_event_lock, flags); - set_vc_kbd_mode(kbd, bit); + set_vc_kbd_mode(kb, bit); spin_unlock_irqrestore(&kbd_event_lock, flags); } @@ -2156,10 +2156,10 @@ void vt_set_kbd_mode_bit(int console, int bit) void vt_clr_kbd_mode_bit(int console, int bit) { - struct kbd_struct * kbd = kbd_table + console; + struct kbd_struct *kb = kbd_table + console; unsigned long flags; spin_lock_irqsave(&kbd_event_lock, flags); - clr_vc_kbd_mode(kbd, bit); + clr_vc_kbd_mode(kb, bit); spin_unlock_irqrestore(&kbd_event_lock, flags); } |