diff options
author | Paul Mundt <lethal@linux-sh.org> | 2011-05-24 10:32:20 +0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2011-05-24 10:32:20 +0400 |
commit | 9fb4c7fbbcb1e947567d13b82e429ae47a46e337 (patch) | |
tree | 6c5f11f347d0f58565381f92680a7a9cc63c0bd8 /drivers/tty | |
parent | dc3e5b6a6e842116ec2436161adf31877f09b6b9 (diff) | |
parent | d762f4383100c2a87b1a3f2d678cd3b5425655b4 (diff) | |
download | linux-9fb4c7fbbcb1e947567d13b82e429ae47a46e337.tar.xz |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/tty')
83 files changed, 2782 insertions, 456 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 3fd7199301b6..bd7cc0527999 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -319,3 +319,34 @@ config N_GSM This line discipline provides support for the GSM MUX protocol and presents the mux as a set of 61 individual tty devices. +config TRACE_ROUTER + tristate "Trace data router for MIPI P1149.7 cJTAG standard" + depends on TRACE_SINK + default n + help + The trace router uses the Linux tty line discipline framework to + route trace data coming from a tty port (say UART for example) to + the trace sink line discipline driver and to another tty port (say + USB). This is part of a solution for the MIPI P1149.7, compact JTAG, + standard, which is for debugging mobile devices. The PTI driver in + drivers/misc/pti.c defines the majority of this MIPI solution. + + You should select this driver if the target kernel is meant for + a mobile device containing a modem. Then you will need to select + "Trace data sink for MIPI P1149.7 cJTAG standard" line discipline + driver. + +config TRACE_SINK + tristate "Trace data sink for MIPI P1149.7 cJTAG standard" + default n + help + The trace sink uses the Linux line discipline framework to receive + trace data coming from the trace router line discipline driver + to a user-defined tty port target, like USB. + This is to provide a way to extract modem trace data on + devices that do not have a PTI HW module, or just need modem + trace data to come out of a different HW output port. + This is part of a solution for the P1149.7, compact JTAG, standard. + + If you select this option, you need to select + "Trace data router for MIPI P1149.7 cJTAG standard". diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 690522fcb338..ea89b0bd15fe 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -6,6 +6,8 @@ obj-$(CONFIG_AUDIT) += tty_audit.o obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o obj-$(CONFIG_N_HDLC) += n_hdlc.o obj-$(CONFIG_N_GSM) += n_gsm.o +obj-$(CONFIG_TRACE_ROUTER) += n_tracerouter.o +obj-$(CONFIG_TRACE_SINK) += n_tracesink.o obj-$(CONFIG_R3964) += n_r3964.o obj-y += vt/ diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index f214e5022472..220579592c20 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/amiserial.c - * * Serial driver for the amiga builtin port. * * This code was created by taking serial.c version 4.30 from kernel diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index c99728f0cd9f..bfa05e801823 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -3,8 +3,6 @@ #undef Z_EXT_CHARS_IN_BUFFER /* - * linux/drivers/char/cyclades.c - * * This file contains the driver for the Cyclades async multiport * serial boards. * @@ -1445,13 +1443,11 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) { struct cyclades_card *card; unsigned long flags; - int channel; if (!(info->port.flags & ASYNC_INITIALIZED)) return; card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { spin_lock_irqsave(&card->card_lock, flags); @@ -1476,6 +1472,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) spin_unlock_irqrestore(&card->card_lock, flags); } else { #ifdef CY_DEBUG_OPEN + int channel = info->line - card->first_line; printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, " "base_addr %p\n", card, channel, card->base_addr); #endif diff --git a/drivers/tty/ipwireless/Makefile b/drivers/tty/ipwireless/Makefile index db80873d7f20..fe2e1730986b 100644 --- a/drivers/tty/ipwireless/Makefile +++ b/drivers/tty/ipwireless/Makefile @@ -1,6 +1,4 @@ # -# drivers/char/pcmcia/ipwireless/Makefile -# # Makefile for the IPWireless driver # diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 35b0c38590e6..ba679ce0a774 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -371,7 +371,7 @@ static int moxa_ioctl(struct tty_struct *tty, tmp.cflag = p->cflag; else tmp.cflag = ttyp->termios->c_cflag; - tty_kref_put(tty); + tty_kref_put(ttyp); copy: if (copy_to_user(argm, &tmp, sizeof(tmp))) return -EFAULT; @@ -1129,7 +1129,6 @@ static void moxa_shutdown(struct tty_port *port) struct moxa_port *ch = container_of(port, struct moxa_port, port); MoxaPortDisable(ch); MoxaPortFlushData(ch, 2); - clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); } static int moxa_carrier_raised(struct tty_port *port) @@ -1155,7 +1154,6 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) struct moxa_board_conf *brd; struct moxa_port *ch; int port; - int retval; port = tty->index; if (port == MAX_PORTS) { @@ -1190,10 +1188,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) mutex_unlock(&ch->port.mutex); mutex_unlock(&moxa_openlock); - retval = tty_port_block_til_ready(&ch->port, tty, filp); - if (retval == 0) - set_bit(ASYNCB_NORMAL_ACTIVE, &ch->port.flags); - return retval; + return tty_port_block_til_ready(&ch->port, tty, filp); } static void moxa_close(struct tty_struct *tty, struct file *filp) @@ -1207,14 +1202,15 @@ static int moxa_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct moxa_port *ch = tty->driver_data; + unsigned long flags; int len; if (ch == NULL) return 0; - spin_lock_bh(&moxa_lock); + spin_lock_irqsave(&moxa_lock, flags); len = MoxaPortWriteData(tty, buf, count); - spin_unlock_bh(&moxa_lock); + spin_unlock_irqrestore(&moxa_lock, flags); set_bit(LOWWAIT, &ch->statusflags); return len; @@ -1281,10 +1277,8 @@ static int moxa_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct moxa_port *ch; - int port; int dtr, rts; - port = tty->index; mutex_lock(&moxa_openlock); ch = tty->driver_data; if (!ch) { @@ -1756,11 +1750,9 @@ static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio, speed_t baud) { void __iomem *ofsAddr; - tcflag_t cflag; tcflag_t mode = 0; ofsAddr = port->tableAddr; - cflag = termio->c_cflag; /* termio->c_cflag */ mode = termio->c_cflag & CSIZE; if (mode == CS5) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 47f8cdb207f1..a4c42a75a3bf 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -526,19 +526,6 @@ static int gsm_stuff_frame(const u8 *input, u8 *output, int len) return olen; } -static void hex_packet(const unsigned char *p, int len) -{ - int i; - for (i = 0; i < len; i++) { - if (i && (i % 16) == 0) { - pr_cont("\n"); - pr_debug(""); - } - pr_cont("%02X ", *p++); - } - pr_cont("\n"); -} - /** * gsm_send - send a control frame * @gsm: our GSM mux @@ -685,10 +672,10 @@ static void gsm_data_kick(struct gsm_mux *gsm) len = msg->len + 2; } - if (debug & 4) { - pr_debug("gsm_data_kick:\n"); - hex_packet(gsm->txframe, len); - } + if (debug & 4) + print_hex_dump_bytes("gsm_data_kick: ", + DUMP_PREFIX_OFFSET, + gsm->txframe, len); if (gsm->output(gsm, gsm->txframe + skip_sof, len - skip_sof) < 0) @@ -1658,8 +1645,12 @@ static void gsm_queue(struct gsm_mux *gsm) if ((gsm->control & ~PF) == UI) gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); - /* generate final CRC with received FCS */ - gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); + if (gsm->encoding == 0){ + /* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only. + In this case it contain the last piece of data + required to generate final CRC */ + gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); + } if (gsm->fcs != GOOD_FCS) { gsm->bad_fcs++; if (debug & 4) @@ -2091,10 +2082,9 @@ static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len) set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags); return -ENOSPC; } - if (debug & 4) { - pr_debug("-->%d bytes out\n", len); - hex_packet(data, len); - } + if (debug & 4) + print_hex_dump_bytes("gsmld_output: ", DUMP_PREFIX_OFFSET, + data, len); gsm->tty->ops->write(gsm->tty, data, len); return len; } @@ -2124,7 +2114,7 @@ static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) /** * gsmld_detach_gsm - stop doing 0710 mux - * @tty: tty atttached to the mux + * @tty: tty attached to the mux * @gsm: mux * * Shutdown and then clean up the resources used by the line discipline @@ -2138,8 +2128,8 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) gsm->tty = NULL; } -static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int gsmld_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { struct gsm_mux *gsm = tty->disc_data; const unsigned char *dp; @@ -2148,10 +2138,9 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, char buf[64]; char flags; - if (debug & 4) { - pr_debug("Inbytes %dd\n", count); - hex_packet(cp, count); - } + if (debug & 4) + print_hex_dump_bytes("gsmld_receive: ", DUMP_PREFIX_OFFSET, + cp, count); for (i = count, dp = cp, f = fp; i; i--, dp++) { flags = *f++; @@ -2173,6 +2162,8 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, } /* FASYNC if needed ? */ /* If clogged call tty_throttle(tty); */ + + return count; } /** diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index cea56033b34c..cac666314aef 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -188,8 +188,8 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait); static int n_hdlc_tty_open(struct tty_struct *tty); static void n_hdlc_tty_close(struct tty_struct *tty); -static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *cp, - char *fp, int count); +static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, + const __u8 *cp, char *fp, int count); static void n_hdlc_tty_wakeup(struct tty_struct *tty); #define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) @@ -509,8 +509,8 @@ static void n_hdlc_tty_wakeup(struct tty_struct *tty) * Called by tty low level driver when receive data is available. Data is * interpreted as one HDLC frame. */ -static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, - char *flags, int count) +static unsigned int n_hdlc_tty_receive(struct tty_struct *tty, + const __u8 *data, char *flags, int count) { register struct n_hdlc *n_hdlc = tty2n_hdlc (tty); register struct n_hdlc_buf *buf; @@ -521,20 +521,20 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, /* This can happen if stuff comes in on the backup tty */ if (!n_hdlc || tty != n_hdlc->tty) - return; + return -ENODEV; /* verify line is using HDLC discipline */ if (n_hdlc->magic != HDLC_MAGIC) { printk("%s(%d) line not using HDLC discipline\n", __FILE__,__LINE__); - return; + return -EINVAL; } if ( count>maxframe ) { if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d) rx count>maxframesize, data discarded\n", __FILE__,__LINE__); - return; + return -EINVAL; } /* get a free HDLC buffer */ @@ -550,7 +550,7 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d) no more rx buffers, data discarded\n", __FILE__,__LINE__); - return; + return -EINVAL; } /* copy received data to HDLC buffer */ @@ -565,6 +565,8 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, if (n_hdlc->tty->fasync != NULL) kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN); + return count; + } /* end of n_hdlc_tty_receive() */ /** diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 5c6c31459a2f..a4bc39c21a43 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -139,8 +139,8 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file, static void r3964_set_termios(struct tty_struct *tty, struct ktermios *old); static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, struct poll_table_struct *wait); -static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count); +static unsigned int r3964_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count); static struct tty_ldisc_ops tty_ldisc_N_R3964 = { .owner = THIS_MODULE, @@ -1239,8 +1239,8 @@ static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, return result; } -static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int r3964_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { struct r3964_info *pInfo = tty->disc_data; const unsigned char *p; @@ -1257,6 +1257,8 @@ static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, } } + + return count; } MODULE_LICENSE("GPL"); diff --git a/drivers/tty/n_tracerouter.c b/drivers/tty/n_tracerouter.c new file mode 100644 index 000000000000..1f063d3aa32f --- /dev/null +++ b/drivers/tty/n_tracerouter.c @@ -0,0 +1,243 @@ +/* + * n_tracerouter.c - Trace data router through tty space + * + * Copyright (C) Intel 2011 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This trace router uses the Linux line discipline framework to route + * trace data coming from a HW Modem to a PTI (Parallel Trace Module) port. + * The solution is not specific to a HW modem and this line disciple can + * be used to route any stream of data in kernel space. + * This is part of a solution for the P1149.7, compact JTAG, standard. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/ioctl.h> +#include <linux/tty.h> +#include <linux/tty_ldisc.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <asm-generic/bug.h> +#include "n_tracesink.h" + +/* + * Other ldisc drivers use 65536 which basically means, + * 'I can always accept 64k' and flow control is off. + * This number is deemed appropriate for this driver. + */ +#define RECEIVE_ROOM 65536 +#define DRIVERNAME "n_tracerouter" + +/* + * struct to hold private configuration data for this ldisc. + * opencalled is used to hold if this ldisc has been opened. + * kref_tty holds the tty reference the ldisc sits on top of. + */ +struct tracerouter_data { + u8 opencalled; + struct tty_struct *kref_tty; +}; +static struct tracerouter_data *tr_data; + +/* lock for when tty reference is being used */ +static DEFINE_MUTEX(routelock); + +/** + * n_tracerouter_open() - Called when a tty is opened by a SW entity. + * @tty: terminal device to the ldisc. + * + * Return: + * 0 for success. + * + * Caveats: This should only be opened one time per SW entity. + */ +static int n_tracerouter_open(struct tty_struct *tty) +{ + int retval = -EEXIST; + + mutex_lock(&routelock); + if (tr_data->opencalled == 0) { + + tr_data->kref_tty = tty_kref_get(tty); + if (tr_data->kref_tty == NULL) { + retval = -EFAULT; + } else { + tr_data->opencalled = 1; + tty->disc_data = tr_data; + tty->receive_room = RECEIVE_ROOM; + tty_driver_flush_buffer(tty); + retval = 0; + } + } + mutex_unlock(&routelock); + return retval; +} + +/** + * n_tracerouter_close() - close connection + * @tty: terminal device to the ldisc. + * + * Called when a software entity wants to close a connection. + */ +static void n_tracerouter_close(struct tty_struct *tty) +{ + struct tracerouter_data *tptr = tty->disc_data; + + mutex_lock(&routelock); + WARN_ON(tptr->kref_tty != tr_data->kref_tty); + tty_driver_flush_buffer(tty); + tty_kref_put(tr_data->kref_tty); + tr_data->kref_tty = NULL; + tr_data->opencalled = 0; + tty->disc_data = NULL; + mutex_unlock(&routelock); +} + +/** + * n_tracerouter_read() - read request from user space + * @tty: terminal device passed into the ldisc. + * @file: pointer to open file object. + * @buf: pointer to the data buffer that gets eventually returned. + * @nr: number of bytes of the data buffer that is returned. + * + * function that allows read() functionality in userspace. By default if this + * is not implemented it returns -EIO. This module is functioning like a + * router via n_tracerouter_receivebuf(), and there is no real requirement + * to implement this function. However, an error return value other than + * -EIO should be used just to show that there was an intent not to have + * this function implemented. Return value based on read() man pages. + * + * Return: + * -EINVAL + */ +static ssize_t n_tracerouter_read(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr) { + return -EINVAL; +} + +/** + * n_tracerouter_write() - Function that allows write() in userspace. + * @tty: terminal device passed into the ldisc. + * @file: pointer to open file object. + * @buf: pointer to the data buffer that gets eventually returned. + * @nr: number of bytes of the data buffer that is returned. + * + * By default if this is not implemented, it returns -EIO. + * This should not be implemented, ever, because + * 1. this driver is functioning like a router via + * n_tracerouter_receivebuf() + * 2. No writes to HW will ever go through this line discpline driver. + * However, an error return value other than -EIO should be used + * just to show that there was an intent not to have this function + * implemented. Return value based on write() man pages. + * + * Return: + * -EINVAL + */ +static ssize_t n_tracerouter_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) { + return -EINVAL; +} + +/** + * n_tracerouter_receivebuf() - Routing function for driver. + * @tty: terminal device passed into the ldisc. It's assumed + * tty will never be NULL. + * @cp: buffer, block of characters to be eventually read by + * someone, somewhere (user read() call or some kernel function). + * @fp: flag buffer. + * @count: number of characters (aka, bytes) in cp. + * + * This function takes the input buffer, cp, and passes it to + * an external API function for processing. + */ +static void n_tracerouter_receivebuf(struct tty_struct *tty, + const unsigned char *cp, + char *fp, int count) +{ + mutex_lock(&routelock); + n_tracesink_datadrain((u8 *) cp, count); + mutex_unlock(&routelock); +} + +/* + * Flush buffer is not impelemented as the ldisc has no internal buffering + * so the tty_driver_flush_buffer() is sufficient for this driver's needs. + */ + +static struct tty_ldisc_ops tty_ptirouter_ldisc = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = DRIVERNAME, + .open = n_tracerouter_open, + .close = n_tracerouter_close, + .read = n_tracerouter_read, + .write = n_tracerouter_write, + .receive_buf = n_tracerouter_receivebuf +}; + +/** + * n_tracerouter_init - module initialisation + * + * Registers this module as a line discipline driver. + * + * Return: + * 0 for success, any other value error. + */ +static int __init n_tracerouter_init(void) +{ + int retval; + + tr_data = kzalloc(sizeof(struct tracerouter_data), GFP_KERNEL); + if (tr_data == NULL) + return -ENOMEM; + + + /* Note N_TRACEROUTER is defined in linux/tty.h */ + retval = tty_register_ldisc(N_TRACEROUTER, &tty_ptirouter_ldisc); + if (retval < 0) { + pr_err("%s: Registration failed: %d\n", __func__, retval); + kfree(tr_data); + } + return retval; +} + +/** + * n_tracerouter_exit - module unload + * + * Removes this module as a line discipline driver. + */ +static void __exit n_tracerouter_exit(void) +{ + int retval = tty_unregister_ldisc(N_TRACEROUTER); + + if (retval < 0) + pr_err("%s: Unregistration failed: %d\n", __func__, retval); + else + kfree(tr_data); +} + +module_init(n_tracerouter_init); +module_exit(n_tracerouter_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jay Freyensee"); +MODULE_ALIAS_LDISC(N_TRACEROUTER); +MODULE_DESCRIPTION("Trace router ldisc driver"); diff --git a/drivers/tty/n_tracesink.c b/drivers/tty/n_tracesink.c new file mode 100644 index 000000000000..ddce58b973d2 --- /dev/null +++ b/drivers/tty/n_tracesink.c @@ -0,0 +1,238 @@ +/* + * n_tracesink.c - Trace data router and sink path through tty space. + * + * Copyright (C) Intel 2011 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The trace sink uses the Linux line discipline framework to receive + * trace data coming from the PTI source line discipline driver + * to a user-desired tty port, like USB. + * This is to provide a way to extract modem trace data on + * devices that do not have a PTI HW module, or just need modem + * trace data to come out of a different HW output port. + * This is part of a solution for the P1149.7, compact JTAG, standard. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/ioctl.h> +#include <linux/tty.h> +#include <linux/tty_ldisc.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <asm-generic/bug.h> +#include "n_tracesink.h" + +/* + * Other ldisc drivers use 65536 which basically means, + * 'I can always accept 64k' and flow control is off. + * This number is deemed appropriate for this driver. + */ +#define RECEIVE_ROOM 65536 +#define DRIVERNAME "n_tracesink" + +/* + * there is a quirk with this ldisc is he can write data + * to a tty from anyone calling his kernel API, which + * meets customer requirements in the drivers/misc/pti.c + * project. So he needs to know when he can and cannot write when + * the API is called. In theory, the API can be called + * after an init() but before a successful open() which + * would crash the system if tty is not checked. + */ +static struct tty_struct *this_tty; +static DEFINE_MUTEX(writelock); + +/** + * n_tracesink_open() - Called when a tty is opened by a SW entity. + * @tty: terminal device to the ldisc. + * + * Return: + * 0 for success, + * -EFAULT = couldn't get a tty kref n_tracesink will sit + * on top of + * -EEXIST = open() called successfully once and it cannot + * be called again. + * + * Caveats: open() should only be successful the first time a + * SW entity calls it. + */ +static int n_tracesink_open(struct tty_struct *tty) +{ + int retval = -EEXIST; + + mutex_lock(&writelock); + if (this_tty == NULL) { + this_tty = tty_kref_get(tty); + if (this_tty == NULL) { + retval = -EFAULT; + } else { + tty->disc_data = this_tty; + tty_driver_flush_buffer(tty); + retval = 0; + } + } + mutex_unlock(&writelock); + + return retval; +} + +/** + * n_tracesink_close() - close connection + * @tty: terminal device to the ldisc. + * + * Called when a software entity wants to close a connection. + */ +static void n_tracesink_close(struct tty_struct *tty) +{ + mutex_lock(&writelock); + tty_driver_flush_buffer(tty); + tty_kref_put(this_tty); + this_tty = NULL; + tty->disc_data = NULL; + mutex_unlock(&writelock); +} + +/** + * n_tracesink_read() - read request from user space + * @tty: terminal device passed into the ldisc. + * @file: pointer to open file object. + * @buf: pointer to the data buffer that gets eventually returned. + * @nr: number of bytes of the data buffer that is returned. + * + * function that allows read() functionality in userspace. By default if this + * is not implemented it returns -EIO. This module is functioning like a + * router via n_tracesink_receivebuf(), and there is no real requirement + * to implement this function. However, an error return value other than + * -EIO should be used just to show that there was an intent not to have + * this function implemented. Return value based on read() man pages. + * + * Return: + * -EINVAL + */ +static ssize_t n_tracesink_read(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr) { + return -EINVAL; +} + +/** + * n_tracesink_write() - Function that allows write() in userspace. + * @tty: terminal device passed into the ldisc. + * @file: pointer to open file object. + * @buf: pointer to the data buffer that gets eventually returned. + * @nr: number of bytes of the data buffer that is returned. + * + * By default if this is not implemented, it returns -EIO. + * This should not be implemented, ever, because + * 1. this driver is functioning like a router via + * n_tracesink_receivebuf() + * 2. No writes to HW will ever go through this line discpline driver. + * However, an error return value other than -EIO should be used + * just to show that there was an intent not to have this function + * implemented. Return value based on write() man pages. + * + * Return: + * -EINVAL + */ +static ssize_t n_tracesink_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) { + return -EINVAL; +} + +/** + * n_tracesink_datadrain() - Kernel API function used to route + * trace debugging data to user-defined + * port like USB. + * + * @buf: Trace debuging data buffer to write to tty target + * port. Null value will return with no write occurring. + * @count: Size of buf. Value of 0 or a negative number will + * return with no write occuring. + * + * Caveat: If this line discipline does not set the tty it sits + * on top of via an open() call, this API function will not + * call the tty's write() call because it will have no pointer + * to call the write(). + */ +void n_tracesink_datadrain(u8 *buf, int count) +{ + mutex_lock(&writelock); + + if ((buf != NULL) && (count > 0) && (this_tty != NULL)) + this_tty->ops->write(this_tty, buf, count); + + mutex_unlock(&writelock); +} +EXPORT_SYMBOL_GPL(n_tracesink_datadrain); + +/* + * Flush buffer is not impelemented as the ldisc has no internal buffering + * so the tty_driver_flush_buffer() is sufficient for this driver's needs. + */ + +/* + * tty_ldisc function operations for this driver. + */ +static struct tty_ldisc_ops tty_n_tracesink = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = DRIVERNAME, + .open = n_tracesink_open, + .close = n_tracesink_close, + .read = n_tracesink_read, + .write = n_tracesink_write +}; + +/** + * n_tracesink_init- module initialisation + * + * Registers this module as a line discipline driver. + * + * Return: + * 0 for success, any other value error. + */ +static int __init n_tracesink_init(void) +{ + /* Note N_TRACESINK is defined in linux/tty.h */ + int retval = tty_register_ldisc(N_TRACESINK, &tty_n_tracesink); + + if (retval < 0) + pr_err("%s: Registration failed: %d\n", __func__, retval); + + return retval; +} + +/** + * n_tracesink_exit - module unload + * + * Removes this module as a line discipline driver. + */ +static void __exit n_tracesink_exit(void) +{ + int retval = tty_unregister_ldisc(N_TRACESINK); + + if (retval < 0) + pr_err("%s: Unregistration failed: %d\n", __func__, retval); +} + +module_init(n_tracesink_init); +module_exit(n_tracesink_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jay Freyensee"); +MODULE_ALIAS_LDISC(N_TRACESINK); +MODULE_DESCRIPTION("Trace sink ldisc driver"); diff --git a/drivers/tty/n_tracesink.h b/drivers/tty/n_tracesink.h new file mode 100644 index 000000000000..a68bb44f1ef5 --- /dev/null +++ b/drivers/tty/n_tracesink.h @@ -0,0 +1,36 @@ +/* + * n_tracesink.h - Kernel driver API to route trace data in kernel space. + * + * Copyright (C) Intel 2011 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The PTI (Parallel Trace Interface) driver directs trace data routed from + * various parts in the system out through the Intel Penwell PTI port and + * out of the mobile device for analysis with a debugging tool + * (Lauterbach, Fido). This is part of a solution for the MIPI P1149.7, + * compact JTAG, standard. + * + * This header file is used by n_tracerouter to be able to send the + * data of it's tty port to the tty port this module sits. This + * mechanism can also be used independent of the PTI module. + * + */ + +#ifndef N_TRACESINK_H_ +#define N_TRACESINK_H_ + +void n_tracesink_datadrain(u8 *buf, int count); + +#endif diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 0ad32888091c..95d0a9c2dd13 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -81,38 +81,6 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } -/** - * n_tty_set__room - receive space - * @tty: terminal - * - * Called by the driver to find out how much data it is - * permitted to feed to the line discipline without any being lost - * and thus to manage flow control. Not serialized. Answers for the - * "instant". - */ - -static void n_tty_set_room(struct tty_struct *tty) -{ - /* tty->read_cnt is not read locked ? */ - int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; - int old_left; - - /* - * If we are doing input canonicalization, and there are no - * pending newlines, let characters through without limit, so - * that erase characters will be handled. Other excess - * characters will be beeped. - */ - if (left <= 0) - left = tty->icanon && !tty->canon_data; - old_left = tty->receive_room; - tty->receive_room = left; - - /* Did this open up the receive buffer? We may need to flip */ - if (left && !old_left) - schedule_work(&tty->buf.work); -} - static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) { if (tty->read_cnt < N_TTY_BUF_SIZE) { @@ -184,7 +152,6 @@ static void reset_buffer_flags(struct tty_struct *tty) tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); - n_tty_set_room(tty); check_unthrottle(tty); } @@ -1360,17 +1327,19 @@ static void n_tty_write_wakeup(struct tty_struct *tty) * calls one at a time and in order (or using flush_to_ldisc) */ -static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static unsigned int n_tty_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { const unsigned char *p; char *f, flags = TTY_NORMAL; int i; char buf[64]; unsigned long cpuflags; + int left; + int ret = 0; if (!tty->read_buf) - return; + return 0; if (tty->real_raw) { spin_lock_irqsave(&tty->read_lock, cpuflags); @@ -1380,6 +1349,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; + ret += i; cp += i; count -= i; @@ -1389,8 +1359,10 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, memcpy(tty->read_buf + tty->read_head, cp, i); tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); tty->read_cnt += i; + ret += i; spin_unlock_irqrestore(&tty->read_lock, cpuflags); } else { + ret = count; for (i = count, p = cp, f = fp; i; i--, p++) { if (f) flags = *f++; @@ -1418,8 +1390,6 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, tty->ops->flush_chars(tty); } - n_tty_set_room(tty); - if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); @@ -1432,8 +1402,12 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, * mode. We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet! */ - if (tty->receive_room < TTY_THRESHOLD_THROTTLE) + left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + + if (left < TTY_THRESHOLD_THROTTLE) tty_throttle(tty); + + return ret; } int is_ignored(int sig) @@ -1477,7 +1451,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { tty->raw = 1; tty->real_raw = 1; - n_tty_set_room(tty); return; } if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || @@ -1530,7 +1503,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) else tty->real_raw = 0; } - n_tty_set_room(tty); /* The termios change make the tty ready for I/O */ wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); @@ -1812,8 +1784,6 @@ do_it_again: retval = -ERESTARTSYS; break; } - /* FIXME: does n_tty_set_room need locking ? */ - n_tty_set_room(tty); timeout = schedule_timeout(timeout); continue; } @@ -1885,10 +1855,8 @@ do_it_again: * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */ - if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { - n_tty_set_room(tty); + if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) check_unthrottle(tty); - } if (b - buf >= minimum) break; @@ -1910,7 +1878,6 @@ do_it_again: } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; - n_tty_set_room(tty); return retval; } diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index fd0a98524d51..b1aecc7bb32a 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -364,8 +364,6 @@ struct port { u8 toggle_ul; u16 token_dl; - /* mutex to ensure one access patch to this port */ - struct mutex tty_sem; wait_queue_head_t tty_wait; struct async_icount tty_icount; @@ -1431,8 +1429,8 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev, } for (i = PORT_MDM; i < MAX_PORT; i++) { - if (kfifo_alloc(&dc->port[i].fifo_ul, - FIFO_BUFFER_SIZE_UL, GFP_ATOMIC)) { + if (kfifo_alloc(&dc->port[i].fifo_ul, FIFO_BUFFER_SIZE_UL, + GFP_KERNEL)) { dev_err(&pdev->dev, "Could not allocate kfifo buffer\n"); ret = -ENOMEM; @@ -1474,7 +1472,6 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev, struct device *tty_dev; struct port *port = &dc->port[i]; port->dc = dc; - mutex_init(&port->tty_sem); tty_port_init(&port->port); port->port.ops = &noz_tty_port_ops; tty_dev = tty_register_device(ntty_driver, dc->index_start + i, @@ -1688,13 +1685,6 @@ static int ntty_write(struct tty_struct *tty, const unsigned char *buffer, if (!dc || !port) return -ENODEV; - mutex_lock(&port->tty_sem); - - if (unlikely(!port->port.count)) { - DBG1(" "); - goto exit; - } - rval = kfifo_in(&port->fifo_ul, (unsigned char *)buffer, count); /* notify card */ @@ -1719,7 +1709,6 @@ static int ntty_write(struct tty_struct *tty, const unsigned char *buffer, spin_unlock_irqrestore(&dc->spin_mutex, flags); exit: - mutex_unlock(&port->tty_sem); return rval; } @@ -1738,12 +1727,9 @@ static int ntty_write_room(struct tty_struct *tty) int room = 4096; const struct nozomi *dc = get_dc_by_tty(tty); - if (dc) { - mutex_lock(&port->tty_sem); - if (port->port.count) - room = kfifo_avail(&port->fifo_ul); - mutex_unlock(&port->tty_sem); - } + if (dc) + room = kfifo_avail(&port->fifo_ul); + return room; } @@ -1889,11 +1875,6 @@ static s32 ntty_chars_in_buffer(struct tty_struct *tty) goto exit_in_buffer; } - if (unlikely(!port->port.count)) { - dev_err(&dc->pdev->dev, "No tty open?\n"); - goto exit_in_buffer; - } - rval = kfifo_len(&port->fifo_ul); exit_in_buffer: diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 210774726add..98b6e3bdb000 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/pty.c - * * Copyright (C) 1991, 1992 Linus Torvalds * * Added support for a Unix98-style ptmx device. @@ -295,8 +293,8 @@ static int pty_install(struct tty_driver *driver, struct tty_struct *tty) return -ENOMEM; if (!try_module_get(driver->other->owner)) { /* This cannot in fact currently happen */ - free_tty_struct(o_tty); - return -ENOMEM; + retval = -ENOMEM; + goto err_free_tty; } initialize_tty_struct(o_tty, driver->other, idx); @@ -304,13 +302,11 @@ static int pty_install(struct tty_driver *driver, struct tty_struct *tty) the easy way .. */ retval = tty_init_termios(tty); if (retval) - goto free_mem_out; + goto err_deinit_tty; retval = tty_init_termios(o_tty); - if (retval) { - tty_free_termios(tty); - goto free_mem_out; - } + if (retval) + goto err_free_termios; /* * Everything allocated ... set up the o_tty structure. @@ -327,10 +323,14 @@ static int pty_install(struct tty_driver *driver, struct tty_struct *tty) tty->count++; driver->ttys[idx] = tty; return 0; -free_mem_out: +err_free_termios: + tty_free_termios(tty); +err_deinit_tty: + deinitialize_tty_struct(o_tty); module_put(o_tty->driver->owner); +err_free_tty: free_tty_struct(o_tty); - return -ENOMEM; + return retval; } static int pty_bsd_ioctl(struct tty_struct *tty, @@ -559,20 +559,19 @@ static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) return -ENOMEM; if (!try_module_get(driver->other->owner)) { /* This cannot in fact currently happen */ - free_tty_struct(o_tty); - return -ENOMEM; + goto err_free_tty; } initialize_tty_struct(o_tty, driver->other, idx); tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); if (tty->termios == NULL) - goto free_mem_out; + goto err_free_mem; *tty->termios = driver->init_termios; tty->termios_locked = tty->termios + 1; o_tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); if (o_tty->termios == NULL) - goto free_mem_out; + goto err_free_mem; *o_tty->termios = driver->other->init_termios; o_tty->termios_locked = o_tty->termios + 1; @@ -591,11 +590,13 @@ static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) tty->count++; pty_count++; return 0; -free_mem_out: +err_free_mem: + deinitialize_tty_struct(o_tty); kfree(o_tty->termios); + kfree(tty->termios); module_put(o_tty->driver->owner); +err_free_tty: free_tty_struct(o_tty); - kfree(tty->termios); return -ENOMEM; } diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 036feeb5e3f6..13043e8d37fe 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -1380,7 +1380,6 @@ static void rp_send_xchar(struct tty_struct *tty, char ch) static void rp_throttle(struct tty_struct *tty) { struct r_port *info = tty->driver_data; - CHANNEL_t *cp; #ifdef ROCKET_DEBUG_THROTTLE printk(KERN_INFO "throttle %s: %d....\n", tty->name, @@ -1390,7 +1389,6 @@ static void rp_throttle(struct tty_struct *tty) if (rocket_paranoia_check(info, "rp_throttle")) return; - cp = &info->channel; if (I_IXOFF(tty)) rp_send_xchar(tty, STOP_CHAR(tty)); @@ -1400,7 +1398,6 @@ static void rp_throttle(struct tty_struct *tty) static void rp_unthrottle(struct tty_struct *tty) { struct r_port *info = tty->driver_data; - CHANNEL_t *cp; #ifdef ROCKET_DEBUG_THROTTLE printk(KERN_INFO "unthrottle %s: %d....\n", tty->name, tty->ldisc.chars_in_buffer(tty)); @@ -1409,7 +1406,6 @@ static void rp_unthrottle(struct tty_struct *tty) if (rocket_paranoia_check(info, "rp_throttle")) return; - cp = &info->channel; if (I_IXOFF(tty)) rp_send_xchar(tty, START_CHAR(tty)); @@ -1722,13 +1718,10 @@ static int rp_write_room(struct tty_struct *tty) static int rp_chars_in_buffer(struct tty_struct *tty) { struct r_port *info = tty->driver_data; - CHANNEL_t *cp; if (rocket_paranoia_check(info, "rp_chars_in_buffer")) return 0; - cp = &info->channel; - #ifdef ROCKET_DEBUG_WRITE printk(KERN_INFO "rp_chars_in_buffer returns %d...\n", info->xmit_cnt); #endif @@ -1779,7 +1772,6 @@ static __init int register_PCI(int i, struct pci_dev *dev) { int num_aiops, aiop, max_num_aiops, num_chan, chan; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; - char *str, *board_type; CONTROLLER_t *ctlp; int fast_clock = 0; @@ -1800,7 +1792,6 @@ static __init int register_PCI(int i, struct pci_dev *dev) /* Depending on the model, set up some config variables */ switch (dev->device) { case PCI_DEVICE_ID_RP4QUAD: - str = "Quadcable"; max_num_aiops = 1; ports_per_aiop = 4; rocketModel[i].model = MODEL_RP4QUAD; @@ -1808,42 +1799,36 @@ static __init int register_PCI(int i, struct pci_dev *dev) rocketModel[i].numPorts = 4; break; case PCI_DEVICE_ID_RP8OCTA: - str = "Octacable"; max_num_aiops = 1; rocketModel[i].model = MODEL_RP8OCTA; strcpy(rocketModel[i].modelString, "RocketPort 8 port w/octa cable"); rocketModel[i].numPorts = 8; break; case PCI_DEVICE_ID_URP8OCTA: - str = "Octacable"; max_num_aiops = 1; rocketModel[i].model = MODEL_UPCI_RP8OCTA; strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/octa cable"); rocketModel[i].numPorts = 8; break; case PCI_DEVICE_ID_RP8INTF: - str = "8"; max_num_aiops = 1; rocketModel[i].model = MODEL_RP8INTF; strcpy(rocketModel[i].modelString, "RocketPort 8 port w/external I/F"); rocketModel[i].numPorts = 8; break; case PCI_DEVICE_ID_URP8INTF: - str = "8"; max_num_aiops = 1; rocketModel[i].model = MODEL_UPCI_RP8INTF; strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/external I/F"); rocketModel[i].numPorts = 8; break; case PCI_DEVICE_ID_RP8J: - str = "8J"; max_num_aiops = 1; rocketModel[i].model = MODEL_RP8J; strcpy(rocketModel[i].modelString, "RocketPort 8 port w/RJ11 connectors"); rocketModel[i].numPorts = 8; break; case PCI_DEVICE_ID_RP4J: - str = "4J"; max_num_aiops = 1; ports_per_aiop = 4; rocketModel[i].model = MODEL_RP4J; @@ -1851,56 +1836,48 @@ static __init int register_PCI(int i, struct pci_dev *dev) rocketModel[i].numPorts = 4; break; case PCI_DEVICE_ID_RP8SNI: - str = "8 (DB78 Custom)"; max_num_aiops = 1; rocketModel[i].model = MODEL_RP8SNI; strcpy(rocketModel[i].modelString, "RocketPort 8 port w/ custom DB78"); rocketModel[i].numPorts = 8; break; case PCI_DEVICE_ID_RP16SNI: - str = "16 (DB78 Custom)"; max_num_aiops = 2; rocketModel[i].model = MODEL_RP16SNI; strcpy(rocketModel[i].modelString, "RocketPort 16 port w/ custom DB78"); rocketModel[i].numPorts = 16; break; case PCI_DEVICE_ID_RP16INTF: - str = "16"; max_num_aiops = 2; rocketModel[i].model = MODEL_RP16INTF; strcpy(rocketModel[i].modelString, "RocketPort 16 port w/external I/F"); rocketModel[i].numPorts = 16; break; case PCI_DEVICE_ID_URP16INTF: - str = "16"; max_num_aiops = 2; rocketModel[i].model = MODEL_UPCI_RP16INTF; strcpy(rocketModel[i].modelString, "RocketPort UPCI 16 port w/external I/F"); rocketModel[i].numPorts = 16; break; case PCI_DEVICE_ID_CRP16INTF: - str = "16"; max_num_aiops = 2; rocketModel[i].model = MODEL_CPCI_RP16INTF; strcpy(rocketModel[i].modelString, "RocketPort Compact PCI 16 port w/external I/F"); rocketModel[i].numPorts = 16; break; case PCI_DEVICE_ID_RP32INTF: - str = "32"; max_num_aiops = 4; rocketModel[i].model = MODEL_RP32INTF; strcpy(rocketModel[i].modelString, "RocketPort 32 port w/external I/F"); rocketModel[i].numPorts = 32; break; case PCI_DEVICE_ID_URP32INTF: - str = "32"; max_num_aiops = 4; rocketModel[i].model = MODEL_UPCI_RP32INTF; strcpy(rocketModel[i].modelString, "RocketPort UPCI 32 port w/external I/F"); rocketModel[i].numPorts = 32; break; case PCI_DEVICE_ID_RPP4: - str = "Plus Quadcable"; max_num_aiops = 1; ports_per_aiop = 4; altChanRingIndicator++; @@ -1910,7 +1887,6 @@ static __init int register_PCI(int i, struct pci_dev *dev) rocketModel[i].numPorts = 4; break; case PCI_DEVICE_ID_RPP8: - str = "Plus Octacable"; max_num_aiops = 2; ports_per_aiop = 4; altChanRingIndicator++; @@ -1920,7 +1896,6 @@ static __init int register_PCI(int i, struct pci_dev *dev) rocketModel[i].numPorts = 8; break; case PCI_DEVICE_ID_RP2_232: - str = "Plus 2 (RS-232)"; max_num_aiops = 1; ports_per_aiop = 2; altChanRingIndicator++; @@ -1930,7 +1905,6 @@ static __init int register_PCI(int i, struct pci_dev *dev) rocketModel[i].numPorts = 2; break; case PCI_DEVICE_ID_RP2_422: - str = "Plus 2 (RS-422)"; max_num_aiops = 1; ports_per_aiop = 2; altChanRingIndicator++; @@ -1943,7 +1917,6 @@ static __init int register_PCI(int i, struct pci_dev *dev) max_num_aiops = 1; ports_per_aiop = 6; - str = "6-port"; /* If revision is 1, the rocketmodem flash must be loaded. * If it is 2 it is a "socketed" version. */ @@ -1961,7 +1934,6 @@ static __init int register_PCI(int i, struct pci_dev *dev) case PCI_DEVICE_ID_RP4M: max_num_aiops = 1; ports_per_aiop = 4; - str = "4-port"; if (dev->revision == 1) { rcktpt_type[i] = ROCKET_TYPE_MODEMII; rocketModel[i].loadrm2 = 1; @@ -1974,7 +1946,6 @@ static __init int register_PCI(int i, struct pci_dev *dev) rocketModel[i].numPorts = 4; break; default: - str = "(unknown/unsupported)"; max_num_aiops = 0; break; } @@ -2000,14 +1971,12 @@ static __init int register_PCI(int i, struct pci_dev *dev) if (! (sInW(ConfigIO + _PCI_9030_GPIO_CTRL) & PCI_GPIO_CTRL_8PORT)) { - str = "Quadcable"; ports_per_aiop = 4; rocketModel[i].numPorts = 4; } } break; case PCI_DEVICE_ID_UPCI_RM3_8PORT: - str = "8 ports"; max_num_aiops = 1; rocketModel[i].model = MODEL_UPCI_RM3_8PORT; strcpy(rocketModel[i].modelString, "RocketModem III 8 port"); @@ -2018,7 +1987,6 @@ static __init int register_PCI(int i, struct pci_dev *dev) rcktpt_type[i] = ROCKET_TYPE_MODEMIII; break; case PCI_DEVICE_ID_UPCI_RM3_4PORT: - str = "4 ports"; max_num_aiops = 1; rocketModel[i].model = MODEL_UPCI_RM3_4PORT; strcpy(rocketModel[i].modelString, "RocketModem III 4 port"); @@ -2032,21 +2000,6 @@ static __init int register_PCI(int i, struct pci_dev *dev) break; } - switch (rcktpt_type[i]) { - case ROCKET_TYPE_MODEM: - board_type = "RocketModem"; - break; - case ROCKET_TYPE_MODEMII: - board_type = "RocketModem II"; - break; - case ROCKET_TYPE_MODEMIII: - board_type = "RocketModem III"; - break; - default: - board_type = "RocketPort"; - break; - } - if (fast_clock) { sClockPrescale = 0x12; /* mod 2 (divide by 3) */ rp_baud_base[i] = 921600; diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c index d89aa38c5cf0..1b37626e8f13 100644 --- a/drivers/tty/serial/21285.c +++ b/drivers/tty/serial/21285.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/21285.c - * * Driver for the serial port on the 21285 StrongArm-110 core logic chip. * * Based on drivers/char/serial.c diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 6611535f4440..b40f7b90c81d 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/8250.c - * * Driver for 8250/16550-type serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. @@ -273,7 +271,7 @@ static const struct serial8250_config uart_config[] = { .fifo_size = 32, .tx_loadsz = 32, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_UUE, + .flags = UART_CAP_FIFO | UART_CAP_UUE | UART_CAP_RTOIE, }, [PORT_RM9000] = { .name = "RM9000", @@ -303,6 +301,14 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_AFE, }, + [PORT_TEGRA] = { + .name = "Tegra", + .fifo_size = 32, + .tx_loadsz = 8, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | + UART_FCR_T_TRIG_01, + .flags = UART_CAP_FIFO | UART_CAP_RTOIE, + }, }; #if defined(CONFIG_MIPS_ALCHEMY) @@ -1427,6 +1433,27 @@ static void serial8250_enable_ms(struct uart_port *port) serial_out(up, UART_IER, up->ier); } +/* + * Clear the Tegra rx fifo after a break + * + * FIXME: This needs to become a port specific callback once we have a + * framework for this + */ +static void clear_rx_fifo(struct uart_8250_port *up) +{ + unsigned int status, tmout = 10000; + do { + status = serial_in(up, UART_LSR); + if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) + status = serial_in(up, UART_RX); + else + break; + if (--tmout == 0) + break; + udelay(1); + } while (1); +} + static void receive_chars(struct uart_8250_port *up, unsigned int *status) { @@ -1462,6 +1489,13 @@ receive_chars(struct uart_8250_port *up, unsigned int *status) lsr &= ~(UART_LSR_FE | UART_LSR_PE); up->port.icount.brk++; /* + * If tegra port then clear the rx fifo to + * accept another break/character. + */ + if (up->port.type == PORT_TEGRA) + clear_rx_fifo(up); + + /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask @@ -2405,7 +2439,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, UART_ENABLE_MS(&up->port, termios->c_cflag)) up->ier |= UART_IER_MSI; if (up->capabilities & UART_CAP_UUE) - up->ier |= UART_IER_UUE | UART_IER_RTOIE; + up->ier |= UART_IER_UUE; + if (up->capabilities & UART_CAP_RTOIE) + up->ier |= UART_IER_RTOIE; serial_out(up, UART_IER, up->ier); diff --git a/drivers/tty/serial/8250.h b/drivers/tty/serial/8250.h index 6e19ea3e48d5..6edf4a6a22d4 100644 --- a/drivers/tty/serial/8250.h +++ b/drivers/tty/serial/8250.h @@ -1,6 +1,4 @@ /* - * linux/drivers/char/8250.h - * * Driver for 8250/16550-type serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. @@ -44,6 +42,7 @@ struct serial8250_config { #define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */ #define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */ #define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ +#define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */ #define UART_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_accent.c b/drivers/tty/serial/8250_accent.c index 9c10262f2469..34b51c651192 100644 --- a/drivers/tty/serial/8250_accent.c +++ b/drivers/tty/serial/8250_accent.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/8250_accent.c - * * Copyright (C) 2005 Russell King. * Data taken from include/asm-i386/serial.h * diff --git a/drivers/tty/serial/8250_boca.c b/drivers/tty/serial/8250_boca.c index 3bfe0f7b26fb..d125dc107985 100644 --- a/drivers/tty/serial/8250_boca.c +++ b/drivers/tty/serial/8250_boca.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/8250_boca.c - * * Copyright (C) 2005 Russell King. * Data taken from include/asm-i386/serial.h * diff --git a/drivers/tty/serial/8250_exar_st16c554.c b/drivers/tty/serial/8250_exar_st16c554.c index 567143ace159..bf53aabf9b5e 100644 --- a/drivers/tty/serial/8250_exar_st16c554.c +++ b/drivers/tty/serial/8250_exar_st16c554.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/8250_exar.c - * * Written by Paul B Schroeder < pschroeder "at" uplogix "dot" com > * Based on 8250_boca. * diff --git a/drivers/tty/serial/8250_fourport.c b/drivers/tty/serial/8250_fourport.c index 6375d68b7913..be1582609626 100644 --- a/drivers/tty/serial/8250_fourport.c +++ b/drivers/tty/serial/8250_fourport.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/8250_fourport.c - * * Copyright (C) 2005 Russell King. * Data taken from include/asm-i386/serial.h * diff --git a/drivers/tty/serial/8250_hub6.c b/drivers/tty/serial/8250_hub6.c index 7609150e7d5e..a5c778e83de0 100644 --- a/drivers/tty/serial/8250_hub6.c +++ b/drivers/tty/serial/8250_hub6.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/8250_hub6.c - * * Copyright (C) 2005 Russell King. * Data taken from include/asm-i386/serial.h * diff --git a/drivers/tty/serial/8250_mca.c b/drivers/tty/serial/8250_mca.c index d10be944ad44..d20abf04541e 100644 --- a/drivers/tty/serial/8250_mca.c +++ b/drivers/tty/serial/8250_mca.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/8250_mca.c - * * Copyright (C) 2005 Russell King. * Data taken from include/asm-i386/serial.h * diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 738cec9807d3..4b4968a294b2 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/8250_pci.c - * * Probe module for 8250/16550-type PCI serial ports. * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. @@ -973,6 +971,14 @@ ce4100_serial_setup(struct serial_private *priv, return ret; } +static int +pci_omegapci_setup(struct serial_private *priv, + struct pciserial_board *board, + struct uart_port *port, int idx) +{ + return setup_port(priv, port, 2, idx * 8, 0); +} + static int skip_tx_en_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_port *port, int idx) @@ -1012,6 +1018,8 @@ static int skip_tx_en_setup(struct serial_private *priv, #define PCI_DEVICE_ID_TITAN_200EI 0xA016 #define PCI_DEVICE_ID_TITAN_200EISI 0xA017 #define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 +#define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6 +#define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -1412,7 +1420,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .setup = pci_default_setup, }, /* - * For Oxford Semiconductor and Mainpine + * For Oxford Semiconductor Tornado based devices */ { .vendor = PCI_VENDOR_ID_OXSEMI, @@ -1430,6 +1438,24 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .init = pci_oxsemi_tornado_init, .setup = pci_default_setup, }, + { + .vendor = PCI_VENDOR_ID_DIGI, + .device = PCIE_DEVICE_ID_NEO_2_OX_IBM, + .subvendor = PCI_SUBVENDOR_ID_IBM, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_default_setup, + }, + /* + * Cronyx Omega PCI (PLX-chip based) + */ + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_CRONYX_OMEGA, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_omegapci_setup, + }, /* * Default "match everything" terminator entry */ @@ -1617,6 +1643,7 @@ enum pci_board_num_t { pbn_ADDIDATA_PCIe_4_3906250, pbn_ADDIDATA_PCIe_8_3906250, pbn_ce4100_1_115200, + pbn_omegapci, }; /* @@ -2312,6 +2339,12 @@ static struct pciserial_board pci_boards[] __devinitdata = { .base_baud = 921600, .reg_shift = 2, }, + [pbn_omegapci] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 0x200, + }, }; static const struct pci_device_id softmodem_blacklist[] = { @@ -3075,6 +3108,14 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 8 Port V.34 Super-G3 Fax */ PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0, pbn_oxsemi_8_4000000 }, + + /* + * Digi/IBM PCIe 2-port Async EIA-232 Adapter utilizing OxSemi Tornado + */ + { PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_2_OX_IBM, + PCI_SUBVENDOR_ID_IBM, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + /* * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards, * from skokodyn@yahoo.com @@ -3801,6 +3842,12 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_ce4100_1_115200 }, + /* + * Cronyx Omega PCI + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_omegapci }, /* * These entries match devices with class COMMUNICATION_SERIAL, diff --git a/drivers/tty/serial/8250_pnp.c b/drivers/tty/serial/8250_pnp.c index 4822cb50cd0f..fc301f6722e1 100644 --- a/drivers/tty/serial/8250_pnp.c +++ b/drivers/tty/serial/8250_pnp.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/8250_pnp.c - * * Probe module for 8250/16550-type ISAPNP serial ports. * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 80484af781e1..636144cea932 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -537,7 +537,7 @@ config SERIAL_S3C6400 config SERIAL_S5PV210 tristate "Samsung S5PV210 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442 || CPU_EXYNOS4210) + depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_EXYNOS4210) select SERIAL_SAMSUNG_UARTS_4 if (CPU_S5PV210 || CPU_EXYNOS4210) default y help @@ -1391,6 +1391,14 @@ config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE help Support for Console on the NWP serial ports. +config SERIAL_LANTIQ + bool "Lantiq serial driver" + depends on LANTIQ + select SERIAL_CORE + select SERIAL_CORE_CONSOLE + help + Support for console and UART on Lantiq SoCs. + config SERIAL_QE tristate "Freescale QUICC Engine serial port support" depends on QUICC_ENGINE @@ -1577,7 +1585,7 @@ config SERIAL_IFX6X60 Support for the IFX6x60 modem devices on Intel MID platforms. config SERIAL_PCH_UART - tristate "Intel EG20T PCH UART/OKI SEMICONDUCTOR ML7213 IOH" + tristate "Intel EG20T PCH / OKI SEMICONDUCTOR IOH(ML7213/ML7223) UART" depends on PCI select SERIAL_CORE help @@ -1585,10 +1593,12 @@ config SERIAL_PCH_UART which is an IOH(Input/Output Hub) for x86 embedded processor. Enabling PCH_DMA, this PCH UART works as DMA mode. - This driver also can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/ - Output Hub) which is for IVI(In-Vehicle Infotainment) use. - ML7213 is companion chip for Intel Atom E6xx series. - ML7213 is completely compatible for Intel EG20T PCH. + This driver also can be used for OKI SEMICONDUCTOR IOH(Input/ + Output Hub), ML7213 and ML7223. + ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is + for MP(Media Phone) use. + ML7213/ML7223 is companion chip for Intel Atom E6xx series. + ML7213/ML7223 is completely compatible for Intel EG20T PCH. config SERIAL_MSM_SMD bool "Enable tty device interface for some SMD ports" @@ -1612,4 +1622,17 @@ config SERIAL_MXS_AUART_CONSOLE help Enable a MXS AUART port to be the system console. +config SERIAL_XILINX_PS_UART + tristate "Xilinx PS UART support" + select SERIAL_CORE + help + This driver supports the Xilinx PS UART port. + +config SERIAL_XILINX_PS_UART_CONSOLE + bool "Xilinx PS UART console support" + depends on SERIAL_XILINX_PS_UART=y + select SERIAL_CORE_CONSOLE + help + Enable a Xilinx PS UART port to be the system console. + endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index fee0690ef8e3..cb2628fee4c7 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -94,3 +94,5 @@ obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o obj-$(CONFIG_SERIAL_MSM_SMD) += msm_smd_tty.o obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o +obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o +obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 6d5b036ac783..50bc5a5ac653 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -540,11 +540,14 @@ static int __devinit altera_uart_probe(struct platform_device *pdev) int i = pdev->id; int ret; - /* -1 emphasizes that the platform must have one port, no .N suffix */ - if (i == -1) - i = 0; + /* if id is -1 scan for a free id and use that one */ + if (i == -1) { + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS; i++) + if (altera_uart_ports[i].port.mapbase == 0) + break; + } - if (i >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) + if (i < 0 || i >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) return -EINVAL; port = &altera_uart_ports[i].port; @@ -587,6 +590,8 @@ static int __devinit altera_uart_probe(struct platform_device *pdev) port->ops = &altera_uart_ops; port->flags = UPF_BOOT_AUTOCONF; + dev_set_drvdata(&pdev->dev, port); + uart_add_one_port(&altera_uart_driver, port); return 0; @@ -594,14 +599,13 @@ static int __devinit altera_uart_probe(struct platform_device *pdev) static int __devexit altera_uart_remove(struct platform_device *pdev) { - struct uart_port *port; - int i = pdev->id; + struct uart_port *port = dev_get_drvdata(&pdev->dev); - if (i == -1) - i = 0; - - port = &altera_uart_ports[i].port; - uart_remove_one_port(&altera_uart_driver, port); + if (port) { + uart_remove_one_port(&altera_uart_driver, port); + dev_set_drvdata(&pdev->dev, NULL); + port->mapbase = 0; + } return 0; } diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index d742dd2c525c..c0d10c4ddb73 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/amba.c - * * Driver for AMBA serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 6deee4e546be..8dc0541feecc 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/amba.c - * * Driver for AMBA serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index f119d1761106..652bdac8ce8e 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/atmel_serial.c - * * Driver for Atmel AT91 / AT32 Serial ports * Copyright (C) 2003 Rick Bronson * diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index c3ec0a61d859..891d194ae754 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -296,8 +296,7 @@ static int sport_startup(struct uart_port *port) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED, "BFIN_SPORT_UART_CTS", up)) { up->cts_pin = -1; - dev_info(port->dev, "Unable to attach BlackFin UART \ - over SPORT CTS interrupt. So, disable it.\n"); + dev_info(port->dev, "Unable to attach BlackFin UART over SPORT CTS interrupt. So, disable it.\n"); } } if (up->rts_pin >= 0) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index b6acd19b458e..e6c3dbd781d6 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/clps711x.c - * * Driver for CLPS711x serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. diff --git a/drivers/tty/serial/cpm_uart/cpm_uart.h b/drivers/tty/serial/cpm_uart/cpm_uart.h index b754dcf0fda5..cf34d26ff6cd 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart.h +++ b/drivers/tty/serial/cpm_uart/cpm_uart.h @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/cpm_uart.h - * * Driver for CPM (SCC/SMC) serial ports * * Copyright (C) 2004 Freescale Semiconductor, Inc. diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index a9a6a5fd169e..9488da74d4f7 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/cpm_uart.c - * * Driver for CPM (SCC/SMC) serial ports; core driver * * Based on arch/ppc/cpm2_io/uart.c by Dan Malek diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c index 3fc1d66e32c6..18f79575894a 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/cpm_uart.c - * * Driver for CPM (SCC/SMC) serial ports; CPM1 definitions * * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2) diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h index 10eecd6af6d4..60c7e94cde1e 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/cpm_uart/cpm_uart_cpm1.h - * * Driver for CPM (SCC/SMC) serial ports * * definitions for cpm1 diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c index 814ac006393f..a4927e66e741 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/cpm_uart_cpm2.c - * * Driver for CPM (SCC/SMC) serial ports; CPM2 definitions * * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2) diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h index 7194c63dcf5f..51e651a69938 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/cpm_uart/cpm_uart_cpm2.h - * * Driver for CPM (SCC/SMC) serial ports * * definitions for cpm2 diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 8ee5a41d340d..5315525220fb 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -41,7 +41,6 @@ #include <linux/tty.h> #include <linux/device.h> #include <linux/spi/spi.h> -#include <linux/tty.h> #include <linux/kfifo.h> #include <linux/tty_flip.h> #include <linux/timer.h> @@ -56,7 +55,6 @@ #include <linux/sched.h> #include <linux/time.h> #include <linux/wait.h> -#include <linux/tty.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/spi/ifx_modem.h> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index cb36b0d4ef3c..a54473123e0a 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/imx.c - * * Driver for Motorola IMX serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. @@ -382,12 +380,13 @@ static void imx_start_tx(struct uart_port *port) static irqreturn_t imx_rtsint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS; + unsigned int val; unsigned long flags; spin_lock_irqsave(&sport->port.lock, flags); writel(USR1_RTSD, sport->port.membase + USR1); + val = readl(sport->port.membase + USR1) & USR1_RTSS; uart_handle_cts_change(&sport->port, !!val); wake_up_interruptible(&sport->port.state->port.delta_msr_wait); diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c new file mode 100644 index 000000000000..58cf279ed879 --- /dev/null +++ b/drivers/tty/serial/lantiq.c @@ -0,0 +1,756 @@ +/* + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) 2004 Infineon IFAP DC COM CPE + * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2007 John Crispin <blogic@openwrt.org> + * Copyright (C) 2010 Thomas Langer, <thomas.langer@lantiq.com> + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/device.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/clk.h> + +#include <lantiq_soc.h> + +#define PORT_LTQ_ASC 111 +#define MAXPORTS 2 +#define UART_DUMMY_UER_RX 1 +#define DRVNAME "ltq_asc" +#ifdef __BIG_ENDIAN +#define LTQ_ASC_TBUF (0x0020 + 3) +#define LTQ_ASC_RBUF (0x0024 + 3) +#else +#define LTQ_ASC_TBUF 0x0020 +#define LTQ_ASC_RBUF 0x0024 +#endif +#define LTQ_ASC_FSTAT 0x0048 +#define LTQ_ASC_WHBSTATE 0x0018 +#define LTQ_ASC_STATE 0x0014 +#define LTQ_ASC_IRNCR 0x00F8 +#define LTQ_ASC_CLC 0x0000 +#define LTQ_ASC_ID 0x0008 +#define LTQ_ASC_PISEL 0x0004 +#define LTQ_ASC_TXFCON 0x0044 +#define LTQ_ASC_RXFCON 0x0040 +#define LTQ_ASC_CON 0x0010 +#define LTQ_ASC_BG 0x0050 +#define LTQ_ASC_IRNREN 0x00F4 + +#define ASC_IRNREN_TX 0x1 +#define ASC_IRNREN_RX 0x2 +#define ASC_IRNREN_ERR 0x4 +#define ASC_IRNREN_TX_BUF 0x8 +#define ASC_IRNCR_TIR 0x1 +#define ASC_IRNCR_RIR 0x2 +#define ASC_IRNCR_EIR 0x4 + +#define ASCOPT_CSIZE 0x3 +#define TXFIFO_FL 1 +#define RXFIFO_FL 1 +#define ASCCLC_DISS 0x2 +#define ASCCLC_RMCMASK 0x0000FF00 +#define ASCCLC_RMCOFFSET 8 +#define ASCCON_M_8ASYNC 0x0 +#define ASCCON_M_7ASYNC 0x2 +#define ASCCON_ODD 0x00000020 +#define ASCCON_STP 0x00000080 +#define ASCCON_BRS 0x00000100 +#define ASCCON_FDE 0x00000200 +#define ASCCON_R 0x00008000 +#define ASCCON_FEN 0x00020000 +#define ASCCON_ROEN 0x00080000 +#define ASCCON_TOEN 0x00100000 +#define ASCSTATE_PE 0x00010000 +#define ASCSTATE_FE 0x00020000 +#define ASCSTATE_ROE 0x00080000 +#define ASCSTATE_ANY (ASCSTATE_ROE|ASCSTATE_PE|ASCSTATE_FE) +#define ASCWHBSTATE_CLRREN 0x00000001 +#define ASCWHBSTATE_SETREN 0x00000002 +#define ASCWHBSTATE_CLRPE 0x00000004 +#define ASCWHBSTATE_CLRFE 0x00000008 +#define ASCWHBSTATE_CLRROE 0x00000020 +#define ASCTXFCON_TXFEN 0x0001 +#define ASCTXFCON_TXFFLU 0x0002 +#define ASCTXFCON_TXFITLMASK 0x3F00 +#define ASCTXFCON_TXFITLOFF 8 +#define ASCRXFCON_RXFEN 0x0001 +#define ASCRXFCON_RXFFLU 0x0002 +#define ASCRXFCON_RXFITLMASK 0x3F00 +#define ASCRXFCON_RXFITLOFF 8 +#define ASCFSTAT_RXFFLMASK 0x003F +#define ASCFSTAT_TXFFLMASK 0x3F00 +#define ASCFSTAT_TXFREEMASK 0x3F000000 +#define ASCFSTAT_TXFREEOFF 24 + +static void lqasc_tx_chars(struct uart_port *port); +static struct ltq_uart_port *lqasc_port[MAXPORTS]; +static struct uart_driver lqasc_reg; +static DEFINE_SPINLOCK(ltq_asc_lock); + +struct ltq_uart_port { + struct uart_port port; + struct clk *clk; + unsigned int tx_irq; + unsigned int rx_irq; + unsigned int err_irq; +}; + +static inline struct +ltq_uart_port *to_ltq_uart_port(struct uart_port *port) +{ + return container_of(port, struct ltq_uart_port, port); +} + +static void +lqasc_stop_tx(struct uart_port *port) +{ + return; +} + +static void +lqasc_start_tx(struct uart_port *port) +{ + unsigned long flags; + spin_lock_irqsave(<q_asc_lock, flags); + lqasc_tx_chars(port); + spin_unlock_irqrestore(<q_asc_lock, flags); + return; +} + +static void +lqasc_stop_rx(struct uart_port *port) +{ + ltq_w32(ASCWHBSTATE_CLRREN, port->membase + LTQ_ASC_WHBSTATE); +} + +static void +lqasc_enable_ms(struct uart_port *port) +{ +} + +static int +lqasc_rx_chars(struct uart_port *port) +{ + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + unsigned int ch = 0, rsr = 0, fifocnt; + + if (!tty) { + dev_dbg(port->dev, "%s:tty is busy now", __func__); + return -EBUSY; + } + fifocnt = + ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK; + while (fifocnt--) { + u8 flag = TTY_NORMAL; + ch = ltq_r8(port->membase + LTQ_ASC_RBUF); + rsr = (ltq_r32(port->membase + LTQ_ASC_STATE) + & ASCSTATE_ANY) | UART_DUMMY_UER_RX; + tty_flip_buffer_push(tty); + port->icount.rx++; + + /* + * Note that the error handling code is + * out of the main execution path + */ + if (rsr & ASCSTATE_ANY) { + if (rsr & ASCSTATE_PE) { + port->icount.parity++; + ltq_w32_mask(0, ASCWHBSTATE_CLRPE, + port->membase + LTQ_ASC_WHBSTATE); + } else if (rsr & ASCSTATE_FE) { + port->icount.frame++; + ltq_w32_mask(0, ASCWHBSTATE_CLRFE, + port->membase + LTQ_ASC_WHBSTATE); + } + if (rsr & ASCSTATE_ROE) { + port->icount.overrun++; + ltq_w32_mask(0, ASCWHBSTATE_CLRROE, + port->membase + LTQ_ASC_WHBSTATE); + } + + rsr &= port->read_status_mask; + + if (rsr & ASCSTATE_PE) + flag = TTY_PARITY; + else if (rsr & ASCSTATE_FE) + flag = TTY_FRAME; + } + + if ((rsr & port->ignore_status_mask) == 0) + tty_insert_flip_char(tty, ch, flag); + + if (rsr & ASCSTATE_ROE) + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character + */ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + if (ch != 0) + tty_flip_buffer_push(tty); + tty_kref_put(tty); + return 0; +} + +static void +lqasc_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + if (uart_tx_stopped(port)) { + lqasc_stop_tx(port); + return; + } + + while (((ltq_r32(port->membase + LTQ_ASC_FSTAT) & + ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF) != 0) { + if (port->x_char) { + ltq_w8(port->x_char, port->membase + LTQ_ASC_TBUF); + port->icount.tx++; + port->x_char = 0; + continue; + } + + if (uart_circ_empty(xmit)) + break; + + ltq_w8(port->state->xmit.buf[port->state->xmit.tail], + port->membase + LTQ_ASC_TBUF); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static irqreturn_t +lqasc_tx_int(int irq, void *_port) +{ + unsigned long flags; + struct uart_port *port = (struct uart_port *)_port; + spin_lock_irqsave(<q_asc_lock, flags); + ltq_w32(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR); + spin_unlock_irqrestore(<q_asc_lock, flags); + lqasc_start_tx(port); + return IRQ_HANDLED; +} + +static irqreturn_t +lqasc_err_int(int irq, void *_port) +{ + unsigned long flags; + struct uart_port *port = (struct uart_port *)_port; + spin_lock_irqsave(<q_asc_lock, flags); + /* clear any pending interrupts */ + ltq_w32_mask(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE | + ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE); + spin_unlock_irqrestore(<q_asc_lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t +lqasc_rx_int(int irq, void *_port) +{ + unsigned long flags; + struct uart_port *port = (struct uart_port *)_port; + spin_lock_irqsave(<q_asc_lock, flags); + ltq_w32(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR); + lqasc_rx_chars(port); + spin_unlock_irqrestore(<q_asc_lock, flags); + return IRQ_HANDLED; +} + +static unsigned int +lqasc_tx_empty(struct uart_port *port) +{ + int status; + status = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_TXFFLMASK; + return status ? 0 : TIOCSER_TEMT; +} + +static unsigned int +lqasc_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_CAR | TIOCM_DSR; +} + +static void +lqasc_set_mctrl(struct uart_port *port, u_int mctrl) +{ +} + +static void +lqasc_break_ctl(struct uart_port *port, int break_state) +{ +} + +static int +lqasc_startup(struct uart_port *port) +{ + struct ltq_uart_port *ltq_port = to_ltq_uart_port(port); + int retval; + + port->uartclk = clk_get_rate(ltq_port->clk); + + ltq_w32_mask(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET), + port->membase + LTQ_ASC_CLC); + + ltq_w32(0, port->membase + LTQ_ASC_PISEL); + ltq_w32( + ((TXFIFO_FL << ASCTXFCON_TXFITLOFF) & ASCTXFCON_TXFITLMASK) | + ASCTXFCON_TXFEN | ASCTXFCON_TXFFLU, + port->membase + LTQ_ASC_TXFCON); + ltq_w32( + ((RXFIFO_FL << ASCRXFCON_RXFITLOFF) & ASCRXFCON_RXFITLMASK) + | ASCRXFCON_RXFEN | ASCRXFCON_RXFFLU, + port->membase + LTQ_ASC_RXFCON); + /* make sure other settings are written to hardware before + * setting enable bits + */ + wmb(); + ltq_w32_mask(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN | + ASCCON_ROEN, port->membase + LTQ_ASC_CON); + + retval = request_irq(ltq_port->tx_irq, lqasc_tx_int, + IRQF_DISABLED, "asc_tx", port); + if (retval) { + pr_err("failed to request lqasc_tx_int\n"); + return retval; + } + + retval = request_irq(ltq_port->rx_irq, lqasc_rx_int, + IRQF_DISABLED, "asc_rx", port); + if (retval) { + pr_err("failed to request lqasc_rx_int\n"); + goto err1; + } + + retval = request_irq(ltq_port->err_irq, lqasc_err_int, + IRQF_DISABLED, "asc_err", port); + if (retval) { + pr_err("failed to request lqasc_err_int\n"); + goto err2; + } + + ltq_w32(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX, + port->membase + LTQ_ASC_IRNREN); + return 0; + +err2: + free_irq(ltq_port->rx_irq, port); +err1: + free_irq(ltq_port->tx_irq, port); + return retval; +} + +static void +lqasc_shutdown(struct uart_port *port) +{ + struct ltq_uart_port *ltq_port = to_ltq_uart_port(port); + free_irq(ltq_port->tx_irq, port); + free_irq(ltq_port->rx_irq, port); + free_irq(ltq_port->err_irq, port); + + ltq_w32(0, port->membase + LTQ_ASC_CON); + ltq_w32_mask(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU, + port->membase + LTQ_ASC_RXFCON); + ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU, + port->membase + LTQ_ASC_TXFCON); +} + +static void +lqasc_set_termios(struct uart_port *port, + struct ktermios *new, struct ktermios *old) +{ + unsigned int cflag; + unsigned int iflag; + unsigned int divisor; + unsigned int baud; + unsigned int con = 0; + unsigned long flags; + + cflag = new->c_cflag; + iflag = new->c_iflag; + + switch (cflag & CSIZE) { + case CS7: + con = ASCCON_M_7ASYNC; + break; + + case CS5: + case CS6: + default: + new->c_cflag &= ~ CSIZE; + new->c_cflag |= CS8; + con = ASCCON_M_8ASYNC; + break; + } + + cflag &= ~CMSPAR; /* Mark/Space parity is not supported */ + + if (cflag & CSTOPB) + con |= ASCCON_STP; + + if (cflag & PARENB) { + if (!(cflag & PARODD)) + con &= ~ASCCON_ODD; + else + con |= ASCCON_ODD; + } + + port->read_status_mask = ASCSTATE_ROE; + if (iflag & INPCK) + port->read_status_mask |= ASCSTATE_FE | ASCSTATE_PE; + + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= ASCSTATE_FE | ASCSTATE_PE; + + if (iflag & IGNBRK) { + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (iflag & IGNPAR) + port->ignore_status_mask |= ASCSTATE_ROE; + } + + if ((cflag & CREAD) == 0) + port->ignore_status_mask |= UART_DUMMY_UER_RX; + + /* set error signals - framing, parity and overrun, enable receiver */ + con |= ASCCON_FEN | ASCCON_TOEN | ASCCON_ROEN; + + spin_lock_irqsave(<q_asc_lock, flags); + + /* set up CON */ + ltq_w32_mask(0, con, port->membase + LTQ_ASC_CON); + + /* Set baud rate - take a divider of 2 into account */ + baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); + divisor = uart_get_divisor(port, baud); + divisor = divisor / 2 - 1; + + /* disable the baudrate generator */ + ltq_w32_mask(ASCCON_R, 0, port->membase + LTQ_ASC_CON); + + /* make sure the fractional divider is off */ + ltq_w32_mask(ASCCON_FDE, 0, port->membase + LTQ_ASC_CON); + + /* set up to use divisor of 2 */ + ltq_w32_mask(ASCCON_BRS, 0, port->membase + LTQ_ASC_CON); + + /* now we can write the new baudrate into the register */ + ltq_w32(divisor, port->membase + LTQ_ASC_BG); + + /* turn the baudrate generator back on */ + ltq_w32_mask(0, ASCCON_R, port->membase + LTQ_ASC_CON); + + /* enable rx */ + ltq_w32(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE); + + spin_unlock_irqrestore(<q_asc_lock, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(new)) + tty_termios_encode_baud_rate(new, baud, baud); +} + +static const char* +lqasc_type(struct uart_port *port) +{ + if (port->type == PORT_LTQ_ASC) + return DRVNAME; + else + return NULL; +} + +static void +lqasc_release_port(struct uart_port *port) +{ + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } +} + +static int +lqasc_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *res; + int size; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot obtain I/O memory region"); + return -ENODEV; + } + size = resource_size(res); + + res = devm_request_mem_region(&pdev->dev, res->start, + size, dev_name(&pdev->dev)); + if (!res) { + dev_err(&pdev->dev, "cannot request I/O memory region"); + return -EBUSY; + } + + if (port->flags & UPF_IOREMAP) { + port->membase = devm_ioremap_nocache(&pdev->dev, + port->mapbase, size); + if (port->membase == NULL) + return -ENOMEM; + } + return 0; +} + +static void +lqasc_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_LTQ_ASC; + lqasc_request_port(port); + } +} + +static int +lqasc_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_LTQ_ASC) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops lqasc_pops = { + .tx_empty = lqasc_tx_empty, + .set_mctrl = lqasc_set_mctrl, + .get_mctrl = lqasc_get_mctrl, + .stop_tx = lqasc_stop_tx, + .start_tx = lqasc_start_tx, + .stop_rx = lqasc_stop_rx, + .enable_ms = lqasc_enable_ms, + .break_ctl = lqasc_break_ctl, + .startup = lqasc_startup, + .shutdown = lqasc_shutdown, + .set_termios = lqasc_set_termios, + .type = lqasc_type, + .release_port = lqasc_release_port, + .request_port = lqasc_request_port, + .config_port = lqasc_config_port, + .verify_port = lqasc_verify_port, +}; + +static void +lqasc_console_putchar(struct uart_port *port, int ch) +{ + int fifofree; + + if (!port->membase) + return; + + do { + fifofree = (ltq_r32(port->membase + LTQ_ASC_FSTAT) + & ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF; + } while (fifofree == 0); + ltq_w8(ch, port->membase + LTQ_ASC_TBUF); +} + + +static void +lqasc_console_write(struct console *co, const char *s, u_int count) +{ + struct ltq_uart_port *ltq_port; + struct uart_port *port; + unsigned long flags; + + if (co->index >= MAXPORTS) + return; + + ltq_port = lqasc_port[co->index]; + if (!ltq_port) + return; + + port = <q_port->port; + + spin_lock_irqsave(<q_asc_lock, flags); + uart_console_write(port, s, count, lqasc_console_putchar); + spin_unlock_irqrestore(<q_asc_lock, flags); +} + +static int __init +lqasc_console_setup(struct console *co, char *options) +{ + struct ltq_uart_port *ltq_port; + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= MAXPORTS) + return -ENODEV; + + ltq_port = lqasc_port[co->index]; + if (!ltq_port) + return -ENODEV; + + port = <q_port->port; + + port->uartclk = clk_get_rate(ltq_port->clk); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console lqasc_console = { + .name = "ttyLTQ", + .write = lqasc_console_write, + .device = uart_console_device, + .setup = lqasc_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &lqasc_reg, +}; + +static int __init +lqasc_console_init(void) +{ + register_console(&lqasc_console); + return 0; +} +console_initcall(lqasc_console_init); + +static struct uart_driver lqasc_reg = { + .owner = THIS_MODULE, + .driver_name = DRVNAME, + .dev_name = "ttyLTQ", + .major = 0, + .minor = 0, + .nr = MAXPORTS, + .cons = &lqasc_console, +}; + +static int __init +lqasc_probe(struct platform_device *pdev) +{ + struct ltq_uart_port *ltq_port; + struct uart_port *port; + struct resource *mmres, *irqres; + int tx_irq, rx_irq, err_irq; + struct clk *clk; + int ret; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mmres || !irqres) + return -ENODEV; + + if (pdev->id >= MAXPORTS) + return -EBUSY; + + if (lqasc_port[pdev->id] != NULL) + return -EBUSY; + + clk = clk_get(&pdev->dev, "fpi"); + if (IS_ERR(clk)) { + pr_err("failed to get fpi clk\n"); + return -ENOENT; + } + + tx_irq = platform_get_irq_byname(pdev, "tx"); + rx_irq = platform_get_irq_byname(pdev, "rx"); + err_irq = platform_get_irq_byname(pdev, "err"); + if ((tx_irq < 0) | (rx_irq < 0) | (err_irq < 0)) + return -ENODEV; + + ltq_port = kzalloc(sizeof(struct ltq_uart_port), GFP_KERNEL); + if (!ltq_port) + return -ENOMEM; + + port = <q_port->port; + + port->iotype = SERIAL_IO_MEM; + port->flags = ASYNC_BOOT_AUTOCONF | UPF_IOREMAP; + port->ops = &lqasc_pops; + port->fifosize = 16; + port->type = PORT_LTQ_ASC, + port->line = pdev->id; + port->dev = &pdev->dev; + + port->irq = tx_irq; /* unused, just to be backward-compatibe */ + port->mapbase = mmres->start; + + ltq_port->clk = clk; + + ltq_port->tx_irq = tx_irq; + ltq_port->rx_irq = rx_irq; + ltq_port->err_irq = err_irq; + + lqasc_port[pdev->id] = ltq_port; + platform_set_drvdata(pdev, ltq_port); + + ret = uart_add_one_port(&lqasc_reg, port); + + return ret; +} + +static struct platform_driver lqasc_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +int __init +init_lqasc(void) +{ + int ret; + + ret = uart_register_driver(&lqasc_reg); + if (ret != 0) + return ret; + + ret = platform_driver_probe(&lqasc_driver, lqasc_probe); + if (ret != 0) + uart_unregister_driver(&lqasc_reg); + + return ret; +} + +module_init(init_lqasc); + +MODULE_DESCRIPTION("Lantiq serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index c111f36f5d21..cab52f4a88b0 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -49,8 +49,8 @@ static int hsu_dma_enable; module_param(hsu_dma_enable, int, 0); -MODULE_PARM_DESC(hsu_dma_enable, "It is a bitmap to set working mode, if \ -bit[x] is 1, then port[x] will work in DMA mode, otherwise in PIO mode."); +MODULE_PARM_DESC(hsu_dma_enable, + "It is a bitmap to set working mode, if bit[x] is 1, then port[x] will work in DMA mode, otherwise in PIO mode."); struct hsu_dma_buffer { u8 *buf; diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 2f548af4e98a..1bd28450ca40 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -56,7 +56,7 @@ struct uart_max3110 { wait_queue_head_t wq; struct task_struct *main_thread; struct task_struct *read_thread; - struct mutex thread_mutex;; + struct mutex thread_mutex; u32 baud; u16 cur_conf; diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index bfee9b4c6661..e6ba83876508 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1,5 +1,5 @@ /* - * drivers/serial/msm_serial.c - driver for msm7k serial device and console + * Driver for msm7k serial device and console * * Copyright (C) 2007 Google, Inc. * Author: Robert Love <rlove@google.com> diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index 9b8dc5d0d855..e4acef5de77e 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -1,6 +1,4 @@ /* - * drivers/serial/msm_serial.h - * * Copyright (C) 2007 Google, Inc. * Author: Robert Love <rlove@google.com> * Copyright (c) 2011, Code Aurora Forum. All rights reserved. diff --git a/drivers/tty/serial/msm_smd_tty.c b/drivers/tty/serial/msm_smd_tty.c index beeff1e86093..4f41dcdcb771 100644 --- a/drivers/tty/serial/msm_smd_tty.c +++ b/drivers/tty/serial/msm_smd_tty.c @@ -1,5 +1,4 @@ -/* drivers/tty/serial/msm_smd_tty.c - * +/* * Copyright (C) 2007 Google, Inc. * Copyright (c) 2011, Code Aurora Forum. All rights reserved. * Author: Brian Swetland <swetland@google.com> diff --git a/drivers/tty/serial/netx-serial.c b/drivers/tty/serial/netx-serial.c index 7735c9f35fa0..d40da78e7c85 100644 --- a/drivers/tty/serial/netx-serial.c +++ b/drivers/tty/serial/netx-serial.c @@ -1,6 +1,4 @@ /* - * drivers/serial/netx-serial.c - * * Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 0e8eec516df4..c911b2419abb 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -80,14 +80,17 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, /* * Try to register a serial port */ +static struct of_device_id of_platform_serial_table[]; static int __devinit of_platform_serial_probe(struct platform_device *ofdev) { + const struct of_device_id *match; struct of_serial_info *info; struct uart_port port; int port_type; int ret; - if (!ofdev->dev.of_match) + match = of_match_device(of_platform_serial_table, &ofdev->dev); + if (!match) return -EINVAL; if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL)) @@ -97,7 +100,7 @@ static int __devinit of_platform_serial_probe(struct platform_device *ofdev) if (info == NULL) return -ENOMEM; - port_type = (unsigned long)ofdev->dev.of_match->data; + port_type = (unsigned long)match->data; ret = of_platform_serial_setup(ofdev, port_type, &port); if (ret) goto out; diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 26403b8e4b9b..c63d0d152af6 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -253,6 +253,8 @@ enum pch_uart_num_t { pch_ml7213_uart0, pch_ml7213_uart1, pch_ml7213_uart2, + pch_ml7223_uart0, + pch_ml7223_uart1, }; static struct pch_uart_driver_data drv_dat[] = { @@ -263,6 +265,8 @@ static struct pch_uart_driver_data drv_dat[] = { [pch_ml7213_uart0] = {PCH_UART_8LINE, 0}, [pch_ml7213_uart1] = {PCH_UART_2LINE, 1}, [pch_ml7213_uart2] = {PCH_UART_2LINE, 2}, + [pch_ml7223_uart0] = {PCH_UART_8LINE, 0}, + [pch_ml7223_uart1] = {PCH_UART_2LINE, 1}, }; static unsigned int default_baud = 9600; @@ -1534,6 +1538,10 @@ static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = { .driver_data = pch_ml7213_uart1}, {PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8029), .driver_data = pch_ml7213_uart2}, + {PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x800C), + .driver_data = pch_ml7223_uart0}, + {PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x800D), + .driver_data = pch_ml7223_uart1}, {0,}, }; diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index e1c8d4f1ce58..5acd24a27d08 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/pmac_zilog.c - * * Driver for PowerMac Z85c30 based ESCC cell found in the * "macio" ASICs of various PowerMac models * diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 1102a39b44f5..4302e6e3768e 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -1,6 +1,4 @@ /* - * linux/drivers/serial/pxa.c - * * Based on drivers/serial/8250.c by Russell King. * * Author: Nicolas Pitre diff --git a/drivers/tty/serial/s3c2400.c b/drivers/tty/serial/s3c2400.c index fed1a9a1ffb4..d13051b3df87 100644 --- a/drivers/tty/serial/s3c2400.c +++ b/drivers/tty/serial/s3c2400.c @@ -1,5 +1,4 @@ -/* linux/drivers/serial/s3c240.c - * +/* * Driver for Samsung SoC onboard UARTs. * * Ben Dooks, Copyright (c) 2003-2005 Simtec Electronics diff --git a/drivers/tty/serial/s3c2410.c b/drivers/tty/serial/s3c2410.c index 73f089d3efd6..bffe6ff9b158 100644 --- a/drivers/tty/serial/s3c2410.c +++ b/drivers/tty/serial/s3c2410.c @@ -1,5 +1,4 @@ -/* linux/drivers/serial/s3c2410.c - * +/* * Driver for Samsung S3C2410 SoC onboard UARTs. * * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics diff --git a/drivers/tty/serial/s3c2412.c b/drivers/tty/serial/s3c2412.c index 1700b1a2fb7e..7e2b9504a687 100644 --- a/drivers/tty/serial/s3c2412.c +++ b/drivers/tty/serial/s3c2412.c @@ -1,5 +1,4 @@ -/* linux/drivers/serial/s3c2412.c - * +/* * Driver for Samsung S3C2412 and S3C2413 SoC onboard UARTs. * * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics diff --git a/drivers/tty/serial/s3c2440.c b/drivers/tty/serial/s3c2440.c index 094cc3904b13..9e10d415d5fd 100644 --- a/drivers/tty/serial/s3c2440.c +++ b/drivers/tty/serial/s3c2440.c @@ -1,5 +1,4 @@ -/* linux/drivers/serial/s3c2440.c - * +/* * Driver for Samsung S3C2440 and S3C2442 SoC onboard UARTs. * * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics diff --git a/drivers/tty/serial/s3c24a0.c b/drivers/tty/serial/s3c24a0.c index fad6083ca427..914eff22e499 100644 --- a/drivers/tty/serial/s3c24a0.c +++ b/drivers/tty/serial/s3c24a0.c @@ -1,5 +1,4 @@ -/* linux/drivers/serial/s3c24a0.c - * +/* * Driver for Samsung S3C24A0 SoC onboard UARTs. * * Based on drivers/serial/s3c2410.c diff --git a/drivers/tty/serial/s3c6400.c b/drivers/tty/serial/s3c6400.c index 4be92ab50058..ded26c42ff37 100644 --- a/drivers/tty/serial/s3c6400.c +++ b/drivers/tty/serial/s3c6400.c @@ -1,5 +1,4 @@ -/* linux/drivers/serial/s3c6400.c - * +/* * Driver for Samsung S3C6400 and S3C6410 SoC onboard UARTs. * * Copyright 2008 Openmoko, Inc. diff --git a/drivers/tty/serial/s5pv210.c b/drivers/tty/serial/s5pv210.c index 6ebccd70a707..fb2619f93d84 100644 --- a/drivers/tty/serial/s5pv210.c +++ b/drivers/tty/serial/s5pv210.c @@ -1,5 +1,4 @@ -/* linux/drivers/serial/s5pv210.c - * +/* * Copyright (c) 2010 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index 2199d819a987..ef7a21a6a01b 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/sa1100.c - * * Driver for SA11x0 serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 9e2fa8d784e2..f66f64829303 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1,5 +1,4 @@ -/* linux/drivers/serial/samsuing.c - * +/* * Driver core for Samsung SoC onboard UARTs. * * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index 0ac06a07d25f..5b098cd76040 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -1,5 +1,4 @@ -/* linux/drivers/serial/samsung.h - * +/* * Driver for Samsung SoC onboard UARTs. * * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c index 602d9845c52f..ea2340b814e9 100644 --- a/drivers/tty/serial/sb1250-duart.c +++ b/drivers/tty/serial/sb1250-duart.c @@ -1,6 +1,4 @@ /* - * drivers/serial/sb1250-duart.c - * * Support for the asynchronous serial interface (DUART) included * in the BCM1250 and derived System-On-a-Chip (SOC) devices. * diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 733fe8e73f0f..db7912cb7ae0 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/core.c - * * Driver core for serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. @@ -172,12 +170,16 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, int in retval = uport->ops->startup(uport); if (retval == 0) { - if (init_hw) { - /* - * Initialise the hardware port settings. - */ - uart_change_speed(tty, state, NULL); + if (uart_console(uport) && uport->cons->cflag) { + tty->termios->c_cflag = uport->cons->cflag; + uport->cons->cflag = 0; + } + /* + * Initialise the hardware port settings. + */ + uart_change_speed(tty, state, NULL); + if (init_hw) { /* * Setup the RTS and DTR signals once the * port is open and ready to respond. @@ -1240,17 +1242,6 @@ static void uart_set_termios(struct tty_struct *tty, } spin_unlock_irqrestore(&state->uart_port->lock, flags); } -#if 0 - /* - * No need to wake up processes in open wait, since they - * sample the CLOCAL flag once, and don't recheck it. - * XXX It's not clear whether the current behavior is correct - * or not. Hence, this may change..... - */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&state->uart_port.open_wait); -#endif } /* @@ -1423,7 +1414,6 @@ static void __uart_wait_until_sent(struct uart_port *port, int timeout) if (time_after(jiffies, expire)) break; } - set_current_state(TASK_RUNNING); /* might not be needed */ } static void uart_wait_until_sent(struct tty_struct *tty, int timeout) @@ -1466,45 +1456,6 @@ static void uart_hangup(struct tty_struct *tty) mutex_unlock(&port->mutex); } -/** - * uart_update_termios - update the terminal hw settings - * @tty: tty associated with UART - * @state: UART to update - * - * Copy across the serial console cflag setting into the termios settings - * for the initial open of the port. This allows continuity between the - * kernel settings, and the settings init adopts when it opens the port - * for the first time. - */ -static void uart_update_termios(struct tty_struct *tty, - struct uart_state *state) -{ - struct uart_port *port = state->uart_port; - - if (uart_console(port) && port->cons->cflag) { - tty->termios->c_cflag = port->cons->cflag; - port->cons->cflag = 0; - } - - /* - * If the device failed to grab its irq resources, - * or some other error occurred, don't try to talk - * to the port hardware. - */ - if (!(tty->flags & (1 << TTY_IO_ERROR))) { - /* - * Make termios settings take effect. - */ - uart_change_speed(tty, state, NULL); - - /* - * And finally enable the RTS and DTR signals. - */ - if (tty->termios->c_cflag & CBAUD) - uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); - } -} - static int uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); @@ -1524,16 +1475,8 @@ static void uart_dtr_rts(struct tty_port *port, int onoff) struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = state->uart_port; - if (onoff) { + if (onoff) uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); - - /* - * If this is the first open to succeed, - * adjust things to suit. - */ - if (!test_and_set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags)) - uart_update_termios(port->tty, state); - } else uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); } @@ -1586,15 +1529,6 @@ static int uart_open(struct tty_struct *tty, struct file *filp) pr_debug("uart_open(%d) called\n", line); /* - * tty->driver->num won't change, so we won't fail here with - * tty->driver_data set to something non-NULL (and therefore - * we won't get caught by uart_close()). - */ - retval = -ENODEV; - if (line >= tty->driver->num) - goto fail; - - /* * We take the semaphore inside uart_get to guarantee that we won't * be re-entered while allocating the state structure, or while we * request any IRQs that the driver may need. This also has the nice @@ -1972,13 +1906,9 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) struct tty_port *port = &state->port; struct device *tty_dev; struct uart_match match = {uport, drv}; - struct tty_struct *tty; mutex_lock(&port->mutex); - /* Must be inside the mutex lock until we convert to tty_port */ - tty = port->tty; - tty_dev = device_find_child(uport->dev, &match, serial_match_port); if (device_may_wakeup(tty_dev)) { if (!enable_irq_wake(uport->irq)) diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c index b1962025b1aa..2430319f2f52 100644 --- a/drivers/tty/serial/serial_ks8695.c +++ b/drivers/tty/serial/serial_ks8695.c @@ -1,6 +1,4 @@ /* - * drivers/serial/serial_ks8695.c - * * Driver for KS8695 serial ports * * Based on drivers/serial/serial_amba.c, by Kam Lee. diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index c50e9fbbf743..8e3fc1944e6d 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -1,6 +1,4 @@ /* - * drivers/serial/serial_txx9.c - * * Derived from many drivers using generic_serial interface, * especially serial_tx3912.c by Steven J. Hill and r39xx_serial.c * (was in Linux/VR tree) by Jim Pick. diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 920a6f929c8b..ebd8629c108d 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1,6 +1,4 @@ /* - * drivers/serial/sh-sci.c - * * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) * * Copyright (C) 2002 - 2011 Paul Mundt @@ -43,6 +41,7 @@ #include <linux/platform_device.h> #include <linux/serial_sci.h> #include <linux/notifier.h> +#include <linux/pm_runtime.h> #include <linux/cpufreq.h> #include <linux/clk.h> #include <linux/ctype.h> @@ -562,6 +561,9 @@ static void sci_break_timer(unsigned long data) { struct sci_port *port = (struct sci_port *)data; + if (port->enable) + port->enable(&port->port); + if (sci_rxd_in(&port->port) == 0) { port->break_flag = 1; sci_schedule_break_timer(port); @@ -571,6 +573,9 @@ static void sci_break_timer(unsigned long data) sci_schedule_break_timer(port); } else port->break_flag = 0; + + if (port->disable) + port->disable(&port->port); } static int sci_handle_errors(struct uart_port *port) @@ -839,6 +844,8 @@ static void sci_clk_enable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); + pm_runtime_get_sync(port->dev); + clk_enable(sci_port->iclk); sci_port->port.uartclk = clk_get_rate(sci_port->iclk); clk_enable(sci_port->fclk); @@ -850,6 +857,8 @@ static void sci_clk_disable(struct uart_port *port) clk_disable(sci_port->fclk); clk_disable(sci_port->iclk); + + pm_runtime_put_sync(port->dev); } static int sci_request_irq(struct sci_port *port) @@ -1758,6 +1767,8 @@ static int __devinit sci_init_single(struct platform_device *dev, sci_port->enable = sci_clk_enable; sci_port->disable = sci_clk_disable; port->dev = &dev->dev; + + pm_runtime_enable(&dev->dev); } sci_port->break_timer.data = (unsigned long)sci_port; @@ -1777,7 +1788,7 @@ static int __devinit sci_init_single(struct platform_device *dev, * * For the muxed case there's nothing more to do. */ - port->irq = p->irqs[SCIx_TXI_IRQ]; + port->irq = p->irqs[SCIx_RXI_IRQ]; if (p->dma_dev) dev_dbg(port->dev, "DMA device %p, tx %d, rx %d\n", @@ -1938,6 +1949,7 @@ static int sci_remove(struct platform_device *dev) clk_put(port->iclk); clk_put(port->fclk); + pm_runtime_disable(&dev->dev); return 0; } diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 5fefed53fa42..b04d937c9110 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -270,12 +270,12 @@ #elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) + defined(CONFIG_ARCH_SH7367) #define SCIF_FNS(name, scif_offset, scif_size) \ CPU_SCIF_FNS(name, scif_offset, scif_size) -#elif defined(CONFIG_ARCH_SH7372) +#elif defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) || \ + defined(CONFIG_ARCH_SH73A0) #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \ CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) #define SCIF_FNS(name, scif_offset, scif_size) \ @@ -313,9 +313,7 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) + defined(CONFIG_ARCH_SH7367) SCIF_FNS(SCSMR, 0x00, 16) SCIF_FNS(SCBRR, 0x04, 8) @@ -326,7 +324,9 @@ SCIF_FNS(SCFDR, 0x1c, 16) SCIF_FNS(SCxTDR, 0x20, 8) SCIF_FNS(SCxRDR, 0x24, 8) SCIF_FNS(SCLSR, 0x00, 0) -#elif defined(CONFIG_ARCH_SH7372) +#elif defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) || \ + defined(CONFIG_ARCH_SH73A0) SCIF_FNS(SCSMR, 0x00, 16) SCIF_FNS(SCBRR, 0x04, 8) SCIF_FNS(SCSCR, 0x08, 16) diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 322bf56c0d89..37fc4e3d487c 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -1,6 +1,4 @@ /* - * drivers/serial/vt8500_serial.c - * * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> * * Based on msm_serial.c, which is: diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c new file mode 100644 index 000000000000..19cc1e8149dd --- /dev/null +++ b/drivers/tty/serial/xilinx_uartps.c @@ -0,0 +1,1113 @@ +/* + * Xilinx PS UART driver + * + * 2011 (c) Xilinx Inc. + * + * 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/platform_device.h> +#include <linux/serial_core.h> +#include <linux/console.h> +#include <linux/serial.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/of.h> + +#define XUARTPS_TTY_NAME "ttyPS" +#define XUARTPS_NAME "xuartps" +#define XUARTPS_MAJOR 0 /* use dynamic node allocation */ +#define XUARTPS_MINOR 0 /* works best with devtmpfs */ +#define XUARTPS_NR_PORTS 2 +#define XUARTPS_FIFO_SIZE 16 /* FIFO size */ +#define XUARTPS_REGISTER_SPACE 0xFFF + +#define xuartps_readl(offset) ioread32(port->membase + offset) +#define xuartps_writel(val, offset) iowrite32(val, port->membase + offset) + +/********************************Register Map********************************/ +/** UART + * + * Register offsets for the UART. + * + */ +#define XUARTPS_CR_OFFSET 0x00 /* Control Register [8:0] */ +#define XUARTPS_MR_OFFSET 0x04 /* Mode Register [10:0] */ +#define XUARTPS_IER_OFFSET 0x08 /* Interrupt Enable [10:0] */ +#define XUARTPS_IDR_OFFSET 0x0C /* Interrupt Disable [10:0] */ +#define XUARTPS_IMR_OFFSET 0x10 /* Interrupt Mask [10:0] */ +#define XUARTPS_ISR_OFFSET 0x14 /* Interrupt Status [10:0]*/ +#define XUARTPS_BAUDGEN_OFFSET 0x18 /* Baud Rate Generator [15:0] */ +#define XUARTPS_RXTOUT_OFFSET 0x1C /* RX Timeout [7:0] */ +#define XUARTPS_RXWM_OFFSET 0x20 /* RX FIFO Trigger Level [5:0] */ +#define XUARTPS_MODEMCR_OFFSET 0x24 /* Modem Control [5:0] */ +#define XUARTPS_MODEMSR_OFFSET 0x28 /* Modem Status [8:0] */ +#define XUARTPS_SR_OFFSET 0x2C /* Channel Status [11:0] */ +#define XUARTPS_FIFO_OFFSET 0x30 /* FIFO [15:0] or [7:0] */ +#define XUARTPS_BAUDDIV_OFFSET 0x34 /* Baud Rate Divider [7:0] */ +#define XUARTPS_FLOWDEL_OFFSET 0x38 /* Flow Delay [15:0] */ +#define XUARTPS_IRRX_PWIDTH_OFFSET 0x3C /* IR Minimum Received Pulse + Width [15:0] */ +#define XUARTPS_IRTX_PWIDTH_OFFSET 0x40 /* IR Transmitted pulse + Width [7:0] */ +#define XUARTPS_TXWM_OFFSET 0x44 /* TX FIFO Trigger Level [5:0] */ + +/** Control Register + * + * The Control register (CR) controls the major functions of the device. + * + * Control Register Bit Definitions + */ +#define XUARTPS_CR_STOPBRK 0x00000100 /* Stop TX break */ +#define XUARTPS_CR_STARTBRK 0x00000080 /* Set TX break */ +#define XUARTPS_CR_TX_DIS 0x00000020 /* TX disabled. */ +#define XUARTPS_CR_TX_EN 0x00000010 /* TX enabled */ +#define XUARTPS_CR_RX_DIS 0x00000008 /* RX disabled. */ +#define XUARTPS_CR_RX_EN 0x00000004 /* RX enabled */ +#define XUARTPS_CR_TXRST 0x00000002 /* TX logic reset */ +#define XUARTPS_CR_RXRST 0x00000001 /* RX logic reset */ +#define XUARTPS_CR_RST_TO 0x00000040 /* Restart Timeout Counter */ + +/** Mode Register + * + * The mode register (MR) defines the mode of transfer as well as the data + * format. If this register is modified during transmission or reception, + * data validity cannot be guaranteed. + * + * Mode Register Bit Definitions + * + */ +#define XUARTPS_MR_CLKSEL 0x00000001 /* Pre-scalar selection */ +#define XUARTPS_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */ +#define XUARTPS_MR_CHMODE_NORM 0x00000000 /* Normal mode */ + +#define XUARTPS_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */ +#define XUARTPS_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */ + +#define XUARTPS_MR_PARITY_NONE 0x00000020 /* No parity mode */ +#define XUARTPS_MR_PARITY_MARK 0x00000018 /* Mark parity mode */ +#define XUARTPS_MR_PARITY_SPACE 0x00000010 /* Space parity mode */ +#define XUARTPS_MR_PARITY_ODD 0x00000008 /* Odd parity mode */ +#define XUARTPS_MR_PARITY_EVEN 0x00000000 /* Even parity mode */ + +#define XUARTPS_MR_CHARLEN_6_BIT 0x00000006 /* 6 bits data */ +#define XUARTPS_MR_CHARLEN_7_BIT 0x00000004 /* 7 bits data */ +#define XUARTPS_MR_CHARLEN_8_BIT 0x00000000 /* 8 bits data */ + +/** Interrupt Registers + * + * Interrupt control logic uses the interrupt enable register (IER) and the + * interrupt disable register (IDR) to set the value of the bits in the + * interrupt mask register (IMR). The IMR determines whether to pass an + * interrupt to the interrupt status register (ISR). + * Writing a 1 to IER Enables an interrupt, writing a 1 to IDR disables an + * interrupt. IMR and ISR are read only, and IER and IDR are write only. + * Reading either IER or IDR returns 0x00. + * + * All four registers have the same bit definitions. + */ +#define XUARTPS_IXR_TOUT 0x00000100 /* RX Timeout error interrupt */ +#define XUARTPS_IXR_PARITY 0x00000080 /* Parity error interrupt */ +#define XUARTPS_IXR_FRAMING 0x00000040 /* Framing error interrupt */ +#define XUARTPS_IXR_OVERRUN 0x00000020 /* Overrun error interrupt */ +#define XUARTPS_IXR_TXFULL 0x00000010 /* TX FIFO Full interrupt */ +#define XUARTPS_IXR_TXEMPTY 0x00000008 /* TX FIFO empty interrupt */ +#define XUARTPS_ISR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt */ +#define XUARTPS_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */ +#define XUARTPS_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */ +#define XUARTPS_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ +#define XUARTPS_IXR_MASK 0x00001FFF /* Valid bit mask */ + +/** Channel Status Register + * + * The channel status register (CSR) is provided to enable the control logic + * to monitor the status of bits in the channel interrupt status register, + * even if these are masked out by the interrupt mask register. + */ +#define XUARTPS_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ +#define XUARTPS_SR_TXEMPTY 0x00000008 /* TX FIFO empty */ +#define XUARTPS_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define XUARTPS_SR_RXTRIG 0x00000001 /* Rx Trigger */ + +/** + * xuartps_isr - Interrupt handler + * @irq: Irq number + * @dev_id: Id of the port + * + * Returns IRQHANDLED + **/ +static irqreturn_t xuartps_isr(int irq, void *dev_id) +{ + struct uart_port *port = (struct uart_port *)dev_id; + struct tty_struct *tty; + unsigned long flags; + unsigned int isrstatus, numbytes; + unsigned int data; + char status = TTY_NORMAL; + + /* Get the tty which could be NULL so don't assume it's valid */ + tty = tty_port_tty_get(&port->state->port); + + spin_lock_irqsave(&port->lock, flags); + + /* Read the interrupt status register to determine which + * interrupt(s) is/are active. + */ + isrstatus = xuartps_readl(XUARTPS_ISR_OFFSET); + + /* drop byte with parity error if IGNPAR specified */ + if (isrstatus & port->ignore_status_mask & XUARTPS_IXR_PARITY) + isrstatus &= ~(XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT); + + isrstatus &= port->read_status_mask; + isrstatus &= ~port->ignore_status_mask; + + if ((isrstatus & XUARTPS_IXR_TOUT) || + (isrstatus & XUARTPS_IXR_RXTRIG)) { + /* Receive Timeout Interrupt */ + while ((xuartps_readl(XUARTPS_SR_OFFSET) & + XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) { + data = xuartps_readl(XUARTPS_FIFO_OFFSET); + port->icount.rx++; + + if (isrstatus & XUARTPS_IXR_PARITY) { + port->icount.parity++; + status = TTY_PARITY; + } else if (isrstatus & XUARTPS_IXR_FRAMING) { + port->icount.frame++; + status = TTY_FRAME; + } else if (isrstatus & XUARTPS_IXR_OVERRUN) + port->icount.overrun++; + + if (tty) + uart_insert_char(port, isrstatus, + XUARTPS_IXR_OVERRUN, data, + status); + } + spin_unlock(&port->lock); + if (tty) + tty_flip_buffer_push(tty); + spin_lock(&port->lock); + } + + /* Dispatch an appropriate handler */ + if ((isrstatus & XUARTPS_IXR_TXEMPTY) == XUARTPS_IXR_TXEMPTY) { + if (uart_circ_empty(&port->state->xmit)) { + xuartps_writel(XUARTPS_IXR_TXEMPTY, + XUARTPS_IDR_OFFSET); + } else { + numbytes = port->fifosize; + /* Break if no more data available in the UART buffer */ + while (numbytes--) { + if (uart_circ_empty(&port->state->xmit)) + break; + /* Get the data from the UART circular buffer + * and write it to the xuartps's TX_FIFO + * register. + */ + xuartps_writel( + port->state->xmit.buf[port->state->xmit. + tail], XUARTPS_FIFO_OFFSET); + + port->icount.tx++; + + /* Adjust the tail of the UART buffer and wrap + * the buffer if it reaches limit. + */ + port->state->xmit.tail = + (port->state->xmit.tail + 1) & \ + (UART_XMIT_SIZE - 1); + } + + if (uart_circ_chars_pending( + &port->state->xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + } + } + + xuartps_writel(isrstatus, XUARTPS_ISR_OFFSET); + + /* be sure to release the lock and tty before leaving */ + spin_unlock_irqrestore(&port->lock, flags); + tty_kref_put(tty); + + return IRQ_HANDLED; +} + +/** + * xuartps_set_baud_rate - Calculate and set the baud rate + * @port: Handle to the uart port structure + * @baud: Baud rate to set + * + * Returns baud rate, requested baud when possible, or actual baud when there + * was too much error + **/ +static unsigned int xuartps_set_baud_rate(struct uart_port *port, + unsigned int baud) +{ + unsigned int sel_clk; + unsigned int calc_baud = 0; + unsigned int brgr_val, brdiv_val; + unsigned int bauderror; + + /* Formula to obtain baud rate is + * baud_tx/rx rate = sel_clk/CD * (BDIV + 1) + * input_clk = (Uart User Defined Clock or Apb Clock) + * depends on UCLKEN in MR Reg + * sel_clk = input_clk or input_clk/8; + * depends on CLKS in MR reg + * CD and BDIV depends on values in + * baud rate generate register + * baud rate clock divisor register + */ + sel_clk = port->uartclk; + if (xuartps_readl(XUARTPS_MR_OFFSET) & XUARTPS_MR_CLKSEL) + sel_clk = sel_clk / 8; + + /* Find the best values for baud generation */ + for (brdiv_val = 4; brdiv_val < 255; brdiv_val++) { + + brgr_val = sel_clk / (baud * (brdiv_val + 1)); + if (brgr_val < 2 || brgr_val > 65535) + continue; + + calc_baud = sel_clk / (brgr_val * (brdiv_val + 1)); + + if (baud > calc_baud) + bauderror = baud - calc_baud; + else + bauderror = calc_baud - baud; + + /* use the values when percent error is acceptable */ + if (((bauderror * 100) / baud) < 3) { + calc_baud = baud; + break; + } + } + + /* Set the values for the new baud rate */ + xuartps_writel(brgr_val, XUARTPS_BAUDGEN_OFFSET); + xuartps_writel(brdiv_val, XUARTPS_BAUDDIV_OFFSET); + + return calc_baud; +} + +/*----------------------Uart Operations---------------------------*/ + +/** + * xuartps_start_tx - Start transmitting bytes + * @port: Handle to the uart port structure + * + **/ +static void xuartps_start_tx(struct uart_port *port) +{ + unsigned int status, numbytes = port->fifosize; + + if (uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port)) + return; + + status = xuartps_readl(XUARTPS_CR_OFFSET); + /* Set the TX enable bit and clear the TX disable bit to enable the + * transmitter. + */ + xuartps_writel((status & ~XUARTPS_CR_TX_DIS) | XUARTPS_CR_TX_EN, + XUARTPS_CR_OFFSET); + + while (numbytes-- && ((xuartps_readl(XUARTPS_SR_OFFSET) + & XUARTPS_SR_TXFULL)) != XUARTPS_SR_TXFULL) { + + /* Break if no more data available in the UART buffer */ + if (uart_circ_empty(&port->state->xmit)) + break; + + /* Get the data from the UART circular buffer and + * write it to the xuartps's TX_FIFO register. + */ + xuartps_writel( + port->state->xmit.buf[port->state->xmit.tail], + XUARTPS_FIFO_OFFSET); + port->icount.tx++; + + /* Adjust the tail of the UART buffer and wrap + * the buffer if it reaches limit. + */ + port->state->xmit.tail = (port->state->xmit.tail + 1) & + (UART_XMIT_SIZE - 1); + } + + /* Enable the TX Empty interrupt */ + xuartps_writel(XUARTPS_IXR_TXEMPTY, XUARTPS_IER_OFFSET); + + if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +/** + * xuartps_stop_tx - Stop TX + * @port: Handle to the uart port structure + * + **/ +static void xuartps_stop_tx(struct uart_port *port) +{ + unsigned int regval; + + regval = xuartps_readl(XUARTPS_CR_OFFSET); + regval |= XUARTPS_CR_TX_DIS; + /* Disable the transmitter */ + xuartps_writel(regval, XUARTPS_CR_OFFSET); +} + +/** + * xuartps_stop_rx - Stop RX + * @port: Handle to the uart port structure + * + **/ +static void xuartps_stop_rx(struct uart_port *port) +{ + unsigned int regval; + + regval = xuartps_readl(XUARTPS_CR_OFFSET); + regval |= XUARTPS_CR_RX_DIS; + /* Disable the receiver */ + xuartps_writel(regval, XUARTPS_CR_OFFSET); +} + +/** + * xuartps_tx_empty - Check whether TX is empty + * @port: Handle to the uart port structure + * + * Returns TIOCSER_TEMT on success, 0 otherwise + **/ +static unsigned int xuartps_tx_empty(struct uart_port *port) +{ + unsigned int status; + + status = xuartps_readl(XUARTPS_ISR_OFFSET) & XUARTPS_IXR_TXEMPTY; + return status ? TIOCSER_TEMT : 0; +} + +/** + * xuartps_break_ctl - Based on the input ctl we have to start or stop + * transmitting char breaks + * @port: Handle to the uart port structure + * @ctl: Value based on which start or stop decision is taken + * + **/ +static void xuartps_break_ctl(struct uart_port *port, int ctl) +{ + unsigned int status; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + status = xuartps_readl(XUARTPS_CR_OFFSET); + + if (ctl == -1) + xuartps_writel(XUARTPS_CR_STARTBRK | status, + XUARTPS_CR_OFFSET); + else { + if ((status & XUARTPS_CR_STOPBRK) == 0) + xuartps_writel(XUARTPS_CR_STOPBRK | status, + XUARTPS_CR_OFFSET); + } + spin_unlock_irqrestore(&port->lock, flags); +} + +/** + * xuartps_set_termios - termios operations, handling data length, parity, + * stop bits, flow control, baud rate + * @port: Handle to the uart port structure + * @termios: Handle to the input termios structure + * @old: Values of the previously saved termios structure + * + **/ +static void xuartps_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + unsigned int cval = 0; + unsigned int baud; + unsigned long flags; + unsigned int ctrl_reg, mode_reg; + + spin_lock_irqsave(&port->lock, flags); + + /* Empty the receive FIFO 1st before making changes */ + while ((xuartps_readl(XUARTPS_SR_OFFSET) & + XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) { + xuartps_readl(XUARTPS_FIFO_OFFSET); + } + + /* Disable the TX and RX to set baud rate */ + xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | + (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS), + XUARTPS_CR_OFFSET); + + /* Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk */ + baud = uart_get_baud_rate(port, termios, old, 0, 10000000); + baud = xuartps_set_baud_rate(port, baud); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* Set TX/RX Reset */ + xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | + (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST), + XUARTPS_CR_OFFSET); + + ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); + + /* Clear the RX disable and TX disable bits and then set the TX enable + * bit and RX enable bit to enable the transmitter and receiver. + */ + xuartps_writel( + (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) + | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN), + XUARTPS_CR_OFFSET); + + xuartps_writel(10, XUARTPS_RXTOUT_OFFSET); + + port->read_status_mask = XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXTRIG | + XUARTPS_IXR_OVERRUN | XUARTPS_IXR_TOUT; + port->ignore_status_mask = 0; + + if (termios->c_iflag & INPCK) + port->read_status_mask |= XUARTPS_IXR_PARITY | + XUARTPS_IXR_FRAMING; + + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= XUARTPS_IXR_PARITY | + XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN; + + /* ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= XUARTPS_IXR_RXTRIG | + XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | + XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN; + + mode_reg = xuartps_readl(XUARTPS_MR_OFFSET); + + /* Handling Data Size */ + switch (termios->c_cflag & CSIZE) { + case CS6: + cval |= XUARTPS_MR_CHARLEN_6_BIT; + break; + case CS7: + cval |= XUARTPS_MR_CHARLEN_7_BIT; + break; + default: + case CS8: + cval |= XUARTPS_MR_CHARLEN_8_BIT; + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + break; + } + + /* Handling Parity and Stop Bits length */ + if (termios->c_cflag & CSTOPB) + cval |= XUARTPS_MR_STOPMODE_2_BIT; /* 2 STOP bits */ + else + cval |= XUARTPS_MR_STOPMODE_1_BIT; /* 1 STOP bit */ + + if (termios->c_cflag & PARENB) { + /* Mark or Space parity */ + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) + cval |= XUARTPS_MR_PARITY_MARK; + else + cval |= XUARTPS_MR_PARITY_SPACE; + } else if (termios->c_cflag & PARODD) + cval |= XUARTPS_MR_PARITY_ODD; + else + cval |= XUARTPS_MR_PARITY_EVEN; + } else + cval |= XUARTPS_MR_PARITY_NONE; + xuartps_writel(cval , XUARTPS_MR_OFFSET); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/** + * xuartps_startup - Called when an application opens a xuartps port + * @port: Handle to the uart port structure + * + * Returns 0 on success, negative error otherwise + **/ +static int xuartps_startup(struct uart_port *port) +{ + unsigned int retval = 0, status = 0; + + retval = request_irq(port->irq, xuartps_isr, 0, XUARTPS_NAME, + (void *)port); + if (retval) + return retval; + + /* Disable the TX and RX */ + xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS, + XUARTPS_CR_OFFSET); + + /* Set the Control Register with TX/RX Enable, TX/RX Reset, + * no break chars. + */ + xuartps_writel(XUARTPS_CR_TXRST | XUARTPS_CR_RXRST, + XUARTPS_CR_OFFSET); + + status = xuartps_readl(XUARTPS_CR_OFFSET); + + /* Clear the RX disable and TX disable bits and then set the TX enable + * bit and RX enable bit to enable the transmitter and receiver. + */ + xuartps_writel((status & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) + | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN | + XUARTPS_CR_STOPBRK), XUARTPS_CR_OFFSET); + + /* Set the Mode Register with normal mode,8 data bits,1 stop bit, + * no parity. + */ + xuartps_writel(XUARTPS_MR_CHMODE_NORM | XUARTPS_MR_STOPMODE_1_BIT + | XUARTPS_MR_PARITY_NONE | XUARTPS_MR_CHARLEN_8_BIT, + XUARTPS_MR_OFFSET); + + /* Set the RX FIFO Trigger level to 14 assuming FIFO size as 16 */ + xuartps_writel(14, XUARTPS_RXWM_OFFSET); + + /* Receive Timeout register is enabled with value of 10 */ + xuartps_writel(10, XUARTPS_RXTOUT_OFFSET); + + + /* Set the Interrupt Registers with desired interrupts */ + xuartps_writel(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | + XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN | + XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET); + xuartps_writel(~(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | + XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN | + XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT), XUARTPS_IDR_OFFSET); + + return retval; +} + +/** + * xuartps_shutdown - Called when an application closes a xuartps port + * @port: Handle to the uart port structure + * + **/ +static void xuartps_shutdown(struct uart_port *port) +{ + int status; + + /* Disable interrupts */ + status = xuartps_readl(XUARTPS_IMR_OFFSET); + xuartps_writel(status, XUARTPS_IDR_OFFSET); + + /* Disable the TX and RX */ + xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS, + XUARTPS_CR_OFFSET); + free_irq(port->irq, port); +} + +/** + * xuartps_type - Set UART type to xuartps port + * @port: Handle to the uart port structure + * + * Returns string on success, NULL otherwise + **/ +static const char *xuartps_type(struct uart_port *port) +{ + return port->type == PORT_XUARTPS ? XUARTPS_NAME : NULL; +} + +/** + * xuartps_verify_port - Verify the port params + * @port: Handle to the uart port structure + * @ser: Handle to the structure whose members are compared + * + * Returns 0 if success otherwise -EINVAL + **/ +static int xuartps_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != PORT_XUARTPS) + return -EINVAL; + if (port->irq != ser->irq) + return -EINVAL; + if (ser->io_type != UPIO_MEM) + return -EINVAL; + if (port->iobase != ser->port) + return -EINVAL; + if (ser->hub6 != 0) + return -EINVAL; + return 0; +} + +/** + * xuartps_request_port - Claim the memory region attached to xuartps port, + * called when the driver adds a xuartps port via + * uart_add_one_port() + * @port: Handle to the uart port structure + * + * Returns 0, -ENOMEM if request fails + **/ +static int xuartps_request_port(struct uart_port *port) +{ + if (!request_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE, + XUARTPS_NAME)) { + return -ENOMEM; + } + + port->membase = ioremap(port->mapbase, XUARTPS_REGISTER_SPACE); + if (!port->membase) { + dev_err(port->dev, "Unable to map registers\n"); + release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE); + return -ENOMEM; + } + return 0; +} + +/** + * xuartps_release_port - Release the memory region attached to a xuartps + * port, called when the driver removes a xuartps + * port via uart_remove_one_port(). + * @port: Handle to the uart port structure + * + **/ +static void xuartps_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE); + iounmap(port->membase); + port->membase = NULL; +} + +/** + * xuartps_config_port - Configure xuartps, called when the driver adds a + * xuartps port + * @port: Handle to the uart port structure + * @flags: If any + * + **/ +static void xuartps_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE && xuartps_request_port(port) == 0) + port->type = PORT_XUARTPS; +} + +/** + * xuartps_get_mctrl - Get the modem control state + * + * @port: Handle to the uart port structure + * + * Returns the modem control state + * + **/ +static unsigned int xuartps_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void xuartps_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* N/A */ +} + +static void xuartps_enable_ms(struct uart_port *port) +{ + /* N/A */ +} + +/** The UART operations structure + */ +static struct uart_ops xuartps_ops = { + .set_mctrl = xuartps_set_mctrl, + .get_mctrl = xuartps_get_mctrl, + .enable_ms = xuartps_enable_ms, + + .start_tx = xuartps_start_tx, /* Start transmitting */ + .stop_tx = xuartps_stop_tx, /* Stop transmission */ + .stop_rx = xuartps_stop_rx, /* Stop reception */ + .tx_empty = xuartps_tx_empty, /* Transmitter busy? */ + .break_ctl = xuartps_break_ctl, /* Start/stop + * transmitting break + */ + .set_termios = xuartps_set_termios, /* Set termios */ + .startup = xuartps_startup, /* App opens xuartps */ + .shutdown = xuartps_shutdown, /* App closes xuartps */ + .type = xuartps_type, /* Set UART type */ + .verify_port = xuartps_verify_port, /* Verification of port + * params + */ + .request_port = xuartps_request_port, /* Claim resources + * associated with a + * xuartps port + */ + .release_port = xuartps_release_port, /* Release resources + * associated with a + * xuartps port + */ + .config_port = xuartps_config_port, /* Configure when driver + * adds a xuartps port + */ +}; + +static struct uart_port xuartps_port[2]; + +/** + * xuartps_get_port - Configure the port from the platform device resource + * info + * + * Returns a pointer to a uart_port or NULL for failure + **/ +static struct uart_port *xuartps_get_port(void) +{ + struct uart_port *port; + int id; + + /* Find the next unused port */ + for (id = 0; id < XUARTPS_NR_PORTS; id++) + if (xuartps_port[id].mapbase == 0) + break; + + if (id >= XUARTPS_NR_PORTS) + return NULL; + + port = &xuartps_port[id]; + + /* At this point, we've got an empty uart_port struct, initialize it */ + spin_lock_init(&port->lock); + port->membase = NULL; + port->iobase = 1; /* mark port in use */ + port->irq = 0; + port->type = PORT_UNKNOWN; + port->iotype = UPIO_MEM32; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &xuartps_ops; + port->fifosize = XUARTPS_FIFO_SIZE; + port->line = id; + port->dev = NULL; + return port; +} + +/*-----------------------Console driver operations--------------------------*/ + +#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE +/** + * xuartps_console_wait_tx - Wait for the TX to be full + * @port: Handle to the uart port structure + * + **/ +static void xuartps_console_wait_tx(struct uart_port *port) +{ + while ((xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY) + != XUARTPS_SR_TXEMPTY) + barrier(); +} + +/** + * xuartps_console_putchar - write the character to the FIFO buffer + * @port: Handle to the uart port structure + * @ch: Character to be written + * + **/ +static void xuartps_console_putchar(struct uart_port *port, int ch) +{ + xuartps_console_wait_tx(port); + xuartps_writel(ch, XUARTPS_FIFO_OFFSET); +} + +/** + * xuartps_console_write - perform write operation + * @port: Handle to the uart port structure + * @s: Pointer to character array + * @count: No of characters + **/ +static void xuartps_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &xuartps_port[co->index]; + unsigned long flags; + unsigned int imr; + int locked = 1; + + if (oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); + + /* save and disable interrupt */ + imr = xuartps_readl(XUARTPS_IMR_OFFSET); + xuartps_writel(imr, XUARTPS_IDR_OFFSET); + + uart_console_write(port, s, count, xuartps_console_putchar); + xuartps_console_wait_tx(port); + + /* restore interrupt state, it seems like there may be a h/w bug + * in that the interrupt enable register should not need to be + * written based on the data sheet + */ + xuartps_writel(~imr, XUARTPS_IDR_OFFSET); + xuartps_writel(imr, XUARTPS_IER_OFFSET); + + if (locked) + spin_unlock_irqrestore(&port->lock, flags); +} + +/** + * xuartps_console_setup - Initialize the uart to default config + * @co: Console handle + * @options: Initial settings of uart + * + * Returns 0, -ENODEV if no device + **/ +static int __init xuartps_console_setup(struct console *co, char *options) +{ + struct uart_port *port = &xuartps_port[co->index]; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= XUARTPS_NR_PORTS) + return -EINVAL; + + if (!port->mapbase) { + pr_debug("console on ttyPS%i not present\n", co->index); + return -ENODEV; + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver xuartps_uart_driver; + +static struct console xuartps_console = { + .name = XUARTPS_TTY_NAME, + .write = xuartps_console_write, + .device = uart_console_device, + .setup = xuartps_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, /* Specified on the cmdline (e.g. console=ttyPS ) */ + .data = &xuartps_uart_driver, +}; + +/** + * xuartps_console_init - Initialization call + * + * Returns 0 on success, negative error otherwise + **/ +static int __init xuartps_console_init(void) +{ + register_console(&xuartps_console); + return 0; +} + +console_initcall(xuartps_console_init); + +#endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */ + +/** Structure Definitions + */ +static struct uart_driver xuartps_uart_driver = { + .owner = THIS_MODULE, /* Owner */ + .driver_name = XUARTPS_NAME, /* Driver name */ + .dev_name = XUARTPS_TTY_NAME, /* Node name */ + .major = XUARTPS_MAJOR, /* Major number */ + .minor = XUARTPS_MINOR, /* Minor number */ + .nr = XUARTPS_NR_PORTS, /* Number of UART ports */ +#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE + .cons = &xuartps_console, /* Console */ +#endif +}; + +/* --------------------------------------------------------------------- + * Platform bus binding + */ +/** + * xuartps_probe - Platform driver probe + * @pdev: Pointer to the platform device structure + * + * Returns 0 on success, negative error otherwise + **/ +static int __devinit xuartps_probe(struct platform_device *pdev) +{ + int rc; + struct uart_port *port; + struct resource *res, *res2; + int clk = 0; + +#ifdef CONFIG_OF + const unsigned int *prop; + + prop = of_get_property(pdev->dev.of_node, "clock", NULL); + if (prop) + clk = be32_to_cpup(prop); +#else + clk = *((unsigned int *)(pdev->dev.platform_data)); +#endif + if (!clk) { + dev_err(&pdev->dev, "no clock specified\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res2) + return -ENODEV; + + /* Initialize the port structure */ + port = xuartps_get_port(); + + if (!port) { + dev_err(&pdev->dev, "Cannot get uart_port structure\n"); + return -ENODEV; + } else { + /* Register the port. + * This function also registers this device with the tty layer + * and triggers invocation of the config_port() entry point. + */ + port->mapbase = res->start; + port->irq = res2->start; + port->dev = &pdev->dev; + port->uartclk = clk; + dev_set_drvdata(&pdev->dev, port); + rc = uart_add_one_port(&xuartps_uart_driver, port); + if (rc) { + dev_err(&pdev->dev, + "uart_add_one_port() failed; err=%i\n", rc); + dev_set_drvdata(&pdev->dev, NULL); + return rc; + } + return 0; + } +} + +/** + * xuartps_remove - called when the platform driver is unregistered + * @pdev: Pointer to the platform device structure + * + * Returns 0 on success, negative error otherwise + **/ +static int __devexit xuartps_remove(struct platform_device *pdev) +{ + struct uart_port *port = dev_get_drvdata(&pdev->dev); + int rc = 0; + + /* Remove the xuartps port from the serial core */ + if (port) { + rc = uart_remove_one_port(&xuartps_uart_driver, port); + dev_set_drvdata(&pdev->dev, NULL); + port->mapbase = 0; + } + return rc; +} + +/** + * xuartps_suspend - suspend event + * @pdev: Pointer to the platform device structure + * @state: State of the device + * + * Returns 0 + **/ +static int xuartps_suspend(struct platform_device *pdev, pm_message_t state) +{ + /* Call the API provided in serial_core.c file which handles + * the suspend. + */ + uart_suspend_port(&xuartps_uart_driver, &xuartps_port[pdev->id]); + return 0; +} + +/** + * xuartps_resume - Resume after a previous suspend + * @pdev: Pointer to the platform device structure + * + * Returns 0 + **/ +static int xuartps_resume(struct platform_device *pdev) +{ + uart_resume_port(&xuartps_uart_driver, &xuartps_port[pdev->id]); + return 0; +} + +/* Match table for of_platform binding */ + +#ifdef CONFIG_OF +static struct of_device_id xuartps_of_match[] __devinitdata = { + { .compatible = "xlnx,xuartps", }, + {} +}; +MODULE_DEVICE_TABLE(of, xuartps_of_match); +#else +#define xuartps_of_match NULL +#endif + +static struct platform_driver xuartps_platform_driver = { + .probe = xuartps_probe, /* Probe method */ + .remove = __exit_p(xuartps_remove), /* Detach method */ + .suspend = xuartps_suspend, /* Suspend */ + .resume = xuartps_resume, /* Resume after a suspend */ + .driver = { + .owner = THIS_MODULE, + .name = XUARTPS_NAME, /* Driver name */ + .of_match_table = xuartps_of_match, + }, +}; + +/* --------------------------------------------------------------------- + * Module Init and Exit + */ +/** + * xuartps_init - Initial driver registration call + * + * Returns whether the registration was successful or not + **/ +static int __init xuartps_init(void) +{ + int retval = 0; + + /* Register the xuartps driver with the serial core */ + retval = uart_register_driver(&xuartps_uart_driver); + if (retval) + return retval; + + /* Register the platform driver */ + retval = platform_driver_register(&xuartps_platform_driver); + if (retval) + uart_unregister_driver(&xuartps_uart_driver); + + return retval; +} + +/** + * xuartps_exit - Driver unregistration call + **/ +static void __exit xuartps_exit(void) +{ + /* The order of unregistration is important. Unregister the + * UART driver before the platform driver crashes the system. + */ + + /* Unregister the platform driver */ + platform_driver_unregister(&xuartps_platform_driver); + + /* Unregister the xuartps driver */ + uart_unregister_driver(&xuartps_uart_driver); +} + +module_init(xuartps_init); +module_exit(xuartps_exit); + +MODULE_DESCRIPTION("Driver for PS UART"); +MODULE_AUTHOR("Xilinx Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 27da23d98e3f..272e417a9b0d 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/synclink.c - * * $Id: synclink.c,v 4.38 2005/11/07 16:30:34 paulkf Exp $ * * Device driver for Microgate SyncLink ISA and PCI diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index f1a7918d71aa..46de2e075dac 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -416,6 +416,7 @@ static void flush_to_ldisc(struct work_struct *work) struct tty_buffer *head, *tail = tty->buf.tail; int seen_tail = 0; while ((head = tty->buf.head) != NULL) { + int copied; int count; char *char_buf; unsigned char *flag_buf; @@ -442,17 +443,19 @@ static void flush_to_ldisc(struct work_struct *work) line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; - if (!tty->receive_room || seen_tail) - break; - if (count > tty->receive_room) - count = tty->receive_room; char_buf = head->char_buf_ptr + head->read; flag_buf = head->flag_buf_ptr + head->read; - head->read += count; spin_unlock_irqrestore(&tty->buf.lock, flags); - disc->ops->receive_buf(tty, char_buf, + copied = disc->ops->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); + + head->read += copied; + + if (copied == 0 || seen_tail) { + schedule_work(&tty->buf.work); + break; + } } clear_bit(TTY_FLUSHING, &tty->flags); } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d7d50b48287e..6556f7452ba6 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/tty_io.c - * * Copyright (C) 1991, 1992 Linus Torvalds */ @@ -964,12 +962,14 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, } void tty_write_unlock(struct tty_struct *tty) + __releases(&tty->atomic_write_lock) { mutex_unlock(&tty->atomic_write_lock); wake_up_interruptible_poll(&tty->write_wait, POLLOUT); } int tty_write_lock(struct tty_struct *tty, int ndelay) + __acquires(&tty->atomic_write_lock) { if (!mutex_trylock(&tty->atomic_write_lock)) { if (ndelay) @@ -1391,16 +1391,15 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, return ERR_PTR(-ENODEV); tty = alloc_tty_struct(); - if (!tty) - goto fail_no_mem; + if (!tty) { + retval = -ENOMEM; + goto err_module_put; + } initialize_tty_struct(tty, driver, idx); retval = tty_driver_install_tty(driver, tty); - if (retval < 0) { - free_tty_struct(tty); - module_put(driver->owner); - return ERR_PTR(retval); - } + if (retval < 0) + goto err_deinit_tty; /* * Structures all installed ... call the ldisc open routines. @@ -1409,15 +1408,18 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, */ retval = tty_ldisc_setup(tty, tty->link); if (retval) - goto release_mem_out; + goto err_release_tty; return tty; -fail_no_mem: +err_deinit_tty: + deinitialize_tty_struct(tty); + free_tty_struct(tty); +err_module_put: module_put(driver->owner); - return ERR_PTR(-ENOMEM); + return ERR_PTR(retval); /* call the tty release_tty routine to clean out this slot */ -release_mem_out: +err_release_tty: if (printk_ratelimit()) printk(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); @@ -1892,6 +1894,7 @@ got_driver: retval = tty_add_file(tty, filp); if (retval) { tty_unlock(); + tty_release(inode, filp); return retval; } @@ -1902,12 +1905,10 @@ got_driver: #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "opening %s...", tty->name); #endif - if (!retval) { - if (tty->ops->open) - retval = tty->ops->open(tty, filp); - else - retval = -ENODEV; - } + if (tty->ops->open) + retval = tty->ops->open(tty, filp); + else + retval = -ENODEV; filp->f_flags = saved_flags; if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && @@ -2888,6 +2889,20 @@ void initialize_tty_struct(struct tty_struct *tty, } /** + * deinitialize_tty_struct + * @tty: tty to deinitialize + * + * This subroutine deinitializes a tty structure that has been newly + * allocated but tty_release cannot be called on that yet. + * + * Locking: none - tty in question must not be exposed at this point + */ +void deinitialize_tty_struct(struct tty_struct *tty) +{ + tty_ldisc_deinit(tty); +} + +/** * tty_put_char - write one character to a tty * @tty: tty * @ch: character diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 21574cb32343..53f2442c6099 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/tty_ioctl.c - * * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds * * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines @@ -309,7 +307,7 @@ EXPORT_SYMBOL(tty_termios_input_baud_rate); * @ospeed: output speed * * Encode the speeds set into the passed termios structure. This is - * used as a library helper for drivers os that they can report back + * 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 diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index e19e13647116..5d01d32e2cf0 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -956,6 +956,19 @@ void tty_ldisc_init(struct tty_struct *tty) tty_ldisc_assign(tty, ld); } +/** + * tty_ldisc_init - ldisc cleanup for new tty + * @tty: tty that was allocated recently + * + * The tty structure must not becompletely set up (tty_ldisc_setup) when + * this call is made. + */ +void tty_ldisc_deinit(struct tty_struct *tty) +{ + put_ldisc(tty->ldisc); + tty_ldisc_assign(tty, NULL); +} + void tty_ldisc_begin(void) { /* Setup the default TTY line discipline. */ diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 133697540c73..3b2bb7719442 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -1,6 +1,3 @@ -/* - * drivers/char/tty_lock.c - */ #include <linux/tty.h> #include <linux/module.h> #include <linux/kallsyms.h> diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index d6b342b5b423..3761ccf0f340 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/keyboard.c - * * Written for linux by Johan Myreen as a translation from * the assembly version by Linus (with diacriticals added) * diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index adf0ad2a8851..67b1d0d7c8ac 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/selection.c - * * This module exports the functions: * * 'int set_selection(struct tiocl_selection __user *, struct tty_struct *)' @@ -334,8 +332,7 @@ int paste_selection(struct tty_struct *tty) continue; } count = sel_buffer_lth - pasted; - count = min(count, tty->receive_room); - tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, + count = tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, NULL, count); pasted += count; } diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 1564261e80c8..66825c9f516a 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/vc_screen.c - * * Provide access to virtual console memory. * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled) * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 4bea1efaec98..b3915b7ad3e2 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/vt.c - * * Copyright (C) 1991, 1992 Linus Torvalds */ @@ -858,7 +856,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, { unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; unsigned long end; - unsigned int old_cols, old_rows, old_row_size, old_screen_size; + unsigned int old_rows, old_row_size; unsigned int new_cols, new_rows, new_row_size, new_screen_size; unsigned int user; unsigned short *newscreen; @@ -887,9 +885,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, return -ENOMEM; old_rows = vc->vc_rows; - old_cols = vc->vc_cols; old_row_size = vc->vc_size_row; - old_screen_size = vc->vc_screenbuf_size; err = resize_screen(vc, new_cols, new_rows, user); if (err) { @@ -1197,6 +1193,13 @@ static void csi_J(struct vc_data *vc, int vpar) vc->vc_x + 1); } break; + case 3: /* erase scroll-back buffer (and whole display) */ + scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char, + vc->vc_screenbuf_size >> 1); + set_origin(vc); + if (CON_IS_VISIBLE(vc)) + update_screen(vc); + /* fall through */ case 2: /* erase whole display */ count = vc->vc_cols * vc->vc_rows; start = (unsigned short *)vc->vc_origin; diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 937d17219984..5e096f43bcea 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -1,6 +1,4 @@ /* - * linux/drivers/char/vt_ioctl.c - * * Copyright (C) 1992 obz under the linux copyright * * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993 @@ -698,10 +696,23 @@ int vt_ioctl(struct tty_struct *tty, break; case KDGKBMODE: - uival = ((kbd->kbdmode == VC_RAW) ? K_RAW : - (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW : - (kbd->kbdmode == VC_UNICODE) ? K_UNICODE : - K_XLATE); + switch (kbd->kbdmode) { + case VC_RAW: + uival = K_RAW; + break; + case VC_MEDIUMRAW: + uival = K_MEDIUMRAW; + break; + case VC_UNICODE: + uival = K_UNICODE; + break; + case VC_OFF: + uival = K_OFF; + break; + default: + uival = K_XLATE; + break; + } goto setint; /* this could be folded into KDSKBMODE, but for compatibility @@ -1499,7 +1510,6 @@ long vt_compat_ioctl(struct tty_struct *tty, { struct vc_data *vc = tty->driver_data; struct console_font_op op; /* used in multiple places here */ - struct kbd_struct *kbd; unsigned int console; void __user *up = (void __user *)arg; int perm; @@ -1522,7 +1532,6 @@ long vt_compat_ioctl(struct tty_struct *tty, if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) perm = 1; - kbd = kbd_table + console; switch (cmd) { /* * these need special handlers for incompatible data structures |