diff options
Diffstat (limited to 'drivers/tty')
75 files changed, 3217 insertions, 1590 deletions
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index b95bed92da9f..8689279afdf1 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -1,5 +1,7 @@ obj-$(CONFIG_TTY) += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \ - tty_buffer.o tty_port.o tty_mutex.o tty_ldsem.o + tty_buffer.o tty_port.o tty_mutex.o \ + tty_ldsem.o tty_baudrate.o tty_jobctrl.o \ + n_null.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o obj-$(CONFIG_AUDIT) += tty_audit.o diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index dea16bb8c46a..9820e20993db 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -570,18 +570,6 @@ static int startup(struct tty_struct *tty, struct serial_state *info) info->xmit.head = info->xmit.tail = 0; /* - * Set up the tty->alt_speed kludge - */ - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - tty->alt_speed = 57600; - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - tty->alt_speed = 115200; - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - tty->alt_speed = 230400; - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - tty->alt_speed = 460800; - - /* * and set the speed of the serial port */ change_speed(tty, info, NULL); @@ -1084,14 +1072,9 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, check_and_exit: if (tty_port_initialized(port)) { if (change_spd) { - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - tty->alt_speed = 57600; - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - tty->alt_speed = 115200; - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - tty->alt_speed = 230400; - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - tty->alt_speed = 460800; + /* warn about deprecation unless clearing */ + if (new_serial.flags & ASYNC_SPD_MASK) + dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n"); change_speed(tty, state, NULL); } } else diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 5e4fa9206861..d272bc4e7fb5 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -156,8 +156,8 @@ static unsigned int cy_isa_addresses[] = { static long maddr[NR_CARDS]; static int irq[NR_CARDS]; -module_param_array(maddr, long, NULL, 0); -module_param_array(irq, int, NULL, 0); +module_param_hw_array(maddr, long, iomem, NULL, 0); +module_param_hw_array(irq, int, irq, NULL, 0); #endif /* CONFIG_ISA */ @@ -1975,18 +1975,6 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) cflag = tty->termios.c_cflag; iflag = tty->termios.c_iflag; - /* - * Set up the tty->alt_speed kludge - */ - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - tty->alt_speed = 57600; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - tty->alt_speed = 115200; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - tty->alt_speed = 230400; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - tty->alt_speed = 460800; - card = info->card; channel = info->line - card->first_line; @@ -2295,12 +2283,16 @@ cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty, struct serial_struct __user *new_info) { struct serial_struct new_serial; + int old_flags; int ret; if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) return -EFAULT; mutex_lock(&info->port.mutex); + + old_flags = info->port.flags; + if (!capable(CAP_SYS_ADMIN)) { if (new_serial.close_delay != info->port.close_delay || new_serial.baud_base != info->baud || @@ -2332,6 +2324,11 @@ cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty, check_and_exit: if (tty_port_initialized(&info->port)) { + if ((new_serial.flags ^ old_flags) & ASYNC_SPD_MASK) { + /* warn about deprecation unless clearing */ + if (new_serial.flags & ASYNC_SPD_MASK) + dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n"); + } cy_set_line_char(info, tty); ret = 0; } else { diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 7ac9bcdf1e61..61fe8d6fd24e 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -764,7 +764,7 @@ static int __init ehv_bc_init(void) ehv_bc_driver = alloc_tty_driver(count); if (!ehv_bc_driver) { ret = -ENOMEM; - goto error; + goto err_free_bcs; } ehv_bc_driver->driver_name = "ehv-bc"; @@ -778,24 +778,23 @@ static int __init ehv_bc_init(void) ret = tty_register_driver(ehv_bc_driver); if (ret) { pr_err("ehv-bc: could not register tty driver (ret=%i)\n", ret); - goto error; + goto err_put_tty_driver; } ret = platform_driver_register(&ehv_bc_tty_driver); if (ret) { pr_err("ehv-bc: could not register platform driver (ret=%i)\n", ret); - goto error; + goto err_deregister_tty_driver; } return 0; -error: - if (ehv_bc_driver) { - tty_unregister_driver(ehv_bc_driver); - put_tty_driver(ehv_bc_driver); - } - +err_deregister_tty_driver: + tty_unregister_driver(ehv_bc_driver); +err_put_tty_driver: + put_tty_driver(ehv_bc_driver); +err_free_bcs: kfree(bcs); return ret; diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 574da15fe618..b8d5ea0ae26b 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -44,7 +44,7 @@ config HVC_RTAS config HVC_IUCV bool "z/VM IUCV Hypervisor console support (VM only)" - depends on S390 + depends on S390 && NET select HVC_DRIVER select IUCV default y diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index b19ae36a05ec..a8d399188242 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -920,17 +920,17 @@ int hvc_remove(struct hvc_struct *hp) tty = tty_port_tty_get(&hp->port); + console_lock(); spin_lock_irqsave(&hp->lock, flags); if (hp->index < MAX_NR_HVC_CONSOLES) { - console_lock(); vtermnos[hp->index] = -1; cons_ops[hp->index] = NULL; - console_unlock(); } /* Don't whack hp->irq because tty_hangup() will need to free the irq. */ spin_unlock_irqrestore(&hp->lock, flags); + console_unlock(); /* * We 'put' the instance that was grabbed when the kref instance diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 7823d6d998cf..79cc5beea2da 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -484,13 +484,13 @@ static struct attribute_group hvcs_attr_group = { .attrs = hvcs_attrs, }; -static ssize_t hvcs_rescan_show(struct device_driver *ddp, char *buf) +static ssize_t rescan_show(struct device_driver *ddp, char *buf) { /* A 1 means it is updating, a 0 means it is done updating */ return snprintf(buf, PAGE_SIZE, "%d\n", hvcs_rescan_status); } -static ssize_t hvcs_rescan_store(struct device_driver *ddp, const char * buf, +static ssize_t rescan_store(struct device_driver *ddp, const char * buf, size_t count) { if ((simple_strtol(buf, NULL, 0) != 1) @@ -505,8 +505,7 @@ static ssize_t hvcs_rescan_store(struct device_driver *ddp, const char * buf, return count; } -static DRIVER_ATTR(rescan, - S_IRUGO | S_IWUSR, hvcs_rescan_show, hvcs_rescan_store); +static DRIVER_ATTR_RW(rescan); static void hvcs_kick(void) { @@ -1242,8 +1241,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) free_irq(irq, hvcsd); return; } else if (hvcsd->port.count < 0) { - printk(KERN_ERR "HVCS: vty-server@%X open_count: %d" - " is missmanaged.\n", + printk(KERN_ERR "HVCS: vty-server@%X open_count: %d is mismanaged.\n", hvcsd->vdev->unit_address, hvcsd->port.count); } @@ -1575,7 +1573,7 @@ static int __init hvcs_module_init(void) */ rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan); if (rc) - pr_warning("HVCS: Failed to create rescan file (err %d)\n", rc); + pr_warn("HVCS: Failed to create rescan file (err %d)\n", rc); return 0; } diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c index c0dfb642383b..c2f9a3263b37 100644 --- a/drivers/tty/ipwireless/network.c +++ b/drivers/tty/ipwireless/network.c @@ -355,7 +355,7 @@ static struct sk_buff *ipw_packet_received_skb(unsigned char *data, if (skb == NULL) return NULL; skb_reserve(skb, 2); - memcpy(skb_put(skb, length), data, length); + skb_put_data(skb, data, length); return skb; } diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 4caf0c3b1f99..3b251f4e5df0 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -179,7 +179,7 @@ MODULE_FIRMWARE("c320tunx.cod"); module_param_array(type, uint, NULL, 0); MODULE_PARM_DESC(type, "card type: C218=2, C320=4"); -module_param_array(baseaddr, ulong, NULL, 0); +module_param_hw_array(baseaddr, ulong, ioport, NULL, 0); MODULE_PARM_DESC(baseaddr, "base address"); module_param_array(numports, uint, NULL, 0); MODULE_PARM_DESC(numports, "numports (ignored for C218)"); diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 7b8f383fb090..8bd6fb6d9391 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -183,7 +183,7 @@ static int ttymajor = MXSERMAJOR; MODULE_AUTHOR("Casper Yang"); MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver"); -module_param_array(ioaddr, ulong, NULL, 0); +module_param_hw_array(ioaddr, ulong, ioport, NULL, 0); MODULE_PARM_DESC(ioaddr, "ISA io addresses to look for a moxa board"); module_param(ttymajor, int, 0); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 55577cf9b6a4..2afe5fce68e3 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -89,18 +89,14 @@ module_param(debug, int, 0600); /** * struct gsm_mux_net - network interface * @struct gsm_dlci* dlci - * @struct net_device_stats stats; * * Created when net interface is initialized. **/ struct gsm_mux_net { struct kref ref; struct gsm_dlci *dlci; - struct net_device_stats stats; }; -#define STATS(net) (((struct gsm_mux_net *)netdev_priv(net))->stats) - /* * Each block of data we have queued to go out is in the form of * a gsm_msg which holds everything we need in a link layer independent @@ -2019,6 +2015,33 @@ static void gsm_error(struct gsm_mux *gsm, gsm->io_error++; } +static int gsm_disconnect(struct gsm_mux *gsm) +{ + struct gsm_dlci *dlci = gsm->dlci[0]; + struct gsm_control *gc; + + if (!dlci) + return 0; + + /* In theory disconnecting DLCI 0 is sufficient but for some + modems this is apparently not the case. */ + gc = gsm_control_send(gsm, CMD_CLD, NULL, 0); + if (gc) + gsm_control_wait(gsm, gc); + + del_timer_sync(&gsm->t2_timer); + /* Now we are sure T2 has stopped */ + + gsm_dlci_begin_close(dlci); + wait_event_interruptible(gsm->event, + dlci->state == DLCI_CLOSED); + + if (signal_pending(current)) + return -EINTR; + + return 0; +} + /** * gsm_cleanup_mux - generic GSM protocol cleanup * @gsm: our mux @@ -2033,7 +2056,6 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm) int i; struct gsm_dlci *dlci = gsm->dlci[0]; struct gsm_msg *txq, *ntxq; - struct gsm_control *gc; gsm->dead = 1; @@ -2049,21 +2071,11 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm) if (i == MAX_MUX) return; - /* In theory disconnecting DLCI 0 is sufficient but for some - modems this is apparently not the case. */ - if (dlci) { - gc = gsm_control_send(gsm, CMD_CLD, NULL, 0); - if (gc) - gsm_control_wait(gsm, gc); - } del_timer_sync(&gsm->t2_timer); /* Now we are sure T2 has stopped */ - if (dlci) { + if (dlci) dlci->dead = 1; - gsm_dlci_begin_close(dlci); - wait_event_interruptible(gsm->event, - dlci->state == DLCI_CLOSED); - } + /* Free up any link layer users */ mutex_lock(&gsm->mutex); for (i = 0; i < NUM_DLCI; i++) @@ -2523,12 +2535,12 @@ static int gsmld_config(struct tty_struct *tty, struct gsm_mux *gsm, */ if (need_close || need_restart) { - gsm_dlci_begin_close(gsm->dlci[0]); - /* This will timeout if the link is down due to N2 expiring */ - wait_event_interruptible(gsm->event, - gsm->dlci[0]->state == DLCI_CLOSED); - if (signal_pending(current)) - return -EINTR; + int ret; + + ret = gsm_disconnect(gsm); + + if (ret) + return ret; } if (need_restart) gsm_cleanup_mux(gsm); @@ -2613,10 +2625,6 @@ static int gsm_mux_net_close(struct net_device *net) return 0; } -static struct net_device_stats *gsm_mux_net_get_stats(struct net_device *net) -{ - return &((struct gsm_mux_net *)netdev_priv(net))->stats; -} static void dlci_net_free(struct gsm_dlci *dlci) { if (!dlci->net) { @@ -2660,8 +2668,8 @@ static int gsm_mux_net_start_xmit(struct sk_buff *skb, muxnet_get(mux_net); skb_queue_head(&dlci->skb_list, skb); - STATS(net).tx_packets++; - STATS(net).tx_bytes += skb->len; + net->stats.tx_packets++; + net->stats.tx_bytes += skb->len; gsm_dlci_data_kick(dlci); /* And tell the kernel when the last transmit started. */ netif_trans_update(net); @@ -2676,7 +2684,7 @@ static void gsm_mux_net_tx_timeout(struct net_device *net) dev_dbg(&net->dev, "Tx timed out.\n"); /* Update statistics */ - STATS(net).tx_errors++; + net->stats.tx_errors++; } static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, @@ -2691,12 +2699,12 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, skb = dev_alloc_skb(size + NET_IP_ALIGN); if (!skb) { /* We got no receive buffer. */ - STATS(net).rx_dropped++; + net->stats.rx_dropped++; muxnet_put(mux_net); return; } skb_reserve(skb, NET_IP_ALIGN); - memcpy(skb_put(skb, size), in_buf, size); + skb_put_data(skb, in_buf, size); skb->dev = net; skb->protocol = htons(ETH_P_IP); @@ -2705,8 +2713,8 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, netif_rx(skb); /* update out statistics */ - STATS(net).rx_packets++; - STATS(net).rx_bytes += size; + net->stats.rx_packets++; + net->stats.rx_bytes += size; muxnet_put(mux_net); return; } @@ -2718,7 +2726,6 @@ static void gsm_mux_net_init(struct net_device *net) .ndo_stop = gsm_mux_net_close, .ndo_start_xmit = gsm_mux_net_start_xmit, .ndo_tx_timeout = gsm_mux_net_tx_timeout, - .ndo_get_stats = gsm_mux_net_get_stats, }; net->netdev_ops = &gsm_netdev_ops; diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index e94aea8c0d05..7b2a466616d6 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -939,11 +939,11 @@ static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list) return buf; } /* end of n_hdlc_buf_get() */ -static char hdlc_banner[] __initdata = +static const char hdlc_banner[] __initconst = KERN_INFO "HDLC line discipline maxframe=%u\n"; -static char hdlc_register_ok[] __initdata = +static const char hdlc_register_ok[] __initconst = KERN_INFO "N_HDLC line discipline registered.\n"; -static char hdlc_register_fail[] __initdata = +static const char hdlc_register_fail[] __initconst = KERN_ERR "error registering line discipline: %d\n"; static int __init n_hdlc_init(void) @@ -968,9 +968,9 @@ static int __init n_hdlc_init(void) } /* end of init_module() */ -static char hdlc_unregister_ok[] __exitdata = +static const char hdlc_unregister_ok[] __exitdata = KERN_INFO "N_HDLC: line discipline unregistered\n"; -static char hdlc_unregister_fail[] __exitdata = +static const char hdlc_unregister_fail[] __exitdata = KERN_ERR "N_HDLC: can't unregister line discipline (err = %d)\n"; static void __exit n_hdlc_exit(void) diff --git a/drivers/tty/n_null.c b/drivers/tty/n_null.c new file mode 100644 index 000000000000..d63261c36e42 --- /dev/null +++ b/drivers/tty/n_null.c @@ -0,0 +1,80 @@ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/module.h> + +/* + * n_null.c - Null line discipline used in the failure path + * + * Copyright (C) Intel 2017 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +static int n_null_open(struct tty_struct *tty) +{ + return 0; +} + +static void n_null_close(struct tty_struct *tty) +{ +} + +static ssize_t n_null_read(struct tty_struct *tty, struct file *file, + unsigned char __user * buf, size_t nr) +{ + return -EOPNOTSUPP; +} + +static ssize_t n_null_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) +{ + return -EOPNOTSUPP; +} + +static void n_null_receivebuf(struct tty_struct *tty, + const unsigned char *cp, char *fp, + int cnt) +{ +} + +static struct tty_ldisc_ops null_ldisc = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "n_null", + .open = n_null_open, + .close = n_null_close, + .read = n_null_read, + .write = n_null_write, + .receive_buf = n_null_receivebuf +}; + +static int __init n_null_init(void) +{ + BUG_ON(tty_register_ldisc(N_NULL, &null_ldisc)); + return 0; +} + +static void __exit n_null_exit(void) +{ + tty_unregister_ldisc(N_NULL); +} + +module_init(n_null_init); +module_exit(n_null_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alan Cox"); +MODULE_ALIAS_LDISC(N_NULL); +MODULE_DESCRIPTION("Null ldisc driver"); diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 66b59a15780d..284749fb0f6b 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -24,6 +24,9 @@ #include <linux/slab.h> #include <linux/mutex.h> #include <linux/poll.h> +#include <linux/mount.h> +#include <linux/file.h> +#include <linux/ioctl.h> #undef TTY_DEBUG_HANGUP #ifdef TTY_DEBUG_HANGUP @@ -66,8 +69,13 @@ static void pty_close(struct tty_struct *tty, struct file *filp) #ifdef CONFIG_UNIX98_PTYS if (tty->driver == ptm_driver) { mutex_lock(&devpts_mutex); - if (tty->link->driver_data) - devpts_pty_kill(tty->link->driver_data); + if (tty->link->driver_data) { + struct path *path = tty->link->driver_data; + + devpts_pty_kill(path->dentry); + path_put(path); + kfree(path); + } mutex_unlock(&devpts_mutex); } #endif @@ -216,16 +224,11 @@ static int pty_signal(struct tty_struct *tty, int sig) static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; - struct tty_ldisc *ld; if (!to) return; - ld = tty_ldisc_ref(to); - tty_buffer_flush(to, ld); - if (ld) - tty_ldisc_deref(ld); - + tty_buffer_flush(to, NULL); if (to->packet) { spin_lock_irq(&tty->ctrl_lock); tty->ctrl_status |= TIOCPKT_FLUSHWRITE; @@ -486,6 +489,16 @@ static int pty_bsd_ioctl(struct tty_struct *tty, return -ENOIOCTLCMD; } +static long pty_bsd_compat_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + /* + * PTY ioctls don't require any special translation between 32-bit and + * 64-bit userspace, they are already compatible. + */ + return pty_bsd_ioctl(tty, cmd, arg); +} + static int legacy_count = CONFIG_LEGACY_PTY_COUNT; /* * not really modular, but the easiest way to keep compat with existing @@ -507,6 +520,7 @@ static const struct tty_operations master_pty_ops_bsd = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .ioctl = pty_bsd_ioctl, + .compat_ioctl = pty_bsd_compat_ioctl, .cleanup = pty_cleanup, .resize = pty_resize, .remove = pty_remove @@ -590,9 +604,50 @@ static inline void legacy_pty_init(void) { } /* Unix98 devices */ #ifdef CONFIG_UNIX98_PTYS - static struct cdev ptmx_cdev; +/** + * pty_open_peer - open the peer of a pty + * @tty: the peer of the pty being opened + * + * Open the cached dentry in tty->link, providing a safe way for userspace + * to get the slave end of a pty (where they have the master fd and cannot + * access or trust the mount namespace /dev/pts was mounted inside). + */ +static struct file *pty_open_peer(struct tty_struct *tty, int flags) +{ + if (tty->driver->subtype != PTY_TYPE_MASTER) + return ERR_PTR(-EIO); + return dentry_open(tty->link->driver_data, flags, current_cred()); +} + +static int pty_get_peer(struct tty_struct *tty, int flags) +{ + int fd = -1; + struct file *filp = NULL; + int retval = -EINVAL; + + fd = get_unused_fd_flags(0); + if (fd < 0) { + retval = fd; + goto err; + } + + filp = pty_open_peer(tty, flags); + if (IS_ERR(filp)) { + retval = PTR_ERR(filp); + goto err_put; + } + + fd_install(fd, filp); + return fd; + +err_put: + put_unused_fd(fd); +err: + return retval; +} + static int pty_unix98_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -607,6 +662,8 @@ static int pty_unix98_ioctl(struct tty_struct *tty, return pty_get_pktmode(tty, (int __user *)arg); case TIOCGPTN: /* Get PT Number */ return put_user(tty->index, (unsigned int __user *)arg); + case TIOCGPTPEER: /* Open the other end */ + return pty_get_peer(tty, (int) arg); case TIOCSIG: /* Send signal to other side of pty */ return pty_signal(tty, (int) arg); } @@ -614,6 +671,16 @@ static int pty_unix98_ioctl(struct tty_struct *tty, return -ENOIOCTLCMD; } +static long pty_unix98_compat_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + /* + * PTY ioctls don't require any special translation between 32-bit and + * 64-bit userspace, they are already compatible. + */ + return pty_unix98_ioctl(tty, cmd, arg); +} + /** * ptm_unix98_lookup - find a pty master * @driver: ptm driver @@ -686,6 +753,7 @@ static const struct tty_operations ptm_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .ioctl = pty_unix98_ioctl, + .compat_ioctl = pty_unix98_compat_ioctl, .resize = pty_resize, .cleanup = pty_cleanup }; @@ -723,6 +791,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) { struct pts_fs_info *fsi; struct tty_struct *tty; + struct path *pts_path; struct dentry *dentry; int retval; int index; @@ -776,16 +845,26 @@ static int ptmx_open(struct inode *inode, struct file *filp) retval = PTR_ERR(dentry); goto err_release; } - tty->link->driver_data = dentry; + /* We need to cache a fake path for TIOCGPTPEER. */ + pts_path = kmalloc(sizeof(struct path), GFP_KERNEL); + if (!pts_path) + goto err_release; + pts_path->mnt = filp->f_path.mnt; + pts_path->dentry = dentry; + path_get(pts_path); + tty->link->driver_data = pts_path; retval = ptm_driver->ops->open(tty, filp); if (retval) - goto err_release; + goto err_path_put; tty_debug_hangup(tty, "opening (count=%d)\n", tty->count); tty_unlock(tty); return 0; +err_path_put: + path_put(pts_path); + kfree(pts_path); err_release: tty_unlock(tty); // This will also put-ref the fsi diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index d66c1edd9892..20d79a6007d5 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -250,15 +250,15 @@ static int sReadAiopNumChan(WordIO_t io); MODULE_AUTHOR("Theodore Ts'o"); MODULE_DESCRIPTION("Comtrol RocketPort driver"); -module_param(board1, ulong, 0); +module_param_hw(board1, ulong, ioport, 0); MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1"); -module_param(board2, ulong, 0); +module_param_hw(board2, ulong, ioport, 0); MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2"); -module_param(board3, ulong, 0); +module_param_hw(board3, ulong, ioport, 0); MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3"); -module_param(board4, ulong, 0); +module_param_hw(board4, ulong, ioport, 0); MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4"); -module_param(controller, ulong, 0); +module_param_hw(controller, ulong, ioport, 0); MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller"); module_param(support_low_speed, bool, 0); MODULE_PARM_DESC(support_low_speed, "1 means support 50 baud, 0 means support 460400 baud"); @@ -947,18 +947,6 @@ static int rp_open(struct tty_struct *tty, struct file *filp) tty_port_set_initialized(&info->port, 1); - /* - * Set up the tty->alt_speed kludge - */ - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) - tty->alt_speed = 57600; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) - tty->alt_speed = 115200; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) - tty->alt_speed = 230400; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) - tty->alt_speed = 460800; - configure_r_port(tty, info, NULL); if (C_BAUD(tty)) { sSetDTR(cp); @@ -1219,23 +1207,20 @@ static int set_config(struct tty_struct *tty, struct r_port *info, return -EPERM; } info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK)); - configure_r_port(tty, info, NULL); mutex_unlock(&info->port.mutex); return 0; } + if ((new_serial.flags ^ info->flags) & ROCKET_SPD_MASK) { + /* warn about deprecation, unless clearing */ + if (new_serial.flags & ROCKET_SPD_MASK) + dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n"); + } + info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS)); info->port.close_delay = new_serial.close_delay; info->port.closing_wait = new_serial.closing_wait; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) - tty->alt_speed = 57600; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) - tty->alt_speed = 115200; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) - tty->alt_speed = 230400; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) - tty->alt_speed = 460800; mutex_unlock(&info->port.mutex); configure_r_port(tty, info, NULL); diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index f4c6c90add78..ae1aaa0075d1 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -116,6 +116,12 @@ void serdev_device_close(struct serdev_device *serdev) } EXPORT_SYMBOL_GPL(serdev_device_close); +void serdev_device_write_wakeup(struct serdev_device *serdev) +{ + complete(&serdev->write_comp); +} +EXPORT_SYMBOL_GPL(serdev_device_write_wakeup); + int serdev_device_write_buf(struct serdev_device *serdev, const unsigned char *buf, size_t count) { @@ -128,6 +134,36 @@ int serdev_device_write_buf(struct serdev_device *serdev, } EXPORT_SYMBOL_GPL(serdev_device_write_buf); +int serdev_device_write(struct serdev_device *serdev, + const unsigned char *buf, size_t count, + unsigned long timeout) +{ + struct serdev_controller *ctrl = serdev->ctrl; + int ret; + + if (!ctrl || !ctrl->ops->write_buf || + (timeout && !serdev->ops->write_wakeup)) + return -EINVAL; + + mutex_lock(&serdev->write_lock); + do { + reinit_completion(&serdev->write_comp); + + ret = ctrl->ops->write_buf(ctrl, buf, count); + if (ret < 0) + break; + + buf += ret; + count -= ret; + + } while (count && + (timeout = wait_for_completion_timeout(&serdev->write_comp, + timeout))); + mutex_unlock(&serdev->write_lock); + return ret < 0 ? ret : (count ? -ETIMEDOUT : 0); +} +EXPORT_SYMBOL_GPL(serdev_device_write); + void serdev_device_write_flush(struct serdev_device *serdev) { struct serdev_controller *ctrl = serdev->ctrl; @@ -173,6 +209,39 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) } EXPORT_SYMBOL_GPL(serdev_device_set_flow_control); +void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->wait_until_sent) + return; + + ctrl->ops->wait_until_sent(ctrl, timeout); +} +EXPORT_SYMBOL_GPL(serdev_device_wait_until_sent); + +int serdev_device_get_tiocm(struct serdev_device *serdev) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->get_tiocm) + return -ENOTSUPP; + + return ctrl->ops->get_tiocm(ctrl); +} +EXPORT_SYMBOL_GPL(serdev_device_get_tiocm); + +int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->set_tiocm) + return -ENOTSUPP; + + return ctrl->ops->set_tiocm(ctrl, set, clear); +} +EXPORT_SYMBOL_GPL(serdev_device_set_tiocm); + static int serdev_drv_probe(struct device *dev) { const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); @@ -191,16 +260,15 @@ static int serdev_drv_remove(struct device *dev) static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { - ssize_t len = of_device_get_modalias(dev, buf, PAGE_SIZE - 2); - buf[len] = '\n'; - buf[len+1] = 0; - return len+1; + return of_device_modalias(dev, buf, PAGE_SIZE); } +DEVICE_ATTR_RO(modalias); -static struct device_attribute serdev_device_attrs[] = { - __ATTR_RO(modalias), - __ATTR_NULL +static struct attribute *serdev_device_attrs[] = { + &dev_attr_modalias.attr, + NULL, }; +ATTRIBUTE_GROUPS(serdev_device); static struct bus_type serdev_bus_type = { .name = "serial", @@ -208,7 +276,7 @@ static struct bus_type serdev_bus_type = { .probe = serdev_drv_probe, .remove = serdev_drv_remove, .uevent = serdev_uevent, - .dev_attrs = serdev_device_attrs, + .dev_groups = serdev_device_groups, }; /** @@ -232,6 +300,8 @@ struct serdev_device *serdev_device_alloc(struct serdev_controller *ctrl) serdev->dev.parent = &ctrl->dev; serdev->dev.bus = &serdev_bus_type; serdev->dev.type = &serdev_device_type; + init_completion(&serdev->write_comp); + mutex_init(&serdev->write_lock); return serdev; } EXPORT_SYMBOL_GPL(serdev_device_alloc); diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index d05393594f15..302018d67efa 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -14,6 +14,7 @@ #include <linux/serdev.h> #include <linux/tty.h> #include <linux/tty_driver.h> +#include <linux/poll.h> #define SERPORT_ACTIVE 1 @@ -46,11 +47,11 @@ static void ttyport_write_wakeup(struct tty_port *port) struct serdev_controller *ctrl = port->client_data; struct serport *serport = serdev_controller_get_drvdata(ctrl); - if (!test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags)) - return; - - if (test_bit(SERPORT_ACTIVE, &serport->flags)) + if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags) && + test_bit(SERPORT_ACTIVE, &serport->flags)) serdev_controller_write_wakeup(ctrl); + + wake_up_interruptible_poll(&port->tty->write_wait, POLLOUT); } static const struct tty_port_client_operations client_ops = { @@ -101,9 +102,6 @@ static int ttyport_open(struct serdev_controller *ctrl) return PTR_ERR(tty); serport->tty = tty; - serport->port->client_ops = &client_ops; - serport->port->client_data = ctrl; - if (tty->ops->open) tty->ops->open(serport->tty, NULL); else @@ -150,7 +148,7 @@ static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigne /* tty_set_termios() return not checked as it is always 0 */ tty_set_termios(tty, &ktermios); - return speed; + return ktermios.c_ospeed; } static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable) @@ -167,6 +165,36 @@ static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable tty_set_termios(tty, &ktermios); } +static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + tty_wait_until_sent(tty, timeout); +} + +static int ttyport_get_tiocm(struct serdev_controller *ctrl) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + if (!tty->ops->tiocmget) + return -ENOTSUPP; + + return tty->driver->ops->tiocmget(tty); +} + +static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + if (!tty->ops->tiocmset) + return -ENOTSUPP; + + return tty->driver->ops->tiocmset(tty, set, clear); +} + static const struct serdev_controller_ops ctrl_ops = { .write_buf = ttyport_write_buf, .write_flush = ttyport_write_flush, @@ -175,12 +203,16 @@ static const struct serdev_controller_ops ctrl_ops = { .close = ttyport_close, .set_flow_control = ttyport_set_flow_control, .set_baudrate = ttyport_set_baudrate, + .wait_until_sent = ttyport_wait_until_sent, + .get_tiocm = ttyport_get_tiocm, + .set_tiocm = ttyport_set_tiocm, }; struct device *serdev_tty_port_register(struct tty_port *port, struct device *parent, struct tty_driver *drv, int idx) { + const struct tty_port_client_operations *old_ops; struct serdev_controller *ctrl; struct serport *serport; int ret; @@ -199,28 +231,37 @@ struct device *serdev_tty_port_register(struct tty_port *port, ctrl->ops = &ctrl_ops; + old_ops = port->client_ops; + port->client_ops = &client_ops; + port->client_data = ctrl; + ret = serdev_controller_add(ctrl); if (ret) - goto err_controller_put; + goto err_reset_data; dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx); return &ctrl->dev; -err_controller_put: +err_reset_data: + port->client_data = NULL; + port->client_ops = old_ops; serdev_controller_put(ctrl); + return ERR_PTR(ret); } -void serdev_tty_port_unregister(struct tty_port *port) +int serdev_tty_port_unregister(struct tty_port *port) { struct serdev_controller *ctrl = port->client_data; struct serport *serport = serdev_controller_get_drvdata(ctrl); if (!serport) - return; + return -ENODEV; serdev_controller_remove(ctrl); port->client_ops = NULL; port->client_data = NULL; serdev_controller_put(ctrl); + + return 0; } diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index ce8d4ffcc425..b2bdc35f7495 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -81,6 +81,9 @@ struct serial8250_config { #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_CAP_IRDA (1 << 16) /* UART supports IrDA line discipline */ +#define UART_CAP_MINI (1 << 17) /* Mini UART on BCM283X family lacks: + * STOP PARITY EPAR SPAR WLEN5 WLEN6 + */ #define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c new file mode 100644 index 000000000000..822be4906763 --- /dev/null +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -0,0 +1,323 @@ +/* + * Serial Port driver for Aspeed VUART device + * + * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp. + * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. + * + * 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. + */ +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/clk.h> + +#include "8250.h" + +#define ASPEED_VUART_GCRA 0x20 +#define ASPEED_VUART_GCRA_VUART_EN BIT(0) +#define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5) +#define ASPEED_VUART_GCRB 0x24 +#define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4) +#define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT 4 +#define ASPEED_VUART_ADDRL 0x28 +#define ASPEED_VUART_ADDRH 0x2c + +struct aspeed_vuart { + struct device *dev; + void __iomem *regs; + struct clk *clk; + int line; +}; + +/* + * The VUART is basically two UART 'front ends' connected by their FIFO + * (no actual serial line in between). One is on the BMC side (management + * controller) and one is on the host CPU side. + * + * It allows the BMC to provide to the host a "UART" that pipes into + * the BMC itself and can then be turned by the BMC into a network console + * of some sort for example. + * + * This driver is for the BMC side. The sysfs files allow the BMC + * userspace which owns the system configuration policy, to specify + * at what IO port and interrupt number the host side will appear + * to the host on the Host <-> BMC LPC bus. It could be different on a + * different system (though most of them use 3f8/4). + */ + +static ssize_t lpc_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + u16 addr; + + addr = (readb(vuart->regs + ASPEED_VUART_ADDRH) << 8) | + (readb(vuart->regs + ASPEED_VUART_ADDRL)); + + return snprintf(buf, PAGE_SIZE - 1, "0x%x\n", addr); +} + +static ssize_t lpc_address_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 0, &val); + if (err) + return err; + + writeb(val >> 8, vuart->regs + ASPEED_VUART_ADDRH); + writeb(val >> 0, vuart->regs + ASPEED_VUART_ADDRL); + + return count; +} + +static DEVICE_ATTR_RW(lpc_address); + +static ssize_t sirq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + u8 reg; + + reg = readb(vuart->regs + ASPEED_VUART_GCRB); + reg &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + reg >>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; + + return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg); +} + +static ssize_t sirq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + unsigned long val; + int err; + u8 reg; + + err = kstrtoul(buf, 0, &val); + if (err) + return err; + + val <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; + val &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + + reg = readb(vuart->regs + ASPEED_VUART_GCRB); + reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + reg |= val; + writeb(reg, vuart->regs + ASPEED_VUART_GCRB); + + return count; +} + +static DEVICE_ATTR_RW(sirq); + +static struct attribute *aspeed_vuart_attrs[] = { + &dev_attr_sirq.attr, + &dev_attr_lpc_address.attr, + NULL, +}; + +static const struct attribute_group aspeed_vuart_attr_group = { + .attrs = aspeed_vuart_attrs, +}; + +static void aspeed_vuart_set_enabled(struct aspeed_vuart *vuart, bool enabled) +{ + u8 reg = readb(vuart->regs + ASPEED_VUART_GCRA); + + if (enabled) + reg |= ASPEED_VUART_GCRA_VUART_EN; + else + reg &= ~ASPEED_VUART_GCRA_VUART_EN; + + writeb(reg, vuart->regs + ASPEED_VUART_GCRA); +} + +static void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart *vuart, + bool discard) +{ + u8 reg; + + reg = readb(vuart->regs + ASPEED_VUART_GCRA); + + /* If the DISABLE_HOST_TX_DISCARD bit is set, discard is disabled */ + if (!discard) + reg |= ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; + else + reg &= ~ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; + + writeb(reg, vuart->regs + ASPEED_VUART_GCRA); +} + +static int aspeed_vuart_startup(struct uart_port *uart_port) +{ + struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); + struct aspeed_vuart *vuart = uart_8250_port->port.private_data; + int rc; + + rc = serial8250_do_startup(uart_port); + if (rc) + return rc; + + aspeed_vuart_set_host_tx_discard(vuart, false); + + return 0; +} + +static void aspeed_vuart_shutdown(struct uart_port *uart_port) +{ + struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); + struct aspeed_vuart *vuart = uart_8250_port->port.private_data; + + aspeed_vuart_set_host_tx_discard(vuart, true); + + serial8250_do_shutdown(uart_port); +} + +static int aspeed_vuart_probe(struct platform_device *pdev) +{ + struct uart_8250_port port; + struct aspeed_vuart *vuart; + struct device_node *np; + struct resource *res; + u32 clk, prop; + int rc; + + np = pdev->dev.of_node; + + vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL); + if (!vuart) + return -ENOMEM; + + vuart->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + vuart->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(vuart->regs)) + return PTR_ERR(vuart->regs); + + memset(&port, 0, sizeof(port)); + port.port.private_data = vuart; + port.port.membase = vuart->regs; + port.port.mapbase = res->start; + port.port.mapsize = resource_size(res); + port.port.startup = aspeed_vuart_startup; + port.port.shutdown = aspeed_vuart_shutdown; + port.port.dev = &pdev->dev; + + rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); + if (rc < 0) + return rc; + + if (of_property_read_u32(np, "clock-frequency", &clk)) { + vuart->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(vuart->clk)) { + dev_warn(&pdev->dev, + "clk or clock-frequency not defined\n"); + return PTR_ERR(vuart->clk); + } + + rc = clk_prepare_enable(vuart->clk); + if (rc < 0) + return rc; + + clk = clk_get_rate(vuart->clk); + } + + /* If current-speed was set, then try not to change it. */ + if (of_property_read_u32(np, "current-speed", &prop) == 0) + port.port.custom_divisor = clk / (16 * prop); + + /* Check for shifted address mapping */ + if (of_property_read_u32(np, "reg-offset", &prop) == 0) + port.port.mapbase += prop; + + /* Check for registers offset within the devices address range */ + if (of_property_read_u32(np, "reg-shift", &prop) == 0) + port.port.regshift = prop; + + /* Check for fifo size */ + if (of_property_read_u32(np, "fifo-size", &prop) == 0) + port.port.fifosize = prop; + + /* Check for a fixed line number */ + rc = of_alias_get_id(np, "serial"); + if (rc >= 0) + port.port.line = rc; + + port.port.irq = irq_of_parse_and_map(np, 0); + port.port.irqflags = IRQF_SHARED; + port.port.iotype = UPIO_MEM; + port.port.type = PORT_16550A; + port.port.uartclk = clk; + port.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF + | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_NO_THRE_TEST; + + if (of_property_read_bool(np, "no-loopback-test")) + port.port.flags |= UPF_SKIP_TEST; + + if (port.port.fifosize) + port.capabilities = UART_CAP_FIFO; + + if (of_property_read_bool(np, "auto-flow-control")) + port.capabilities |= UART_CAP_AFE; + + rc = serial8250_register_8250_port(&port); + if (rc < 0) + goto err_clk_disable; + + vuart->line = rc; + + aspeed_vuart_set_enabled(vuart, true); + aspeed_vuart_set_host_tx_discard(vuart, true); + platform_set_drvdata(pdev, vuart); + + return 0; + +err_clk_disable: + clk_disable_unprepare(vuart->clk); + irq_dispose_mapping(port.port.irq); + return rc; +} + +static int aspeed_vuart_remove(struct platform_device *pdev) +{ + struct aspeed_vuart *vuart = platform_get_drvdata(pdev); + + aspeed_vuart_set_enabled(vuart, false); + serial8250_unregister_port(vuart->line); + sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); + clk_disable_unprepare(vuart->clk); + + return 0; +} + +static const struct of_device_id aspeed_vuart_table[] = { + { .compatible = "aspeed,ast2400-vuart" }, + { .compatible = "aspeed,ast2500-vuart" }, + { }, +}; + +static struct platform_driver aspeed_vuart_driver = { + .driver = { + .name = "aspeed-vuart", + .of_match_table = aspeed_vuart_table, + }, + .probe = aspeed_vuart_probe, + .remove = aspeed_vuart_remove, +}; + +module_platform_driver(aspeed_vuart_driver); + +MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Driver for Aspeed VUART device"); diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index e10f1244409b..a23c7da42ea8 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -39,7 +39,7 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev) /* initialize data */ spin_lock_init(&data->uart.port.lock); - data->uart.capabilities = UART_CAP_FIFO; + data->uart.capabilities = UART_CAP_FIFO | UART_CAP_MINI; data->uart.port.dev = &pdev->dev; data->uart.port.regshift = 2; data->uart.port.type = PORT_16550; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 76e03a7de9cc..1aab3010fbfa 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -218,7 +218,7 @@ static int serial_link_irq_chain(struct uart_8250_port *up) spin_unlock_irq(&i->lock); irq_flags |= up->port.irqflags; ret = request_irq(up->port.irq, serial8250_interrupt, - irq_flags, "serial", i); + irq_flags, up->port.name, i); if (ret < 0) serial_do_unlink(i, up); } @@ -1191,7 +1191,7 @@ module_exit(serial8250_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); -module_param(share_irqs, uint, 0644); +module_param_hw(share_irqs, uint, other, 0644); MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices (unsafe)"); module_param(nr_uarts, uint, 0644); @@ -1201,7 +1201,7 @@ module_param(skip_txen_test, uint, 0644); MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); #ifdef CONFIG_SERIAL_8250_RSA -module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444); +module_param_hw_array(probe_rsa, ulong, ioport, &probe_rsa_count, 0444); MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); #endif MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index e65808c482f1..787b1160d3a5 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -530,12 +530,11 @@ static int dw8250_probe(struct platform_device *pdev) } data->rst = devm_reset_control_get_optional(dev, NULL); - if (IS_ERR(data->rst) && PTR_ERR(data->rst) == -EPROBE_DEFER) { - err = -EPROBE_DEFER; + if (IS_ERR(data->rst)) { + err = PTR_ERR(data->rst); goto err_pclk; } - if (!IS_ERR(data->rst)) - reset_control_deassert(data->rst); + reset_control_deassert(data->rst); dw8250_quirks(p, data); @@ -567,8 +566,7 @@ static int dw8250_probe(struct platform_device *pdev) return 0; err_reset: - if (!IS_ERR(data->rst)) - reset_control_assert(data->rst); + reset_control_assert(data->rst); err_pclk: if (!IS_ERR(data->pclk)) @@ -589,8 +587,7 @@ static int dw8250_remove(struct platform_device *pdev) serial8250_unregister_port(data->line); - if (!IS_ERR(data->rst)) - reset_control_assert(data->rst); + reset_control_assert(data->rst); if (!IS_ERR(data->pclk)) clk_disable_unprepare(data->pclk); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index 85a12f032402..82fc48eca1df 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -39,6 +39,7 @@ static unsigned int __init serial8250_early_in(struct uart_port *port, int offset) { + int reg_offset = offset; offset <<= port->regshift; switch (port->iotype) { @@ -52,6 +53,8 @@ static unsigned int __init serial8250_early_in(struct uart_port *port, int offse return ioread32be(port->membase + offset); case UPIO_PORT: return inb(port->iobase + offset); + case UPIO_AU: + return port->serial_in(port, reg_offset); default: return 0; } @@ -59,6 +62,7 @@ static unsigned int __init serial8250_early_in(struct uart_port *port, int offse static void __init serial8250_early_out(struct uart_port *port, int offset, int value) { + int reg_offset = offset; offset <<= port->regshift; switch (port->iotype) { @@ -77,6 +81,9 @@ static void __init serial8250_early_out(struct uart_port *port, int offset, int case UPIO_PORT: outb(value, port->iobase + offset); break; + case UPIO_AU: + port->serial_out(port, reg_offset, value); + break; } } @@ -172,3 +179,20 @@ OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup); OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup); #endif + +#ifdef CONFIG_SERIAL_8250_RT288X + +unsigned int au_serial_in(struct uart_port *p, int offset); +void au_serial_out(struct uart_port *p, int offset, int value); + +static int __init early_au_setup(struct earlycon_device *dev, const char *opt) +{ + dev->port.serial_in = au_serial_in; + dev->port.serial_out = au_serial_out; + dev->port.iotype = UPIO_AU; + dev->con->write = early_serial8250_write; + return 0; +} +OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup); + +#endif diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index b89c4ffc0486..c6360fbdf808 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -9,10 +9,13 @@ * 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/acpi.h> +#include <linux/dmi.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/property.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> #include <linux/slab.h> @@ -60,8 +63,50 @@ #define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */ #define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */ +#define UART_EXAR_RS485_DLY(x) ((x) << 4) + +/* + * IOT2040 MPIO wiring semantics: + * + * MPIO Port Function + * ---- ---- -------- + * 0 2 Mode bit 0 + * 1 2 Mode bit 1 + * 2 2 Terminate bus + * 3 - <reserved> + * 4 3 Mode bit 0 + * 5 3 Mode bit 1 + * 6 3 Terminate bus + * 7 - <reserved> + * 8 2 Enable + * 9 3 Enable + * 10 - Red LED + * 11..15 - <unused> + */ + +/* IOT2040 MPIOs 0..7 */ +#define IOT2040_UART_MODE_RS232 0x01 +#define IOT2040_UART_MODE_RS485 0x02 +#define IOT2040_UART_MODE_RS422 0x03 +#define IOT2040_UART_TERMINATE_BUS 0x04 + +#define IOT2040_UART1_MASK 0x0f +#define IOT2040_UART2_SHIFT 4 + +#define IOT2040_UARTS_DEFAULT_MODE 0x11 /* both RS232 */ +#define IOT2040_UARTS_GPIO_LO_MODE 0x88 /* reserved pins as input */ + +/* IOT2040 MPIOs 8..15 */ +#define IOT2040_UARTS_ENABLE 0x03 +#define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */ + struct exar8250; +struct exar8250_platform { + int (*rs485_config)(struct uart_port *, struct serial_rs485 *); + int (*register_gpio)(struct pci_dev *, struct uart_8250_port *); +}; + /** * struct exar8250_board - board information * @num_ports: number of serial ports @@ -109,7 +154,6 @@ pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, u8 __iomem *p; int err; - port->port.flags |= UPF_EXAR_EFR; port->port.uartclk = baud * 16; err = default_setup(priv, pcidev, idx, offset, port); @@ -171,24 +215,32 @@ pci_xr17c154_setup(struct exar8250 *priv, struct pci_dev *pcidev, return default_setup(priv, pcidev, idx, offset, port); } -static void setup_gpio(u8 __iomem *p) +static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p) { + /* + * The Commtech adapters required the MPIOs to be driven low. The Exar + * devices will export them as GPIOs, so we pre-configure them safely + * as inputs. + */ + u8 dir = pcidev->vendor == PCI_VENDOR_ID_EXAR ? 0xff : 0x00; + writeb(0x00, p + UART_EXAR_MPIOINT_7_0); writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); writeb(0x00, p + UART_EXAR_MPIO3T_7_0); writeb(0x00, p + UART_EXAR_MPIOINV_7_0); - writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); + writeb(dir, p + UART_EXAR_MPIOSEL_7_0); writeb(0x00, p + UART_EXAR_MPIOOD_7_0); writeb(0x00, p + UART_EXAR_MPIOINT_15_8); writeb(0x00, p + UART_EXAR_MPIOLVL_15_8); writeb(0x00, p + UART_EXAR_MPIO3T_15_8); writeb(0x00, p + UART_EXAR_MPIOINV_15_8); - writeb(0x00, p + UART_EXAR_MPIOSEL_15_8); + writeb(dir, p + UART_EXAR_MPIOSEL_15_8); writeb(0x00, p + UART_EXAR_MPIOOD_15_8); } static void * -xr17v35x_register_gpio(struct pci_dev *pcidev) +__xr17v35x_register_gpio(struct pci_dev *pcidev, + const struct property_entry *properties) { struct platform_device *pdev; @@ -196,8 +248,11 @@ xr17v35x_register_gpio(struct pci_dev *pcidev) if (!pdev) return NULL; - platform_set_drvdata(pdev, pcidev); - if (platform_device_add(pdev) < 0) { + pdev->dev.parent = &pcidev->dev; + ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev)); + + if (platform_device_add_properties(pdev, properties) < 0 || + platform_device_add(pdev) < 0) { platform_device_put(pdev); return NULL; } @@ -205,17 +260,131 @@ xr17v35x_register_gpio(struct pci_dev *pcidev) return pdev; } +static const struct property_entry exar_gpio_properties[] = { + PROPERTY_ENTRY_U32("exar,first-pin", 0), + PROPERTY_ENTRY_U32("ngpios", 16), + { } +}; + +static int xr17v35x_register_gpio(struct pci_dev *pcidev, + struct uart_8250_port *port) +{ + if (pcidev->vendor == PCI_VENDOR_ID_EXAR) + port->port.private_data = + __xr17v35x_register_gpio(pcidev, exar_gpio_properties); + + return 0; +} + +static const struct exar8250_platform exar8250_default_platform = { + .register_gpio = xr17v35x_register_gpio, +}; + +static int iot2040_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485) +{ + bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); + u8 __iomem *p = port->membase; + u8 mask = IOT2040_UART1_MASK; + u8 mode, value; + + if (is_rs485) { + if (rs485->flags & SER_RS485_RX_DURING_TX) + mode = IOT2040_UART_MODE_RS422; + else + mode = IOT2040_UART_MODE_RS485; + + if (rs485->flags & SER_RS485_TERMINATE_BUS) + mode |= IOT2040_UART_TERMINATE_BUS; + } else { + mode = IOT2040_UART_MODE_RS232; + } + + if (port->line == 3) { + mask <<= IOT2040_UART2_SHIFT; + mode <<= IOT2040_UART2_SHIFT; + } + + value = readb(p + UART_EXAR_MPIOLVL_7_0); + value &= ~mask; + value |= mode; + writeb(value, p + UART_EXAR_MPIOLVL_7_0); + + value = readb(p + UART_EXAR_FCTR); + if (is_rs485) + value |= UART_FCTR_EXAR_485; + else + value &= ~UART_FCTR_EXAR_485; + writeb(value, p + UART_EXAR_FCTR); + + if (is_rs485) + writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); + + port->rs485 = *rs485; + + return 0; +} + +static const struct property_entry iot2040_gpio_properties[] = { + PROPERTY_ENTRY_U32("exar,first-pin", 10), + PROPERTY_ENTRY_U32("ngpios", 1), + { } +}; + +static int iot2040_register_gpio(struct pci_dev *pcidev, + struct uart_8250_port *port) +{ + u8 __iomem *p = port->port.membase; + + writeb(IOT2040_UARTS_DEFAULT_MODE, p + UART_EXAR_MPIOLVL_7_0); + writeb(IOT2040_UARTS_GPIO_LO_MODE, p + UART_EXAR_MPIOSEL_7_0); + writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8); + writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8); + + port->port.private_data = + __xr17v35x_register_gpio(pcidev, iot2040_gpio_properties); + + return 0; +} + +static const struct exar8250_platform iot2040_platform = { + .rs485_config = iot2040_rs485_config, + .register_gpio = iot2040_register_gpio, +}; + +static const struct dmi_system_id exar_platforms[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), + DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG, + "6ES7647-0AA00-1YA2"), + }, + .driver_data = (void *)&iot2040_platform, + }, + {} +}; + static int pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx) { const struct exar8250_board *board = priv->board; + const struct exar8250_platform *platform; + const struct dmi_system_id *dmi_match; unsigned int offset = idx * 0x400; unsigned int baud = 7812500; u8 __iomem *p; int ret; + dmi_match = dmi_first_match(exar_platforms); + if (dmi_match) + platform = dmi_match->driver_data; + else + platform = &exar8250_default_platform; + port->port.uartclk = baud * 16; + port->port.rs485_config = platform->rs485_config; + /* * Setup the uart clock for the devices on expansion slot to * half the clock speed of the main chip (which is 125MHz) @@ -236,12 +405,12 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, if (idx == 0) { /* Setup Multipurpose Input/Output pins. */ - setup_gpio(p); + setup_gpio(pcidev, p); - port->port.private_data = xr17v35x_register_gpio(pcidev); + ret = platform->register_gpio(pcidev, port); } - return 0; + return ret; } static void pci_xr17v35x_exit(struct pci_dev *pcidev) @@ -483,5 +652,5 @@ static struct pci_driver exar_pci_driver = { module_pci_driver(exar_pci_driver); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Exar Serial Dricer"); +MODULE_DESCRIPTION("Exar Serial Driver"); MODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>"); diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index b67e7a544935..e500f7dd2470 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -61,6 +61,12 @@ * The IRQ setting mode of F81866 is not the same with F81216 series. * Level/Low: IRQ_MODE0:0, IRQ_MODE1:0 * Edge/High: IRQ_MODE0:1, IRQ_MODE1:0 + * + * Clock speeds for UART (register F2h) + * 00: 1.8432MHz. + * 01: 18.432MHz. + * 10: 24MHz. + * 11: 14.769MHz. */ #define F81866_IRQ_MODE 0xf0 #define F81866_IRQ_SHARE BIT(0) @@ -72,6 +78,13 @@ #define F81866_LDN_LOW 0x10 #define F81866_LDN_HIGH 0x16 +#define F81866_UART_CLK 0xF2 +#define F81866_UART_CLK_MASK (BIT(1) | BIT(0)) +#define F81866_UART_CLK_1_8432MHZ 0 +#define F81866_UART_CLK_14_769MHZ (BIT(1) | BIT(0)) +#define F81866_UART_CLK_18_432MHZ BIT(0) +#define F81866_UART_CLK_24MHZ BIT(1) + struct fintek_8250 { u16 pid; u16 base_port; @@ -256,8 +269,26 @@ static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) } } -static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address, - unsigned int irq) +static void fintek_8250_goto_highspeed(struct uart_8250_port *uart, + struct fintek_8250 *pdata) +{ + sio_write_reg(pdata, LDN, pdata->index); + + switch (pdata->pid) { + case CHIP_ID_F81866: /* set uart clock for high speed serial mode */ + sio_write_mask_reg(pdata, F81866_UART_CLK, + F81866_UART_CLK_MASK, + F81866_UART_CLK_14_769MHZ); + + uart->port.uartclk = 921600 * 16; + break; + default: /* leave clock speed untouched */ + break; + } +} + +static int probe_setup_port(struct fintek_8250 *pdata, + struct uart_8250_port *uart) { static const u16 addr[] = {0x4e, 0x2e}; static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67}; @@ -284,18 +315,20 @@ static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address, sio_write_reg(pdata, LDN, k); aux = sio_read_reg(pdata, IO_ADDR1); aux |= sio_read_reg(pdata, IO_ADDR2) << 8; - if (aux != io_address) + if (aux != uart->port.iobase) continue; pdata->index = k; - irq_data = irq_get_irq_data(irq); + irq_data = irq_get_irq_data(uart->port.irq); if (irq_data) level_mode = irqd_is_level_type(irq_data); fintek_8250_set_irq_mode(pdata, level_mode); fintek_8250_set_max_fifo(pdata); + fintek_8250_goto_highspeed(uart, pdata); + fintek_8250_exit_key(addr[i]); return 0; @@ -330,7 +363,7 @@ int fintek_8250_probe(struct uart_8250_port *uart) struct fintek_8250 *pdata; struct fintek_8250 probe_data; - if (probe_setup_port(&probe_data, uart->port.iobase, uart->port.irq)) + if (probe_setup_port(&probe_data, uart)) return -ENODEV; pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_KERNEL); diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index f3ea90f0e411..7dddd7e6a01c 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -183,7 +183,6 @@ static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) if (ret) return; - pci_set_master(pdev); pci_try_set_mwi(pdev); /* Special DMA address for UART */ @@ -216,6 +215,8 @@ static int qrk_serial_setup(struct lpss8250 *lpss, struct uart_port *port) struct pci_dev *pdev = to_pci_dev(port->dev); int ret; + pci_set_master(pdev); + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); if (ret < 0) return ret; diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 1cbadafc6889..0cf95fddccfc 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -19,11 +19,13 @@ #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/clk.h> +#include <linux/reset.h> #include "8250.h" struct of_serial_info { struct clk *clk; + struct reset_control *rst; int type; int line; }; @@ -132,6 +134,13 @@ static int of_platform_serial_setup(struct platform_device *ofdev, } } + info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL); + if (IS_ERR(info->rst)) + goto out; + ret = reset_control_deassert(info->rst); + if (ret) + goto out; + port->type = type; port->uartclk = clk; port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP @@ -229,6 +238,7 @@ static int of_platform_serial_remove(struct platform_device *ofdev) serial8250_unregister_port(info->line); + reset_control_assert(info->rst); if (info->clk) clk_disable_unprepare(info->clk); kfree(info); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index e7e64913a748..833771bca0a5 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -613,6 +613,10 @@ static int omap_8250_startup(struct uart_port *port) up->lsr_saved_flags = 0; up->msr_saved_flags = 0; + /* Disable DMA for console UART */ + if (uart_console(port)) + up->dma = NULL; + if (up->dma) { ret = serial8250_request_dma(up); if (ret) { @@ -782,8 +786,27 @@ unlock: static void __dma_rx_complete(void *param) { - __dma_rx_do_complete(param); - omap_8250_rx_dma(param); + struct uart_8250_port *p = param; + struct uart_8250_dma *dma = p->dma; + struct dma_tx_state state; + unsigned long flags; + + spin_lock_irqsave(&p->port.lock, flags); + + /* + * If the tx status is not DMA_COMPLETE, then this is a delayed + * completion callback. A previous RX timeout flush would have + * already pushed the data, so exit. + */ + if (dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state) != + DMA_COMPLETE) { + spin_unlock_irqrestore(&p->port.lock, flags); + return; + } + __dma_rx_do_complete(p); + omap_8250_rx_dma(p); + + spin_unlock_irqrestore(&p->port.lock, flags); } static void omap_8250_rx_dma_flush(struct uart_8250_port *p) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 6119516ef5fc..a5fe0e66c607 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -47,6 +47,7 @@ /* * These are definitions for the Exar XR17V35X and XR17(C|D)15X */ +#define UART_EXAR_INT0 0x80 #define UART_EXAR_SLEEP 0x8b /* Sleep mode */ #define UART_EXAR_DVID 0x8d /* Device identification */ @@ -328,7 +329,7 @@ static const s8 au_io_out_map[8] = { -1, /* UART_SCR (unmapped) */ }; -static unsigned int au_serial_in(struct uart_port *p, int offset) +unsigned int au_serial_in(struct uart_port *p, int offset) { if (offset >= ARRAY_SIZE(au_io_in_map)) return UINT_MAX; @@ -338,7 +339,7 @@ static unsigned int au_serial_in(struct uart_port *p, int offset) return __raw_readl(p->membase + (offset << p->regshift)); } -static void au_serial_out(struct uart_port *p, int offset, int value) +void au_serial_out(struct uart_port *p, int offset, int value) { if (offset >= ARRAY_SIZE(au_io_out_map)) return; @@ -1337,7 +1338,7 @@ out_lock: /* * Check if the device is a Fintek F81216A */ - if (port->type == PORT_16550A) + if (port->type == PORT_16550A && port->iotype == UPIO_PORT) fintek_8250_probe(up); if (up->capabilities != old_capabilities) { @@ -1763,6 +1764,10 @@ void serial8250_tx_chars(struct uart_8250_port *up) if ((up->capabilities & UART_CAP_HFIFO) && (serial_in(up, UART_LSR) & BOTH_EMPTY) != BOTH_EMPTY) break; + /* The BCM2835 MINI UART THRE bit is really a not-full bit. */ + if ((up->capabilities & UART_CAP_MINI) && + !(serial_in(up, UART_LSR) & UART_LSR_THRE)) + break; } while (--count > 0); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -1869,17 +1874,13 @@ static int serial8250_default_handle_irq(struct uart_port *port) static int exar_handle_irq(struct uart_port *port) { unsigned int iir = serial_port_in(port, UART_IIR); - int ret; + int ret = 0; - ret = serial8250_handle_irq(port, iir); + if (((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X)) && + serial_port_in(port, UART_EXAR_INT0) != 0) + ret = 1; - if ((port->type == PORT_XR17V35X) || - (port->type == PORT_XR17D15X)) { - serial_port_in(port, 0x80); - serial_port_in(port, 0x81); - serial_port_in(port, 0x82); - serial_port_in(port, 0x83); - } + ret |= serial8250_handle_irq(port, iir); return ret; } @@ -2177,6 +2178,8 @@ int serial8250_do_startup(struct uart_port *port) serial_port_in(port, UART_RX); serial_port_in(port, UART_IIR); serial_port_in(port, UART_MSR); + if ((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X)) + serial_port_in(port, UART_EXAR_INT0); /* * At this point, there's no way the LSR could still be 0xff; @@ -2229,7 +2232,7 @@ int serial8250_do_startup(struct uart_port *port) } } - if (port->irq) { + if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) { unsigned char iir1; /* * Test for UARTs that do not reassert THRE when the @@ -2335,6 +2338,8 @@ dont_test_tx_en: serial_port_in(port, UART_RX); serial_port_in(port, UART_IIR); serial_port_in(port, UART_MSR); + if ((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X)) + serial_port_in(port, UART_EXAR_INT0); up->lsr_saved_flags = 0; up->msr_saved_flags = 0; @@ -2584,6 +2589,12 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long flags; unsigned int baud, quot, frac = 0; + if (up->capabilities & UART_CAP_MINI) { + termios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CMSPAR); + if ((termios->c_cflag & CSIZE) == CS5 || + (termios->c_cflag & CSIZE) == CS6) + termios->c_cflag = (termios->c_cflag & ~CSIZE) | CS7; + } cval = serial8250_compute_lcr(up, termios->c_cflag); baud = serial8250_get_baud_rate(port, termios, old); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 0e3f529d50e9..a1161ec0256f 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -224,6 +224,16 @@ config SERIAL_8250_ACCENT To compile this driver as a module, choose M here: the module will be called 8250_accent. +config SERIAL_8250_ASPEED_VUART + tristate "Aspeed Virtual UART" + depends on SERIAL_8250 + depends on OF + help + If you want to use the virtual UART (VUART) device on Aspeed + BMC platforms, enable this option. This enables the 16550A- + compatible device on the local LPC bus, giving a UART device + with no physical RS232 connections. + config SERIAL_8250_BOCA tristate "Support Boca cards" depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 2f30f9ecdb1b..a44a99a3e623 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o +obj-$(CONFIG_SERIAL_8250_ASPEED_VUART) += 8250_aspeed_vuart.o obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 6117ac8da48f..1f096e2bb398 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -114,32 +114,32 @@ config SERIAL_SB1250_DUART_CONSOLE If unsure, say Y. config SERIAL_ATMEL - bool "AT91 / AT32 on-chip serial port support" + bool "AT91 on-chip serial port support" depends on HAS_DMA - depends on ARCH_AT91 || AVR32 || COMPILE_TEST + depends on ARCH_AT91 || COMPILE_TEST select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB help This enables the driver for the on-chip UARTs of the Atmel - AT91 and AT32 processors. + AT91 processors. config SERIAL_ATMEL_CONSOLE - bool "Support for console on AT91 / AT32 serial port" + bool "Support for console on AT91 serial port" depends on SERIAL_ATMEL=y select SERIAL_CORE_CONSOLE help Say Y here if you wish to use an on-chip UART on a Atmel - AT91 or AT32 processor as the system console (the system + AT91 processor 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). config SERIAL_ATMEL_PDC - bool "Support DMA transfers on AT91 / AT32 serial port" + bool "Support DMA transfers on AT91 serial port" depends on SERIAL_ATMEL default y help Say Y here if you wish to use the PDC to do DMA transfers to - and from the Atmel AT91 / AT32 serial port. In order to + and from the Atmel AT91 serial port. In order to actually use DMA transfers, make sure that the use_dma_tx and use_dma_rx members in the atmel_uart_data struct is set appropriately for each port. @@ -152,7 +152,7 @@ config SERIAL_ATMEL_TTYAT bool "Install as device ttyATn instead of ttySn" depends on SERIAL_ATMEL=y help - Say Y here if you wish to have the internal AT91 / AT32 UARTs + Say Y here if you wish to have the internal AT91 UARTs appear as /dev/ttyATn (major 204, minor starting at 154) instead of the normal /dev/ttySn (major 4, minor starting at 64). This is necessary if you also want other UARTs, such as @@ -630,6 +630,15 @@ config SERIAL_UARTLITE_CONSOLE console (the system console is the device which receives all kernel messages and warnings and which allows logins in single user mode). +config SERIAL_UARTLITE_NR_UARTS + int "Maximum number of uartlite serial ports" + depends on SERIAL_UARTLITE + range 1 256 + default 1 + help + Set this to the number of uartlites in your system, or the number + you think you might implement. + config SERIAL_SUNCORE bool depends on SPARC @@ -1343,6 +1352,7 @@ config SERIAL_ALTERA_JTAGUART_CONSOLE bool "Altera JTAG UART console support" depends on SERIAL_ALTERA_JTAGUART=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Enable a Altera JTAG UART port to be the system console. @@ -1382,6 +1392,7 @@ config SERIAL_ALTERA_UART_CONSOLE bool "Altera UART console support" depends on SERIAL_ALTERA_UART=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Enable a Altera UART port to be the system console. @@ -1677,6 +1688,25 @@ config SERIAL_MVEBU_CONSOLE and warnings and which allows logins in single user mode) Otherwise, say 'N'. +config SERIAL_OWL + bool "Actions Semi Owl serial port support" + depends on ARCH_ACTIONS || COMPILE_TEST + select SERIAL_CORE + help + This driver is for Actions Semiconductor S500/S900 SoC's UART. + Say 'Y' here if you wish to use the on-board serial port. + Otherwise, say 'N'. + +config SERIAL_OWL_CONSOLE + bool "Console on Actions Semi Owl serial port" + depends on SERIAL_OWL=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + default y + help + Say 'Y' here if you wish to use Actions Semiconductor S500/S900 UART + as the system console. Only earlycon is implemented currently. + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 2d6288bc4554..fe88a75d9a59 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -3,7 +3,6 @@ # obj-$(CONFIG_SERIAL_CORE) += serial_core.o -obj-$(CONFIG_SERIAL_21285) += 21285.o obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o @@ -17,6 +16,8 @@ obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o +obj-$(CONFIG_SERIAL_21285) += 21285.o + # Now bring in any enabled 8250/16450/16550 type drivers. obj-$(CONFIG_SERIAL_8250) += 8250/ @@ -91,6 +92,7 @@ obj-$(CONFIG_SERIAL_STM32) += stm32-usart.o obj-$(CONFIG_SERIAL_MVEBU_UART) += mvebu-uart.o obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o +obj-$(CONFIG_SERIAL_OWL) += owl-uart.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index e409d7dac7ab..0475f5d261ce 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -383,6 +383,26 @@ console_initcall(altera_jtaguart_console_init); #define ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console) +static void altera_jtaguart_earlycon_write(struct console *co, const char *s, + unsigned int count) +{ + struct earlycon_device *dev = co->data; + + uart_console_write(&dev->port, s, count, altera_jtaguart_console_putc); +} + +static int __init altera_jtaguart_earlycon_setup(struct earlycon_device *dev, + const char *options) +{ + if (!dev->port.membase) + return -ENODEV; + + dev->con->write = altera_jtaguart_earlycon_write; + return 0; +} + +OF_EARLYCON_DECLARE(juart, "altr,juart-1.0", altera_jtaguart_earlycon_setup); + #else #define ALTERA_JTAGUART_CONSOLE NULL @@ -458,6 +478,7 @@ static int altera_jtaguart_remove(struct platform_device *pdev) port = &altera_jtaguart_ports[i].port; uart_remove_one_port(&altera_jtaguart_driver, port); + iounmap(port->membase); return 0; } diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 820a74208696..3e4b717670d7 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -489,6 +489,38 @@ console_initcall(altera_uart_console_init); #define ALTERA_UART_CONSOLE (&altera_uart_console) +static void altera_uart_earlycon_write(struct console *co, const char *s, + unsigned int count) +{ + struct earlycon_device *dev = co->data; + + uart_console_write(&dev->port, s, count, altera_uart_console_putc); +} + +static int __init altera_uart_earlycon_setup(struct earlycon_device *dev, + const char *options) +{ + struct uart_port *port = &dev->port; + + if (!port->membase) + return -ENODEV; + + /* Enable RX interrupts now */ + writel(ALTERA_UART_CONTROL_RRDY_MSK, + port->membase + ALTERA_UART_CONTROL_REG); + + if (dev->baud) { + unsigned int baudclk = port->uartclk / dev->baud; + + writel(baudclk, port->membase + ALTERA_UART_DIVISOR_REG); + } + + dev->con->write = altera_uart_earlycon_write; + return 0; +} + +OF_EARLYCON_DECLARE(uart, "altr,uart-1.0", altera_uart_earlycon_setup); + #else #define ALTERA_UART_CONSOLE NULL @@ -583,6 +615,7 @@ static int altera_uart_remove(struct platform_device *pdev) if (port) { uart_remove_one_port(&altera_uart_driver, port); port->mapbase = 0; + iounmap(port->membase); } return 0; diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index f2f251075109..24180adb1cbb 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -697,6 +697,7 @@ static struct console amba_console = { #define AMBA_CONSOLE NULL #endif +static DEFINE_MUTEX(amba_reg_lock); static struct uart_driver amba_reg = { .owner = THIS_MODULE, .driver_name = "ttyAM", @@ -749,6 +750,19 @@ static int pl010_probe(struct amba_device *dev, const struct amba_id *id) amba_ports[i] = uap; amba_set_drvdata(dev, uap); + + mutex_lock(&amba_reg_lock); + if (!amba_reg.state) { + ret = uart_register_driver(&amba_reg); + if (ret < 0) { + mutex_unlock(&amba_reg_lock); + dev_err(uap->port.dev, + "Failed to register AMBA-PL010 driver\n"); + return ret; + } + } + mutex_unlock(&amba_reg_lock); + ret = uart_add_one_port(&amba_reg, &uap->port); if (ret) amba_ports[i] = NULL; @@ -760,12 +774,18 @@ static int pl010_remove(struct amba_device *dev) { struct uart_amba_port *uap = amba_get_drvdata(dev); int i; + bool busy = false; uart_remove_one_port(&amba_reg, &uap->port); for (i = 0; i < ARRAY_SIZE(amba_ports); i++) if (amba_ports[i] == uap) amba_ports[i] = NULL; + else if (amba_ports[i]) + busy = true; + + if (!busy) + uart_unregister_driver(&amba_reg); return 0; } @@ -816,23 +836,14 @@ static struct amba_driver pl010_driver = { static int __init pl010_init(void) { - int ret; - printk(KERN_INFO "Serial: AMBA driver\n"); - ret = uart_register_driver(&amba_reg); - if (ret == 0) { - ret = amba_driver_register(&pl010_driver); - if (ret) - uart_unregister_driver(&amba_reg); - } - return ret; + return amba_driver_register(&pl010_driver); } static void __exit pl010_exit(void) { amba_driver_unregister(&pl010_driver); - uart_unregister_driver(&amba_reg); } module_init(pl010_init); diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index b0a377725d63..1888d168a41c 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -142,15 +142,7 @@ static struct vendor_data vendor_sbsa = { .fixed_options = true, }; -/* - * Erratum 44 for QDF2432v1 and QDF2400v1 SoCs describes the BUSY bit as - * occasionally getting stuck as 1. To avoid the potential for a hang, check - * TXFE == 0 instead of BUSY == 1. This may not be suitable for all UART - * implementations, so only do so if an affected platform is detected in - * parse_spcr(). - */ -static bool qdf2400_e44_present = false; - +#ifdef CONFIG_ACPI_SPCR_TABLE static struct vendor_data vendor_qdt_qdf2400_e44 = { .reg_offset = pl011_std_offsets, .fr_busy = UART011_FR_TXFE, @@ -165,6 +157,7 @@ static struct vendor_data vendor_qdt_qdf2400_e44 = { .always_enabled = true, .fixed_options = true, }; +#endif static u16 pl011_st_offsets[REG_ARRAY_SIZE] = { [REG_DR] = UART01x_DR, @@ -1327,14 +1320,15 @@ static void pl011_stop_tx(struct uart_port *port) pl011_dma_tx_stop(uap); } -static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq); +static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq); /* Start TX with programmed I/O only (no DMA) */ static void pl011_start_tx_pio(struct uart_amba_port *uap) { - uap->im |= UART011_TXIM; - pl011_write(uap->im, uap, REG_IMSC); - pl011_tx_chars(uap, false); + if (pl011_tx_chars(uap, false)) { + uap->im |= UART011_TXIM; + pl011_write(uap->im, uap, REG_IMSC); + } } static void pl011_start_tx(struct uart_port *port) @@ -1414,25 +1408,26 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, return true; } -static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) +/* Returns true if tx interrupts have to be (kept) enabled */ +static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) { struct circ_buf *xmit = &uap->port.state->xmit; int count = uap->fifosize >> 1; if (uap->port.x_char) { if (!pl011_tx_char(uap, uap->port.x_char, from_irq)) - return; + return true; uap->port.x_char = 0; --count; } if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { pl011_stop_tx(&uap->port); - return; + return false; } /* If we are using DMA mode, try to send some characters. */ if (pl011_dma_tx_irq(uap)) - return; + return true; do { if (likely(from_irq) && count-- == 0) @@ -1447,8 +1442,11 @@ static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); - if (uart_circ_empty(xmit)) + if (uart_circ_empty(xmit)) { pl011_stop_tx(&uap->port); + return false; + } + return true; } static void pl011_modem_status(struct uart_amba_port *uap) @@ -2370,12 +2368,14 @@ static int __init pl011_console_match(struct console *co, char *name, int idx, resource_size_t addr; int i; - if (strcmp(name, "qdf2400_e44") == 0) { - pr_info_once("UART: Working around QDF2400 SoC erratum 44"); - qdf2400_e44_present = true; - } else if (strcmp(name, "pl011") != 0) { + /* + * Systems affected by the Qualcomm Technologies QDF2400 E44 erratum + * have a distinct console name, so make sure we check for that. + * The actual implementation of the erratum occurs in the probe + * function. + */ + if ((strcmp(name, "qdf2400_e44") != 0) && (strcmp(name, "pl011") != 0)) return -ENODEV; - } if (uart_parse_earlycon(options, &iotype, &addr, &options)) return -ENODEV; @@ -2470,19 +2470,34 @@ static int __init pl011_early_console_setup(struct earlycon_device *device, if (!device->port.membase) return -ENODEV; - /* On QDF2400 SOCs affected by Erratum 44, the "qdf2400_e44" must - * also be specified, e.g. "earlycon=pl011,<address>,qdf2400_e44". - */ - if (!strcmp(device->options, "qdf2400_e44")) - device->con->write = qdf2400_e44_early_write; - else - device->con->write = pl011_early_write; + device->con->write = pl011_early_write; return 0; } OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", pl011_early_console_setup); -EARLYCON_DECLARE(qdf2400_e44, pl011_early_console_setup); + +/* + * On Qualcomm Datacenter Technologies QDF2400 SOCs affected by + * Erratum 44, traditional earlycon can be enabled by specifying + * "earlycon=qdf2400_e44,<address>". Any options are ignored. + * + * Alternatively, you can just specify "earlycon", and the early console + * will be enabled with the information from the SPCR table. In this + * case, the SPCR code will detect the need for the E44 work-around, + * and set the console name to "qdf2400_e44". + */ +static int __init +qdf2400_e44_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = qdf2400_e44_early_write; + return 0; +} +EARLYCON_DECLARE(qdf2400_e44, qdf2400_e44_early_console_setup); #else #define AMBA_CONSOLE NULL @@ -2714,11 +2729,17 @@ static int sbsa_uart_probe(struct platform_device *pdev) } uap->port.irq = ret; - uap->reg_offset = vendor_sbsa.reg_offset; - uap->vendor = qdf2400_e44_present ? - &vendor_qdt_qdf2400_e44 : &vendor_sbsa; +#ifdef CONFIG_ACPI_SPCR_TABLE + if (qdf2400_e44_present) { + dev_info(&pdev->dev, "working around QDF2400 SoC erratum 44\n"); + uap->vendor = &vendor_qdt_qdf2400_e44; + } else +#endif + uap->vendor = &vendor_sbsa; + + uap->reg_offset = uap->vendor->reg_offset; uap->fifosize = 32; - uap->port.iotype = vendor_sbsa.access_32b ? UPIO_MEM32 : UPIO_MEM; + uap->port.iotype = uap->vendor->access_32b ? UPIO_MEM32 : UPIO_MEM; uap->port.ops = &sbsa_uart_pops; uap->fixed_baud = baudrate; diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 1f50a83ef958..7551cab438ff 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1,5 +1,5 @@ /* - * Driver for Atmel AT91 / AT32 Serial ports + * Driver for Atmel AT91 Serial ports * Copyright (C) 2003 Rick Bronson * * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. @@ -38,7 +38,6 @@ #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/atmel_pdc.h> -#include <linux/atmel_serial.h> #include <linux/uaccess.h> #include <linux/platform_data/atmel.h> #include <linux/timer.h> @@ -47,6 +46,7 @@ #include <linux/err.h> #include <linux/irq.h> #include <linux/suspend.h> +#include <linux/mm.h> #include <asm/io.h> #include <asm/ioctls.h> @@ -71,6 +71,7 @@ #include <linux/serial_core.h> #include "serial_mctrl_gpio.h" +#include "atmel_serial.h" static void atmel_start_rx(struct uart_port *port); static void atmel_stop_rx(struct uart_port *port); @@ -118,9 +119,9 @@ struct atmel_uart_char { /* * at91: 6 USARTs and one DBGU port (SAM9260) - * avr32: 4 + * samx7: 3 USARTs and 5 UARTs */ -#define ATMEL_MAX_UART 7 +#define ATMEL_MAX_UART 8 /* * We wrap our port structure around the generic uart_port. @@ -175,6 +176,7 @@ struct atmel_uart_port { unsigned int pending_status; spinlock_t lock_suspended; +#ifdef CONFIG_PM struct { u32 cr; u32 mr; @@ -185,6 +187,7 @@ struct atmel_uart_port { u32 fmr; u32 fimr; } cache; +#endif int (*prepare_rx)(struct uart_port *port); int (*prepare_tx)(struct uart_port *port); @@ -225,21 +228,6 @@ static inline void atmel_uart_writel(struct uart_port *port, u32 reg, u32 value) __raw_writel(value, port->membase + reg); } -#ifdef CONFIG_AVR32 - -/* AVR32 cannot handle 8 or 16bit I/O accesses but only 32bit I/O accesses */ -static inline u8 atmel_uart_read_char(struct uart_port *port) -{ - return __raw_readl(port->membase + ATMEL_US_RHR); -} - -static inline void atmel_uart_write_char(struct uart_port *port, u8 value) -{ - __raw_writel(value, port->membase + ATMEL_US_THR); -} - -#else - static inline u8 atmel_uart_read_char(struct uart_port *port) { return __raw_readb(port->membase + ATMEL_US_RHR); @@ -250,8 +238,6 @@ static inline void atmel_uart_write_char(struct uart_port *port, u8 value) __raw_writeb(value, port->membase + ATMEL_US_THR); } -#endif - #ifdef CONFIG_SERIAL_ATMEL_PDC static bool atmel_use_pdc_rx(struct uart_port *port) { @@ -956,7 +942,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port) sg_set_page(&atmel_port->sg_tx, virt_to_page(port->state->xmit.buf), UART_XMIT_SIZE, - (unsigned long)port->state->xmit.buf & ~PAGE_MASK); + offset_in_page(port->state->xmit.buf)); nent = dma_map_sg(port->dev, &atmel_port->sg_tx, 1, @@ -1138,7 +1124,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port) sg_set_page(&atmel_port->sg_rx, virt_to_page(ring->buf), sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE, - (unsigned long)ring->buf & ~PAGE_MASK); + offset_in_page(ring->buf)); nent = dma_map_sg(port->dev, &atmel_port->sg_rx, 1, @@ -1652,72 +1638,56 @@ static void atmel_init_property(struct atmel_uart_port *atmel_port, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); - - if (np) { - /* DMA/PDC usage specification */ - if (of_property_read_bool(np, "atmel,use-dma-rx")) { - if (of_property_read_bool(np, "dmas")) { - atmel_port->use_dma_rx = true; - atmel_port->use_pdc_rx = false; - } else { - atmel_port->use_dma_rx = false; - atmel_port->use_pdc_rx = true; - } + + /* DMA/PDC usage specification */ + if (of_property_read_bool(np, "atmel,use-dma-rx")) { + if (of_property_read_bool(np, "dmas")) { + atmel_port->use_dma_rx = true; + atmel_port->use_pdc_rx = false; } else { atmel_port->use_dma_rx = false; - atmel_port->use_pdc_rx = false; + atmel_port->use_pdc_rx = true; } + } else { + atmel_port->use_dma_rx = false; + atmel_port->use_pdc_rx = false; + } - if (of_property_read_bool(np, "atmel,use-dma-tx")) { - if (of_property_read_bool(np, "dmas")) { - atmel_port->use_dma_tx = true; - atmel_port->use_pdc_tx = false; - } else { - atmel_port->use_dma_tx = false; - atmel_port->use_pdc_tx = true; - } + if (of_property_read_bool(np, "atmel,use-dma-tx")) { + if (of_property_read_bool(np, "dmas")) { + atmel_port->use_dma_tx = true; + atmel_port->use_pdc_tx = false; } else { atmel_port->use_dma_tx = false; - atmel_port->use_pdc_tx = false; + atmel_port->use_pdc_tx = true; } - } else { - atmel_port->use_pdc_rx = pdata->use_dma_rx; - atmel_port->use_pdc_tx = pdata->use_dma_tx; - atmel_port->use_dma_rx = false; atmel_port->use_dma_tx = false; + atmel_port->use_pdc_tx = false; } - } static void atmel_init_rs485(struct uart_port *port, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); - - if (np) { - struct serial_rs485 *rs485conf = &port->rs485; - u32 rs485_delay[2]; - /* rs485 properties */ - if (of_property_read_u32_array(np, "rs485-rts-delay", - rs485_delay, 2) == 0) { - rs485conf->delay_rts_before_send = rs485_delay[0]; - rs485conf->delay_rts_after_send = rs485_delay[1]; - rs485conf->flags = 0; - } - if (of_get_property(np, "rs485-rx-during-tx", NULL)) - rs485conf->flags |= SER_RS485_RX_DURING_TX; + struct serial_rs485 *rs485conf = &port->rs485; + u32 rs485_delay[2]; - if (of_get_property(np, "linux,rs485-enabled-at-boot-time", - NULL)) - rs485conf->flags |= SER_RS485_ENABLED; - } else { - port->rs485 = pdata->rs485; + /* rs485 properties */ + if (of_property_read_u32_array(np, "rs485-rts-delay", + rs485_delay, 2) == 0) { + rs485conf->delay_rts_before_send = rs485_delay[0]; + rs485conf->delay_rts_after_send = rs485_delay[1]; + rs485conf->flags = 0; } + if (of_get_property(np, "rs485-rx-during-tx", NULL)) + rs485conf->flags |= SER_RS485_RX_DURING_TX; + + if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL)) + rs485conf->flags |= SER_RS485_ENABLED; } static void atmel_set_ops(struct uart_port *port) @@ -2399,7 +2369,6 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, { int ret; struct uart_port *port = &atmel_port->uart; - struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); atmel_init_property(atmel_port, pdev); atmel_set_ops(port); @@ -2407,24 +2376,17 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, atmel_init_rs485(port, pdev); port->iotype = UPIO_MEM; - port->flags = UPF_BOOT_AUTOCONF; + port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; port->ops = &atmel_pops; port->fifosize = 1; port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; port->rs485_config = atmel_config_rs485; + port->membase = NULL; memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring)); - if (pdata && pdata->regs) { - /* Already mapped by setup code */ - port->membase = pdata->regs; - } else { - port->flags |= UPF_IOREMAP; - port->membase = NULL; - } - /* for console, the clock could already be configured */ if (!atmel_port->clk) { atmel_port->clk = clk_get(&pdev->dev, "usart"); @@ -2457,8 +2419,6 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, return 0; } -struct platform_device *atmel_default_console_device; /* the serial console device */ - #ifdef CONFIG_SERIAL_ATMEL_CONSOLE static void atmel_console_putchar(struct uart_port *port, int ch) { @@ -2591,47 +2551,6 @@ static struct console atmel_console = { #define ATMEL_CONSOLE_DEVICE (&atmel_console) -/* - * Early console initialization (before VM subsystem initialized). - */ -static int __init atmel_console_init(void) -{ - int ret; - if (atmel_default_console_device) { - struct atmel_uart_data *pdata = - dev_get_platdata(&atmel_default_console_device->dev); - int id = pdata->num; - struct atmel_uart_port *atmel_port = &atmel_ports[id]; - - atmel_port->backup_imr = 0; - atmel_port->uart.line = id; - - add_preferred_console(ATMEL_DEVICENAME, id, NULL); - ret = atmel_init_port(atmel_port, atmel_default_console_device); - if (ret) - return ret; - register_console(&atmel_console); - } - - return 0; -} - -console_initcall(atmel_console_init); - -/* - * Late console initialization. - */ -static int __init atmel_late_console_init(void) -{ - if (atmel_default_console_device - && !(atmel_console.flags & CON_ENABLED)) - register_console(&atmel_console); - - return 0; -} - -core_initcall(atmel_late_console_init); - static inline bool atmel_is_console_port(struct uart_port *port) { return port->cons && port->cons->index == port->line; @@ -2801,19 +2720,13 @@ static int atmel_serial_probe(struct platform_device *pdev) { struct atmel_uart_port *atmel_port; struct device_node *np = pdev->dev.of_node; - struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); void *data; int ret = -ENODEV; bool rs485_enabled; BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); - if (np) - ret = of_alias_get_id(np, "serial"); - else - if (pdata) - ret = pdata->num; - + ret = of_alias_get_id(np, "serial"); if (ret < 0) /* port id not found in platform data nor device-tree aliases: * auto-enumerate it */ diff --git a/drivers/tty/serial/atmel_serial.h b/drivers/tty/serial/atmel_serial.h new file mode 100644 index 000000000000..bd2560502f3c --- /dev/null +++ b/drivers/tty/serial/atmel_serial.h @@ -0,0 +1,169 @@ +/* + * include/linux/atmel_serial.h + * + * Copyright (C) 2005 Ivan Kokshaysky + * Copyright (C) SAN People + * + * USART registers. + * Based on AT91RM9200 datasheet revision E. + * + * 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. + */ + +#ifndef ATMEL_SERIAL_H +#define ATMEL_SERIAL_H + +#define ATMEL_US_CR 0x00 /* Control Register */ +#define ATMEL_US_RSTRX BIT(2) /* Reset Receiver */ +#define ATMEL_US_RSTTX BIT(3) /* Reset Transmitter */ +#define ATMEL_US_RXEN BIT(4) /* Receiver Enable */ +#define ATMEL_US_RXDIS BIT(5) /* Receiver Disable */ +#define ATMEL_US_TXEN BIT(6) /* Transmitter Enable */ +#define ATMEL_US_TXDIS BIT(7) /* Transmitter Disable */ +#define ATMEL_US_RSTSTA BIT(8) /* Reset Status Bits */ +#define ATMEL_US_STTBRK BIT(9) /* Start Break */ +#define ATMEL_US_STPBRK BIT(10) /* Stop Break */ +#define ATMEL_US_STTTO BIT(11) /* Start Time-out */ +#define ATMEL_US_SENDA BIT(12) /* Send Address */ +#define ATMEL_US_RSTIT BIT(13) /* Reset Iterations */ +#define ATMEL_US_RSTNACK BIT(14) /* Reset Non Acknowledge */ +#define ATMEL_US_RETTO BIT(15) /* Rearm Time-out */ +#define ATMEL_US_DTREN BIT(16) /* Data Terminal Ready Enable */ +#define ATMEL_US_DTRDIS BIT(17) /* Data Terminal Ready Disable */ +#define ATMEL_US_RTSEN BIT(18) /* Request To Send Enable */ +#define ATMEL_US_RTSDIS BIT(19) /* Request To Send Disable */ +#define ATMEL_US_TXFCLR BIT(24) /* Transmit FIFO Clear */ +#define ATMEL_US_RXFCLR BIT(25) /* Receive FIFO Clear */ +#define ATMEL_US_TXFLCLR BIT(26) /* Transmit FIFO Lock Clear */ +#define ATMEL_US_FIFOEN BIT(30) /* FIFO enable */ +#define ATMEL_US_FIFODIS BIT(31) /* FIFO disable */ + +#define ATMEL_US_MR 0x04 /* Mode Register */ +#define ATMEL_US_USMODE GENMASK(3, 0) /* Mode of the USART */ +#define ATMEL_US_USMODE_NORMAL 0 +#define ATMEL_US_USMODE_RS485 1 +#define ATMEL_US_USMODE_HWHS 2 +#define ATMEL_US_USMODE_MODEM 3 +#define ATMEL_US_USMODE_ISO7816_T0 4 +#define ATMEL_US_USMODE_ISO7816_T1 6 +#define ATMEL_US_USMODE_IRDA 8 +#define ATMEL_US_USCLKS GENMASK(5, 4) /* Clock Selection */ +#define ATMEL_US_USCLKS_MCK (0 << 4) +#define ATMEL_US_USCLKS_MCK_DIV8 (1 << 4) +#define ATMEL_US_USCLKS_SCK (3 << 4) +#define ATMEL_US_CHRL GENMASK(7, 6) /* Character Length */ +#define ATMEL_US_CHRL_5 (0 << 6) +#define ATMEL_US_CHRL_6 (1 << 6) +#define ATMEL_US_CHRL_7 (2 << 6) +#define ATMEL_US_CHRL_8 (3 << 6) +#define ATMEL_US_SYNC BIT(8) /* Synchronous Mode Select */ +#define ATMEL_US_PAR GENMASK(11, 9) /* Parity Type */ +#define ATMEL_US_PAR_EVEN (0 << 9) +#define ATMEL_US_PAR_ODD (1 << 9) +#define ATMEL_US_PAR_SPACE (2 << 9) +#define ATMEL_US_PAR_MARK (3 << 9) +#define ATMEL_US_PAR_NONE (4 << 9) +#define ATMEL_US_PAR_MULTI_DROP (6 << 9) +#define ATMEL_US_NBSTOP GENMASK(13, 12) /* Number of Stop Bits */ +#define ATMEL_US_NBSTOP_1 (0 << 12) +#define ATMEL_US_NBSTOP_1_5 (1 << 12) +#define ATMEL_US_NBSTOP_2 (2 << 12) +#define ATMEL_US_CHMODE GENMASK(15, 14) /* Channel Mode */ +#define ATMEL_US_CHMODE_NORMAL (0 << 14) +#define ATMEL_US_CHMODE_ECHO (1 << 14) +#define ATMEL_US_CHMODE_LOC_LOOP (2 << 14) +#define ATMEL_US_CHMODE_REM_LOOP (3 << 14) +#define ATMEL_US_MSBF BIT(16) /* Bit Order */ +#define ATMEL_US_MODE9 BIT(17) /* 9-bit Character Length */ +#define ATMEL_US_CLKO BIT(18) /* Clock Output Select */ +#define ATMEL_US_OVER BIT(19) /* Oversampling Mode */ +#define ATMEL_US_INACK BIT(20) /* Inhibit Non Acknowledge */ +#define ATMEL_US_DSNACK BIT(21) /* Disable Successive NACK */ +#define ATMEL_US_MAX_ITER GENMASK(26, 24) /* Max Iterations */ +#define ATMEL_US_FILTER BIT(28) /* Infrared Receive Line Filter */ + +#define ATMEL_US_IER 0x08 /* Interrupt Enable Register */ +#define ATMEL_US_RXRDY BIT(0) /* Receiver Ready */ +#define ATMEL_US_TXRDY BIT(1) /* Transmitter Ready */ +#define ATMEL_US_RXBRK BIT(2) /* Break Received / End of Break */ +#define ATMEL_US_ENDRX BIT(3) /* End of Receiver Transfer */ +#define ATMEL_US_ENDTX BIT(4) /* End of Transmitter Transfer */ +#define ATMEL_US_OVRE BIT(5) /* Overrun Error */ +#define ATMEL_US_FRAME BIT(6) /* Framing Error */ +#define ATMEL_US_PARE BIT(7) /* Parity Error */ +#define ATMEL_US_TIMEOUT BIT(8) /* Receiver Time-out */ +#define ATMEL_US_TXEMPTY BIT(9) /* Transmitter Empty */ +#define ATMEL_US_ITERATION BIT(10) /* Max number of Repetitions Reached */ +#define ATMEL_US_TXBUFE BIT(11) /* Transmission Buffer Empty */ +#define ATMEL_US_RXBUFF BIT(12) /* Reception Buffer Full */ +#define ATMEL_US_NACK BIT(13) /* Non Acknowledge */ +#define ATMEL_US_RIIC BIT(16) /* Ring Indicator Input Change */ +#define ATMEL_US_DSRIC BIT(17) /* Data Set Ready Input Change */ +#define ATMEL_US_DCDIC BIT(18) /* Data Carrier Detect Input Change */ +#define ATMEL_US_CTSIC BIT(19) /* Clear to Send Input Change */ +#define ATMEL_US_RI BIT(20) /* RI */ +#define ATMEL_US_DSR BIT(21) /* DSR */ +#define ATMEL_US_DCD BIT(22) /* DCD */ +#define ATMEL_US_CTS BIT(23) /* CTS */ + +#define ATMEL_US_IDR 0x0c /* Interrupt Disable Register */ +#define ATMEL_US_IMR 0x10 /* Interrupt Mask Register */ +#define ATMEL_US_CSR 0x14 /* Channel Status Register */ +#define ATMEL_US_RHR 0x18 /* Receiver Holding Register */ +#define ATMEL_US_THR 0x1c /* Transmitter Holding Register */ +#define ATMEL_US_SYNH BIT(15) /* Transmit/Receive Sync */ + +#define ATMEL_US_BRGR 0x20 /* Baud Rate Generator Register */ +#define ATMEL_US_CD GENMASK(15, 0) /* Clock Divider */ +#define ATMEL_US_FP_OFFSET 16 /* Fractional Part */ +#define ATMEL_US_FP_MASK 0x7 + +#define ATMEL_US_RTOR 0x24 /* Receiver Time-out Register for USART */ +#define ATMEL_UA_RTOR 0x28 /* Receiver Time-out Register for UART */ +#define ATMEL_US_TO GENMASK(15, 0) /* Time-out Value */ + +#define ATMEL_US_TTGR 0x28 /* Transmitter Timeguard Register */ +#define ATMEL_US_TG GENMASK(7, 0) /* Timeguard Value */ + +#define ATMEL_US_FIDI 0x40 /* FI DI Ratio Register */ +#define ATMEL_US_NER 0x44 /* Number of Errors Register */ +#define ATMEL_US_IF 0x4c /* IrDA Filter Register */ + +#define ATMEL_US_CMPR 0x90 /* Comparaison Register */ +#define ATMEL_US_FMR 0xa0 /* FIFO Mode Register */ +#define ATMEL_US_TXRDYM(data) (((data) & 0x3) << 0) /* TX Ready Mode */ +#define ATMEL_US_RXRDYM(data) (((data) & 0x3) << 4) /* RX Ready Mode */ +#define ATMEL_US_ONE_DATA 0x0 +#define ATMEL_US_TWO_DATA 0x1 +#define ATMEL_US_FOUR_DATA 0x2 +#define ATMEL_US_FRTSC BIT(7) /* FIFO RTS pin Control */ +#define ATMEL_US_TXFTHRES(thr) (((thr) & 0x3f) << 8) /* TX FIFO Threshold */ +#define ATMEL_US_RXFTHRES(thr) (((thr) & 0x3f) << 16) /* RX FIFO Threshold */ +#define ATMEL_US_RXFTHRES2(thr) (((thr) & 0x3f) << 24) /* RX FIFO Threshold2 */ + +#define ATMEL_US_FLR 0xa4 /* FIFO Level Register */ +#define ATMEL_US_TXFL(reg) (((reg) >> 0) & 0x3f) /* TX FIFO Level */ +#define ATMEL_US_RXFL(reg) (((reg) >> 16) & 0x3f) /* RX FIFO Level */ + +#define ATMEL_US_FIER 0xa8 /* FIFO Interrupt Enable Register */ +#define ATMEL_US_FIDR 0xac /* FIFO Interrupt Disable Register */ +#define ATMEL_US_FIMR 0xb0 /* FIFO Interrupt Mask Register */ +#define ATMEL_US_FESR 0xb4 /* FIFO Event Status Register */ +#define ATMEL_US_TXFEF BIT(0) /* Transmit FIFO Empty Flag */ +#define ATMEL_US_TXFFF BIT(1) /* Transmit FIFO Full Flag */ +#define ATMEL_US_TXFTHF BIT(2) /* Transmit FIFO Threshold Flag */ +#define ATMEL_US_RXFEF BIT(3) /* Receive FIFO Empty Flag */ +#define ATMEL_US_RXFFF BIT(4) /* Receive FIFO Full Flag */ +#define ATMEL_US_RXFTHF BIT(5) /* Receive FIFO Threshold Flag */ +#define ATMEL_US_TXFPTEF BIT(6) /* Transmit FIFO Pointer Error Flag */ +#define ATMEL_US_RXFPTEF BIT(7) /* Receive FIFO Pointer Error Flag */ +#define ATMEL_US_TXFLOCK BIT(8) /* Transmit FIFO Lock (FESR only) */ +#define ATMEL_US_RXFTHF2 BIT(9) /* Receive FIFO Threshold Flag 2 */ + +#define ATMEL_US_NAME 0xf0 /* Ip Name */ +#define ATMEL_US_VERSION 0xfc /* Ip Version */ + +#endif diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c index ebd8569f9ad5..9fff25be87f9 100644 --- a/drivers/tty/serial/efm32-uart.c +++ b/drivers/tty/serial/efm32-uart.c @@ -27,6 +27,7 @@ #define UARTn_FRAME 0x04 #define UARTn_FRAME_DATABITS__MASK 0x000f #define UARTn_FRAME_DATABITS(n) ((n) - 3) +#define UARTn_FRAME_PARITY__MASK 0x0300 #define UARTn_FRAME_PARITY_NONE 0x0000 #define UARTn_FRAME_PARITY_EVEN 0x0200 #define UARTn_FRAME_PARITY_ODD 0x0300 @@ -572,12 +573,16 @@ static void efm32_uart_console_get_options(struct efm32_uart_port *efm_port, 16 * (4 + (clkdiv >> 6))); frame = efm32_uart_read32(efm_port, UARTn_FRAME); - if (frame & UARTn_FRAME_PARITY_ODD) + switch (frame & UARTn_FRAME_PARITY__MASK) { + case UARTn_FRAME_PARITY_ODD: *parity = 'o'; - else if (frame & UARTn_FRAME_PARITY_EVEN) + break; + case UARTn_FRAME_PARITY_EVEN: *parity = 'e'; - else + break; + default: *parity = 'n'; + } *bits = (frame & UARTn_FRAME_DATABITS__MASK) - UARTn_FRAME_DATABITS(4) + 4; diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index f02934ffb329..898dcb091a27 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -140,6 +140,8 @@ #define UARTBAUD_SBNS 0x00002000 #define UARTBAUD_SBR 0x00000000 #define UARTBAUD_SBR_MASK 0x1fff +#define UARTBAUD_OSR_MASK 0x1f +#define UARTBAUD_OSR_SHIFT 24 #define UARTSTAT_LBKDIF 0x80000000 #define UARTSTAT_RXEDGIF 0x40000000 @@ -231,12 +233,14 @@ #define DEV_NAME "ttyLP" #define UART_NR 6 +/* IMX lpuart has four extra unused regs located at the beginning */ +#define IMX_REG_OFF 0x10 + struct lpuart_port { struct uart_port port; struct clk *clk; unsigned int txfifo_size; unsigned int rxfifo_size; - bool lpuart32; bool lpuart_dma_tx_use; bool lpuart_dma_rx_use; @@ -258,13 +262,28 @@ struct lpuart_port { wait_queue_head_t dma_wait; }; +struct lpuart_soc_data { + char iotype; + u8 reg_off; +}; + +static const struct lpuart_soc_data vf_data = { + .iotype = UPIO_MEM, +}; + +static const struct lpuart_soc_data ls_data = { + .iotype = UPIO_MEM32BE, +}; + +static struct lpuart_soc_data imx_data = { + .iotype = UPIO_MEM32, + .reg_off = IMX_REG_OFF, +}; + static const struct of_device_id lpuart_dt_ids[] = { - { - .compatible = "fsl,vf610-lpuart", - }, - { - .compatible = "fsl,ls1021a-lpuart", - }, + { .compatible = "fsl,vf610-lpuart", .data = &vf_data, }, + { .compatible = "fsl,ls1021a-lpuart", .data = &ls_data, }, + { .compatible = "fsl,imx7ulp-lpuart", .data = &imx_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, lpuart_dt_ids); @@ -272,14 +291,29 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids); /* Forward declare this for the dma callbacks*/ static void lpuart_dma_tx_complete(void *arg); -static u32 lpuart32_read(void __iomem *addr) +static inline u32 lpuart32_read(struct uart_port *port, u32 off) { - return ioread32be(addr); + switch (port->iotype) { + case UPIO_MEM32: + return readl(port->membase + off); + case UPIO_MEM32BE: + return ioread32be(port->membase + off); + default: + return 0; + } } -static void lpuart32_write(u32 val, void __iomem *addr) +static inline void lpuart32_write(struct uart_port *port, u32 val, + u32 off) { - iowrite32be(val, addr); + switch (port->iotype) { + case UPIO_MEM32: + writel(val, port->membase + off); + break; + case UPIO_MEM32BE: + iowrite32be(val, port->membase + off); + break; + } } static void lpuart_stop_tx(struct uart_port *port) @@ -295,9 +329,9 @@ static void lpuart32_stop_tx(struct uart_port *port) { unsigned long temp; - temp = lpuart32_read(port->membase + UARTCTRL); + temp = lpuart32_read(port, UARTCTRL); temp &= ~(UARTCTRL_TIE | UARTCTRL_TCIE); - lpuart32_write(temp, port->membase + UARTCTRL); + lpuart32_write(port, temp, UARTCTRL); } static void lpuart_stop_rx(struct uart_port *port) @@ -312,8 +346,8 @@ static void lpuart32_stop_rx(struct uart_port *port) { unsigned long temp; - temp = lpuart32_read(port->membase + UARTCTRL); - lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL); + temp = lpuart32_read(port, UARTCTRL); + lpuart32_write(port, temp & ~UARTCTRL_RE, UARTCTRL); } static void lpuart_dma_tx(struct lpuart_port *sport) @@ -512,14 +546,14 @@ static inline void lpuart32_transmit_buffer(struct lpuart_port *sport) struct circ_buf *xmit = &sport->port.state->xmit; unsigned long txcnt; - txcnt = lpuart32_read(sport->port.membase + UARTWATER); + txcnt = lpuart32_read(&sport->port, UARTWATER); txcnt = txcnt >> UARTWATER_TXCNT_OFF; txcnt &= UARTWATER_COUNT_MASK; while (!uart_circ_empty(xmit) && (txcnt < sport->txfifo_size)) { - lpuart32_write(xmit->buf[xmit->tail], sport->port.membase + UARTDATA); + lpuart32_write(&sport->port, xmit->buf[xmit->tail], UARTDATA); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); sport->port.icount.tx++; - txcnt = lpuart32_read(sport->port.membase + UARTWATER); + txcnt = lpuart32_read(&sport->port, UARTWATER); txcnt = txcnt >> UARTWATER_TXCNT_OFF; txcnt &= UARTWATER_COUNT_MASK; } @@ -555,10 +589,10 @@ static void lpuart32_start_tx(struct uart_port *port) struct lpuart_port *sport = container_of(port, struct lpuart_port, port); unsigned long temp; - temp = lpuart32_read(port->membase + UARTCTRL); - lpuart32_write(temp | UARTCTRL_TIE, port->membase + UARTCTRL); + temp = lpuart32_read(port, UARTCTRL); + lpuart32_write(port, temp | UARTCTRL_TIE, UARTCTRL); - if (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TDRE) + if (lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE) lpuart32_transmit_buffer(sport); } @@ -581,10 +615,16 @@ static unsigned int lpuart_tx_empty(struct uart_port *port) static unsigned int lpuart32_tx_empty(struct uart_port *port) { - return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ? + return (lpuart32_read(port, UARTSTAT) & UARTSTAT_TC) ? TIOCSER_TEMT : 0; } +static bool lpuart_is_32(struct lpuart_port *sport) +{ + return sport->port.iotype == UPIO_MEM32 || + sport->port.iotype == UPIO_MEM32BE; +} + static irqreturn_t lpuart_txint(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; @@ -593,22 +633,22 @@ static irqreturn_t lpuart_txint(int irq, void *dev_id) spin_lock_irqsave(&sport->port.lock, flags); if (sport->port.x_char) { - if (sport->lpuart32) - lpuart32_write(sport->port.x_char, sport->port.membase + UARTDATA); + if (lpuart_is_32(sport)) + lpuart32_write(&sport->port, sport->port.x_char, UARTDATA); else writeb(sport->port.x_char, sport->port.membase + UARTDR); goto out; } if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - if (sport->lpuart32) + if (lpuart_is_32(sport)) lpuart32_stop_tx(&sport->port); else lpuart_stop_tx(&sport->port); goto out; } - if (sport->lpuart32) + if (lpuart_is_32(sport)) lpuart32_transmit_buffer(sport); else lpuart_transmit_buffer(sport); @@ -694,15 +734,15 @@ static irqreturn_t lpuart32_rxint(int irq, void *dev_id) spin_lock_irqsave(&sport->port.lock, flags); - while (!(lpuart32_read(sport->port.membase + UARTFIFO) & UARTFIFO_RXEMPT)) { + while (!(lpuart32_read(&sport->port, UARTFIFO) & UARTFIFO_RXEMPT)) { flg = TTY_NORMAL; sport->port.icount.rx++; /* * to clear the FE, OR, NF, FE, PE flags, * read STAT then read DATA reg */ - sr = lpuart32_read(sport->port.membase + UARTSTAT); - rx = lpuart32_read(sport->port.membase + UARTDATA); + sr = lpuart32_read(&sport->port, UARTSTAT); + rx = lpuart32_read(&sport->port, UARTDATA); rx &= 0x3ff; if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) @@ -769,18 +809,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) struct lpuart_port *sport = dev_id; unsigned long sts, rxcount; - sts = lpuart32_read(sport->port.membase + UARTSTAT); - rxcount = lpuart32_read(sport->port.membase + UARTWATER); + sts = lpuart32_read(&sport->port, UARTSTAT); + rxcount = lpuart32_read(&sport->port, UARTWATER); rxcount = rxcount >> UARTWATER_RXCNT_OFF; if (sts & UARTSTAT_RDRF || rxcount > 0) lpuart32_rxint(irq, dev_id); if ((sts & UARTSTAT_TDRE) && - !(lpuart32_read(sport->port.membase + UARTBAUD) & UARTBAUD_TDMAE)) + !(lpuart32_read(&sport->port, UARTBAUD) & UARTBAUD_TDMAE)) lpuart_txint(irq, dev_id); - lpuart32_write(sts, sport->port.membase + UARTSTAT); + lpuart32_write(&sport->port, sts, UARTSTAT); return IRQ_HANDLED; } @@ -1041,7 +1081,7 @@ static unsigned int lpuart32_get_mctrl(struct uart_port *port) unsigned int temp = 0; unsigned long reg; - reg = lpuart32_read(port->membase + UARTMODIR); + reg = lpuart32_read(port, UARTMODIR); if (reg & UARTMODIR_TXCTSE) temp |= TIOCM_CTS; @@ -1076,7 +1116,7 @@ static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned long temp; - temp = lpuart32_read(port->membase + UARTMODIR) & + temp = lpuart32_read(port, UARTMODIR) & ~(UARTMODIR_RXRTSE | UARTMODIR_TXCTSE); if (mctrl & TIOCM_RTS) @@ -1085,7 +1125,7 @@ static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl) if (mctrl & TIOCM_CTS) temp |= UARTMODIR_TXCTSE; - lpuart32_write(temp, port->membase + UARTMODIR); + lpuart32_write(port, temp, UARTMODIR); } static void lpuart_break_ctl(struct uart_port *port, int break_state) @@ -1104,12 +1144,12 @@ static void lpuart32_break_ctl(struct uart_port *port, int break_state) { unsigned long temp; - temp = lpuart32_read(port->membase + UARTCTRL) & ~UARTCTRL_SBK; + temp = lpuart32_read(port, UARTCTRL) & ~UARTCTRL_SBK; if (break_state != 0) temp |= UARTCTRL_SBK; - lpuart32_write(temp, port->membase + UARTCTRL); + lpuart32_write(port, temp, UARTCTRL); } static void lpuart_setup_watermark(struct lpuart_port *sport) @@ -1149,24 +1189,24 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) unsigned long val, ctrl; unsigned long ctrl_saved; - ctrl = lpuart32_read(sport->port.membase + UARTCTRL); + ctrl = lpuart32_read(&sport->port, UARTCTRL); ctrl_saved = ctrl; ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_TE | UARTCTRL_RIE | UARTCTRL_RE); - lpuart32_write(ctrl, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, ctrl, UARTCTRL); /* enable FIFO mode */ - val = lpuart32_read(sport->port.membase + UARTFIFO); + val = lpuart32_read(&sport->port, UARTFIFO); val |= UARTFIFO_TXFE | UARTFIFO_RXFE; val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; - lpuart32_write(val, sport->port.membase + UARTFIFO); + lpuart32_write(&sport->port, val, UARTFIFO); /* set the watermark */ val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF); - lpuart32_write(val, sport->port.membase + UARTWATER); + lpuart32_write(&sport->port, val, UARTWATER); /* Restore cr2 */ - lpuart32_write(ctrl_saved, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, ctrl_saved, UARTCTRL); } static void rx_dma_timer_init(struct lpuart_port *sport) @@ -1242,7 +1282,7 @@ static int lpuart32_startup(struct uart_port *port) unsigned long temp; /* determine FIFO size */ - temp = lpuart32_read(sport->port.membase + UARTFIFO); + temp = lpuart32_read(&sport->port, UARTFIFO); sport->txfifo_size = 0x1 << (((temp >> UARTFIFO_TXSIZE_OFF) & UARTFIFO_FIFOSIZE_MASK) - 1); @@ -1259,10 +1299,10 @@ static int lpuart32_startup(struct uart_port *port) lpuart32_setup_watermark(sport); - temp = lpuart32_read(sport->port.membase + UARTCTRL); + temp = lpuart32_read(&sport->port, UARTCTRL); temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | UARTCTRL_TE); temp |= UARTCTRL_ILIE; - lpuart32_write(temp, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, temp, UARTCTRL); spin_unlock_irqrestore(&sport->port.lock, flags); return 0; @@ -1311,10 +1351,10 @@ static void lpuart32_shutdown(struct uart_port *port) spin_lock_irqsave(&port->lock, flags); /* disable Rx/Tx and interrupts */ - temp = lpuart32_read(port->membase + UARTCTRL); + temp = lpuart32_read(port, UARTCTRL); temp &= ~(UARTCTRL_TE | UARTCTRL_RE | UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE); - lpuart32_write(temp, port->membase + UARTCTRL); + lpuart32_write(port, temp, UARTCTRL); spin_unlock_irqrestore(&port->lock, flags); @@ -1479,6 +1519,75 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, } static void +lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate) +{ + u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp; + u32 clk = sport->port.uartclk; + + /* + * The idea is to use the best OSR (over-sampling rate) possible. + * Note, OSR is typically hard-set to 16 in other LPUART instantiations. + * Loop to find the best OSR value possible, one that generates minimum + * baud_diff iterate through the rest of the supported values of OSR. + * + * Calculation Formula: + * Baud Rate = baud clock / ((OSR+1) × SBR) + */ + baud_diff = baudrate; + osr = 0; + sbr = 0; + + for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) { + /* calculate the temporary sbr value */ + tmp_sbr = (clk / (baudrate * tmp_osr)); + if (tmp_sbr == 0) + tmp_sbr = 1; + + /* + * calculate the baud rate difference based on the temporary + * osr and sbr values + */ + tmp_diff = clk / (tmp_osr * tmp_sbr) - baudrate; + + /* select best values between sbr and sbr+1 */ + tmp = clk / (tmp_osr * (tmp_sbr + 1)); + if (tmp_diff > (baudrate - tmp)) { + tmp_diff = baudrate - tmp; + tmp_sbr++; + } + + if (tmp_diff <= baud_diff) { + baud_diff = tmp_diff; + osr = tmp_osr; + sbr = tmp_sbr; + + if (!baud_diff) + break; + } + } + + /* handle buadrate outside acceptable rate */ + if (baud_diff > ((baudrate / 100) * 3)) + dev_warn(sport->port.dev, + "unacceptable baud rate difference of more than 3%%\n"); + + tmp = lpuart32_read(&sport->port, UARTBAUD); + + if ((osr > 3) && (osr < 8)) + tmp |= UARTBAUD_BOTHEDGE; + + tmp &= ~(UARTBAUD_OSR_MASK << UARTBAUD_OSR_SHIFT); + tmp |= (((osr-1) & UARTBAUD_OSR_MASK) << UARTBAUD_OSR_SHIFT); + + tmp &= ~UARTBAUD_SBR_MASK; + tmp |= sbr & UARTBAUD_SBR_MASK; + + tmp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE); + + lpuart32_write(&sport->port, tmp, UARTBAUD); +} + +static void lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { @@ -1487,11 +1596,10 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long ctrl, old_ctrl, bd, modem; unsigned int baud; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; - unsigned int sbr; - ctrl = old_ctrl = lpuart32_read(sport->port.membase + UARTCTRL); - bd = lpuart32_read(sport->port.membase + UARTBAUD); - modem = lpuart32_read(sport->port.membase + UARTMODIR); + ctrl = old_ctrl = lpuart32_read(&sport->port, UARTCTRL); + bd = lpuart32_read(&sport->port, UARTBAUD); + modem = lpuart32_read(&sport->port, UARTMODIR); /* * only support CS8 and CS7, and for CS7 must enable PE. * supported mode: @@ -1577,21 +1685,16 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, termios->c_cflag, baud); /* wait transmit engin complete */ - while (!(lpuart32_read(sport->port.membase + UARTSTAT) & UARTSTAT_TC)) + while (!(lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_TC)) barrier(); /* disable transmit and receive */ - lpuart32_write(old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE), - sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE), + UARTCTRL); - sbr = sport->port.uartclk / (16 * baud); - bd &= ~UARTBAUD_SBR_MASK; - bd |= sbr & UARTBAUD_SBR_MASK; - bd |= UARTBAUD_BOTHEDGE; - bd &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE); - lpuart32_write(bd, sport->port.membase + UARTBAUD); - lpuart32_write(modem, sport->port.membase + UARTMODIR); - lpuart32_write(ctrl, sport->port.membase + UARTCTRL); + lpuart32_serial_setbrg(sport, baud); + lpuart32_write(&sport->port, modem, UARTMODIR); + lpuart32_write(&sport->port, ctrl, UARTCTRL); /* restore control register */ spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1694,10 +1797,10 @@ static void lpuart_console_putchar(struct uart_port *port, int ch) static void lpuart32_console_putchar(struct uart_port *port, int ch) { - while (!(lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TDRE)) + while (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE)) barrier(); - lpuart32_write(ch, port->membase + UARTDATA); + lpuart32_write(port, ch, UARTDATA); } static void @@ -1705,6 +1808,13 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) { struct lpuart_port *sport = lpuart_ports[co->index]; unsigned char old_cr2, cr2; + unsigned long flags; + int locked = 1; + + if (sport->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&sport->port.lock, flags); + else + spin_lock_irqsave(&sport->port.lock, flags); /* first save CR2 and then disable interrupts */ cr2 = old_cr2 = readb(sport->port.membase + UARTCR2); @@ -1719,6 +1829,9 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) barrier(); writeb(old_cr2, sport->port.membase + UARTCR2); + + if (locked) + spin_unlock_irqrestore(&sport->port.lock, flags); } static void @@ -1726,20 +1839,30 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) { struct lpuart_port *sport = lpuart_ports[co->index]; unsigned long old_cr, cr; + unsigned long flags; + int locked = 1; + + if (sport->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&sport->port.lock, flags); + else + spin_lock_irqsave(&sport->port.lock, flags); /* first save CR2 and then disable interrupts */ - cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL); + cr = old_cr = lpuart32_read(&sport->port, UARTCTRL); cr |= (UARTCTRL_TE | UARTCTRL_RE); cr &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE); - lpuart32_write(cr, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, cr, UARTCTRL); uart_console_write(&sport->port, s, count, lpuart32_console_putchar); /* wait for transmitter finish complete and restore CR2 */ - while (!(lpuart32_read(sport->port.membase + UARTSTAT) & UARTSTAT_TC)) + while (!(lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_TC)) barrier(); - lpuart32_write(old_cr, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, old_cr, UARTCTRL); + + if (locked) + spin_unlock_irqrestore(&sport->port.lock, flags); } /* @@ -1802,14 +1925,14 @@ lpuart32_console_get_options(struct lpuart_port *sport, int *baud, unsigned long cr, bd; unsigned int sbr, uartclk, baud_raw; - cr = lpuart32_read(sport->port.membase + UARTCTRL); + cr = lpuart32_read(&sport->port, UARTCTRL); cr &= UARTCTRL_TE | UARTCTRL_RE; if (!cr) return; /* ok, the port was enabled */ - cr = lpuart32_read(sport->port.membase + UARTCTRL); + cr = lpuart32_read(&sport->port, UARTCTRL); *parity = 'n'; if (cr & UARTCTRL_PE) { @@ -1824,7 +1947,7 @@ lpuart32_console_get_options(struct lpuart_port *sport, int *baud, else *bits = 8; - bd = lpuart32_read(sport->port.membase + UARTBAUD); + bd = lpuart32_read(&sport->port, UARTBAUD); bd &= UARTBAUD_SBR_MASK; sbr = bd; uartclk = clk_get_rate(sport->clk); @@ -1861,12 +1984,12 @@ static int __init lpuart_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - if (sport->lpuart32) + if (lpuart_is_32(sport)) lpuart32_console_get_options(sport, &baud, &parity, &bits); else lpuart_console_get_options(sport, &baud, &parity, &bits); - if (sport->lpuart32) + if (lpuart_is_32(sport)) lpuart32_setup_watermark(sport); else lpuart_setup_watermark(sport); @@ -1925,12 +2048,26 @@ static int __init lpuart32_early_console_setup(struct earlycon_device *device, if (!device->port.membase) return -ENODEV; + device->port.iotype = UPIO_MEM32BE; device->con->write = lpuart32_early_write; return 0; } +static int __init lpuart32_imx_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->port.iotype = UPIO_MEM32; + device->port.membase += IMX_REG_OFF; + device->con->write = lpuart32_early_write; + + return 0; +} OF_EARLYCON_DECLARE(lpuart, "fsl,vf610-lpuart", lpuart_early_console_setup); OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup); +OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup); EARLYCON_DECLARE(lpuart, lpuart_early_console_setup); EARLYCON_DECLARE(lpuart32, lpuart32_early_console_setup); @@ -1951,6 +2088,9 @@ static struct uart_driver lpuart_reg = { static int lpuart_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = of_match_device(lpuart_dt_ids, + &pdev->dev); + const struct lpuart_soc_data *sdata = of_id->data; struct device_node *np = pdev->dev.of_node; struct lpuart_port *sport; struct resource *res; @@ -1968,25 +2108,23 @@ static int lpuart_probe(struct platform_device *pdev) return ret; } sport->port.line = ret; - sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart"); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sport->port.membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(sport->port.membase)) return PTR_ERR(sport->port.membase); + sport->port.membase += sdata->reg_off; sport->port.mapbase = res->start; sport->port.dev = &pdev->dev; sport->port.type = PORT_LPUART; - sport->port.iotype = UPIO_MEM; ret = platform_get_irq(pdev, 0); if (ret < 0) { dev_err(&pdev->dev, "cannot obtain irq\n"); return ret; } sport->port.irq = ret; - - if (sport->lpuart32) + sport->port.iotype = sdata->iotype; + if (lpuart_is_32(sport)) sport->port.ops = &lpuart32_pops; else sport->port.ops = &lpuart_pops; @@ -2013,7 +2151,7 @@ static int lpuart_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &sport->port); - if (sport->lpuart32) + if (lpuart_is_32(sport)) lpuart_reg.cons = LPUART32_CONSOLE; else lpuart_reg.cons = LPUART_CONSOLE; @@ -2066,11 +2204,11 @@ static int lpuart_suspend(struct device *dev) struct lpuart_port *sport = dev_get_drvdata(dev); unsigned long temp; - if (sport->lpuart32) { + if (lpuart_is_32(sport)) { /* disable Rx/Tx and interrupts */ - temp = lpuart32_read(sport->port.membase + UARTCTRL); + temp = lpuart32_read(&sport->port, UARTCTRL); temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE); - lpuart32_write(temp, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, temp, UARTCTRL); } else { /* disable Rx/Tx and interrupts */ temp = readb(sport->port.membase + UARTCR2); @@ -2117,12 +2255,12 @@ static int lpuart_resume(struct device *dev) if (sport->port.suspended && !sport->port.irq_wake) clk_prepare_enable(sport->clk); - if (sport->lpuart32) { + if (lpuart_is_32(sport)) { lpuart32_setup_watermark(sport); - temp = lpuart32_read(sport->port.membase + UARTCTRL); + temp = lpuart32_read(&sport->port, UARTCTRL); temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE); - lpuart32_write(temp, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, temp, UARTCTRL); } else { lpuart_setup_watermark(sport); temp = readb(sport->port.membase + UARTCR2); diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 157883653256..f190a84a0246 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -1382,9 +1382,9 @@ static struct spi_driver ifx_spi_driver = { static void __exit ifx_spi_exit(void) { /* unregister */ + spi_unregister_driver(&ifx_spi_driver); tty_unregister_driver(tty_drv); put_tty_driver(tty_drv); - spi_unregister_driver(&ifx_spi_driver); unregister_reboot_notifier(&ifx_modem_reboot_notifier_block); } diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index e3e152cbc75e..80934e7bd67f 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -207,9 +207,6 @@ struct imx_port { unsigned int have_rtscts:1; unsigned int have_rtsgpio:1; unsigned int dte_mode:1; - unsigned int irda_inv_rx:1; - unsigned int irda_inv_tx:1; - unsigned short trcv_delay; /* transceiver delay */ struct clk *clk_ipg; struct clk *clk_per; const struct imx_uart_data *devdata; @@ -461,7 +458,7 @@ static inline void imx_transmit_buffer(struct imx_port *sport) } } - while (!uart_circ_empty(xmit) && + while (!uart_circ_empty(xmit) && !sport->dma_is_txing && !(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) { /* send xmit->buf[xmit->tail] * out the port here */ @@ -719,6 +716,27 @@ out: return IRQ_HANDLED; } +static void imx_disable_rx_int(struct imx_port *sport) +{ + unsigned long temp; + + sport->dma_is_rxing = 1; + + /* disable the receiver ready and aging timer interrupts */ + temp = readl(sport->port.membase + UCR1); + temp &= ~(UCR1_RRDYEN); + writel(temp, sport->port.membase + UCR1); + + temp = readl(sport->port.membase + UCR2); + temp &= ~(UCR2_ATEN); + writel(temp, sport->port.membase + UCR2); + + /* disable the rx errors interrupts */ + temp = readl(sport->port.membase + UCR4); + temp &= ~UCR4_OREN; + writel(temp, sport->port.membase + UCR4); +} + static void clear_rx_errors(struct imx_port *sport); static int start_rx_dma(struct imx_port *sport); /* @@ -734,21 +752,8 @@ static void imx_dma_rxint(struct imx_port *sport) temp = readl(sport->port.membase + USR2); if ((temp & USR2_RDR) && !sport->dma_is_rxing) { - sport->dma_is_rxing = 1; - - /* disable the receiver ready and aging timer interrupts */ - temp = readl(sport->port.membase + UCR1); - temp &= ~(UCR1_RRDYEN); - writel(temp, sport->port.membase + UCR1); - temp = readl(sport->port.membase + UCR2); - temp &= ~(UCR2_ATEN); - writel(temp, sport->port.membase + UCR2); - - /* disable the rx errors interrupts */ - temp = readl(sport->port.membase + UCR4); - temp &= ~UCR4_OREN; - writel(temp, sport->port.membase + UCR4); + imx_disable_rx_int(sport); /* tell the DMA to receive the data. */ start_rx_dma(sport); @@ -1294,7 +1299,9 @@ static int imx_startup(struct uart_port *port) imx_enable_dma(sport); temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; + temp |= UCR1_RRDYEN | UCR1_UARTEN; + if (sport->have_rtscts) + temp |= UCR1_RTSDEN; writel(temp, sport->port.membase + UCR1); @@ -1317,19 +1324,10 @@ static int imx_startup(struct uart_port *port) if (!is_imx1_uart(sport)) { temp = readl(sport->port.membase + UCR3); - /* - * The effect of RI and DCD differs depending on the UFCR_DCEDTE - * bit. In DCE mode they control the outputs, in DTE mode they - * enable the respective irqs. At least the DCD irq cannot be - * cleared on i.MX25 at least, so it's not usable and must be - * disabled. I don't have test hardware to check if RI has the - * same problem but I consider this likely so it's disabled for - * now, too. - */ - temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | - UCR3_DTRDEN | UCR3_RI | UCR3_DCD; + temp |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD; if (sport->dte_mode) + /* disable broken interrupts */ temp &= ~(UCR3_RI | UCR3_DCD); writel(temp, sport->port.membase + UCR3); @@ -1339,6 +1337,17 @@ static int imx_startup(struct uart_port *port) * Enable modem status interrupts */ imx_enable_ms(&sport->port); + + /* + * Start RX DMA immediately instead of waiting for RX FIFO interrupts. + * In our iMX53 the average delay for the first reception dropped from + * approximately 35000 microseconds to 1000 microseconds. + */ + if (sport->dma_is_enabled) { + imx_disable_rx_int(sport); + start_rx_dma(sport); + } + spin_unlock_irqrestore(&sport->port.lock, flags); return 0; @@ -1584,8 +1593,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, ufcr = readl(sport->port.membase + UFCR); ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); - if (sport->dte_mode) - ufcr |= UFCR_DCEDTE; writel(ufcr, sport->port.membase + UFCR); writel(num, sport->port.membase + UBIR); @@ -2153,6 +2160,37 @@ static int serial_imx_probe(struct platform_device *pdev) UCR1_TXMPTYEN | UCR1_RTSDEN); writel_relaxed(reg, sport->port.membase + UCR1); + if (!is_imx1_uart(sport) && sport->dte_mode) { + /* + * The DCEDTE bit changes the direction of DSR, DCD, DTR and RI + * and influences if UCR3_RI and UCR3_DCD changes the level of RI + * and DCD (when they are outputs) or enables the respective + * irqs. So set this bit early, i.e. before requesting irqs. + */ + reg = readl(sport->port.membase + UFCR); + if (!(reg & UFCR_DCEDTE)) + writel(reg | UFCR_DCEDTE, sport->port.membase + UFCR); + + /* + * Disable UCR3_RI and UCR3_DCD irqs. They are also not + * enabled later because they cannot be cleared + * (confirmed on i.MX25) which makes them unusable. + */ + writel(IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR, + sport->port.membase + UCR3); + + } else { + unsigned long ucr3 = UCR3_DSR; + + reg = readl(sport->port.membase + UFCR); + if (reg & UFCR_DCEDTE) + writel(reg & ~UFCR_DCEDTE, sport->port.membase + UFCR); + + if (!is_imx1_uart(sport)) + ucr3 |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP; + writel(ucr3, sport->port.membase + UCR3); + } + clk_disable_unprepare(sport->clk_ipg); /* diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c index 2a61dd6b4009..906ee770ff4a 100644 --- a/drivers/tty/serial/ioc3_serial.c +++ b/drivers/tty/serial/ioc3_serial.c @@ -377,7 +377,7 @@ static struct ioc3_port *get_ioc3_port(struct uart_port *the_port) * called per port from attach... * @port: port to initialize */ -static int inline port_init(struct ioc3_port *port) +static inline int port_init(struct ioc3_port *port) { uint32_t sio_cr; struct port_hooks *hooks = port->ip_hooks; @@ -1430,7 +1430,7 @@ static int receive_chars(struct uart_port *the_port) * @pending: interrupts to handle */ -static int inline +static inline int ioc3uart_intr_one(struct ioc3_submodule *is, struct ioc3_driver_data *idd, unsigned int pending) diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index f96bcf9bee25..43d7d32eb150 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -824,7 +824,7 @@ pending_intrs(struct ioc4_soft *soft, int type) * called per port from attach... * @port: port to initialize */ -static int inline port_init(struct ioc4_port *port) +static inline int port_init(struct ioc4_port *port) { uint32_t sio_cr; struct hooks *hooks = port->ip_hooks; @@ -1048,7 +1048,7 @@ static irqreturn_t ioc4_intr(int irq, void *arg) * IOC4 with serial ports in the system. * @idd: Master module data for this IOC4 */ -static int inline ioc4_attach_local(struct ioc4_driver_data *idd) +static inline int ioc4_attach_local(struct ioc4_driver_data *idd) { struct ioc4_port *port; struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS]; diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 60f16795d16b..42e4a4c7597f 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -286,7 +286,7 @@ static int meson_uart_startup(struct uart_port *port) writel(val, port->membase + AML_UART_MISC); ret = request_irq(port->irq, meson_uart_interrupt, 0, - meson_uart_type(port), port); + port->name, port); return ret; } @@ -298,8 +298,6 @@ static void meson_uart_change_speed(struct uart_port *port, unsigned long baud) while (!meson_uart_tx_empty(port)) cpu_relax(); - val = readl(port->membase + AML_UART_REG5); - val &= ~AML_UART_BAUD_MASK; if (port->uartclk == 24000000) { val = ((port->uartclk / 3) / baud) - 1; val |= AML_UART_BAUD_XTAL; @@ -355,7 +353,7 @@ static void meson_uart_set_termios(struct uart_port *port, if (cflags & CSTOPB) val |= AML_UART_STOP_BIN_2SB; else - val &= ~AML_UART_STOP_BIN_1SB; + val |= AML_UART_STOP_BIN_1SB; if (cflags & CRTSCTS) val &= ~AML_UART_TWO_WIRE_EN; @@ -395,51 +393,25 @@ static int meson_uart_verify_port(struct uart_port *port, return ret; } -static int meson_uart_res_size(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(port->dev, "cannot obtain I/O memory region"); - return -ENODEV; - } - - return resource_size(res); -} - static void meson_uart_release_port(struct uart_port *port) { - int size = meson_uart_res_size(port); - - if (port->flags & UPF_IOREMAP) { - devm_release_mem_region(port->dev, port->mapbase, size); - devm_iounmap(port->dev, port->membase); - port->membase = NULL; - } + devm_iounmap(port->dev, port->membase); + port->membase = NULL; + devm_release_mem_region(port->dev, port->mapbase, port->mapsize); } static int meson_uart_request_port(struct uart_port *port) { - int size = meson_uart_res_size(port); - - if (size < 0) - return size; - - if (!devm_request_mem_region(port->dev, port->mapbase, size, + if (!devm_request_mem_region(port->dev, port->mapbase, port->mapsize, 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; - } + port->membase = devm_ioremap_nocache(port->dev, port->mapbase, + port->mapsize); + if (!port->membase) + return -ENOMEM; return 0; } @@ -470,6 +442,14 @@ static struct uart_ops meson_uart_ops = { }; #ifdef CONFIG_SERIAL_MESON_CONSOLE +static void meson_uart_enable_tx_engine(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_console_putchar(struct uart_port *port, int ch) { @@ -499,7 +479,6 @@ static void meson_serial_port_write(struct uart_port *port, const char *s, } val = readl(port->membase + AML_UART_CONTROL); - val |= AML_UART_TX_EN; tmp = val & ~(AML_UART_TX_INT_EN | AML_UART_RX_INT_EN); writel(tmp, port->membase + AML_UART_CONTROL); @@ -538,6 +517,8 @@ static int meson_serial_console_setup(struct console *co, char *options) if (!port || !port->membase) return -ENODEV; + meson_uart_enable_tx_engine(port); + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -576,11 +557,16 @@ meson_serial_early_console_setup(struct earlycon_device *device, const char *opt if (!device->port.membase) return -ENODEV; + meson_uart_enable_tx_engine(&device->port); device->con->write = meson_serial_early_console_write; return 0; } +/* Legacy bindings, should be removed when no more used */ OF_EARLYCON_DECLARE(meson, "amlogic,meson-uart", meson_serial_early_console_setup); +/* Stable bindings */ +OF_EARLYCON_DECLARE(meson, "amlogic,meson-ao-uart", + meson_serial_early_console_setup); #define MESON_SERIAL_CONSOLE (&meson_serial_console) #else @@ -595,11 +581,76 @@ static struct uart_driver meson_uart_driver = { .cons = MESON_SERIAL_CONSOLE, }; +static inline struct clk *meson_uart_probe_clock(struct device *dev, + const char *id) +{ + struct clk *clk = NULL; + int ret; + + clk = devm_clk_get(dev, id); + if (IS_ERR(clk)) + return clk; + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "couldn't enable clk\n"); + return ERR_PTR(ret); + } + + devm_add_action_or_reset(dev, + (void(*)(void *))clk_disable_unprepare, + clk); + + return clk; +} + +/* + * This function gets clocks in the legacy non-stable DT bindings. + * This code will be remove once all the platforms switch to the + * new DT bindings. + */ +static int meson_uart_probe_clocks_legacy(struct platform_device *pdev, + struct uart_port *port) +{ + struct clk *clk = NULL; + + clk = meson_uart_probe_clock(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + port->uartclk = clk_get_rate(clk); + + return 0; +} + +static int meson_uart_probe_clocks(struct platform_device *pdev, + struct uart_port *port) +{ + struct clk *clk_xtal = NULL; + struct clk *clk_pclk = NULL; + struct clk *clk_baud = NULL; + + clk_pclk = meson_uart_probe_clock(&pdev->dev, "pclk"); + if (IS_ERR(clk_pclk)) + return PTR_ERR(clk_pclk); + + clk_xtal = meson_uart_probe_clock(&pdev->dev, "xtal"); + if (IS_ERR(clk_xtal)) + return PTR_ERR(clk_xtal); + + clk_baud = meson_uart_probe_clock(&pdev->dev, "baud"); + if (IS_ERR(clk_baud)) + return PTR_ERR(clk_baud); + + port->uartclk = clk_get_rate(clk_baud); + + return 0; +} + 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) @@ -625,15 +676,20 @@ static int meson_uart_probe(struct platform_device *pdev) if (!port) return -ENOMEM; - clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) - return PTR_ERR(clk); + /* Use legacy way until all platforms switch to new bindings */ + if (of_device_is_compatible(pdev->dev.of_node, "amlogic,meson-uart")) + ret = meson_uart_probe_clocks_legacy(pdev, port); + else + ret = meson_uart_probe_clocks(pdev, port); + + if (ret) + return ret; - port->uartclk = clk_get_rate(clk); port->iotype = UPIO_MEM; port->mapbase = res_mem->start; + port->mapsize = resource_size(res_mem); port->irq = res_irq->start; - port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_LOW_LATENCY; + port->flags = UPF_BOOT_AUTOCONF | UPF_LOW_LATENCY; port->dev = &pdev->dev; port->line = pdev->id; port->type = PORT_MESON; @@ -668,9 +724,14 @@ static int meson_uart_remove(struct platform_device *pdev) return 0; } - static const struct of_device_id meson_uart_dt_match[] = { + /* Legacy bindings, should be removed when no more used */ { .compatible = "amlogic,meson-uart" }, + /* Stable bindings */ + { .compatible = "amlogic,meson6-uart" }, + { .compatible = "amlogic,meson8-uart" }, + { .compatible = "amlogic,meson8b-uart" }, + { .compatible = "amlogic,meson-gx-uart" }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, meson_uart_dt_match); diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index 1a60a2063e75..67ffecc50e42 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -754,9 +754,10 @@ static int mpsc_alloc_ring_mem(struct mpsc_port_info *pi) if (!dma_set_mask(pi->port.dev, 0xffffffff)) { printk(KERN_ERR "MPSC: Inadequate DMA support\n"); rc = -ENXIO; - } else if ((pi->dma_region = dma_alloc_noncoherent(pi->port.dev, + } else if ((pi->dma_region = dma_alloc_attrs(pi->port.dev, MPSC_DMA_ALLOC_SIZE, - &pi->dma_region_p, GFP_KERNEL)) + &pi->dma_region_p, GFP_KERNEL, + DMA_ATTR_NON_CONSISTENT)) == NULL) { printk(KERN_ERR "MPSC: Can't alloc Desc region\n"); rc = -ENOMEM; @@ -771,8 +772,9 @@ static void mpsc_free_ring_mem(struct mpsc_port_info *pi) pr_debug("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line); if (pi->dma_region) { - dma_free_noncoherent(pi->port.dev, MPSC_DMA_ALLOC_SIZE, - pi->dma_region, pi->dma_region_p); + dma_free_attrs(pi->port.dev, MPSC_DMA_ALLOC_SIZE, + pi->dma_region, pi->dma_region_p, + DMA_ATTR_NON_CONSISTENT); pi->dma_region = NULL; pi->dma_region_p = (dma_addr_t)NULL; } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 6c6f82ad8d5c..1ea05ac57aa7 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1597,6 +1597,9 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) of_property_read_u32(dev->of_node, "clock-frequency", &omap_up_info->uartclk); + + omap_up_info->flags = UPF_BOOT_AUTOCONF; + return omap_up_info; } @@ -1767,7 +1770,8 @@ static int serial_omap_probe(struct platform_device *pdev) return 0; err_add_port: - pm_runtime_put(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_qos_remove_request(&up->pm_qos_request); device_init_wakeup(up->dev, false); @@ -1780,9 +1784,13 @@ static int serial_omap_remove(struct platform_device *dev) { struct uart_omap_port *up = platform_get_drvdata(dev); + pm_runtime_get_sync(up->dev); + + uart_remove_one_port(&serial_omap_reg, &up->port); + + pm_runtime_dont_use_autosuspend(up->dev); pm_runtime_put_sync(up->dev); pm_runtime_disable(up->dev); - uart_remove_one_port(&serial_omap_reg, &up->port); pm_qos_remove_request(&up->pm_qos_request); device_init_wakeup(&dev->dev, false); diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c new file mode 100644 index 000000000000..1b8008797a1b --- /dev/null +++ b/drivers/tty/serial/owl-uart.c @@ -0,0 +1,135 @@ +/* + * Actions Semi Owl family serial console + * + * Copyright 2013 Actions Semi Inc. + * Author: Actions Semi, Inc. + * + * Copyright (c) 2016-2017 Andreas Färber + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/serial.h> +#include <linux/serial_core.h> + +#define OWL_UART_CTL 0x000 +#define OWL_UART_TXDAT 0x008 +#define OWL_UART_STAT 0x00c + +#define OWL_UART_CTL_TRFS_TX BIT(14) +#define OWL_UART_CTL_EN BIT(15) +#define OWL_UART_CTL_RXIE BIT(18) +#define OWL_UART_CTL_TXIE BIT(19) + +#define OWL_UART_STAT_RIP BIT(0) +#define OWL_UART_STAT_TIP BIT(1) +#define OWL_UART_STAT_TFFU BIT(6) +#define OWL_UART_STAT_TRFL_MASK (0x1f << 11) +#define OWL_UART_STAT_UTBB BIT(17) + +static inline void owl_uart_write(struct uart_port *port, u32 val, unsigned int off) +{ + writel(val, port->membase + off); +} + +static inline u32 owl_uart_read(struct uart_port *port, unsigned int off) +{ + return readl(port->membase + off); +} + +#ifdef CONFIG_SERIAL_OWL_CONSOLE + +static void owl_console_putchar(struct uart_port *port, int ch) +{ + if (!port->membase) + return; + + while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU) + cpu_relax(); + + owl_uart_write(port, ch, OWL_UART_TXDAT); +} + +static void owl_uart_port_write(struct uart_port *port, const char *s, + u_int count) +{ + u32 old_ctl, val; + unsigned long flags; + int locked; + + 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; + } + + old_ctl = owl_uart_read(port, OWL_UART_CTL); + val = old_ctl | OWL_UART_CTL_TRFS_TX; + /* disable IRQ */ + val &= ~(OWL_UART_CTL_RXIE | OWL_UART_CTL_TXIE); + owl_uart_write(port, val, OWL_UART_CTL); + + uart_console_write(port, s, count, owl_console_putchar); + + /* wait until all contents have been sent out */ + while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TRFL_MASK) + cpu_relax(); + + /* clear IRQ pending */ + val = owl_uart_read(port, OWL_UART_STAT); + val |= OWL_UART_STAT_TIP | OWL_UART_STAT_RIP; + owl_uart_write(port, val, OWL_UART_STAT); + + owl_uart_write(port, old_ctl, OWL_UART_CTL); + + if (locked) + spin_unlock(&port->lock); + + local_irq_restore(flags); +} + +static void owl_uart_early_console_write(struct console *co, + const char *s, + u_int count) +{ + struct earlycon_device *dev = co->data; + + owl_uart_port_write(&dev->port, s, count); +} + +static int __init +owl_uart_early_console_setup(struct earlycon_device *device, const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = owl_uart_early_console_write; + + return 0; +} +OF_EARLYCON_DECLARE(owl, "actions,owl-uart", + owl_uart_early_console_setup); + +#endif /* CONFIG_SERIAL_OWL_CONSOLE */ diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 42caccb5e87e..d3796dc26fa9 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -878,8 +878,7 @@ static int dma_handle_rx(struct eg20t_port *priv) sg_dma_len(sg) = priv->trigger_level; sg_set_page(&priv->sg_rx, virt_to_page(priv->rx_buf_virt), - sg_dma_len(sg), (unsigned long)priv->rx_buf_virt & - ~PAGE_MASK); + sg_dma_len(sg), offset_in_page(priv->rx_buf_virt)); sg_dma_address(sg) = priv->rx_buf_dma; diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 7a17aedbf902..8aca18c4cdea 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -859,7 +859,7 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) { struct s3c24xx_uart_dma *dma = p->dma; - unsigned long flags; + int ret; /* Default slave configuration parameters */ dma->rx_conf.direction = DMA_DEV_TO_MEM; @@ -884,8 +884,8 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) dma->tx_chan = dma_request_chan(p->port.dev, "tx"); if (IS_ERR(dma->tx_chan)) { - dma_release_channel(dma->rx_chan); - return PTR_ERR(dma->tx_chan); + ret = PTR_ERR(dma->tx_chan); + goto err_release_rx; } dmaengine_slave_config(dma->tx_chan, &dma->tx_conf); @@ -894,26 +894,38 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) dma->rx_size = PAGE_SIZE; dma->rx_buf = kmalloc(dma->rx_size, GFP_KERNEL); - if (!dma->rx_buf) { - dma_release_channel(dma->rx_chan); - dma_release_channel(dma->tx_chan); - return -ENOMEM; + ret = -ENOMEM; + goto err_release_tx; } - dma->rx_addr = dma_map_single(dma->rx_chan->device->dev, dma->rx_buf, + dma->rx_addr = dma_map_single(p->port.dev, dma->rx_buf, dma->rx_size, DMA_FROM_DEVICE); - - spin_lock_irqsave(&p->port.lock, flags); + if (dma_mapping_error(p->port.dev, dma->rx_addr)) { + ret = -EIO; + goto err_free_rx; + } /* TX buffer */ - dma->tx_addr = dma_map_single(dma->tx_chan->device->dev, - p->port.state->xmit.buf, + dma->tx_addr = dma_map_single(p->port.dev, p->port.state->xmit.buf, UART_XMIT_SIZE, DMA_TO_DEVICE); - - spin_unlock_irqrestore(&p->port.lock, flags); + if (dma_mapping_error(p->port.dev, dma->tx_addr)) { + ret = -EIO; + goto err_unmap_rx; + } return 0; + +err_unmap_rx: + dma_unmap_single(p->port.dev, dma->rx_addr, dma->rx_size, + DMA_FROM_DEVICE); +err_free_rx: + kfree(dma->rx_buf); +err_release_tx: + dma_release_channel(dma->tx_chan); +err_release_rx: + dma_release_channel(dma->rx_chan); + return ret; } static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p) @@ -922,7 +934,7 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p) if (dma->rx_chan) { dmaengine_terminate_all(dma->rx_chan); - dma_unmap_single(dma->rx_chan->device->dev, dma->rx_addr, + dma_unmap_single(p->port.dev, dma->rx_addr, dma->rx_size, DMA_FROM_DEVICE); kfree(dma->rx_buf); dma_release_channel(dma->rx_chan); @@ -931,7 +943,7 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p) if (dma->tx_chan) { dmaengine_terminate_all(dma->tx_chan); - dma_unmap_single(dma->tx_chan->device->dev, dma->tx_addr, + dma_unmap_single(p->port.dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); dma_release_channel(dma->tx_chan); dma->tx_chan = NULL; diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c index 771f361c47ea..041625cc24bb 100644 --- a/drivers/tty/serial/sb1250-duart.c +++ b/drivers/tty/serial/sb1250-duart.c @@ -41,7 +41,7 @@ #include <linux/tty_flip.h> #include <linux/types.h> -#include <linux/atomic.h> +#include <linux/refcount.h> #include <asm/io.h> #include <asm/war.h> @@ -103,7 +103,7 @@ struct sbd_port { struct sbd_duart { struct sbd_port sport[2]; unsigned long mapctrl; - atomic_t map_guard; + refcount_t map_guard; }; #define to_sport(uport) container_of(uport, struct sbd_port, port) @@ -654,15 +654,13 @@ static void sbd_release_port(struct uart_port *uport) { struct sbd_port *sport = to_sport(uport); struct sbd_duart *duart = sport->duart; - int map_guard; iounmap(sport->memctrl); sport->memctrl = NULL; iounmap(uport->membase); uport->membase = NULL; - map_guard = atomic_add_return(-1, &duart->map_guard); - if (!map_guard) + if(refcount_dec_and_test(&duart->map_guard)) release_mem_region(duart->mapctrl, DUART_CHANREG_SPACING); release_mem_region(uport->mapbase, DUART_CHANREG_SPACING); } @@ -698,7 +696,6 @@ static int sbd_request_port(struct uart_port *uport) { const char *err = KERN_ERR "sbd: Unable to reserve MMIO resource\n"; struct sbd_duart *duart = to_sport(uport)->duart; - int map_guard; int ret = 0; if (!request_mem_region(uport->mapbase, DUART_CHANREG_SPACING, @@ -706,11 +703,11 @@ static int sbd_request_port(struct uart_port *uport) printk(err); return -EBUSY; } - map_guard = atomic_add_return(1, &duart->map_guard); - if (map_guard == 1) { + refcount_inc(&duart->map_guard); + if (refcount_read(&duart->map_guard) == 1) { if (!request_mem_region(duart->mapctrl, DUART_CHANREG_SPACING, "sb1250-duart")) { - atomic_add(-1, &duart->map_guard); + refcount_dec(&duart->map_guard); printk(err); ret = -EBUSY; } @@ -718,8 +715,7 @@ static int sbd_request_port(struct uart_port *uport) if (!ret) { ret = sbd_map_port(uport); if (ret) { - map_guard = atomic_add_return(-1, &duart->map_guard); - if (!map_guard) + if (refcount_dec_and_test(&duart->map_guard)) release_mem_region(duart->mapctrl, DUART_CHANREG_SPACING); } diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index fcf803ffad19..cdd2f942317c 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -884,14 +884,19 @@ static int sccnxp_probe(struct platform_device *pdev) clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { - if (PTR_ERR(clk) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; + ret = PTR_ERR(clk); + if (ret == -EPROBE_DEFER) goto err_out; - } + uartclk = 0; + } else { + clk_prepare_enable(clk); + uartclk = clk_get_rate(clk); + } + + if (!uartclk) { dev_notice(&pdev->dev, "Using default clock frequency\n"); uartclk = s->chip->freq_std; - } else - uartclk = clk_get_rate(clk); + } /* Check input frequency */ if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) { diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 3fe56894974a..f534a40aebde 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -954,11 +954,10 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, old_custom_divisor != uport->custom_divisor) { /* * If they're setting up a custom divisor or speed, - * instead of clearing it, then bitch about it. No - * need to rate-limit; it's CAP_SYS_ADMIN only. + * instead of clearing it, then bitch about it. */ if (uport->flags & UPF_SPD_MASK) { - dev_notice(uport->dev, + dev_notice_ratelimited(uport->dev, "%s sets custom speed on %s. This is deprecated.\n", current->comm, tty_name(port->tty)); @@ -2083,7 +2082,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) mutex_lock(&port->mutex); tty_dev = device_find_child(uport->dev, &match, serial_match_port); - if (device_may_wakeup(tty_dev)) { + if (tty_dev && device_may_wakeup(tty_dev)) { if (!enable_irq_wake(uport->irq)) uport->irq_wake = 1; put_device(tty_dev); @@ -2117,9 +2116,8 @@ 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) - dev_err(uport->dev, "%s%d: Unable to drain transmitter\n", - drv->dev_name, - drv->tty_driver->name_base + uport->line); + dev_err(uport->dev, "%s: Unable to drain transmitter\n", + uport->name); ops->shutdown(uport); } @@ -2248,11 +2246,10 @@ 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", + pr_info("%s%s%s at %s (irq = %d, base_baud = %d) is a %s\n", port->dev ? dev_name(port->dev) : "", port->dev ? ": " : "", - drv->dev_name, - drv->tty_driver->name_base + port->line, + port->name, address, port->irq, port->uartclk / 16, uart_type(port)); } @@ -2331,9 +2328,6 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) int flow = 'n'; int ret = 0; - if (!state) - return -1; - tport = &state->port; mutex_lock(&tport->mutex); @@ -2368,13 +2362,12 @@ static int uart_poll_get_char(struct tty_driver *driver, int line) struct uart_port *port; int ret = -1; - if (state) { - port = uart_port_ref(state); - if (port) { - ret = port->ops->poll_get_char(port); - uart_port_deref(port); - } + port = uart_port_ref(state); + if (port) { + ret = port->ops->poll_get_char(port); + uart_port_deref(port); } + return ret; } @@ -2384,9 +2377,6 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) struct uart_state *state = drv->state + line; struct uart_port *port; - if (!state) - return; - port = uart_port_ref(state); if (!port) return; @@ -2751,6 +2741,12 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) state->pm_state = UART_PM_STATE_UNDEFINED; uport->cons = drv->cons; uport->minor = drv->tty_driver->minor_start + uport->line; + uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name, + drv->tty_driver->name_base + uport->line); + if (!uport->name) { + ret = -ENOMEM; + goto out; + } /* * If this port is a console, then the spinlock is already @@ -2785,7 +2781,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) * Register the port whether it's detected or not. This allows * setserial to be used to alter this port's parameters. */ - tty_dev = tty_port_register_device_attr(port, drv->tty_driver, + tty_dev = tty_port_register_device_attr_serdev(port, drv->tty_driver, uport->line, uport->dev, port, uport->tty_groups); if (likely(!IS_ERR(tty_dev))) { device_set_wakeup_capable(tty_dev, 1); @@ -2848,7 +2844,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) /* * Remove the devices from the tty layer */ - tty_unregister_device(drv->tty_driver, uport->line); + tty_port_unregister_device(port, drv->tty_driver, uport->line); tty = tty_port_tty_get(port); if (tty) { @@ -2868,6 +2864,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) if (uport->type != PORT_UNKNOWN && uport->ops->release_port) uport->ops->release_port(uport); kfree(uport->tty_groups); + kfree(uport->name); /* * Indicate that there isn't a port here anymore. diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 9a47cc4f16a2..e08b16b070c0 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -683,24 +683,37 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) } if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + u16 data = serial_port_in(port, SCPDR); u16 ctrl = serial_port_in(port, SCPCR); /* Enable RXD and TXD pin functions */ ctrl &= ~(SCPCR_RXDC | SCPCR_TXDC); if (to_sci_port(port)->has_rtscts) { - /* RTS# is output, driven 1 */ - ctrl |= SCPCR_RTSC; - serial_port_out(port, SCPDR, - serial_port_in(port, SCPDR) | SCPDR_RTSD); + /* RTS# is output, active low, unless autorts */ + if (!(port->mctrl & TIOCM_RTS)) { + ctrl |= SCPCR_RTSC; + data |= SCPDR_RTSD; + } else if (!s->autorts) { + ctrl |= SCPCR_RTSC; + data &= ~SCPDR_RTSD; + } else { + /* Enable RTS# pin function */ + ctrl &= ~SCPCR_RTSC; + } /* Enable CTS# pin function */ ctrl &= ~SCPCR_CTSC; } + serial_port_out(port, SCPDR, data); serial_port_out(port, SCPCR, ctrl); } else if (sci_getreg(port, SCSPTR)->size) { u16 status = serial_port_in(port, SCSPTR); - /* RTS# is output, driven 1 */ - status |= SCSPTR_RTSIO | SCSPTR_RTSDT; + /* RTS# is always output; and active low, unless autorts */ + status |= SCSPTR_RTSIO; + if (!(port->mctrl & TIOCM_RTS)) + status |= SCSPTR_RTSDT; + else if (!s->autorts) + status &= ~SCSPTR_RTSDT; /* CTS# and SCK are inputs */ status &= ~(SCSPTR_CTSIO | SCSPTR_SCKIO); serial_port_out(port, SCSPTR, status); @@ -1072,10 +1085,12 @@ static ssize_t rx_trigger_store(struct device *dev, { struct uart_port *port = dev_get_drvdata(dev); struct sci_port *sci = to_sci_port(port); + int ret; long r; - if (kstrtol(buf, 0, &r) == -EINVAL) - return -EINVAL; + ret = kstrtol(buf, 0, &r); + if (ret) + return ret; sci->rx_trigger = scif_set_rtrg(port, r); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) @@ -1103,10 +1118,12 @@ static ssize_t rx_fifo_timeout_store(struct device *dev, { struct uart_port *port = dev_get_drvdata(dev); struct sci_port *sci = to_sci_port(port); + int ret; long r; - if (kstrtol(buf, 0, &r) == -EINVAL) - return -EINVAL; + ret = kstrtol(buf, 0, &r); + if (ret) + return ret; sci->rx_fifo_timeout = r; scif_set_rtrg(port, 1); if (r > 0) @@ -1437,8 +1454,7 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port, chan = dma_request_slave_channel(port->dev, dir == DMA_MEM_TO_DEV ? "tx" : "rx"); if (!chan) { - dev_warn(port->dev, - "dma_request_slave_channel_compat failed\n"); + dev_warn(port->dev, "dma_request_slave_channel failed\n"); return NULL; } @@ -1545,7 +1561,16 @@ static void sci_free_dma(struct uart_port *port) if (s->chan_rx) sci_rx_dma_release(s, false); } -#else + +static void sci_flush_buffer(struct uart_port *port) +{ + /* + * In uart_flush_buffer(), the xmit circular buffer has just been + * cleared, so we have to reset tx_dma_len accordingly. + */ + to_sci_port(port)->tx_dma_len = 0; +} +#else /* !CONFIG_SERIAL_SH_SCI_DMA */ static inline void sci_request_dma(struct uart_port *port) { } @@ -1553,7 +1578,9 @@ static inline void sci_request_dma(struct uart_port *port) static inline void sci_free_dma(struct uart_port *port) { } -#endif + +#define sci_flush_buffer NULL +#endif /* !CONFIG_SERIAL_SH_SCI_DMA */ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) { @@ -1985,11 +2012,13 @@ static int sci_startup(struct uart_port *port) dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + sci_request_dma(port); + ret = sci_request_irq(s); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + sci_free_dma(port); return ret; - - sci_request_dma(port); + } return 0; } @@ -2021,8 +2050,8 @@ static void sci_shutdown(struct uart_port *port) } #endif - sci_free_dma(port); sci_free_irq(s); + sci_free_dma(port); } static int sci_sck_calc(struct sci_port *s, unsigned int bps, @@ -2157,10 +2186,6 @@ static void sci_reset(struct uart_port *port) unsigned int status; struct sci_port *s = to_sci_port(port); - do { - status = serial_port_in(port, SCxSR); - } while (!(status & SCxSR_TEND(port))); - serial_port_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ reg = sci_getreg(port, SCFCR); @@ -2374,6 +2399,10 @@ done: serial_port_out(port, SCFCR, ctrl); } + if (port->flags & UPF_HARD_FLOW) { + /* Refresh (Auto) RTS */ + sci_set_mctrl(port, port->mctrl); + } scr_val |= SCSCR_RE | SCSCR_TE | (s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)); @@ -2566,6 +2595,7 @@ static const struct uart_ops sci_uart_ops = { .break_ctl = sci_break_ctl, .startup = sci_startup, .shutdown = sci_shutdown, + .flush_buffer = sci_flush_buffer, .set_termios = sci_set_termios, .pm = sci_pm, .type = sci_type, @@ -2935,6 +2965,7 @@ static inline int sci_probe_earlyprintk(struct platform_device *pdev) static const char banner[] __initconst = "SuperH (H)SCI(F) driver initialized"; +static DEFINE_MUTEX(sci_uart_registration_lock); static struct uart_driver sci_uart_driver = { .owner = THIS_MODULE, .driver_name = "sci", @@ -3063,6 +3094,16 @@ static int sci_probe_single(struct platform_device *dev, return -EINVAL; } + mutex_lock(&sci_uart_registration_lock); + if (!sci_uart_driver.state) { + ret = uart_register_driver(&sci_uart_driver); + if (ret) { + mutex_unlock(&sci_uart_registration_lock); + return ret; + } + } + mutex_unlock(&sci_uart_registration_lock); + ret = sci_init_single(dev, sciport, index, p, false); if (ret) return ret; @@ -3186,24 +3227,17 @@ static struct platform_driver sci_driver = { static int __init sci_init(void) { - int ret; - pr_info("%s\n", banner); - ret = uart_register_driver(&sci_uart_driver); - if (likely(ret == 0)) { - ret = platform_driver_register(&sci_driver); - if (unlikely(ret)) - uart_unregister_driver(&sci_uart_driver); - } - - return ret; + return platform_driver_register(&sci_driver); } static void __exit sci_exit(void) { platform_driver_unregister(&sci_driver); - uart_unregister_driver(&sci_uart_driver); + + if (sci_uart_driver.state) + uart_unregister_driver(&sci_uart_driver); } #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index e03282d92b59..684cb8dd8050 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -1253,7 +1253,7 @@ next_hrt: return HRTIMER_RESTART; } -static struct of_device_id sirfsoc_uart_ids[] = { +static const struct of_device_id sirfsoc_uart_ids[] = { { .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,}, { .compatible = "sirf,atlas7-uart", .data = &sirfsoc_uart}, { .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp}, diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index d98e3dc4838e..90996ad97b37 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -36,7 +36,7 @@ #define SPRD_FIFO_SIZE 128 #define SPRD_DEF_RATE 26000000 #define SPRD_BAUD_IO_LIMIT 3000000 -#define SPRD_TIMEOUT 256 +#define SPRD_TIMEOUT 256000 /* the offset of serial registers and BITs for them */ /* data registers */ diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index c334bcc59c64..6b0ca65027d0 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -758,6 +758,7 @@ static int asc_init_port(struct asc_port *ascport, if (IS_ERR(ascport->pinctrl)) { ret = PTR_ERR(ascport->pinctrl); dev_err(&pdev->dev, "Failed to get Pinctrl: %d\n", ret); + return ret; } ascport->states[DEFAULT] = @@ -887,13 +888,12 @@ static void asc_console_write(struct console *co, const char *s, unsigned count) int locked = 1; u32 intenable; - local_irq_save(flags); if (port->sysrq) locked = 0; /* asc_interrupt has already claimed the lock */ else if (oops_in_progress) - locked = spin_trylock(&port->lock); + locked = spin_trylock_irqsave(&port->lock, flags); else - spin_lock(&port->lock); + spin_lock_irqsave(&port->lock, flags); /* * Disable interrupts so we don't get the IRQ line bouncing @@ -911,14 +911,13 @@ static void asc_console_write(struct console *co, const char *s, unsigned count) asc_out(port, ASC_INTEN, intenable); if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&port->lock, flags); } static int asc_console_setup(struct console *co, char *options) { struct asc_port *ascport; - int baud = 9600; + int baud = 115200; int bits = 8; int parity = 'n'; int flow = 'n'; @@ -986,7 +985,7 @@ static struct platform_driver asc_serial_driver = { static int __init asc_init(void) { int ret; - static char banner[] __initdata = + static const char banner[] __initconst = KERN_INFO "STMicroelectronics ASC driver initialized\n"; printk(banner); diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 817bb0d3f326..c9b8d702dadc 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -28,7 +28,7 @@ #define ULITE_NAME "ttyUL" #define ULITE_MAJOR 204 #define ULITE_MINOR 187 -#define ULITE_NR_UARTS 16 +#define ULITE_NR_UARTS CONFIG_SERIAL_UARTLITE_NR_UARTS /* --------------------------------------------------------------------- * Register definitions diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index ad77d0ed0c46..fde55dcdea5a 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -30,6 +30,7 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" @@ -176,6 +177,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_BDIV_MIN 4 #define CDNS_UART_BDIV_MAX 255 #define CDNS_UART_CD_MAX 65535 +#define UART_AUTOSUSPEND_TIMEOUT 3000 /** * struct cdns_uart - device data @@ -184,6 +186,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * @pclk: APB clock * @baud: Current baud rate * @clk_rate_change_nb: Notifier block for clock changes + * @quirks: Flags for RXBS support. */ struct cdns_uart { struct uart_port *port; @@ -1065,16 +1068,13 @@ static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) static void cdns_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { - struct cdns_uart *cdns_uart = port->private_data; - switch (state) { case UART_PM_STATE_OFF: - clk_disable(cdns_uart->uartclk); - clk_disable(cdns_uart->pclk); + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); break; default: - clk_enable(cdns_uart->pclk); - clk_enable(cdns_uart->uartclk); + pm_runtime_get_sync(port->dev); break; } } @@ -1353,12 +1353,7 @@ static int cdns_uart_suspend(struct device *device) * the suspend. */ uart_suspend_port(&cdns_uart_uart_driver, port); - if (console_suspend_enabled && !may_wake) { - struct cdns_uart *cdns_uart = port->private_data; - - clk_disable(cdns_uart->uartclk); - clk_disable(cdns_uart->pclk); - } else { + if (!(console_suspend_enabled && !may_wake)) { unsigned long flags = 0; spin_lock_irqsave(&port->lock, flags); @@ -1423,6 +1418,8 @@ static int cdns_uart_resume(struct device *device) ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; writel(ctrl_reg, port->membase + CDNS_UART_CR); + clk_disable(cdns_uart->uartclk); + clk_disable(cdns_uart->pclk); spin_unlock_irqrestore(&port->lock, flags); } else { spin_lock_irqsave(&port->lock, flags); @@ -1436,9 +1433,33 @@ static int cdns_uart_resume(struct device *device) return uart_resume_port(&cdns_uart_uart_driver, port); } #endif /* ! CONFIG_PM_SLEEP */ +static int __maybe_unused cdns_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct uart_port *port = platform_get_drvdata(pdev); + struct cdns_uart *cdns_uart = port->private_data; + + clk_disable(cdns_uart->uartclk); + clk_disable(cdns_uart->pclk); + return 0; +}; -static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend, - cdns_uart_resume); +static int __maybe_unused cdns_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct uart_port *port = platform_get_drvdata(pdev); + struct cdns_uart *cdns_uart = port->private_data; + + clk_enable(cdns_uart->pclk); + clk_enable(cdns_uart->uartclk); + return 0; +}; + +static const struct dev_pm_ops cdns_uart_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cdns_uart_suspend, cdns_uart_resume) + SET_RUNTIME_PM_OPS(cdns_runtime_suspend, + cdns_runtime_resume, NULL) +}; static const struct cdns_platform_data zynqmp_uart_def = { .quirks = CDNS_UART_RXBS_SUPPORT, }; @@ -1501,12 +1522,12 @@ static int cdns_uart_probe(struct platform_device *pdev) return PTR_ERR(cdns_uart_data->uartclk); } - rc = clk_prepare(cdns_uart_data->pclk); + rc = clk_prepare_enable(cdns_uart_data->pclk); if (rc) { dev_err(&pdev->dev, "Unable to enable pclk clock.\n"); return rc; } - rc = clk_prepare(cdns_uart_data->uartclk); + rc = clk_prepare_enable(cdns_uart_data->uartclk); if (rc) { dev_err(&pdev->dev, "Unable to enable device clock.\n"); goto err_out_clk_dis_pclk; @@ -1558,24 +1579,33 @@ static int cdns_uart_probe(struct platform_device *pdev) cdns_uart_data->port = port; platform_set_drvdata(pdev, port); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + rc = uart_add_one_port(&cdns_uart_uart_driver, port); if (rc) { dev_err(&pdev->dev, "uart_add_one_port() failed; err=%i\n", rc); - goto err_out_notif_unreg; + goto err_out_pm_disable; } return 0; +err_out_pm_disable: + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); err_out_notif_unreg: #ifdef CONFIG_COMMON_CLK clk_notifier_unregister(cdns_uart_data->uartclk, &cdns_uart_data->clk_rate_change_nb); #endif err_out_clk_disable: - clk_unprepare(cdns_uart_data->uartclk); + clk_disable_unprepare(cdns_uart_data->uartclk); err_out_clk_dis_pclk: - clk_unprepare(cdns_uart_data->pclk); + clk_disable_unprepare(cdns_uart_data->pclk); return rc; } @@ -1599,8 +1629,11 @@ static int cdns_uart_remove(struct platform_device *pdev) #endif rc = uart_remove_one_port(&cdns_uart_uart_driver, port); port->mapbase = 0; - clk_unprepare(cdns_uart_data->uartclk); - clk_unprepare(cdns_uart_data->pclk); + clk_disable_unprepare(cdns_uart_data->uartclk); + clk_disable_unprepare(cdns_uart_data->pclk); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); return rc; } diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 657eed82eeb3..3fafc5a1b2e0 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -869,9 +869,9 @@ static int txholdbufs[MAX_TOTAL_DEVICES]; module_param(break_on_load, bool, 0); module_param(ttymajor, int, 0); -module_param_array(io, int, NULL, 0); -module_param_array(irq, int, NULL, 0); -module_param_array(dma, int, NULL, 0); +module_param_hw_array(io, int, ioport, NULL, 0); +module_param_hw_array(irq, int, irq, NULL, 0); +module_param_hw_array(dma, int, dma, NULL, 0); module_param(debug_level, int, 0); module_param_array(maxframe, int, NULL, 0); module_param_array(txdmabufs, int, NULL, 0); @@ -7960,7 +7960,7 @@ static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size) return; } - memcpy(skb_put(skb, size), buf, size); + skb_put_data(skb, buf, size); skb->protocol = hdlc_type_trans(skb, dev); diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 31885f20fc15..529c6e3cd537 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -184,7 +184,7 @@ static void hdlcdev_exit(struct slgt_info *info); struct cond_wait { struct cond_wait *next; wait_queue_head_t q; - wait_queue_t wait; + wait_queue_entry_t wait; unsigned int data; }; static void init_cond_wait(struct cond_wait *w, unsigned int data); @@ -1755,7 +1755,7 @@ static void hdlcdev_rx(struct slgt_info *info, char *buf, int size) return; } - memcpy(skb_put(skb, size), buf, size); + skb_put_data(skb, buf, size); skb->protocol = hdlc_type_trans(skb, dev); diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index 51e8846cd68f..9b4fb0251c1a 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -1874,7 +1874,7 @@ static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size) return; } - memcpy(skb_put(skb, size), buf, size); + skb_put_data(skb, buf, size); skb->protocol = hdlc_type_trans(skb, dev); diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index c6fc7141d7b2..3ffc1ce29023 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -372,7 +372,7 @@ static void moom_callback(struct work_struct *ignored) mutex_lock(&oom_lock); if (!out_of_memory(&oc)) - pr_info("OOM request ignored because killer is disabled\n"); + pr_info("OOM request ignored. No task eligible\n"); mutex_unlock(&oom_lock); } @@ -446,7 +446,7 @@ static struct sysrq_key_op *sysrq_key_table[36] = { */ NULL, /* a */ &sysrq_reboot_op, /* b */ - &sysrq_crash_op, /* c & ibm_emac driver debug */ + &sysrq_crash_op, /* c */ &sysrq_showlocks_op, /* d */ &sysrq_term_op, /* e */ &sysrq_moom_op, /* f */ diff --git a/drivers/tty/tty_baudrate.c b/drivers/tty/tty_baudrate.c new file mode 100644 index 000000000000..5c33fd25676d --- /dev/null +++ b/drivers/tty/tty_baudrate.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/termios.h> +#include <linux/tty.h> +#include <linux/export.h> + + +/* + * Routine which returns the baud rate of the tty + * + * Note that the baud_table needs to be kept in sync with the + * include/asm/termbits.h file. + */ +static const speed_t baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, +#ifdef __sparc__ + 76800, 153600, 307200, 614400, 921600 +#else + 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, + 2500000, 3000000, 3500000, 4000000 +#endif +}; + +#ifndef __sparc__ +static const tcflag_t baud_bits[] = { + B0, B50, B75, B110, B134, B150, B200, B300, B600, + B1200, B1800, B2400, B4800, B9600, B19200, B38400, + B57600, B115200, B230400, B460800, B500000, B576000, + B921600, B1000000, B1152000, B1500000, B2000000, B2500000, + B3000000, B3500000, B4000000 +}; +#else +static const tcflag_t baud_bits[] = { + B0, B50, B75, B110, B134, B150, B200, B300, B600, + B1200, B1800, B2400, B4800, B9600, B19200, B38400, + B57600, B115200, B230400, B460800, B76800, B153600, + B307200, B614400, B921600 +}; +#endif + +static int n_baud_table = ARRAY_SIZE(baud_table); + +/** + * tty_termios_baud_rate + * @termios: termios structure + * + * Convert termios baud rate data into a speed. This should be called + * with the termios lock held if this termios is a terminal termios + * structure. May change the termios data. Device drivers can call this + * function but should use ->c_[io]speed directly as they are updated. + * + * Locking: none + */ + +speed_t tty_termios_baud_rate(struct ktermios *termios) +{ + unsigned int cbaud; + + cbaud = termios->c_cflag & CBAUD; + +#ifdef BOTHER + /* Magic token for arbitrary speed via c_ispeed/c_ospeed */ + if (cbaud == BOTHER) + return termios->c_ospeed; +#endif + if (cbaud & CBAUDEX) { + cbaud &= ~CBAUDEX; + + if (cbaud < 1 || cbaud + 15 > n_baud_table) + termios->c_cflag &= ~CBAUDEX; + else + cbaud += 15; + } + return baud_table[cbaud]; +} +EXPORT_SYMBOL(tty_termios_baud_rate); + +/** + * tty_termios_input_baud_rate + * @termios: termios structure + * + * Convert termios baud rate data into a speed. This should be called + * with the termios lock held if this termios is a terminal termios + * structure. May change the termios data. Device drivers can call this + * function but should use ->c_[io]speed directly as they are updated. + * + * Locking: none + */ + +speed_t tty_termios_input_baud_rate(struct ktermios *termios) +{ +#ifdef IBSHIFT + unsigned int cbaud = (termios->c_cflag >> IBSHIFT) & CBAUD; + + if (cbaud == B0) + return tty_termios_baud_rate(termios); + + /* Magic token for arbitrary speed via c_ispeed*/ + if (cbaud == BOTHER) + return termios->c_ispeed; + + if (cbaud & CBAUDEX) { + cbaud &= ~CBAUDEX; + + if (cbaud < 1 || cbaud + 15 > n_baud_table) + termios->c_cflag &= ~(CBAUDEX << IBSHIFT); + else + cbaud += 15; + } + return baud_table[cbaud]; +#else + return tty_termios_baud_rate(termios); +#endif +} +EXPORT_SYMBOL(tty_termios_input_baud_rate); + +/** + * tty_termios_encode_baud_rate + * @termios: ktermios structure holding user requested state + * @ispeed: input speed + * @ospeed: output speed + * + * Encode the speeds set into the passed termios structure. This is + * used as a library helper for drivers so that they can report back + * the actual speed selected when it differs from the speed requested + * + * For maximal back compatibility with legacy SYS5/POSIX *nix behaviour + * we need to carefully set the bits when the user does not get the + * desired speed. We allow small margins and preserve as much of possible + * of the input intent to keep compatibility. + * + * Locking: Caller should hold termios lock. This is already held + * when calling this function from the driver termios handler. + * + * The ifdefs deal with platforms whose owners have yet to update them + * and will all go away once this is done. + */ + +void tty_termios_encode_baud_rate(struct ktermios *termios, + speed_t ibaud, speed_t obaud) +{ + int i = 0; + int ifound = -1, ofound = -1; + int iclose = ibaud/50, oclose = obaud/50; + int ibinput = 0; + + if (obaud == 0) /* CD dropped */ + ibaud = 0; /* Clear ibaud to be sure */ + + termios->c_ispeed = ibaud; + termios->c_ospeed = obaud; + +#ifdef BOTHER + /* If the user asked for a precise weird speed give a precise weird + answer. If they asked for a Bfoo speed they may have problems + digesting non-exact replies so fuzz a bit */ + + if ((termios->c_cflag & CBAUD) == BOTHER) + oclose = 0; + if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER) + iclose = 0; + if ((termios->c_cflag >> IBSHIFT) & CBAUD) + ibinput = 1; /* An input speed was specified */ +#endif + termios->c_cflag &= ~CBAUD; + + /* + * Our goal is to find a close match to the standard baud rate + * returned. Walk the baud rate table and if we get a very close + * match then report back the speed as a POSIX Bxxxx value by + * preference + */ + + do { + if (obaud - oclose <= baud_table[i] && + obaud + oclose >= baud_table[i]) { + termios->c_cflag |= baud_bits[i]; + ofound = i; + } + if (ibaud - iclose <= baud_table[i] && + ibaud + iclose >= baud_table[i]) { + /* For the case input == output don't set IBAUD bits + if the user didn't do so */ + if (ofound == i && !ibinput) + ifound = i; +#ifdef IBSHIFT + else { + ifound = i; + termios->c_cflag |= (baud_bits[i] << IBSHIFT); + } +#endif + } + } while (++i < n_baud_table); + + /* + * If we found no match then use BOTHER if provided or warn + * the user their platform maintainer needs to wake up if not. + */ +#ifdef BOTHER + if (ofound == -1) + termios->c_cflag |= BOTHER; + /* Set exact input bits only if the input and output differ or the + user already did */ + if (ifound == -1 && (ibaud != obaud || ibinput)) + termios->c_cflag |= (BOTHER << IBSHIFT); +#else + if (ifound == -1 || ofound == -1) + pr_warn_once("tty: Unable to return correct speed data as your architecture needs updating.\n"); +#endif +} +EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); + +/** + * tty_encode_baud_rate - set baud rate of the tty + * @ibaud: input baud rate + * @obad: output baud rate + * + * Update the current termios data for the tty with the new speed + * settings. The caller must hold the termios_rwsem for the tty in + * question. + */ + +void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) +{ + tty_termios_encode_baud_rate(&tty->termios, ibaud, obaud); +} +EXPORT_SYMBOL_GPL(tty_encode_baud_rate); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e6d1a6510886..974b13d24401 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -325,6 +325,56 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index) return NULL; } +/** + * tty_dev_name_to_number - return dev_t for device name + * @name: user space name of device under /dev + * @number: pointer to dev_t that this function will populate + * + * This function converts device names like ttyS0 or ttyUSB1 into dev_t + * like (4, 64) or (188, 1). If no corresponding driver is registered then + * the function returns -ENODEV. + * + * Locking: this acquires tty_mutex to protect the tty_drivers list from + * being modified while we are traversing it, and makes sure to + * release it before exiting. + */ +int tty_dev_name_to_number(const char *name, dev_t *number) +{ + struct tty_driver *p; + int ret; + int index, prefix_length = 0; + const char *str; + + for (str = name; *str && !isdigit(*str); str++) + ; + + if (!*str) + return -EINVAL; + + ret = kstrtoint(str, 10, &index); + if (ret) + return ret; + + prefix_length = str - name; + mutex_lock(&tty_mutex); + + list_for_each_entry(p, &tty_drivers, tty_drivers) + if (prefix_length == strlen(p->name) && strncmp(name, + p->name, prefix_length) == 0) { + if (index < p->num) { + *number = MKDEV(p->major, p->minor_start + index); + goto out; + } + } + + /* if here then driver wasn't found */ + ret = -ENODEV; +out: + mutex_unlock(&tty_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(tty_dev_name_to_number); + #ifdef CONFIG_CONSOLE_POLL /** @@ -377,65 +427,6 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) EXPORT_SYMBOL_GPL(tty_find_polling_driver); #endif -static int is_ignored(int sig) -{ - return (sigismember(¤t->blocked, sig) || - current->sighand->action[sig-1].sa.sa_handler == SIG_IGN); -} - -/** - * tty_check_change - check for POSIX terminal changes - * @tty: tty to check - * - * If we try to write to, or set the state of, a terminal and we're - * not in the foreground, send a SIGTTOU. If the signal is blocked or - * ignored, go ahead and perform the operation. (POSIX 7.2) - * - * Locking: ctrl_lock - */ - -int __tty_check_change(struct tty_struct *tty, int sig) -{ - unsigned long flags; - struct pid *pgrp, *tty_pgrp; - int ret = 0; - - if (current->signal->tty != tty) - return 0; - - rcu_read_lock(); - pgrp = task_pgrp(current); - - spin_lock_irqsave(&tty->ctrl_lock, flags); - tty_pgrp = tty->pgrp; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - - if (tty_pgrp && pgrp != tty->pgrp) { - if (is_ignored(sig)) { - if (sig == SIGTTIN) - ret = -EIO; - } else if (is_current_pgrp_orphaned()) - ret = -EIO; - else { - kill_pgrp(pgrp, sig, 1); - set_thread_flag(TIF_SIGPENDING); - ret = -ERESTARTSYS; - } - } - rcu_read_unlock(); - - if (!tty_pgrp) - tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig); - - return ret; -} - -int tty_check_change(struct tty_struct *tty) -{ - return __tty_check_change(tty, SIGTTOU); -} -EXPORT_SYMBOL(tty_check_change); - static ssize_t hung_up_tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -509,79 +500,6 @@ static const struct file_operations hung_up_tty_fops = { static DEFINE_SPINLOCK(redirect_lock); static struct file *redirect; - -void proc_clear_tty(struct task_struct *p) -{ - unsigned long flags; - struct tty_struct *tty; - spin_lock_irqsave(&p->sighand->siglock, flags); - tty = p->signal->tty; - p->signal->tty = NULL; - spin_unlock_irqrestore(&p->sighand->siglock, flags); - tty_kref_put(tty); -} - -/** - * proc_set_tty - set the controlling terminal - * - * Only callable by the session leader and only if it does not already have - * a controlling terminal. - * - * Caller must hold: tty_lock() - * a readlock on tasklist_lock - * sighand lock - */ -static void __proc_set_tty(struct tty_struct *tty) -{ - unsigned long flags; - - spin_lock_irqsave(&tty->ctrl_lock, flags); - /* - * The session and fg pgrp references will be non-NULL if - * tiocsctty() is stealing the controlling tty - */ - put_pid(tty->session); - put_pid(tty->pgrp); - tty->pgrp = get_pid(task_pgrp(current)); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - tty->session = get_pid(task_session(current)); - if (current->signal->tty) { - tty_debug(tty, "current tty %s not NULL!!\n", - current->signal->tty->name); - tty_kref_put(current->signal->tty); - } - put_pid(current->signal->tty_old_pgrp); - current->signal->tty = tty_kref_get(tty); - current->signal->tty_old_pgrp = NULL; -} - -static void proc_set_tty(struct tty_struct *tty) -{ - spin_lock_irq(¤t->sighand->siglock); - __proc_set_tty(tty); - spin_unlock_irq(¤t->sighand->siglock); -} - -struct tty_struct *get_current_tty(void) -{ - struct tty_struct *tty; - unsigned long flags; - - spin_lock_irqsave(¤t->sighand->siglock, flags); - tty = tty_kref_get(current->signal->tty); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - return tty; -} -EXPORT_SYMBOL_GPL(get_current_tty); - -static void session_clear_tty(struct pid *session) -{ - struct task_struct *p; - do_each_pid_task(session, PIDTYPE_SID, p) { - proc_clear_tty(p); - } while_each_pid_task(session, PIDTYPE_SID, p); -} - /** * tty_wakeup - request more data * @tty: terminal @@ -609,60 +527,6 @@ void tty_wakeup(struct tty_struct *tty) EXPORT_SYMBOL_GPL(tty_wakeup); /** - * tty_signal_session_leader - sends SIGHUP to session leader - * @tty controlling tty - * @exit_session if non-zero, signal all foreground group processes - * - * Send SIGHUP and SIGCONT to the session leader and its process group. - * Optionally, signal all processes in the foreground process group. - * - * Returns the number of processes in the session with this tty - * as their controlling terminal. This value is used to drop - * tty references for those processes. - */ -static int tty_signal_session_leader(struct tty_struct *tty, int exit_session) -{ - struct task_struct *p; - int refs = 0; - struct pid *tty_pgrp = NULL; - - read_lock(&tasklist_lock); - if (tty->session) { - do_each_pid_task(tty->session, PIDTYPE_SID, p) { - spin_lock_irq(&p->sighand->siglock); - if (p->signal->tty == tty) { - p->signal->tty = NULL; - /* We defer the dereferences outside fo - the tasklist lock */ - refs++; - } - if (!p->signal->leader) { - spin_unlock_irq(&p->sighand->siglock); - continue; - } - __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); - __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); - put_pid(p->signal->tty_old_pgrp); /* A noop */ - spin_lock(&tty->ctrl_lock); - tty_pgrp = get_pid(tty->pgrp); - if (tty->pgrp) - p->signal->tty_old_pgrp = get_pid(tty->pgrp); - spin_unlock(&tty->ctrl_lock); - spin_unlock_irq(&p->sighand->siglock); - } while_each_pid_task(tty->session, PIDTYPE_SID, p); - } - read_unlock(&tasklist_lock); - - if (tty_pgrp) { - if (exit_session) - kill_pgrp(tty_pgrp, SIGHUP, exit_session); - put_pid(tty_pgrp); - } - - return refs; -} - -/** * __tty_hangup - actual handler for hangup events * @work: tty device * @@ -840,7 +704,7 @@ void tty_vhangup_self(void) * is complete. That guarantee is necessary for security reasons. */ -static void tty_vhangup_session(struct tty_struct *tty) +void tty_vhangup_session(struct tty_struct *tty) { tty_debug_hangup(tty, "session hangup\n"); __tty_hangup(tty, 1); @@ -862,106 +726,6 @@ int tty_hung_up_p(struct file *filp) EXPORT_SYMBOL(tty_hung_up_p); /** - * disassociate_ctty - disconnect controlling tty - * @on_exit: true if exiting so need to "hang up" the session - * - * This function is typically called only by the session leader, when - * it wants to disassociate itself from its controlling tty. - * - * It performs the following functions: - * (1) Sends a SIGHUP and SIGCONT to the foreground process group - * (2) Clears the tty from being controlling the session - * (3) Clears the controlling tty for all processes in the - * session group. - * - * The argument on_exit is set to 1 if called when a process is - * exiting; it is 0 if called by the ioctl TIOCNOTTY. - * - * Locking: - * BTM is taken for hysterical raisins, and held when - * called from no_tty(). - * tty_mutex is taken to protect tty - * ->siglock is taken to protect ->signal/->sighand - * tasklist_lock is taken to walk process list for sessions - * ->siglock is taken to protect ->signal/->sighand - */ - -void disassociate_ctty(int on_exit) -{ - struct tty_struct *tty; - - if (!current->signal->leader) - return; - - tty = get_current_tty(); - if (tty) { - if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) { - tty_vhangup_session(tty); - } else { - struct pid *tty_pgrp = tty_get_pgrp(tty); - if (tty_pgrp) { - kill_pgrp(tty_pgrp, SIGHUP, on_exit); - if (!on_exit) - kill_pgrp(tty_pgrp, SIGCONT, on_exit); - put_pid(tty_pgrp); - } - } - tty_kref_put(tty); - - } else if (on_exit) { - struct pid *old_pgrp; - spin_lock_irq(¤t->sighand->siglock); - old_pgrp = current->signal->tty_old_pgrp; - current->signal->tty_old_pgrp = NULL; - spin_unlock_irq(¤t->sighand->siglock); - if (old_pgrp) { - kill_pgrp(old_pgrp, SIGHUP, on_exit); - kill_pgrp(old_pgrp, SIGCONT, on_exit); - put_pid(old_pgrp); - } - return; - } - - spin_lock_irq(¤t->sighand->siglock); - put_pid(current->signal->tty_old_pgrp); - current->signal->tty_old_pgrp = NULL; - - tty = tty_kref_get(current->signal->tty); - if (tty) { - unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); - put_pid(tty->session); - put_pid(tty->pgrp); - tty->session = NULL; - tty->pgrp = NULL; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - tty_kref_put(tty); - } else - tty_debug_hangup(tty, "no current tty\n"); - - spin_unlock_irq(¤t->sighand->siglock); - /* Now clear signal->tty under the lock */ - read_lock(&tasklist_lock); - session_clear_tty(task_session(current)); - read_unlock(&tasklist_lock); -} - -/** - * - * no_tty - Ensure the current process does not have a controlling tty - */ -void no_tty(void) -{ - /* FIXME: Review locking here. The tty_lock never covered any race - between a new association and proc_clear_tty but possible we need - to protect against this anyway */ - struct task_struct *tsk = current; - disassociate_ctty(0); - proc_clear_tty(tsk); -} - - -/** * stop_tty - propagate flow control * @tty: tty to stop * @@ -1369,7 +1133,10 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, struct tty_struct *tty; if (driver->ops->lookup) - tty = driver->ops->lookup(driver, file, idx); + if (!file) + tty = ERR_PTR(-EIO); + else + tty = driver->ops->lookup(driver, file, idx); else tty = driver->ttys[idx]; @@ -1520,7 +1287,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) * This code guarantees that either everything succeeds and the * TTY is ready for operation, or else the table slots are vacated * and the allocated memory released. (Except that the termios - * and locked termios may be retained.) + * may be retained.) */ if (!try_module_get(driver->owner)) @@ -2001,7 +1768,7 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, struct tty_driver *console_driver = console_device(index); if (console_driver) { driver = tty_driver_kref_get(console_driver); - if (driver) { + if (driver && filp) { /* Don't let /dev/console block */ filp->f_flags |= O_NONBLOCK; break; @@ -2034,7 +1801,7 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, * - concurrent tty driver removal w/ lookup * - concurrent tty removal from driver table */ -static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode, +struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode, struct file *filp) { struct tty_struct *tty; @@ -2079,6 +1846,7 @@ out: tty_driver_kref_put(driver); return tty; } +EXPORT_SYMBOL_GPL(tty_open_by_driver); /** * tty_open - open a tty device @@ -2163,38 +1931,13 @@ retry_open: } clear_bit(TTY_HUPPED, &tty->flags); - - read_lock(&tasklist_lock); - spin_lock_irq(¤t->sighand->siglock); noctty = (filp->f_flags & O_NOCTTY) || - (IS_ENABLED(CONFIG_VT) && device == MKDEV(TTY_MAJOR, 0)) || - device == MKDEV(TTYAUX_MAJOR, 1) || - (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER); - - if (!noctty && - current->signal->leader && - !current->signal->tty && - tty->session == NULL) { - /* - * Don't let a process that only has write access to the tty - * obtain the privileges associated with having a tty as - * controlling terminal (being able to reopen it with full - * access through /dev/tty, being able to perform pushback). - * Many distributions set the group of all ttys to "tty" and - * grant write-only access to all terminals for setgid tty - * binaries, which should not imply full privileges on all ttys. - * - * This could theoretically break old code that performs open() - * on a write-only file descriptor. In that case, it might be - * necessary to also permit this if - * inode_permission(inode, MAY_READ) == 0. - */ - if (filp->f_mode & FMODE_READ) - __proc_set_tty(tty); - } - spin_unlock_irq(¤t->sighand->siglock); - read_unlock(&tasklist_lock); + (IS_ENABLED(CONFIG_VT) && device == MKDEV(TTY_MAJOR, 0)) || + device == MKDEV(TTYAUX_MAJOR, 1) || + (tty->driver->type == TTY_DRIVER_TYPE_PTY && + tty->driver->subtype == PTY_TYPE_MASTER); + if (!noctty) + tty_open_proc_set_tty(filp, tty); tty_unlock(tty); return 0; } @@ -2457,211 +2200,6 @@ static int fionbio(struct file *file, int __user *p) } /** - * tiocsctty - set controlling tty - * @tty: tty structure - * @arg: user argument - * - * This ioctl is used to manage job control. It permits a session - * leader to set this tty as the controlling tty for the session. - * - * Locking: - * Takes tty_lock() to serialize proc_set_tty() for this tty - * Takes tasklist_lock internally to walk sessions - * Takes ->siglock() when updating signal->tty - */ - -static int tiocsctty(struct tty_struct *tty, struct file *file, int arg) -{ - int ret = 0; - - tty_lock(tty); - read_lock(&tasklist_lock); - - if (current->signal->leader && (task_session(current) == tty->session)) - goto unlock; - - /* - * The process must be a session leader and - * not have a controlling tty already. - */ - if (!current->signal->leader || current->signal->tty) { - ret = -EPERM; - goto unlock; - } - - if (tty->session) { - /* - * This tty is already the controlling - * tty for another session group! - */ - if (arg == 1 && capable(CAP_SYS_ADMIN)) { - /* - * Steal it away - */ - session_clear_tty(tty->session); - } else { - ret = -EPERM; - goto unlock; - } - } - - /* See the comment in tty_open(). */ - if ((file->f_mode & FMODE_READ) == 0 && !capable(CAP_SYS_ADMIN)) { - ret = -EPERM; - goto unlock; - } - - proc_set_tty(tty); -unlock: - read_unlock(&tasklist_lock); - tty_unlock(tty); - return ret; -} - -/** - * tty_get_pgrp - return a ref counted pgrp pid - * @tty: tty to read - * - * Returns a refcounted instance of the pid struct for the process - * group controlling the tty. - */ - -struct pid *tty_get_pgrp(struct tty_struct *tty) -{ - unsigned long flags; - struct pid *pgrp; - - spin_lock_irqsave(&tty->ctrl_lock, flags); - pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - - return pgrp; -} -EXPORT_SYMBOL_GPL(tty_get_pgrp); - -/* - * This checks not only the pgrp, but falls back on the pid if no - * satisfactory pgrp is found. I dunno - gdb doesn't work correctly - * without this... - * - * The caller must hold rcu lock or the tasklist lock. - */ -static struct pid *session_of_pgrp(struct pid *pgrp) -{ - struct task_struct *p; - struct pid *sid = NULL; - - p = pid_task(pgrp, PIDTYPE_PGID); - if (p == NULL) - p = pid_task(pgrp, PIDTYPE_PID); - if (p != NULL) - sid = task_session(p); - - return sid; -} - -/** - * tiocgpgrp - get process group - * @tty: tty passed by user - * @real_tty: tty side of the tty passed by the user if a pty else the tty - * @p: returned pid - * - * Obtain the process group of the tty. If there is no process group - * return an error. - * - * Locking: none. Reference to current->signal->tty is safe. - */ - -static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) -{ - struct pid *pid; - int ret; - /* - * (tty == real_tty) is a cheap way of - * testing if the tty is NOT a master pty. - */ - if (tty == real_tty && current->signal->tty != real_tty) - return -ENOTTY; - pid = tty_get_pgrp(real_tty); - ret = put_user(pid_vnr(pid), p); - put_pid(pid); - return ret; -} - -/** - * tiocspgrp - attempt to set process group - * @tty: tty passed by user - * @real_tty: tty side device matching tty passed by user - * @p: pid pointer - * - * Set the process group of the tty to the session passed. Only - * permitted where the tty session is our session. - * - * Locking: RCU, ctrl lock - */ - -static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) -{ - struct pid *pgrp; - pid_t pgrp_nr; - int retval = tty_check_change(real_tty); - - if (retval == -EIO) - return -ENOTTY; - if (retval) - return retval; - if (!current->signal->tty || - (current->signal->tty != real_tty) || - (real_tty->session != task_session(current))) - return -ENOTTY; - if (get_user(pgrp_nr, p)) - return -EFAULT; - if (pgrp_nr < 0) - return -EINVAL; - rcu_read_lock(); - pgrp = find_vpid(pgrp_nr); - retval = -ESRCH; - if (!pgrp) - goto out_unlock; - retval = -EPERM; - if (session_of_pgrp(pgrp) != task_session(current)) - goto out_unlock; - retval = 0; - spin_lock_irq(&tty->ctrl_lock); - put_pid(real_tty->pgrp); - real_tty->pgrp = get_pid(pgrp); - spin_unlock_irq(&tty->ctrl_lock); -out_unlock: - rcu_read_unlock(); - return retval; -} - -/** - * tiocgsid - get session id - * @tty: tty passed by user - * @real_tty: tty side of the tty passed by the user if a pty else the tty - * @p: pointer to returned session id - * - * Obtain the session id of the tty. If there is no session - * return an error. - * - * Locking: none. Reference to current->signal->tty is safe. - */ - -static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) -{ - /* - * (tty == real_tty) is a cheap way of - * testing if the tty is NOT a master pty. - */ - if (tty == real_tty && current->signal->tty != real_tty) - return -ENOTTY; - if (!real_tty->session) - return -ENOTTY; - return put_user(pid_vnr(real_tty->session), p); -} - -/** * tiocsetd - set line discipline * @tty: tty device * @p: pointer to user data @@ -2843,8 +2381,8 @@ static void tty_warn_deprecated_flags(struct serial_struct __user *ss) flags &= ASYNC_DEPRECATED; if (flags && __ratelimit(&depr_flags)) - pr_warning("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n", - __func__, get_task_comm(comm, current), flags); + pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n", + __func__, get_task_comm(comm, current), flags); } /* @@ -2920,19 +2458,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) int excl = test_bit(TTY_EXCLUSIVE, &tty->flags); return put_user(excl, (int __user *)p); } - case TIOCNOTTY: - if (current->signal->tty != tty) - return -ENOTTY; - no_tty(); - return 0; - case TIOCSCTTY: - return tiocsctty(real_tty, file, arg); - case TIOCGPGRP: - return tiocgpgrp(tty, real_tty, p); - case TIOCSPGRP: - return tiocspgrp(tty, real_tty, p); - case TIOCGSID: - return tiocgsid(tty, real_tty, p); case TIOCGETD: return tiocgetd(tty, p); case TIOCSETD: @@ -2993,6 +2518,10 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCSSERIAL: tty_warn_deprecated_flags(p); break; + default: + retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg); + if (retval != -ENOIOCTLCMD) + return retval; } if (tty->ops->ioctl) { retval = tty->ops->ioctl(tty, cmd, arg); @@ -3293,9 +2822,9 @@ struct device *tty_register_device_attr(struct tty_driver *driver, { char name[64]; dev_t devt = MKDEV(driver->major, driver->minor_start) + index; - struct device *dev = NULL; - int retval = -ENODEV; - bool cdev = false; + struct ktermios *tp; + struct device *dev; + int retval; if (index >= driver->num) { pr_err("%s: Attempt to register invalid tty line number (%d)\n", @@ -3308,18 +2837,9 @@ struct device *tty_register_device_attr(struct tty_driver *driver, else tty_line_name(driver, index, name); - if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { - retval = tty_cdev_add(driver, devt, index, 1); - if (retval) - goto error; - cdev = true; - } - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - retval = -ENOMEM; - goto error; - } + if (!dev) + return ERR_PTR(-ENOMEM); dev->devt = devt; dev->class = tty_class; @@ -3329,18 +2849,38 @@ struct device *tty_register_device_attr(struct tty_driver *driver, dev->groups = attr_grp; dev_set_drvdata(dev, drvdata); + dev_set_uevent_suppress(dev, 1); + retval = device_register(dev); if (retval) - goto error; + goto err_put; + + if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { + /* + * Free any saved termios data so that the termios state is + * reset when reusing a minor number. + */ + tp = driver->termios[index]; + if (tp) { + driver->termios[index] = NULL; + kfree(tp); + } + + retval = tty_cdev_add(driver, devt, index, 1); + if (retval) + goto err_del; + } + + dev_set_uevent_suppress(dev, 0); + kobject_uevent(&dev->kobj, KOBJ_ADD); return dev; -error: +err_del: + device_del(dev); +err_put: put_device(dev); - if (cdev) { - cdev_del(driver->cdevs[index]); - driver->cdevs[index] = NULL; - } + return ERR_PTR(retval); } EXPORT_SYMBOL_GPL(tty_register_device_attr); @@ -3370,7 +2910,7 @@ EXPORT_SYMBOL(tty_unregister_device); /** * __tty_alloc_driver -- allocate tty driver * @lines: count of lines this driver can handle at most - * @owner: module which is repsonsible for this driver + * @owner: module which is responsible for this driver * @flags: some of TTY_DRIVER_* flags, will be set in driver->flags * * This should not be called directly, some of the provided macros should be @@ -3441,11 +2981,6 @@ static void destruct_tty_driver(struct kref *kref) struct ktermios *tp; if (driver->flags & TTY_DRIVER_INSTALLED) { - /* - * Free the termios and termios_locked structures because - * we don't want to get memory leaks when modular tty - * drivers are removed from the kernel. - */ for (i = 0; i < driver->num; i++) { tp = driver->termios[i]; if (tp) { @@ -3578,30 +3113,6 @@ void tty_default_fops(struct file_operations *fops) *fops = tty_fops; } -/* - * Initialize the console device. This is called *early*, so - * we can't necessarily depend on lots of kernel help here. - * Just do some early initializations, and do the complex setup - * later. - */ -void __init console_init(void) -{ - initcall_t *call; - - /* Setup the default TTY line discipline. */ - n_tty_init(); - - /* - * set up the console device so that later boot sequences can - * inform about problems etc.. - */ - call = __con_initcall_start; - while (call < __con_initcall_end) { - (*call)(); - call++; - } -} - static char *tty_devnode(struct device *dev, umode_t *mode) { if (!mode) diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index a9a978731c5b..efa96e6c4c1b 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -258,228 +258,6 @@ static void unset_locked_termios(struct tty_struct *tty, struct ktermios *old) /* FIXME: What should we do for i/ospeed */ } -/* - * Routine which returns the baud rate of the tty - * - * Note that the baud_table needs to be kept in sync with the - * include/asm/termbits.h file. - */ -static const speed_t baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, -#ifdef __sparc__ - 76800, 153600, 307200, 614400, 921600 -#else - 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, - 2500000, 3000000, 3500000, 4000000 -#endif -}; - -#ifndef __sparc__ -static const tcflag_t baud_bits[] = { - B0, B50, B75, B110, B134, B150, B200, B300, B600, - B1200, B1800, B2400, B4800, B9600, B19200, B38400, - B57600, B115200, B230400, B460800, B500000, B576000, - B921600, B1000000, B1152000, B1500000, B2000000, B2500000, - B3000000, B3500000, B4000000 -}; -#else -static const tcflag_t baud_bits[] = { - B0, B50, B75, B110, B134, B150, B200, B300, B600, - B1200, B1800, B2400, B4800, B9600, B19200, B38400, - B57600, B115200, B230400, B460800, B76800, B153600, - B307200, B614400, B921600 -}; -#endif - -static int n_baud_table = ARRAY_SIZE(baud_table); - -/** - * tty_termios_baud_rate - * @termios: termios structure - * - * Convert termios baud rate data into a speed. This should be called - * with the termios lock held if this termios is a terminal termios - * structure. May change the termios data. Device drivers can call this - * function but should use ->c_[io]speed directly as they are updated. - * - * Locking: none - */ - -speed_t tty_termios_baud_rate(struct ktermios *termios) -{ - unsigned int cbaud; - - cbaud = termios->c_cflag & CBAUD; - -#ifdef BOTHER - /* Magic token for arbitrary speed via c_ispeed/c_ospeed */ - if (cbaud == BOTHER) - return termios->c_ospeed; -#endif - if (cbaud & CBAUDEX) { - cbaud &= ~CBAUDEX; - - if (cbaud < 1 || cbaud + 15 > n_baud_table) - termios->c_cflag &= ~CBAUDEX; - else - cbaud += 15; - } - return baud_table[cbaud]; -} -EXPORT_SYMBOL(tty_termios_baud_rate); - -/** - * tty_termios_input_baud_rate - * @termios: termios structure - * - * Convert termios baud rate data into a speed. This should be called - * with the termios lock held if this termios is a terminal termios - * structure. May change the termios data. Device drivers can call this - * function but should use ->c_[io]speed directly as they are updated. - * - * Locking: none - */ - -speed_t tty_termios_input_baud_rate(struct ktermios *termios) -{ -#ifdef IBSHIFT - unsigned int cbaud = (termios->c_cflag >> IBSHIFT) & CBAUD; - - if (cbaud == B0) - return tty_termios_baud_rate(termios); - - /* Magic token for arbitrary speed via c_ispeed*/ - if (cbaud == BOTHER) - return termios->c_ispeed; - - if (cbaud & CBAUDEX) { - cbaud &= ~CBAUDEX; - - if (cbaud < 1 || cbaud + 15 > n_baud_table) - termios->c_cflag &= ~(CBAUDEX << IBSHIFT); - else - cbaud += 15; - } - return baud_table[cbaud]; -#else - return tty_termios_baud_rate(termios); -#endif -} -EXPORT_SYMBOL(tty_termios_input_baud_rate); - -/** - * tty_termios_encode_baud_rate - * @termios: ktermios structure holding user requested state - * @ispeed: input speed - * @ospeed: output speed - * - * Encode the speeds set into the passed termios structure. This is - * used as a library helper for drivers so that they can report back - * the actual speed selected when it differs from the speed requested - * - * For maximal back compatibility with legacy SYS5/POSIX *nix behaviour - * we need to carefully set the bits when the user does not get the - * desired speed. We allow small margins and preserve as much of possible - * of the input intent to keep compatibility. - * - * Locking: Caller should hold termios lock. This is already held - * when calling this function from the driver termios handler. - * - * The ifdefs deal with platforms whose owners have yet to update them - * and will all go away once this is done. - */ - -void tty_termios_encode_baud_rate(struct ktermios *termios, - speed_t ibaud, speed_t obaud) -{ - int i = 0; - int ifound = -1, ofound = -1; - int iclose = ibaud/50, oclose = obaud/50; - int ibinput = 0; - - if (obaud == 0) /* CD dropped */ - ibaud = 0; /* Clear ibaud to be sure */ - - termios->c_ispeed = ibaud; - termios->c_ospeed = obaud; - -#ifdef BOTHER - /* If the user asked for a precise weird speed give a precise weird - answer. If they asked for a Bfoo speed they may have problems - digesting non-exact replies so fuzz a bit */ - - if ((termios->c_cflag & CBAUD) == BOTHER) - oclose = 0; - if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER) - iclose = 0; - if ((termios->c_cflag >> IBSHIFT) & CBAUD) - ibinput = 1; /* An input speed was specified */ -#endif - termios->c_cflag &= ~CBAUD; - - /* - * Our goal is to find a close match to the standard baud rate - * returned. Walk the baud rate table and if we get a very close - * match then report back the speed as a POSIX Bxxxx value by - * preference - */ - - do { - if (obaud - oclose <= baud_table[i] && - obaud + oclose >= baud_table[i]) { - termios->c_cflag |= baud_bits[i]; - ofound = i; - } - if (ibaud - iclose <= baud_table[i] && - ibaud + iclose >= baud_table[i]) { - /* For the case input == output don't set IBAUD bits - if the user didn't do so */ - if (ofound == i && !ibinput) - ifound = i; -#ifdef IBSHIFT - else { - ifound = i; - termios->c_cflag |= (baud_bits[i] << IBSHIFT); - } -#endif - } - } while (++i < n_baud_table); - - /* - * If we found no match then use BOTHER if provided or warn - * the user their platform maintainer needs to wake up if not. - */ -#ifdef BOTHER - if (ofound == -1) - termios->c_cflag |= BOTHER; - /* Set exact input bits only if the input and output differ or the - user already did */ - if (ifound == -1 && (ibaud != obaud || ibinput)) - termios->c_cflag |= (BOTHER << IBSHIFT); -#else - if (ifound == -1 || ofound == -1) - pr_warn_once("tty: Unable to return correct speed data as your architecture needs updating.\n"); -#endif -} -EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); - -/** - * tty_encode_baud_rate - set baud rate of the tty - * @ibaud: input baud rate - * @obad: output baud rate - * - * Update the current termios data for the tty with the new speed - * settings. The caller must hold the termios_rwsem for the tty in - * question. - */ - -void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) -{ - tty_termios_encode_baud_rate(&tty->termios, ibaud, obaud); -} -EXPORT_SYMBOL_GPL(tty_encode_baud_rate); - /** * tty_termios_copy_hw - copy hardware settings * @new: New termios diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c new file mode 100644 index 000000000000..e7032309ee87 --- /dev/null +++ b/drivers/tty/tty_jobctrl.c @@ -0,0 +1,554 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched/signal.h> +#include <linux/sched/task.h> +#include <linux/tty.h> +#include <linux/fcntl.h> +#include <linux/uaccess.h> + +static int is_ignored(int sig) +{ + return (sigismember(¤t->blocked, sig) || + current->sighand->action[sig-1].sa.sa_handler == SIG_IGN); +} + +/** + * tty_check_change - check for POSIX terminal changes + * @tty: tty to check + * + * If we try to write to, or set the state of, a terminal and we're + * not in the foreground, send a SIGTTOU. If the signal is blocked or + * ignored, go ahead and perform the operation. (POSIX 7.2) + * + * Locking: ctrl_lock + */ +int __tty_check_change(struct tty_struct *tty, int sig) +{ + unsigned long flags; + struct pid *pgrp, *tty_pgrp; + int ret = 0; + + if (current->signal->tty != tty) + return 0; + + rcu_read_lock(); + pgrp = task_pgrp(current); + + spin_lock_irqsave(&tty->ctrl_lock, flags); + tty_pgrp = tty->pgrp; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + + if (tty_pgrp && pgrp != tty->pgrp) { + if (is_ignored(sig)) { + if (sig == SIGTTIN) + ret = -EIO; + } else if (is_current_pgrp_orphaned()) + ret = -EIO; + else { + kill_pgrp(pgrp, sig, 1); + set_thread_flag(TIF_SIGPENDING); + ret = -ERESTARTSYS; + } + } + rcu_read_unlock(); + + if (!tty_pgrp) + tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig); + + return ret; +} + +int tty_check_change(struct tty_struct *tty) +{ + return __tty_check_change(tty, SIGTTOU); +} +EXPORT_SYMBOL(tty_check_change); + +void proc_clear_tty(struct task_struct *p) +{ + unsigned long flags; + struct tty_struct *tty; + spin_lock_irqsave(&p->sighand->siglock, flags); + tty = p->signal->tty; + p->signal->tty = NULL; + spin_unlock_irqrestore(&p->sighand->siglock, flags); + tty_kref_put(tty); +} + +/** + * proc_set_tty - set the controlling terminal + * + * Only callable by the session leader and only if it does not already have + * a controlling terminal. + * + * Caller must hold: tty_lock() + * a readlock on tasklist_lock + * sighand lock + */ +static void __proc_set_tty(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + /* + * The session and fg pgrp references will be non-NULL if + * tiocsctty() is stealing the controlling tty + */ + put_pid(tty->session); + put_pid(tty->pgrp); + tty->pgrp = get_pid(task_pgrp(current)); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + tty->session = get_pid(task_session(current)); + if (current->signal->tty) { + tty_debug(tty, "current tty %s not NULL!!\n", + current->signal->tty->name); + tty_kref_put(current->signal->tty); + } + put_pid(current->signal->tty_old_pgrp); + current->signal->tty = tty_kref_get(tty); + current->signal->tty_old_pgrp = NULL; +} + +static void proc_set_tty(struct tty_struct *tty) +{ + spin_lock_irq(¤t->sighand->siglock); + __proc_set_tty(tty); + spin_unlock_irq(¤t->sighand->siglock); +} + +/* + * Called by tty_open() to set the controlling tty if applicable. + */ +void tty_open_proc_set_tty(struct file *filp, struct tty_struct *tty) +{ + read_lock(&tasklist_lock); + spin_lock_irq(¤t->sighand->siglock); + if (current->signal->leader && + !current->signal->tty && + tty->session == NULL) { + /* + * Don't let a process that only has write access to the tty + * obtain the privileges associated with having a tty as + * controlling terminal (being able to reopen it with full + * access through /dev/tty, being able to perform pushback). + * Many distributions set the group of all ttys to "tty" and + * grant write-only access to all terminals for setgid tty + * binaries, which should not imply full privileges on all ttys. + * + * This could theoretically break old code that performs open() + * on a write-only file descriptor. In that case, it might be + * necessary to also permit this if + * inode_permission(inode, MAY_READ) == 0. + */ + if (filp->f_mode & FMODE_READ) + __proc_set_tty(tty); + } + spin_unlock_irq(¤t->sighand->siglock); + read_unlock(&tasklist_lock); +} + +struct tty_struct *get_current_tty(void) +{ + struct tty_struct *tty; + unsigned long flags; + + spin_lock_irqsave(¤t->sighand->siglock, flags); + tty = tty_kref_get(current->signal->tty); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); + return tty; +} +EXPORT_SYMBOL_GPL(get_current_tty); + +/* + * Called from tty_release(). + */ +void session_clear_tty(struct pid *session) +{ + struct task_struct *p; + do_each_pid_task(session, PIDTYPE_SID, p) { + proc_clear_tty(p); + } while_each_pid_task(session, PIDTYPE_SID, p); +} + +/** + * tty_signal_session_leader - sends SIGHUP to session leader + * @tty controlling tty + * @exit_session if non-zero, signal all foreground group processes + * + * Send SIGHUP and SIGCONT to the session leader and its process group. + * Optionally, signal all processes in the foreground process group. + * + * Returns the number of processes in the session with this tty + * as their controlling terminal. This value is used to drop + * tty references for those processes. + */ +int tty_signal_session_leader(struct tty_struct *tty, int exit_session) +{ + struct task_struct *p; + int refs = 0; + struct pid *tty_pgrp = NULL; + + read_lock(&tasklist_lock); + if (tty->session) { + do_each_pid_task(tty->session, PIDTYPE_SID, p) { + spin_lock_irq(&p->sighand->siglock); + if (p->signal->tty == tty) { + p->signal->tty = NULL; + /* We defer the dereferences outside fo + the tasklist lock */ + refs++; + } + if (!p->signal->leader) { + spin_unlock_irq(&p->sighand->siglock); + continue; + } + __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); + __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); + put_pid(p->signal->tty_old_pgrp); /* A noop */ + spin_lock(&tty->ctrl_lock); + tty_pgrp = get_pid(tty->pgrp); + if (tty->pgrp) + p->signal->tty_old_pgrp = get_pid(tty->pgrp); + spin_unlock(&tty->ctrl_lock); + spin_unlock_irq(&p->sighand->siglock); + } while_each_pid_task(tty->session, PIDTYPE_SID, p); + } + read_unlock(&tasklist_lock); + + if (tty_pgrp) { + if (exit_session) + kill_pgrp(tty_pgrp, SIGHUP, exit_session); + put_pid(tty_pgrp); + } + + return refs; +} + +/** + * disassociate_ctty - disconnect controlling tty + * @on_exit: true if exiting so need to "hang up" the session + * + * This function is typically called only by the session leader, when + * it wants to disassociate itself from its controlling tty. + * + * It performs the following functions: + * (1) Sends a SIGHUP and SIGCONT to the foreground process group + * (2) Clears the tty from being controlling the session + * (3) Clears the controlling tty for all processes in the + * session group. + * + * The argument on_exit is set to 1 if called when a process is + * exiting; it is 0 if called by the ioctl TIOCNOTTY. + * + * Locking: + * BTM is taken for hysterical raisons, and held when + * called from no_tty(). + * tty_mutex is taken to protect tty + * ->siglock is taken to protect ->signal/->sighand + * tasklist_lock is taken to walk process list for sessions + * ->siglock is taken to protect ->signal/->sighand + */ +void disassociate_ctty(int on_exit) +{ + struct tty_struct *tty; + + if (!current->signal->leader) + return; + + tty = get_current_tty(); + if (tty) { + if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) { + tty_vhangup_session(tty); + } else { + struct pid *tty_pgrp = tty_get_pgrp(tty); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, SIGHUP, on_exit); + if (!on_exit) + kill_pgrp(tty_pgrp, SIGCONT, on_exit); + put_pid(tty_pgrp); + } + } + tty_kref_put(tty); + + } else if (on_exit) { + struct pid *old_pgrp; + spin_lock_irq(¤t->sighand->siglock); + old_pgrp = current->signal->tty_old_pgrp; + current->signal->tty_old_pgrp = NULL; + spin_unlock_irq(¤t->sighand->siglock); + if (old_pgrp) { + kill_pgrp(old_pgrp, SIGHUP, on_exit); + kill_pgrp(old_pgrp, SIGCONT, on_exit); + put_pid(old_pgrp); + } + return; + } + + spin_lock_irq(¤t->sighand->siglock); + put_pid(current->signal->tty_old_pgrp); + current->signal->tty_old_pgrp = NULL; + + tty = tty_kref_get(current->signal->tty); + if (tty) { + unsigned long flags; + spin_lock_irqsave(&tty->ctrl_lock, flags); + put_pid(tty->session); + put_pid(tty->pgrp); + tty->session = NULL; + tty->pgrp = NULL; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + tty_kref_put(tty); + } + + spin_unlock_irq(¤t->sighand->siglock); + /* Now clear signal->tty under the lock */ + read_lock(&tasklist_lock); + session_clear_tty(task_session(current)); + read_unlock(&tasklist_lock); +} + +/** + * + * no_tty - Ensure the current process does not have a controlling tty + */ +void no_tty(void) +{ + /* FIXME: Review locking here. The tty_lock never covered any race + between a new association and proc_clear_tty but possible we need + to protect against this anyway */ + struct task_struct *tsk = current; + disassociate_ctty(0); + proc_clear_tty(tsk); +} + +/** + * tiocsctty - set controlling tty + * @tty: tty structure + * @arg: user argument + * + * This ioctl is used to manage job control. It permits a session + * leader to set this tty as the controlling tty for the session. + * + * Locking: + * Takes tty_lock() to serialize proc_set_tty() for this tty + * Takes tasklist_lock internally to walk sessions + * Takes ->siglock() when updating signal->tty + */ +static int tiocsctty(struct tty_struct *tty, struct file *file, int arg) +{ + int ret = 0; + + tty_lock(tty); + read_lock(&tasklist_lock); + + if (current->signal->leader && (task_session(current) == tty->session)) + goto unlock; + + /* + * The process must be a session leader and + * not have a controlling tty already. + */ + if (!current->signal->leader || current->signal->tty) { + ret = -EPERM; + goto unlock; + } + + if (tty->session) { + /* + * This tty is already the controlling + * tty for another session group! + */ + if (arg == 1 && capable(CAP_SYS_ADMIN)) { + /* + * Steal it away + */ + session_clear_tty(tty->session); + } else { + ret = -EPERM; + goto unlock; + } + } + + /* See the comment in tty_open_proc_set_tty(). */ + if ((file->f_mode & FMODE_READ) == 0 && !capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto unlock; + } + + proc_set_tty(tty); +unlock: + read_unlock(&tasklist_lock); + tty_unlock(tty); + return ret; +} + +/** + * tty_get_pgrp - return a ref counted pgrp pid + * @tty: tty to read + * + * Returns a refcounted instance of the pid struct for the process + * group controlling the tty. + */ +struct pid *tty_get_pgrp(struct tty_struct *tty) +{ + unsigned long flags; + struct pid *pgrp; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + pgrp = get_pid(tty->pgrp); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + + return pgrp; +} +EXPORT_SYMBOL_GPL(tty_get_pgrp); + +/* + * This checks not only the pgrp, but falls back on the pid if no + * satisfactory pgrp is found. I dunno - gdb doesn't work correctly + * without this... + * + * The caller must hold rcu lock or the tasklist lock. + */ +static struct pid *session_of_pgrp(struct pid *pgrp) +{ + struct task_struct *p; + struct pid *sid = NULL; + + p = pid_task(pgrp, PIDTYPE_PGID); + if (p == NULL) + p = pid_task(pgrp, PIDTYPE_PID); + if (p != NULL) + sid = task_session(p); + + return sid; +} + +/** + * tiocgpgrp - get process group + * @tty: tty passed by user + * @real_tty: tty side of the tty passed by the user if a pty else the tty + * @p: returned pid + * + * Obtain the process group of the tty. If there is no process group + * return an error. + * + * Locking: none. Reference to current->signal->tty is safe. + */ +static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) +{ + struct pid *pid; + int ret; + /* + * (tty == real_tty) is a cheap way of + * testing if the tty is NOT a master pty. + */ + if (tty == real_tty && current->signal->tty != real_tty) + return -ENOTTY; + pid = tty_get_pgrp(real_tty); + ret = put_user(pid_vnr(pid), p); + put_pid(pid); + return ret; +} + +/** + * tiocspgrp - attempt to set process group + * @tty: tty passed by user + * @real_tty: tty side device matching tty passed by user + * @p: pid pointer + * + * Set the process group of the tty to the session passed. Only + * permitted where the tty session is our session. + * + * Locking: RCU, ctrl lock + */ +static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) +{ + struct pid *pgrp; + pid_t pgrp_nr; + int retval = tty_check_change(real_tty); + + if (retval == -EIO) + return -ENOTTY; + if (retval) + return retval; + if (!current->signal->tty || + (current->signal->tty != real_tty) || + (real_tty->session != task_session(current))) + return -ENOTTY; + if (get_user(pgrp_nr, p)) + return -EFAULT; + if (pgrp_nr < 0) + return -EINVAL; + rcu_read_lock(); + pgrp = find_vpid(pgrp_nr); + retval = -ESRCH; + if (!pgrp) + goto out_unlock; + retval = -EPERM; + if (session_of_pgrp(pgrp) != task_session(current)) + goto out_unlock; + retval = 0; + spin_lock_irq(&tty->ctrl_lock); + put_pid(real_tty->pgrp); + real_tty->pgrp = get_pid(pgrp); + spin_unlock_irq(&tty->ctrl_lock); +out_unlock: + rcu_read_unlock(); + return retval; +} + +/** + * tiocgsid - get session id + * @tty: tty passed by user + * @real_tty: tty side of the tty passed by the user if a pty else the tty + * @p: pointer to returned session id + * + * Obtain the session id of the tty. If there is no session + * return an error. + * + * Locking: none. Reference to current->signal->tty is safe. + */ +static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) +{ + /* + * (tty == real_tty) is a cheap way of + * testing if the tty is NOT a master pty. + */ + if (tty == real_tty && current->signal->tty != real_tty) + return -ENOTTY; + if (!real_tty->session) + return -ENOTTY; + return put_user(pid_vnr(real_tty->session), p); +} + +/* + * Called from tty_ioctl(). If tty is a pty then real_tty is the slave side, + * if not then tty == real_tty. + */ +long tty_jobctrl_ioctl(struct tty_struct *tty, struct tty_struct *real_tty, + struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *p = (void __user *)arg; + + switch (cmd) { + case TIOCNOTTY: + if (current->signal->tty != tty) + return -ENOTTY; + no_tty(); + return 0; + case TIOCSCTTY: + return tiocsctty(real_tty, file, arg); + case TIOCGPGRP: + return tiocgpgrp(tty, real_tty, p); + case TIOCSPGRP: + return tiocspgrp(tty, real_tty, p); + case TIOCGSID: + return tiocgsid(tty, real_tty, p); + } + return -ENOIOCTLCMD; +} diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index e4603b09863a..2fe216b276e2 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -492,6 +492,29 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) } /** + * tty_ldisc_failto - helper for ldisc failback + * @tty: tty to open the ldisc on + * @ld: ldisc we are trying to fail back to + * + * Helper to try and recover a tty when switching back to the old + * ldisc fails and we need something attached. + */ + +static int tty_ldisc_failto(struct tty_struct *tty, int ld) +{ + struct tty_ldisc *disc = tty_ldisc_get(tty, ld); + int r; + + if (IS_ERR(disc)) + return PTR_ERR(disc); + tty->ldisc = disc; + tty_set_termios_ldisc(tty, ld); + if ((r = tty_ldisc_open(tty, disc)) < 0) + tty_ldisc_put(disc); + return r; +} + +/** * tty_ldisc_restore - helper for tty ldisc change * @tty: tty to recover * @old: previous ldisc @@ -502,9 +525,6 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) { - struct tty_ldisc *new_ldisc; - int r; - /* There is an outstanding reference here so this is safe */ old = tty_ldisc_get(tty, old->ops->num); WARN_ON(IS_ERR(old)); @@ -512,17 +532,13 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) tty_set_termios_ldisc(tty, old->ops->num); if (tty_ldisc_open(tty, old) < 0) { tty_ldisc_put(old); - /* This driver is always present */ - new_ldisc = tty_ldisc_get(tty, N_TTY); - if (IS_ERR(new_ldisc)) - panic("n_tty: get"); - tty->ldisc = new_ldisc; - tty_set_termios_ldisc(tty, N_TTY); - r = tty_ldisc_open(tty, new_ldisc); - if (r < 0) - panic("Couldn't open N_TTY ldisc for " - "%s --- error %d.", - tty_name(tty), r); + /* The traditional behaviour is to fall back to N_TTY, we + want to avoid falling back to N_NULL unless we have no + choice to avoid the risk of breaking anything */ + if (tty_ldisc_failto(tty, N_TTY) < 0 && + tty_ldisc_failto(tty, N_NULL) < 0) + panic("Couldn't open N_NULL ldisc for %s.", + tty_name(tty)); } } @@ -605,6 +621,7 @@ err: tty_unlock(tty); return retval; } +EXPORT_SYMBOL_GPL(tty_set_ldisc); /** * tty_ldisc_kill - teardown ldisc @@ -797,6 +814,7 @@ void tty_ldisc_release(struct tty_struct *tty) tty_ldisc_debug(tty, "released\n"); } +EXPORT_SYMBOL_GPL(tty_ldisc_release); /** * tty_ldisc_init - ldisc setup for new tty diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 1d21a9c1d33e..6b137194069f 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -129,19 +129,85 @@ struct device *tty_port_register_device_attr(struct tty_port *port, struct device *device, void *drvdata, const struct attribute_group **attr_grp) { + tty_port_link_device(port, driver, index); + return tty_register_device_attr(driver, index, device, drvdata, + attr_grp); +} +EXPORT_SYMBOL_GPL(tty_port_register_device_attr); + +/** + * tty_port_register_device_attr_serdev - register tty or serdev device + * @port: tty_port of the device + * @driver: tty_driver for this device + * @index: index of the tty + * @device: parent if exists, otherwise NULL + * @drvdata: driver data for the device + * @attr_grp: attribute group for the device + * + * Register a serdev or tty device depending on if the parent device has any + * defined serdev clients or not. + */ +struct device *tty_port_register_device_attr_serdev(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device, void *drvdata, + const struct attribute_group **attr_grp) +{ struct device *dev; tty_port_link_device(port, driver, index); dev = serdev_tty_port_register(port, device, driver, index); - if (PTR_ERR(dev) != -ENODEV) + if (PTR_ERR(dev) != -ENODEV) { /* Skip creating cdev if we registered a serdev device */ return dev; + } return tty_register_device_attr(driver, index, device, drvdata, attr_grp); } -EXPORT_SYMBOL_GPL(tty_port_register_device_attr); +EXPORT_SYMBOL_GPL(tty_port_register_device_attr_serdev); + +/** + * tty_port_register_device_serdev - register tty or serdev device + * @port: tty_port of the device + * @driver: tty_driver for this device + * @index: index of the tty + * @device: parent if exists, otherwise NULL + * + * Register a serdev or tty device depending on if the parent device has any + * defined serdev clients or not. + */ +struct device *tty_port_register_device_serdev(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device) +{ + return tty_port_register_device_attr_serdev(port, driver, index, + device, NULL, NULL); +} +EXPORT_SYMBOL_GPL(tty_port_register_device_serdev); + +/** + * tty_port_unregister_device - deregister a tty or serdev device + * @port: tty_port of the device + * @driver: tty_driver for this device + * @index: index of the tty + * + * If a tty or serdev device is registered with a call to + * tty_port_register_device_serdev() then this function must be called when + * the device is gone. + */ +void tty_port_unregister_device(struct tty_port *port, + struct tty_driver *driver, unsigned index) +{ + int ret; + + ret = serdev_tty_port_unregister(port); + if (ret == 0) + return; + + tty_unregister_device(driver, index); +} +EXPORT_SYMBOL_GPL(tty_port_unregister_device); int tty_port_alloc_xmit_buf(struct tty_port *port) { @@ -189,9 +255,6 @@ static void tty_port_destructor(struct kref *kref) /* check if last port ref was dropped before tty release */ if (WARN_ON(port->itty)) return; - - serdev_tty_port_unregister(port); - if (port->xmit_buf) free_page((unsigned long)port->xmit_buf); tty_port_destroy(port); diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 1f6e17fc3fb0..a5f88cf0f61d 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -322,15 +322,13 @@ int con_set_trans_old(unsigned char __user * arg) { int i; unsigned short inbuf[E_TABSZ]; + unsigned char ubuf[E_TABSZ]; - if (!access_ok(VERIFY_READ, arg, E_TABSZ)) + if (copy_from_user(ubuf, arg, E_TABSZ)) return -EFAULT; - for (i = 0; i < E_TABSZ ; i++) { - unsigned char uc; - __get_user(uc, arg+i); - inbuf[i] = UNI_DIRECT_BASE | uc; - } + for (i = 0; i < E_TABSZ ; i++) + inbuf[i] = UNI_DIRECT_BASE | ubuf[i]; console_lock(); memcpy(translations[USER_MAP], inbuf, sizeof(inbuf)); @@ -345,9 +343,6 @@ int con_get_trans_old(unsigned char __user * arg) unsigned short *p = translations[USER_MAP]; unsigned char outbuf[E_TABSZ]; - if (!access_ok(VERIFY_WRITE, arg, E_TABSZ)) - return -EFAULT; - console_lock(); for (i = 0; i < E_TABSZ ; i++) { @@ -356,22 +351,16 @@ int con_get_trans_old(unsigned char __user * arg) } console_unlock(); - for (i = 0; i < E_TABSZ ; i++) - __put_user(outbuf[i], arg+i); - return 0; + return copy_to_user(arg, outbuf, sizeof(outbuf)) ? -EFAULT : 0; } int con_set_trans_new(ushort __user * arg) { - int i; unsigned short inbuf[E_TABSZ]; - if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short))) + if (copy_from_user(inbuf, arg, sizeof(inbuf))) return -EFAULT; - for (i = 0; i < E_TABSZ ; i++) - __get_user(inbuf[i], arg+i); - console_lock(); memcpy(translations[USER_MAP], inbuf, sizeof(inbuf)); update_user_maps(); @@ -381,19 +370,13 @@ int con_set_trans_new(ushort __user * arg) int con_get_trans_new(ushort __user * arg) { - int i; unsigned short outbuf[E_TABSZ]; - if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short))) - return -EFAULT; - console_lock(); memcpy(outbuf, translations[USER_MAP], sizeof(outbuf)); console_unlock(); - for (i = 0; i < E_TABSZ ; i++) - __put_user(outbuf[i], arg+i); - return 0; + return copy_to_user(arg, outbuf, sizeof(outbuf)) ? -EFAULT : 0; } /* @@ -557,14 +540,9 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) if (!ct) return 0; - unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); - if (!unilist) - return -ENOMEM; - - for (i = ct, plist = unilist; i; i--, plist++, list++) { - __get_user(plist->unicode, &list->unicode); - __get_user(plist->fontpos, &list->fontpos); - } + unilist = memdup_user(list, ct * sizeof(struct unipair)); + if (IS_ERR(unilist)) + return PTR_ERR(unilist); console_lock(); @@ -757,11 +735,11 @@ EXPORT_SYMBOL(con_copy_unimap); */ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list) { - int i, j, k; + int i, j, k, ret = 0; ushort ect; u16 **p1, *p2; struct uni_pagedir *p; - struct unipair *unilist, *plist; + struct unipair *unilist; unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); if (!unilist) @@ -792,13 +770,11 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni } } console_unlock(); - for (i = min(ect, ct), plist = unilist; i; i--, list++, plist++) { - __put_user(plist->unicode, &list->unicode); - __put_user(plist->fontpos, &list->fontpos); - } - __put_user(ect, uct); + if (copy_to_user(list, unilist, min(ect, ct) * sizeof(struct unipair))) + ret = -EFAULT; + put_user(ect, uct); kfree(unilist); - return ((ect <= ct) ? 0 : -ENOMEM); + return ret ? ret : (ect <= ct) ? 0 : -ENOMEM; } /* diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 8af8d9542663..f4166263bb3a 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -1203,8 +1203,7 @@ DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); #if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(CONFIG_ALPHA) ||\ defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) ||\ defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\ - (defined(CONFIG_ARM) && defined(CONFIG_KEYBOARD_ATKBD) && !defined(CONFIG_ARCH_RPC)) ||\ - defined(CONFIG_AVR32) + (defined(CONFIG_ARM) && defined(CONFIG_KEYBOARD_ATKBD) && !defined(CONFIG_ARCH_RPC)) #define HW_RAW(dev) (test_bit(EV_MSC, dev->evbit) && test_bit(MSC_RAW, dev->mscbit) &&\ ((dev)->id.bustype == BUS_I8042) && ((dev)->id.vendor == 0x0001) && ((dev)->id.product == 0x0001)) diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 36e1b8c7680f..accbd1257bc4 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -80,21 +80,17 @@ void clear_selection(void) /* * User settable table: what characters are to be considered alphabetic? - * 256 bits. Locked by the console lock. + * 128 bits. Locked by the console lock. */ -static u32 inwordLut[8]={ +static u32 inwordLut[]={ 0x00000000, /* control chars */ - 0x03FF0000, /* digits */ + 0x03FFE000, /* digits and "-./" */ 0x87FFFFFE, /* uppercase and '_' */ 0x07FFFFFE, /* lowercase */ - 0x00000000, - 0x00000000, - 0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */ - 0xFF7FFFFF /* latin-1 accented letters, not division sign */ }; static inline int inword(const u16 c) { - return c > 0xff || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1); + return c > 0x7f || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1); } /** @@ -106,10 +102,10 @@ static inline int inword(const u16 c) { */ int sel_loadlut(char __user *p) { - u32 tmplut[8]; - if (copy_from_user(tmplut, (u32 __user *)(p+4), 32)) + u32 tmplut[ARRAY_SIZE(inwordLut)]; + if (copy_from_user(tmplut, (u32 __user *)(p+4), sizeof(inwordLut))) return -EFAULT; - memcpy(inwordLut, tmplut, 32); + memcpy(inwordLut, tmplut, sizeof(inwordLut)); return 0; } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 5c4933bb4b53..2ebaba16f785 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -181,7 +181,7 @@ int console_blanked; static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */ static int vesa_off_interval; -static int blankinterval = 10*60; +static int blankinterval; core_param(consoleblank, blankinterval, int, 0444); static DECLARE_WORK(console_work, console_callback); @@ -425,7 +425,7 @@ static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink, else if (_underline) a = (a & 0xf0) | vc->vc_ulcolor; else if (_intensity == 0) - a = (a & 0xf0) | vc->vc_ulcolor; + a = (a & 0xf0) | vc->vc_halfcolor; if (_reverse) a = ((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77); if (_blink) @@ -2709,13 +2709,13 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) * related to the kernel should not use this. */ data = vt_get_shift_state(); - ret = __put_user(data, p); + ret = put_user(data, p); break; case TIOCL_GETMOUSEREPORTING: console_lock(); /* May be overkill */ data = mouse_reporting(); console_unlock(); - ret = __put_user(data, p); + ret = put_user(data, p); break; case TIOCL_SETVESABLANK: console_lock(); @@ -2724,7 +2724,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) break; case TIOCL_GETKMSGREDIRECT: data = vt_get_kmsg_redirect(); - ret = __put_user(data, p); + ret = put_user(data, p); break; case TIOCL_SETKMSGREDIRECT: if (!capable(CAP_SYS_ADMIN)) { diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 0cbfe1ff6f6c..96d389cb506c 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -266,10 +266,6 @@ do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_ if (copy_from_user(&tmp, user_ud, sizeof tmp)) return -EFAULT; - if (tmp.entries) - if (!access_ok(VERIFY_WRITE, tmp.entries, - tmp.entry_ct*sizeof(struct unipair))) - return -EFAULT; switch (cmd) { case PIO_UNIMAP: if (!perm) @@ -1170,10 +1166,6 @@ compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud, if (copy_from_user(&tmp, user_ud, sizeof tmp)) return -EFAULT; tmp_entries = compat_ptr(tmp.entries); - if (tmp_entries) - if (!access_ok(VERIFY_WRITE, tmp_entries, - tmp.entry_ct*sizeof(struct unipair))) - return -EFAULT; switch (cmd) { case PIO_UNIMAP: if (!perm) |