From daee779718a319ff9f83e1ba3339334ac650bb22 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 22 Sep 2012 19:52:11 +0200 Subject: [PATCH 001/146] console: implement lockdep support for console_lock Dave Airlie recently discovered a locking bug in the fbcon layer, where a timer_del_sync (for the blinking cursor) deadlocks with the timer itself, since both (want to) hold the console_lock: https://lkml.org/lkml/2012/8/21/36 Unfortunately the console_lock isn't a plain mutex and hence has no lockdep support. Which resulted in a few days wasted of tracking down this bug (complicated by the fact that printk doesn't show anything when the console is locked) instead of noticing the bug much earlier with the lockdep splat. Hence I've figured I need to fix that for the next deadlock involving console_lock - and with kms/drm growing ever more complex locking that'll eventually happen. Now the console_lock has rather funky semantics, so after a quick irc discussion with Thomas Gleixner and Dave Airlie I've quickly ditched the original idead of switching to a real mutex (since it won't work) and instead opted to annotate the console_lock with lockdep information manually. There are a few special cases: - The console_lock state is protected by the console_sem, and usually grabbed/dropped at _lock/_unlock time. But the suspend/resume code drops the semaphore without dropping the console_lock (see suspend_console/resume_console). But since the same thread that did the suspend will do the resume, we don't need to fix up anything. - In the printk code there's a special trylock, only used to kick off the logbuffer printk'ing in console_unlock. But all that happens while lockdep is disable (since printk does a few other evil tricks). So no issue there, either. - The console_lock can also be acquired form irq context (but only with a trylock). lockdep already handles that. This all leaves us with annotating the normal console_lock, _unlock and _trylock functions. And yes, it works - simply unloading a drm kms driver resulted in lockdep complaining about the deadlock in fbcon_deinit: ====================================================== [ INFO: possible circular locking dependency detected ] 3.6.0-rc2+ #552 Not tainted ------------------------------------------------------- kms-reload/3577 is trying to acquire lock: ((&info->queue)){+.+...}, at: [] wait_on_work+0x0/0xa7 but task is already holding lock: (console_lock){+.+.+.}, at: [] bind_con_driver+0x38/0x263 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (console_lock){+.+.+.}: [] lock_acquire+0x95/0x105 [] console_lock+0x59/0x5b [] fb_flashcursor+0x2e/0x12c [] process_one_work+0x1d9/0x3b4 [] worker_thread+0x1a7/0x24b [] kthread+0x7f/0x87 [] kernel_thread_helper+0x4/0x10 -> #0 ((&info->queue)){+.+...}: [] __lock_acquire+0x999/0xcf6 [] lock_acquire+0x95/0x105 [] wait_on_work+0x3b/0xa7 [] __cancel_work_timer+0xbf/0x102 [] cancel_work_sync+0xb/0xd [] fbcon_deinit+0x11c/0x1dc [] bind_con_driver+0x145/0x263 [] unbind_con_driver+0x14f/0x195 [] store_bind+0x1ad/0x1c1 [] dev_attr_store+0x13/0x1f [] sysfs_write_file+0xe9/0x121 [] vfs_write+0x9b/0xfd [] sys_write+0x3e/0x6b [] system_call_fastpath+0x16/0x1b other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(console_lock); lock((&info->queue)); lock(console_lock); lock((&info->queue)); *** DEADLOCK *** v2: Mark the lockdep_map static, noticed by Jani Nikula. Cc: Dave Airlie Cc: Thomas Gleixner Cc: Alan Cox Cc: Peter Zijlstra Signed-off-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- kernel/printk.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kernel/printk.c b/kernel/printk.c index 2d607f4d179..ee79f14db35 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -87,6 +87,12 @@ static DEFINE_SEMAPHORE(console_sem); struct console *console_drivers; EXPORT_SYMBOL_GPL(console_drivers); +#ifdef CONFIG_LOCKDEP +static struct lockdep_map console_lock_dep_map = { + .name = "console_lock" +}; +#endif + /* * This is used for debugging the mess that is the VT code by * keeping track if we have the console semaphore held. It's @@ -1914,6 +1920,7 @@ void console_lock(void) return; console_locked = 1; console_may_schedule = 1; + mutex_acquire(&console_lock_dep_map, 0, 0, _RET_IP_); } EXPORT_SYMBOL(console_lock); @@ -1935,6 +1942,7 @@ int console_trylock(void) } console_locked = 1; console_may_schedule = 0; + mutex_acquire(&console_lock_dep_map, 0, 1, _RET_IP_); return 1; } EXPORT_SYMBOL(console_trylock); @@ -2095,6 +2103,7 @@ skip: local_irq_restore(flags); } console_locked = 0; + mutex_release(&console_lock_dep_map, 1, _RET_IP_); /* Release the exclusive_console once it is used */ if (unlikely(exclusive_console)) From cee4ad1ed90a0959fc29f9d30a2526e5e9522cfa Mon Sep 17 00:00:00 2001 From: Ivo Sieben Date: Thu, 27 Sep 2012 14:02:05 +0200 Subject: [PATCH 002/146] tty: prevent unnecessary work queue lock checking on flip buffer copy When low_latency flag is set the TTY receive flip buffer is copied to the line discipline directly instead of using a work queue in the background. Therefor only in case a workqueue is actually used for copying data to the line discipline we'll have to flush the workqueue. This prevents unnecessary spin lock/unlock on the workqueue spin lock that can cause additional scheduling overhead on a PREEMPT_RT system. On a 200 MHz AT91SAM9261 processor setup this fixes about 100us of scheduling overhead on the TTY read call. Signed-off-by: Ivo Sieben Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 91e326ffe7d..8b00f6a34a7 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -342,6 +342,8 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); * Takes any pending buffers and transfers their ownership to the * ldisc side of the queue. It then schedules those characters for * processing by the line discipline. + * Note that this function can only be used when the low_latency flag + * is unset. Otherwise the workqueue won't be flushed. * * Locking: Takes tty->buf.lock */ @@ -514,7 +516,8 @@ static void flush_to_ldisc(struct work_struct *work) */ void tty_flush_to_ldisc(struct tty_struct *tty) { - flush_work(&tty->buf.work); + if (!tty->low_latency) + flush_work(&tty->buf.work); } /** From 8fcbaa2b7f5b70dba9ed1c7f91d0a270ce752e2c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:27 +0200 Subject: [PATCH 003/146] TTY: devpts, don't care about TTY in devpts_get_tty The goal is to stop setting and using tty->driver_data in devpts code. It should be used solely by the driver's code, pty in this case. First, here we remove TTY from devpts_get_tty and rename it to devpts_get_priv. Note we do not remove type safety, we just shift the [implicit] (void *) cast one layer up. index was unused in devpts_get_tty, so remove that from the prototype too. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 2 +- fs/devpts/inode.c | 9 ++++----- include/linux/devpts_fs.h | 7 +++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index a82b39939a9..65f767154d1 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -547,7 +547,7 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, struct tty_struct *tty; mutex_lock(&devpts_mutex); - tty = devpts_get_tty(pts_inode, idx); + tty = devpts_get_priv(pts_inode); mutex_unlock(&devpts_mutex); /* Master must be open before slave */ if (!tty) diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 14afbabe654..47965807884 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -593,10 +593,10 @@ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) return ret; } -struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number) +void *devpts_get_priv(struct inode *pts_inode) { struct dentry *dentry; - struct tty_struct *tty; + void *priv = NULL; BUG_ON(pts_inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR)); @@ -605,13 +605,12 @@ struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number) if (!dentry) return NULL; - tty = NULL; if (pts_inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) - tty = (struct tty_struct *)pts_inode->i_private; + priv = pts_inode->i_private; dput(dentry); - return tty; + return priv; } void devpts_pty_kill(struct tty_struct *tty) diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index 5ce0e5fd712..de635a5505e 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -21,8 +21,8 @@ int devpts_new_index(struct inode *ptmx_inode); void devpts_kill_index(struct inode *ptmx_inode, int idx); /* mknod in devpts */ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); -/* get tty structure */ -struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number); +/* get private structure */ +void *devpts_get_priv(struct inode *pts_inode); /* unlink */ void devpts_pty_kill(struct tty_struct *tty); @@ -36,8 +36,7 @@ static inline int devpts_pty_new(struct inode *ptmx_inode, { return -EINVAL; } -static inline struct tty_struct *devpts_get_tty(struct inode *pts_inode, - int number) +static inline void *devpts_get_priv(struct inode *pts_inode) { return NULL; } From 162b97cfa21f816f39ede1944f2a4220e3cf8969 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:28 +0200 Subject: [PATCH 004/146] TTY: devpts, return created inode from devpts_pty_new The goal is to stop setting and using tty->driver_data in devpts code. It should be used solely by the driver's code, pty in this case. For the cleanup of layering, we will need the inode created in devpts_pty_new to be stored into slave's driver_data. So we convert devpts_pty_new to return the inode or an ERR_PTR-encoded error in case of failure. The move of 'inode = new_inode(sb);' from declarators to the code is only cosmetical, but it makes the code easier to read. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 7 +++++-- fs/devpts/inode.c | 12 ++++++------ include/linux/devpts_fs.h | 8 ++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 65f767154d1..9985b451e93 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -614,6 +614,7 @@ static const struct tty_operations pty_unix98_ops = { static int ptmx_open(struct inode *inode, struct file *filp) { struct tty_struct *tty; + struct inode *slave_inode; int retval; int index; @@ -650,9 +651,11 @@ static int ptmx_open(struct inode *inode, struct file *filp) tty_add_file(tty, filp); - retval = devpts_pty_new(inode, tty->link); - if (retval) + slave_inode = devpts_pty_new(inode, tty->link); + if (IS_ERR(slave_inode)) { + retval = PTR_ERR(slave_inode); goto err_release; + } retval = ptm_driver->ops->open(tty, filp); if (retval) diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 47965807884..ec3bab716c0 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -545,7 +545,7 @@ void devpts_kill_index(struct inode *ptmx_inode, int idx) mutex_unlock(&allocated_ptys_lock); } -int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) +struct inode *devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) { /* tty layer puts index from devpts_new_index() in here */ int number = tty->index; @@ -553,19 +553,19 @@ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) dev_t device = MKDEV(driver->major, driver->minor_start+number); struct dentry *dentry; struct super_block *sb = pts_sb_from_inode(ptmx_inode); - struct inode *inode = new_inode(sb); + struct inode *inode; struct dentry *root = sb->s_root; struct pts_fs_info *fsi = DEVPTS_SB(sb); struct pts_mount_opts *opts = &fsi->mount_opts; - int ret = 0; char s[12]; /* We're supposed to be given the slave end of a pty */ BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY); BUG_ON(driver->subtype != PTY_TYPE_SLAVE); + inode = new_inode(sb); if (!inode) - return -ENOMEM; + return ERR_PTR(-ENOMEM); inode->i_ino = number + 3; inode->i_uid = opts->setuid ? opts->uid : current_fsuid(); @@ -585,12 +585,12 @@ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) fsnotify_create(root->d_inode, dentry); } else { iput(inode); - ret = -ENOMEM; + inode = ERR_PTR(-ENOMEM); } mutex_unlock(&root->d_inode->i_mutex); - return ret; + return inode; } void *devpts_get_priv(struct inode *pts_inode) diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index de635a5505e..4ca846f16fe 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -20,7 +20,7 @@ int devpts_new_index(struct inode *ptmx_inode); void devpts_kill_index(struct inode *ptmx_inode, int idx); /* mknod in devpts */ -int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); +struct inode *devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); /* get private structure */ void *devpts_get_priv(struct inode *pts_inode); /* unlink */ @@ -31,10 +31,10 @@ void devpts_pty_kill(struct tty_struct *tty); /* Dummy stubs in the no-pty case */ static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; } static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { } -static inline int devpts_pty_new(struct inode *ptmx_inode, - struct tty_struct *tty) +static inline struct inode *devpts_pty_new(struct inode *ptmx_inode, + struct tty_struct *tty) { - return -EINVAL; + return ERR_PTR(-EINVAL); } static inline void *devpts_get_priv(struct inode *pts_inode) { From f11afb61247016162aa92225a337c1575556c9d9 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:29 +0200 Subject: [PATCH 005/146] TTY: devpts, do not set driver_data The goal is to stop setting and using tty->driver_data in devpts code. It should be used solely by the driver's code, pty in this case. Now driver_data are managed only in the pty driver. devpts_pty_new is switched to accept what we used to dig out of tty_struct, i.e. device node number and index. This also removes a note about driver_data being set outside of the driver. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 10 +++++----- fs/devpts/inode.c | 21 ++++++--------------- include/linux/devpts_fs.h | 9 +++++---- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 9985b451e93..559e5b27941 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -4,9 +4,6 @@ * Added support for a Unix98-style ptmx device. * -- C. Scott Ananian , 14-Jan-1998 * - * When reading this code see also fs/devpts. In particular note that the - * driver_data field is used by the devpts side as a binding to the devpts - * inode. */ #include @@ -59,7 +56,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) #ifdef CONFIG_UNIX98_PTYS if (tty->driver == ptm_driver) { mutex_lock(&devpts_mutex); - devpts_pty_kill(tty->link); + devpts_pty_kill(tty->link->driver_data); mutex_unlock(&devpts_mutex); } #endif @@ -651,7 +648,9 @@ static int ptmx_open(struct inode *inode, struct file *filp) tty_add_file(tty, filp); - slave_inode = devpts_pty_new(inode, tty->link); + slave_inode = devpts_pty_new(inode, + MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index, + tty->link); if (IS_ERR(slave_inode)) { retval = PTR_ERR(slave_inode); goto err_release; @@ -662,6 +661,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) goto err_release; tty_unlock(tty); + tty->link->driver_data = slave_inode; return 0; err_release: tty_unlock(tty); diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index ec3bab716c0..7a20d673bb8 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -545,12 +545,9 @@ void devpts_kill_index(struct inode *ptmx_inode, int idx) mutex_unlock(&allocated_ptys_lock); } -struct inode *devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) +struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, + void *priv) { - /* tty layer puts index from devpts_new_index() in here */ - int number = tty->index; - struct tty_driver *driver = tty->driver; - dev_t device = MKDEV(driver->major, driver->minor_start+number); struct dentry *dentry; struct super_block *sb = pts_sb_from_inode(ptmx_inode); struct inode *inode; @@ -559,23 +556,18 @@ struct inode *devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) struct pts_mount_opts *opts = &fsi->mount_opts; char s[12]; - /* We're supposed to be given the slave end of a pty */ - BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY); - BUG_ON(driver->subtype != PTY_TYPE_SLAVE); - inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); - inode->i_ino = number + 3; + inode->i_ino = index + 3; inode->i_uid = opts->setuid ? opts->uid : current_fsuid(); inode->i_gid = opts->setgid ? opts->gid : current_fsgid(); inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; init_special_inode(inode, S_IFCHR|opts->mode, device); - inode->i_private = tty; - tty->driver_data = inode; + inode->i_private = priv; - sprintf(s, "%d", number); + sprintf(s, "%d", index); mutex_lock(&root->d_inode->i_mutex); @@ -613,9 +605,8 @@ void *devpts_get_priv(struct inode *pts_inode) return priv; } -void devpts_pty_kill(struct tty_struct *tty) +void devpts_pty_kill(struct inode *inode) { - struct inode *inode = tty->driver_data; struct super_block *sb = pts_sb_from_inode(inode); struct dentry *root = sb->s_root; struct dentry *dentry; diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index 4ca846f16fe..251a2090a55 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -20,11 +20,12 @@ int devpts_new_index(struct inode *ptmx_inode); void devpts_kill_index(struct inode *ptmx_inode, int idx); /* mknod in devpts */ -struct inode *devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); +struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, + void *priv); /* get private structure */ void *devpts_get_priv(struct inode *pts_inode); /* unlink */ -void devpts_pty_kill(struct tty_struct *tty); +void devpts_pty_kill(struct inode *inode); #else @@ -32,7 +33,7 @@ void devpts_pty_kill(struct tty_struct *tty); static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; } static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { } static inline struct inode *devpts_pty_new(struct inode *ptmx_inode, - struct tty_struct *tty) + dev_t device, int index, void *priv) { return ERR_PTR(-EINVAL); } @@ -40,7 +41,7 @@ static inline void *devpts_get_priv(struct inode *pts_inode) { return NULL; } -static inline void devpts_pty_kill(struct tty_struct *tty) { } +static inline void devpts_pty_kill(struct inode *inode) { } #endif From 1dcb8e6d1c23f2e021639199fdf64d5b42689207 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:30 +0200 Subject: [PATCH 006/146] TTY: devpts, document devpts inode operations Add kernel-doc texts for some devpts functions, i.e. document them. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- fs/devpts/inode.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 7a20d673bb8..472e6befc54 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -545,6 +545,15 @@ void devpts_kill_index(struct inode *ptmx_inode, int idx) mutex_unlock(&allocated_ptys_lock); } +/** + * devpts_pty_new -- create a new inode in /dev/pts/ + * @ptmx_inode: inode of the master + * @device: major+minor of the node to be created + * @index: used as a name of the node + * @priv: what's given back by devpts_get_priv + * + * The created inode is returned. Remove it from /dev/pts/ by devpts_pty_kill. + */ struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, void *priv) { @@ -585,6 +594,12 @@ struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, return inode; } +/** + * devpts_get_priv -- get private data for a slave + * @pts_inode: inode of the slave + * + * Returns whatever was passed as priv in devpts_pty_new for a given inode. + */ void *devpts_get_priv(struct inode *pts_inode) { struct dentry *dentry; @@ -605,6 +620,12 @@ void *devpts_get_priv(struct inode *pts_inode) return priv; } +/** + * devpts_pty_kill -- remove inode form /dev/pts/ + * @inode: inode of the slave to be removed + * + * This is an inverse operation of devpts_pty_new. + */ void devpts_pty_kill(struct inode *inode) { struct super_block *sb = pts_sb_from_inode(inode); From fa2ecfc5a68d85624bbd84f7d010860776b7e602 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:31 +0200 Subject: [PATCH 007/146] TTY: move devpts kill to pty Now that we have control over tty->driver_data in pty, we can just kill the /dev/pts/ in pty code too. Namely, in ->shutdown hook of tty. For pty, this is called only once, for whichever end is closed last. But we don't care, both driver_data are the inode as it used to be till now. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 9 +++++++++ drivers/tty/tty_io.c | 5 ----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 559e5b27941..2728afe52ee 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -363,6 +363,12 @@ err: return retval; } +/* this is called once with whichever end is closed last */ +static void pty_unix98_shutdown(struct tty_struct *tty) +{ + devpts_kill_index(tty->driver_data, tty->index); +} + static void pty_cleanup(struct tty_struct *tty) { kfree(tty->port); @@ -578,6 +584,7 @@ static const struct tty_operations ptm_unix98_ops = { .set_termios = pty_set_termios, .ioctl = pty_unix98_ioctl, .resize = pty_resize, + .shutdown = pty_unix98_shutdown, .cleanup = pty_cleanup }; @@ -593,6 +600,7 @@ static const struct tty_operations pty_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, + .shutdown = pty_unix98_shutdown, .cleanup = pty_cleanup, }; @@ -661,6 +669,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) goto err_release; tty_unlock(tty); + tty->driver_data = inode; tty->link->driver_data = slave_inode; return 0; err_release: diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 2ea176b2280..e835a5b8d08 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1625,7 +1625,6 @@ int tty_release(struct inode *inode, struct file *filp) struct tty_struct *tty = file_tty(filp); struct tty_struct *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; - int devpts; int idx; char buf[64]; @@ -1640,7 +1639,6 @@ int tty_release(struct inode *inode, struct file *filp) idx = tty->index; pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); - devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; /* Review: parallel close */ o_tty = tty->link; @@ -1799,9 +1797,6 @@ int tty_release(struct inode *inode, struct file *filp) release_tty(tty, idx); mutex_unlock(&tty_mutex); - /* Make this pty number available for reallocation */ - if (devpts) - devpts_kill_index(inode, idx); return 0; } From 7ee00fdb16418dd5078ec73e4a631c278a366501 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:32 +0200 Subject: [PATCH 008/146] TTY: vt, fix paste_selection ldisc handling There used to be a single tty_ldisc_ref_wait. But then, when a big-tty-mutex (BTM) was introduced, it has to be tty_ldisc_ref + tty_unlock + tty_ldisc_ref_wait + tty_lock. Later, BTM was removed from that path and tty_ldisc_ref + tty_ldisc_ref_wait remained there. But it makes no sense now. So leave there only tty_ldisc_ref_wait. And when we have a reference to an ldisc, actually use it in the loop. Otherwise it may be racy. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/selection.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 8e9b4be97a2..60b7b692605 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -341,15 +341,11 @@ int paste_selection(struct tty_struct *tty) struct tty_ldisc *ld; DECLARE_WAITQUEUE(wait, current); - console_lock(); poke_blanked_console(); console_unlock(); - /* FIXME: wtf is this supposed to achieve ? */ - ld = tty_ldisc_ref(tty); - if (!ld) - ld = tty_ldisc_ref_wait(tty); + ld = tty_ldisc_ref_wait(tty); /* FIXME: this is completely unsafe */ add_wait_queue(&vc->paste_wait, &wait); @@ -361,8 +357,7 @@ int paste_selection(struct tty_struct *tty) } count = sel_buffer_lth - pasted; count = min(count, tty->receive_room); - tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, - NULL, count); + ld->ops->receive_buf(tty, sel_buffer + pasted, NULL, count); pasted += count; } remove_wait_queue(&vc->paste_wait, &wait); From 31e121284f90bf559618330e230b286f969b6b7f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:33 +0200 Subject: [PATCH 009/146] TTY: ldisc, wait for idle ldisc in release We reintroduced tty_ldisc_wait_idle in 100eeae2c5c (TTY: restore tty_ldisc_wait_idle) and used in set_ldisc. Then we added it also to the hangup path in 92f6fa09bd453 (TTY: ldisc, do not close until there are readers). And we noted that there is one more path: ~ Before 65b770468e98 tty_ldisc_wait_idle was called also from ~ tty_ldisc_release. It is called from tty_release, so I don't think ~ we need to restore that one. Well, I was wrong. There might still be holders of an ldisc reference. Not from userspace, but drivers. If they take a reference and a user closes the device immediately after that, we have a problem. ldisc is halted and closed by TTY, but the driver still may call some ldisc's operation and cause a crash. So restore the tty_ldisc_wait_idle call also to the third location where it was before 65b770468e98 (tty-ldisc: turn ldisc user count into a proper refcount). Now we should be safe with respect to the ldisc reference counting as all* tty_ldisc_close paths are safely called with reference count of one. * Not the one in tty_ldisc_setup's fail path. But that is called before the first open finishes. So userspace does not see it yet. Even thought the driver is given the TTY already via ->install, it should not take a reference to the ldisc yet. If some driver is to do this, we should put one tty_ldisc_wait_idle also in the setup. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 0f2a2c5e704..47e3968df10 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -897,6 +897,11 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) static void tty_ldisc_kill(struct tty_struct *tty) { + /* There cannot be users from userspace now. But there still might be + * drivers holding a reference via tty_ldisc_ref. Do not steal them the + * ldisc until they are done. */ + tty_ldisc_wait_idle(tty, MAX_SCHEDULE_TIMEOUT); + mutex_lock(&tty->ldisc_mutex); /* * Now kill off the ldisc From f327b340e35b210c936cd109544e672aa7a0e49d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:34 +0200 Subject: [PATCH 010/146] TTY: hci_ldisc, remove invalid check in open hci_ldisc's open checks if tty_struct->disc_data is set. And if so it returns with an error. But nothing ensures disc_data to be NULL. And since ld->ops->open shall be called only once, we do not need the check at all. So remove it. Note that this is not an issue now, but n_tty will start using the disc_data pointer and this invalid 'if' would trigger then rendering TTYs over BT unusable. Signed-off-by: Jiri Slaby Acked-by: Marcel Holtmann Cc: Gustavo Padovan Cc: Johan Hedberg Cc: linux-bluetooth@vger.kernel.org Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_ldisc.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index c8abce3d2d9..ed0fade46ae 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -270,15 +270,10 @@ static int hci_uart_send_frame(struct sk_buff *skb) */ static int hci_uart_tty_open(struct tty_struct *tty) { - struct hci_uart *hu = (void *) tty->disc_data; + struct hci_uart *hu; BT_DBG("tty %p", tty); - /* FIXME: This btw is bogus, nothing requires the old ldisc to clear - the pointer */ - if (hu) - return -EEXIST; - /* Error if the tty has no write op instead of leaving an exploitable hole */ if (tty->ops->write == NULL) From b91939f528fa80e95a7e9592425dd1026d159b2a Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:35 +0200 Subject: [PATCH 011/146] TTY: n_tty, simplify read_buf+echo_buf allocation ldisc->open and close are called only once and cannot cross. So the tests in open and close are superfluous. Remove them. (But leave sets to NULL to ensure there is not a bug somewhere.) And when the tests are gone, handle properly failures in open. We leaked read_buf if allocation of echo_buf failed before. Now this is not the case anymore. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 8c0b7b42319..f27289d910b 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1561,14 +1561,10 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) static void n_tty_close(struct tty_struct *tty) { n_tty_flush_buffer(tty); - if (tty->read_buf) { - kfree(tty->read_buf); - tty->read_buf = NULL; - } - if (tty->echo_buf) { - kfree(tty->echo_buf); - tty->echo_buf = NULL; - } + kfree(tty->read_buf); + kfree(tty->echo_buf); + tty->read_buf = NULL; + tty->echo_buf = NULL; } /** @@ -1587,17 +1583,11 @@ static int n_tty_open(struct tty_struct *tty) return -EINVAL; /* These are ugly. Currently a malloc failure here can panic */ - if (!tty->read_buf) { - tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - if (!tty->read_buf) - return -ENOMEM; - } - if (!tty->echo_buf) { - tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + if (!tty->read_buf || !tty->echo_buf) + goto err_free_bufs; - if (!tty->echo_buf) - return -ENOMEM; - } reset_buffer_flags(tty); tty_unthrottle(tty); tty->column = 0; @@ -1605,6 +1595,11 @@ static int n_tty_open(struct tty_struct *tty) tty->minimum_to_wake = 1; tty->closing = 0; return 0; +err_free_bufs: + kfree(tty->read_buf); + kfree(tty->echo_buf); + + return -ENOMEM; } static inline int input_available_p(struct tty_struct *tty, int amt) From 3383427a7b325e50af03d6f42b9587ca66d941a6 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:36 +0200 Subject: [PATCH 012/146] TTY: n_tty, remove bogus checks * BUG_ON(!tty) in n_tty_set_termios -- it cannot be called with tty == NULL. It is called from two call sites. First, from n_tty_open where we have a valid tty. Second, as ld->ops->set_termios from tty_set_termios. But there we have a valid tty too. * if (!tty) in n_tty_open -- why would the TTY layer call ldisc's open with an invalid TTY? No it indeed does not. All call sites have a tty and dereference that. * BUG_ON(!tty->read_buf) in n_tty_read -- this used to be a valid check. The ldisc handling was broken some time ago when I added the check to ensure everything is OK. It still can catch the case, but no later than we move the buffer to ldisc data. Then there will be no read_buf in tty_struct, i.e. nothing to check for. * if (!tty->read_buf) in n_tty_receive_buf -- this should never happen. All callers of ldisc->ops->receive_ops should hold a reference to an ldisc and close (which frees read_buf) cannot be called until the reference is dropped. * if (WARN_ON(!tty->read_buf)) in n_tty_read -- the same as in the previous case. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index f27289d910b..ceae0744cbb 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1375,9 +1375,6 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char buf[64]; unsigned long cpuflags; - if (!tty->read_buf) - return; - if (tty->real_raw) { spin_lock_irqsave(&tty->read_lock, cpuflags); i = min(N_TTY_BUF_SIZE - tty->read_cnt, @@ -1471,7 +1468,6 @@ int is_ignored(int sig) static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { int canon_change = 1; - BUG_ON(!tty); if (old) canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON; @@ -1579,9 +1575,6 @@ static void n_tty_close(struct tty_struct *tty) static int n_tty_open(struct tty_struct *tty) { - if (!tty) - return -EINVAL; - /* These are ugly. Currently a malloc failure here can panic */ tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); @@ -1736,10 +1729,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, int packet; do_it_again: - - if (WARN_ON(!tty->read_buf)) - return -EAGAIN; - c = job_control(tty, file); if (c < 0) return c; @@ -1825,7 +1814,6 @@ do_it_again: /* FIXME: does n_tty_set_room need locking ? */ n_tty_set_room(tty); timeout = schedule_timeout(timeout); - BUG_ON(!tty->read_buf); continue; } __set_current_state(TASK_RUNNING); From 6c633f27ccf783e9a782b84e34aeaeb7949a3359 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:37 +0200 Subject: [PATCH 013/146] TTY: audit, stop accessing tty->icount This is a private member of n_tty. Stop accessing it. Instead, take is as an argument. This is needed to allow clean switch of the private members to a separate private structure of n_tty. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 5 +++-- drivers/tty/tty_audit.c | 15 ++++++++------- include/linux/tty.h | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index ceae0744cbb..3ebab0cfceb 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -76,7 +76,7 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, unsigned char __user *ptr) { - tty_audit_add_data(tty, &x, 1); + tty_audit_add_data(tty, &x, 1, tty->icanon); return put_user(x, ptr); } @@ -1644,7 +1644,8 @@ static int copy_from_read_buf(struct tty_struct *tty, n -= retval; is_eof = n == 1 && tty->read_buf[tty->read_tail] == EOF_CHAR(tty); - tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n); + tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n, + tty->icanon); spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); tty->read_cnt -= n; diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index b0b39b823cc..6953dc82850 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -23,7 +23,7 @@ struct tty_audit_buf { }; static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor, - int icanon) + unsigned icanon) { struct tty_audit_buf *buf; @@ -239,7 +239,8 @@ int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid) * if TTY auditing is disabled or out of memory. Otherwise, return a new * reference to the buffer. */ -static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty) +static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, + unsigned icanon) { struct tty_audit_buf *buf, *buf2; @@ -257,7 +258,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty) buf2 = tty_audit_buf_alloc(tty->driver->major, tty->driver->minor_start + tty->index, - tty->icanon); + icanon); if (buf2 == NULL) { audit_log_lost("out of memory in TTY auditing"); return NULL; @@ -287,7 +288,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty) * Audit @data of @size from @tty, if necessary. */ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, - size_t size) + size_t size, unsigned icanon) { struct tty_audit_buf *buf; int major, minor; @@ -299,7 +300,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, && tty->driver->subtype == PTY_TYPE_MASTER) return; - buf = tty_audit_buf_get(tty); + buf = tty_audit_buf_get(tty, icanon); if (!buf) return; @@ -307,11 +308,11 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, major = tty->driver->major; minor = tty->driver->minor_start + tty->index; if (buf->major != major || buf->minor != minor - || buf->icanon != tty->icanon) { + || buf->icanon != icanon) { tty_audit_buf_push_current(buf); buf->major = major; buf->minor = minor; - buf->icanon = tty->icanon; + buf->icanon = icanon; } do { size_t run; diff --git a/include/linux/tty.h b/include/linux/tty.h index f0b4eb47297..f02712da5d8 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -535,7 +535,7 @@ extern void n_tty_inherit_ops(struct tty_ldisc_ops *ops); /* tty_audit.c */ #ifdef CONFIG_AUDIT extern void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, - size_t size); + size_t size, unsigned icanon); extern void tty_audit_exit(void); extern void tty_audit_fork(struct signal_struct *sig); extern void tty_audit_tiocsti(struct tty_struct *tty, char ch); @@ -544,7 +544,7 @@ extern int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid); #else static inline void tty_audit_add_data(struct tty_struct *tty, - unsigned char *data, size_t size) + unsigned char *data, size_t size, unsigned icanon) { } static inline void tty_audit_tiocsti(struct tty_struct *tty, char ch) From 70ece7a731598ac760c2a34421fcb2de18d2713f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:38 +0200 Subject: [PATCH 014/146] TTY: n_tty, add ldisc data to n_tty All n_tty related members from tty_struct will be moved here. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 3ebab0cfceb..3d1594e10d0 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -73,6 +73,10 @@ #define ECHO_OP_SET_CANON_COL 0x81 #define ECHO_OP_ERASE_TAB 0x82 +struct n_tty_data { + char dummy; +}; + static inline int tty_put_user(struct tty_struct *tty, unsigned char x, unsigned char __user *ptr) { @@ -1556,11 +1560,15 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) static void n_tty_close(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; + n_tty_flush_buffer(tty); kfree(tty->read_buf); kfree(tty->echo_buf); + kfree(ldata); tty->read_buf = NULL; tty->echo_buf = NULL; + tty->disc_data = NULL; } /** @@ -1575,23 +1583,32 @@ static void n_tty_close(struct tty_struct *tty) static int n_tty_open(struct tty_struct *tty) { + struct n_tty_data *ldata; + + ldata = kzalloc(sizeof(*ldata), GFP_KERNEL); + if (!ldata) + goto err; + /* These are ugly. Currently a malloc failure here can panic */ tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); if (!tty->read_buf || !tty->echo_buf) goto err_free_bufs; + tty->disc_data = ldata; reset_buffer_flags(tty); tty_unthrottle(tty); tty->column = 0; n_tty_set_termios(tty, NULL); tty->minimum_to_wake = 1; tty->closing = 0; + return 0; err_free_bufs: kfree(tty->read_buf); kfree(tty->echo_buf); - + kfree(ldata); +err: return -ENOMEM; } From 53c5ee2cfb4dadc4f5c24fe671e2fbfc034c875e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:39 +0200 Subject: [PATCH 015/146] TTY: move ldisc data from tty_struct: simple members Here we start moving all the n_tty related bits from tty_struct to the newly defined n_tty_data struct in n_tty proper. In this patch primitive members and bits are moved. The rest will be done per-partes in the next patches. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 160 +++++++++++++++++++++++++------------------ drivers/tty/tty_io.c | 1 - include/linux/tty.h | 5 -- 3 files changed, 93 insertions(+), 73 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 3d1594e10d0..bd775a7c062 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -74,13 +74,20 @@ #define ECHO_OP_ERASE_TAB 0x82 struct n_tty_data { - char dummy; + unsigned int column; + unsigned long overrun_time; + int num_overrun; + + unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; + unsigned char echo_overrun:1; }; static inline int tty_put_user(struct tty_struct *tty, unsigned char x, unsigned char __user *ptr) { - tty_audit_add_data(tty, &x, 1, tty->icanon); + struct n_tty_data *ldata = tty->disc_data; + + tty_audit_add_data(tty, &x, 1, ldata->icanon); return put_user(x, ptr); } @@ -96,6 +103,7 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, static void n_tty_set_room(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int left; int old_left; @@ -115,7 +123,7 @@ static void n_tty_set_room(struct tty_struct *tty) * characters will be beeped. */ if (left <= 0) - left = tty->icanon && !tty->canon_data; + left = ldata->icanon && !tty->canon_data; old_left = tty->receive_room; tty->receive_room = left; @@ -183,6 +191,7 @@ static void check_unthrottle(struct tty_struct *tty) static void reset_buffer_flags(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; spin_lock_irqsave(&tty->read_lock, flags); @@ -190,10 +199,10 @@ static void reset_buffer_flags(struct tty_struct *tty) spin_unlock_irqrestore(&tty->read_lock, flags); mutex_lock(&tty->echo_lock); - tty->echo_pos = tty->echo_cnt = tty->echo_overrun = 0; + tty->echo_pos = tty->echo_cnt = ldata->echo_overrun = 0; mutex_unlock(&tty->echo_lock); - tty->canon_head = tty->canon_data = tty->erasing = 0; + tty->canon_head = tty->canon_data = ldata->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); n_tty_set_room(tty); } @@ -239,11 +248,12 @@ static void n_tty_flush_buffer(struct tty_struct *tty) static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; ssize_t n = 0; spin_lock_irqsave(&tty->read_lock, flags); - if (!tty->icanon) { + if (!ldata->icanon) { n = tty->read_cnt; } else if (tty->canon_data) { n = (tty->canon_head > tty->read_tail) ? @@ -305,6 +315,7 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty) static int do_output_char(unsigned char c, struct tty_struct *tty, int space) { + struct n_tty_data *ldata = tty->disc_data; int spaces; if (!space) @@ -313,48 +324,48 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) switch (c) { case '\n': if (O_ONLRET(tty)) - tty->column = 0; + ldata->column = 0; if (O_ONLCR(tty)) { if (space < 2) return -1; - tty->canon_column = tty->column = 0; + tty->canon_column = ldata->column = 0; tty->ops->write(tty, "\r\n", 2); return 2; } - tty->canon_column = tty->column; + tty->canon_column = ldata->column; break; case '\r': - if (O_ONOCR(tty) && tty->column == 0) + if (O_ONOCR(tty) && ldata->column == 0) return 0; if (O_OCRNL(tty)) { c = '\n'; if (O_ONLRET(tty)) - tty->canon_column = tty->column = 0; + tty->canon_column = ldata->column = 0; break; } - tty->canon_column = tty->column = 0; + tty->canon_column = ldata->column = 0; break; case '\t': - spaces = 8 - (tty->column & 7); + spaces = 8 - (ldata->column & 7); if (O_TABDLY(tty) == XTABS) { if (space < spaces) return -1; - tty->column += spaces; + ldata->column += spaces; tty->ops->write(tty, " ", spaces); return spaces; } - tty->column += spaces; + ldata->column += spaces; break; case '\b': - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; break; default: if (!iscntrl(c)) { if (O_OLCUC(tty)) c = toupper(c); if (!is_continuation(c, tty)) - tty->column++; + ldata->column++; } break; } @@ -415,6 +426,7 @@ static int process_output(unsigned char c, struct tty_struct *tty) static ssize_t process_output_block(struct tty_struct *tty, const unsigned char *buf, unsigned int nr) { + struct n_tty_data *ldata = tty->disc_data; int space; int i; const unsigned char *cp; @@ -435,30 +447,30 @@ static ssize_t process_output_block(struct tty_struct *tty, switch (c) { case '\n': if (O_ONLRET(tty)) - tty->column = 0; + ldata->column = 0; if (O_ONLCR(tty)) goto break_out; - tty->canon_column = tty->column; + tty->canon_column = ldata->column; break; case '\r': - if (O_ONOCR(tty) && tty->column == 0) + if (O_ONOCR(tty) && ldata->column == 0) goto break_out; if (O_OCRNL(tty)) goto break_out; - tty->canon_column = tty->column = 0; + tty->canon_column = ldata->column = 0; break; case '\t': goto break_out; case '\b': - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; break; default: if (!iscntrl(c)) { if (O_OLCUC(tty)) goto break_out; if (!is_continuation(c, tty)) - tty->column++; + ldata->column++; } break; } @@ -498,6 +510,7 @@ break_out: static void process_echoes(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int space, nr; unsigned char c; unsigned char *cp, *buf_end; @@ -559,22 +572,22 @@ static void process_echoes(struct tty_struct *tty) space -= num_bs; while (num_bs--) { tty_put_char(tty, '\b'); - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; } cp += 3; nr -= 3; break; case ECHO_OP_SET_CANON_COL: - tty->canon_column = tty->column; + tty->canon_column = ldata->column; cp += 2; nr -= 2; break; case ECHO_OP_MOVE_BACK_COL: - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; cp += 2; nr -= 2; break; @@ -586,7 +599,7 @@ static void process_echoes(struct tty_struct *tty) break; } tty_put_char(tty, ECHO_OP_START); - tty->column++; + ldata->column++; space--; cp += 2; nr -= 2; @@ -608,7 +621,7 @@ static void process_echoes(struct tty_struct *tty) } tty_put_char(tty, '^'); tty_put_char(tty, op ^ 0100); - tty->column += 2; + ldata->column += 2; space -= 2; cp += 2; nr -= 2; @@ -641,14 +654,14 @@ static void process_echoes(struct tty_struct *tty) if (nr == 0) { tty->echo_pos = 0; tty->echo_cnt = 0; - tty->echo_overrun = 0; + ldata->echo_overrun = 0; } else { int num_processed = tty->echo_cnt - nr; tty->echo_pos += num_processed; tty->echo_pos &= N_TTY_BUF_SIZE - 1; tty->echo_cnt = nr; if (num_processed > 0) - tty->echo_overrun = 0; + ldata->echo_overrun = 0; } mutex_unlock(&tty->echo_lock); @@ -670,6 +683,7 @@ static void process_echoes(struct tty_struct *tty) static void add_echo_byte(unsigned char c, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int new_byte_pos; if (tty->echo_cnt == N_TTY_BUF_SIZE) { @@ -695,7 +709,7 @@ static void add_echo_byte(unsigned char c, struct tty_struct *tty) } tty->echo_pos &= N_TTY_BUF_SIZE - 1; - tty->echo_overrun = 1; + ldata->echo_overrun = 1; } else { new_byte_pos = tty->echo_pos + tty->echo_cnt; new_byte_pos &= N_TTY_BUF_SIZE - 1; @@ -845,9 +859,10 @@ static void echo_char(unsigned char c, struct tty_struct *tty) static inline void finish_erasing(struct tty_struct *tty) { - if (tty->erasing) { + struct n_tty_data *ldata = tty->disc_data; + if (ldata->erasing) { echo_char_raw('/', tty); - tty->erasing = 0; + ldata->erasing = 0; } } @@ -865,6 +880,7 @@ static inline void finish_erasing(struct tty_struct *tty) static void eraser(unsigned char c, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; enum { ERASE, WERASE, KILL } kill_type; int head, seen_alnums, cnt; unsigned long flags; @@ -932,9 +948,9 @@ static void eraser(unsigned char c, struct tty_struct *tty) spin_unlock_irqrestore(&tty->read_lock, flags); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { - if (!tty->erasing) { + if (!ldata->erasing) { echo_char_raw('\\', tty); - tty->erasing = 1; + ldata->erasing = 1; } /* if cnt > 1, output a multi-byte character */ echo_char(c, tty); @@ -1056,16 +1072,17 @@ static inline void n_tty_receive_break(struct tty_struct *tty) static inline void n_tty_receive_overrun(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; char buf[64]; - tty->num_overrun++; - if (time_before(tty->overrun_time, jiffies - HZ) || - time_after(tty->overrun_time, jiffies)) { + ldata->num_overrun++; + if (time_after(jiffies, ldata->overrun_time + HZ) || + time_after(ldata->overrun_time, jiffies)) { printk(KERN_WARNING "%s: %d input overrun(s)\n", tty_name(tty, buf), - tty->num_overrun); - tty->overrun_time = jiffies; - tty->num_overrun = 0; + ldata->num_overrun); + ldata->overrun_time = jiffies; + ldata->num_overrun = 0; } } @@ -1105,10 +1122,11 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty, static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; int parmrk; - if (tty->raw) { + if (ldata->raw) { put_tty_queue(c, tty); return; } @@ -1147,8 +1165,8 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) * handle specially, do shortcut processing to speed things * up. */ - if (!test_bit(c, tty->process_char_map) || tty->lnext) { - tty->lnext = 0; + if (!test_bit(c, tty->process_char_map) || ldata->lnext) { + ldata->lnext = 0; parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { /* beep if no space */ @@ -1222,7 +1240,7 @@ send_signal: } else if (c == '\n' && I_INLCR(tty)) c = '\r'; - if (tty->icanon) { + if (ldata->icanon) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); @@ -1230,7 +1248,7 @@ send_signal: return; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { - tty->lnext = 1; + ldata->lnext = 1; if (L_ECHO(tty)) { finish_erasing(tty); if (L_ECHOCTL(tty)) { @@ -1373,13 +1391,14 @@ static void n_tty_write_wakeup(struct tty_struct *tty) static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { + struct n_tty_data *ldata = tty->disc_data; const unsigned char *p; char *f, flags = TTY_NORMAL; int i; char buf[64]; unsigned long cpuflags; - if (tty->real_raw) { + if (ldata->real_raw) { spin_lock_irqsave(&tty->read_lock, cpuflags); i = min(N_TTY_BUF_SIZE - tty->read_cnt, N_TTY_BUF_SIZE - tty->read_head); @@ -1427,7 +1446,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, n_tty_set_room(tty); - if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || + if ((!ldata->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) @@ -1471,6 +1490,7 @@ int is_ignored(int sig) static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { + struct n_tty_data *ldata = tty->disc_data; int canon_change = 1; if (old) @@ -1479,16 +1499,16 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) memset(&tty->read_flags, 0, sizeof tty->read_flags); tty->canon_head = tty->read_tail; tty->canon_data = 0; - tty->erasing = 0; + ldata->erasing = 0; } if (canon_change && !L_ICANON(tty) && tty->read_cnt) wake_up_interruptible(&tty->read_wait); - tty->icanon = (L_ICANON(tty) != 0); + ldata->icanon = (L_ICANON(tty) != 0); if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { - tty->raw = 1; - tty->real_raw = 1; + ldata->raw = 1; + ldata->real_raw = 1; n_tty_set_room(tty); return; } @@ -1531,16 +1551,16 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) set_bit(SUSP_CHAR(tty), tty->process_char_map); } clear_bit(__DISABLED_CHAR, tty->process_char_map); - tty->raw = 0; - tty->real_raw = 0; + ldata->raw = 0; + ldata->real_raw = 0; } else { - tty->raw = 1; + ldata->raw = 1; if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) && (I_IGNPAR(tty) || !I_INPCK(tty)) && (tty->driver->flags & TTY_DRIVER_REAL_RAW)) - tty->real_raw = 1; + ldata->real_raw = 1; else - tty->real_raw = 0; + ldata->real_raw = 0; } n_tty_set_room(tty); /* The termios change make the tty ready for I/O */ @@ -1589,6 +1609,8 @@ static int n_tty_open(struct tty_struct *tty) if (!ldata) goto err; + ldata->overrun_time = jiffies; + /* These are ugly. Currently a malloc failure here can panic */ tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); @@ -1598,7 +1620,7 @@ static int n_tty_open(struct tty_struct *tty) tty->disc_data = ldata; reset_buffer_flags(tty); tty_unthrottle(tty); - tty->column = 0; + ldata->column = 0; n_tty_set_termios(tty, NULL); tty->minimum_to_wake = 1; tty->closing = 0; @@ -1614,8 +1636,10 @@ err: static inline int input_available_p(struct tty_struct *tty, int amt) { + struct n_tty_data *ldata = tty->disc_data; + tty_flush_to_ldisc(tty); - if (tty->icanon && !L_EXTPROC(tty)) { + if (ldata->icanon && !L_EXTPROC(tty)) { if (tty->canon_data) return 1; } else if (tty->read_cnt >= (amt ? amt : 1)) @@ -1646,6 +1670,7 @@ static int copy_from_read_buf(struct tty_struct *tty, size_t *nr) { + struct n_tty_data *ldata = tty->disc_data; int retval; size_t n; unsigned long flags; @@ -1662,12 +1687,12 @@ static int copy_from_read_buf(struct tty_struct *tty, is_eof = n == 1 && tty->read_buf[tty->read_tail] == EOF_CHAR(tty); tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n, - tty->icanon); + ldata->icanon); spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); tty->read_cnt -= n; /* Turn single EOF into zero-length read */ - if (L_EXTPROC(tty) && tty->icanon && is_eof && !tty->read_cnt) + if (L_EXTPROC(tty) && ldata->icanon && is_eof && !tty->read_cnt) n = 0; spin_unlock_irqrestore(&tty->read_lock, flags); *b += n; @@ -1736,6 +1761,7 @@ static int job_control(struct tty_struct *tty, struct file *file) static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr) { + struct n_tty_data *ldata = tty->disc_data; unsigned char __user *b = buf; DECLARE_WAITQUEUE(wait, current); int c; @@ -1753,7 +1779,7 @@ do_it_again: minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; - if (!tty->icanon) { + if (!ldata->icanon) { time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { @@ -1846,7 +1872,7 @@ do_it_again: nr--; } - if (tty->icanon && !L_EXTPROC(tty)) { + if (ldata->icanon && !L_EXTPROC(tty)) { /* N.B. avoid overrun if nr == 0 */ spin_lock_irqsave(&tty->read_lock, flags); while (nr && tty->read_cnt) { diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e835a5b8d08..67b024ca16e 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2932,7 +2932,6 @@ void initialize_tty_struct(struct tty_struct *tty, tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; - tty->overrun_time = jiffies; tty_buffer_init(tty); mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); diff --git a/include/linux/tty.h b/include/linux/tty.h index f02712da5d8..de590cec973 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -270,13 +270,8 @@ struct tty_struct { * historical reasons, this is included in the tty structure. * Mostly locked by the BKL. */ - unsigned int column; - unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; unsigned char closing:1; - unsigned char echo_overrun:1; unsigned short minimum_to_wake; - unsigned long overrun_time; - int num_overrun; unsigned long process_char_map[256/(8*sizeof(unsigned long))]; char *read_buf; int read_head; From 3fe780b379fac2e1eeb5907ee7c864756ce7ec83 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:40 +0200 Subject: [PATCH 016/146] TTY: move ldisc data from tty_struct: bitmaps Here we move bitmaps and use DECLARE_BITMAP to declare them in the new structure. And instead of memset, we use bitmap_zero as it is more appropriate. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 52 ++++++++++++++++++++++++--------------------- include/linux/tty.h | 2 -- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index bd775a7c062..702dd4adbdc 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -80,6 +80,9 @@ struct n_tty_data { unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; unsigned char echo_overrun:1; + + DECLARE_BITMAP(process_char_map, 256); + DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); }; static inline int tty_put_user(struct tty_struct *tty, unsigned char x, @@ -203,7 +206,7 @@ static void reset_buffer_flags(struct tty_struct *tty) mutex_unlock(&tty->echo_lock); tty->canon_head = tty->canon_data = ldata->erasing = 0; - memset(&tty->read_flags, 0, sizeof tty->read_flags); + bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); n_tty_set_room(tty); } @@ -1165,7 +1168,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) * handle specially, do shortcut processing to speed things * up. */ - if (!test_bit(c, tty->process_char_map) || ldata->lnext) { + if (!test_bit(c, ldata->process_char_map) || ldata->lnext) { ldata->lnext = 0; parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { @@ -1321,7 +1324,7 @@ send_signal: handle_newline: spin_lock_irqsave(&tty->read_lock, flags); - set_bit(tty->read_head, tty->read_flags); + set_bit(tty->read_head, ldata->read_flags); put_tty_queue_nolock(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; @@ -1496,7 +1499,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) if (old) canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON; if (canon_change) { - memset(&tty->read_flags, 0, sizeof tty->read_flags); + bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); tty->canon_head = tty->read_tail; tty->canon_data = 0; ldata->erasing = 0; @@ -1516,41 +1519,41 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || I_PARMRK(tty)) { - memset(tty->process_char_map, 0, 256/8); + bitmap_zero(ldata->process_char_map, 256); if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', tty->process_char_map); + set_bit('\r', ldata->process_char_map); if (I_INLCR(tty)) - set_bit('\n', tty->process_char_map); + set_bit('\n', ldata->process_char_map); if (L_ICANON(tty)) { - set_bit(ERASE_CHAR(tty), tty->process_char_map); - set_bit(KILL_CHAR(tty), tty->process_char_map); - set_bit(EOF_CHAR(tty), tty->process_char_map); - set_bit('\n', tty->process_char_map); - set_bit(EOL_CHAR(tty), tty->process_char_map); + set_bit(ERASE_CHAR(tty), ldata->process_char_map); + set_bit(KILL_CHAR(tty), ldata->process_char_map); + set_bit(EOF_CHAR(tty), ldata->process_char_map); + set_bit('\n', ldata->process_char_map); + set_bit(EOL_CHAR(tty), ldata->process_char_map); if (L_IEXTEN(tty)) { set_bit(WERASE_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); set_bit(LNEXT_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); set_bit(EOL2_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); } } if (I_IXON(tty)) { - set_bit(START_CHAR(tty), tty->process_char_map); - set_bit(STOP_CHAR(tty), tty->process_char_map); + set_bit(START_CHAR(tty), ldata->process_char_map); + set_bit(STOP_CHAR(tty), ldata->process_char_map); } if (L_ISIG(tty)) { - set_bit(INTR_CHAR(tty), tty->process_char_map); - set_bit(QUIT_CHAR(tty), tty->process_char_map); - set_bit(SUSP_CHAR(tty), tty->process_char_map); + set_bit(INTR_CHAR(tty), ldata->process_char_map); + set_bit(QUIT_CHAR(tty), ldata->process_char_map); + set_bit(SUSP_CHAR(tty), ldata->process_char_map); } - clear_bit(__DISABLED_CHAR, tty->process_char_map); + clear_bit(__DISABLED_CHAR, ldata->process_char_map); ldata->raw = 0; ldata->real_raw = 0; } else { @@ -1879,7 +1882,7 @@ do_it_again: int eol; eol = test_and_clear_bit(tty->read_tail, - tty->read_flags); + ldata->read_flags); c = tty->read_buf[tty->read_tail]; tty->read_tail = ((tty->read_tail+1) & (N_TTY_BUF_SIZE-1)); @@ -2105,6 +2108,7 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, static unsigned long inq_canon(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int nr, head, tail; if (!tty->canon_data) @@ -2114,7 +2118,7 @@ static unsigned long inq_canon(struct tty_struct *tty) nr = (head - tail) & (N_TTY_BUF_SIZE-1); /* Skip EOF-chars.. */ while (head != tail) { - if (test_bit(tail, tty->read_flags) && + if (test_bit(tail, ldata->read_flags) && tty->read_buf[tail] == __DISABLED_CHAR) nr--; tail = (tail+1) & (N_TTY_BUF_SIZE-1); diff --git a/include/linux/tty.h b/include/linux/tty.h index de590cec973..2161e6b5a94 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -272,12 +272,10 @@ struct tty_struct { */ unsigned char closing:1; unsigned short minimum_to_wake; - unsigned long process_char_map[256/(8*sizeof(unsigned long))]; char *read_buf; int read_head; int read_tail; int read_cnt; - unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))]; unsigned char *echo_buf; unsigned int echo_pos; unsigned int echo_cnt; From ba2e68ac6157004ee4922fb39ebd9459bbae883e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:41 +0200 Subject: [PATCH 017/146] TTY: move ldisc data from tty_struct: read_* and echo_* and canon_* stuff All the ring-buffers... Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 260 +++++++++++++++++++++++--------------------- include/linux/tty.h | 10 -- 2 files changed, 137 insertions(+), 133 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 702dd4adbdc..4794537a50f 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -83,6 +83,19 @@ struct n_tty_data { DECLARE_BITMAP(process_char_map, 256); DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); + + char *read_buf; + int read_head; + int read_tail; + int read_cnt; + + unsigned char *echo_buf; + unsigned int echo_pos; + unsigned int echo_cnt; + + int canon_data; + unsigned long canon_head; + unsigned int canon_column; }; static inline int tty_put_user(struct tty_struct *tty, unsigned char x, @@ -110,14 +123,14 @@ static void n_tty_set_room(struct tty_struct *tty) int left; int old_left; - /* tty->read_cnt is not read locked ? */ + /* ldata->read_cnt is not read locked ? */ if (I_PARMRK(tty)) { /* Multiply read_cnt by 3, since each byte might take up to * three times as many spaces when PARMRK is set (depending on * its flags, e.g. parity error). */ - left = N_TTY_BUF_SIZE - tty->read_cnt * 3 - 1; + left = N_TTY_BUF_SIZE - ldata->read_cnt * 3 - 1; } else - left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + left = N_TTY_BUF_SIZE - ldata->read_cnt - 1; /* * If we are doing input canonicalization, and there are no @@ -126,7 +139,7 @@ static void n_tty_set_room(struct tty_struct *tty) * characters will be beeped. */ if (left <= 0) - left = ldata->icanon && !tty->canon_data; + left = ldata->icanon && !ldata->canon_data; old_left = tty->receive_room; tty->receive_room = left; @@ -137,10 +150,12 @@ static void n_tty_set_room(struct tty_struct *tty) static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) { - if (tty->read_cnt < N_TTY_BUF_SIZE) { - tty->read_buf[tty->read_head] = c; - tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1); - tty->read_cnt++; + struct n_tty_data *ldata = tty->disc_data; + + if (ldata->read_cnt < N_TTY_BUF_SIZE) { + ldata->read_buf[ldata->read_head] = c; + ldata->read_head = (ldata->read_head + 1) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt++; } } @@ -198,14 +213,14 @@ static void reset_buffer_flags(struct tty_struct *tty) unsigned long flags; spin_lock_irqsave(&tty->read_lock, flags); - tty->read_head = tty->read_tail = tty->read_cnt = 0; + ldata->read_head = ldata->read_tail = ldata->read_cnt = 0; spin_unlock_irqrestore(&tty->read_lock, flags); mutex_lock(&tty->echo_lock); - tty->echo_pos = tty->echo_cnt = ldata->echo_overrun = 0; + ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0; mutex_unlock(&tty->echo_lock); - tty->canon_head = tty->canon_data = ldata->erasing = 0; + ldata->canon_head = ldata->canon_data = ldata->erasing = 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); n_tty_set_room(tty); } @@ -257,11 +272,11 @@ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) spin_lock_irqsave(&tty->read_lock, flags); if (!ldata->icanon) { - n = tty->read_cnt; - } else if (tty->canon_data) { - n = (tty->canon_head > tty->read_tail) ? - tty->canon_head - tty->read_tail : - tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail); + n = ldata->read_cnt; + } else if (ldata->canon_data) { + n = (ldata->canon_head > ldata->read_tail) ? + ldata->canon_head - ldata->read_tail : + ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail); } spin_unlock_irqrestore(&tty->read_lock, flags); return n; @@ -331,11 +346,11 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) if (O_ONLCR(tty)) { if (space < 2) return -1; - tty->canon_column = ldata->column = 0; + ldata->canon_column = ldata->column = 0; tty->ops->write(tty, "\r\n", 2); return 2; } - tty->canon_column = ldata->column; + ldata->canon_column = ldata->column; break; case '\r': if (O_ONOCR(tty) && ldata->column == 0) @@ -343,10 +358,10 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) if (O_OCRNL(tty)) { c = '\n'; if (O_ONLRET(tty)) - tty->canon_column = ldata->column = 0; + ldata->canon_column = ldata->column = 0; break; } - tty->canon_column = ldata->column = 0; + ldata->canon_column = ldata->column = 0; break; case '\t': spaces = 8 - (ldata->column & 7); @@ -453,14 +468,14 @@ static ssize_t process_output_block(struct tty_struct *tty, ldata->column = 0; if (O_ONLCR(tty)) goto break_out; - tty->canon_column = ldata->column; + ldata->canon_column = ldata->column; break; case '\r': if (O_ONOCR(tty) && ldata->column == 0) goto break_out; if (O_OCRNL(tty)) goto break_out; - tty->canon_column = ldata->column = 0; + ldata->canon_column = ldata->column = 0; break; case '\t': goto break_out; @@ -518,7 +533,7 @@ static void process_echoes(struct tty_struct *tty) unsigned char c; unsigned char *cp, *buf_end; - if (!tty->echo_cnt) + if (!ldata->echo_cnt) return; mutex_lock(&tty->output_lock); @@ -526,9 +541,9 @@ static void process_echoes(struct tty_struct *tty) space = tty_write_room(tty); - buf_end = tty->echo_buf + N_TTY_BUF_SIZE; - cp = tty->echo_buf + tty->echo_pos; - nr = tty->echo_cnt; + buf_end = ldata->echo_buf + N_TTY_BUF_SIZE; + cp = ldata->echo_buf + ldata->echo_pos; + nr = ldata->echo_cnt; while (nr > 0) { c = *cp; if (c == ECHO_OP_START) { @@ -565,7 +580,7 @@ static void process_echoes(struct tty_struct *tty) * Otherwise, tab spacing is normal. */ if (!(num_chars & 0x80)) - num_chars += tty->canon_column; + num_chars += ldata->canon_column; num_bs = 8 - (num_chars & 7); if (num_bs > space) { @@ -583,7 +598,7 @@ static void process_echoes(struct tty_struct *tty) break; case ECHO_OP_SET_CANON_COL: - tty->canon_column = ldata->column; + ldata->canon_column = ldata->column; cp += 2; nr -= 2; break; @@ -655,14 +670,14 @@ static void process_echoes(struct tty_struct *tty) } if (nr == 0) { - tty->echo_pos = 0; - tty->echo_cnt = 0; + ldata->echo_pos = 0; + ldata->echo_cnt = 0; ldata->echo_overrun = 0; } else { - int num_processed = tty->echo_cnt - nr; - tty->echo_pos += num_processed; - tty->echo_pos &= N_TTY_BUF_SIZE - 1; - tty->echo_cnt = nr; + int num_processed = ldata->echo_cnt - nr; + ldata->echo_pos += num_processed; + ldata->echo_pos &= N_TTY_BUF_SIZE - 1; + ldata->echo_cnt = nr; if (num_processed > 0) ldata->echo_overrun = 0; } @@ -689,37 +704,37 @@ static void add_echo_byte(unsigned char c, struct tty_struct *tty) struct n_tty_data *ldata = tty->disc_data; int new_byte_pos; - if (tty->echo_cnt == N_TTY_BUF_SIZE) { + if (ldata->echo_cnt == N_TTY_BUF_SIZE) { /* Circular buffer is already at capacity */ - new_byte_pos = tty->echo_pos; + new_byte_pos = ldata->echo_pos; /* * Since the buffer start position needs to be advanced, * be sure to step by a whole operation byte group. */ - if (tty->echo_buf[tty->echo_pos] == ECHO_OP_START) { - if (tty->echo_buf[(tty->echo_pos + 1) & + if (ldata->echo_buf[ldata->echo_pos] == ECHO_OP_START) { + if (ldata->echo_buf[(ldata->echo_pos + 1) & (N_TTY_BUF_SIZE - 1)] == ECHO_OP_ERASE_TAB) { - tty->echo_pos += 3; - tty->echo_cnt -= 2; + ldata->echo_pos += 3; + ldata->echo_cnt -= 2; } else { - tty->echo_pos += 2; - tty->echo_cnt -= 1; + ldata->echo_pos += 2; + ldata->echo_cnt -= 1; } } else { - tty->echo_pos++; + ldata->echo_pos++; } - tty->echo_pos &= N_TTY_BUF_SIZE - 1; + ldata->echo_pos &= N_TTY_BUF_SIZE - 1; ldata->echo_overrun = 1; } else { - new_byte_pos = tty->echo_pos + tty->echo_cnt; + new_byte_pos = ldata->echo_pos + ldata->echo_cnt; new_byte_pos &= N_TTY_BUF_SIZE - 1; - tty->echo_cnt++; + ldata->echo_cnt++; } - tty->echo_buf[new_byte_pos] = c; + ldata->echo_buf[new_byte_pos] = c; } /** @@ -889,7 +904,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) unsigned long flags; /* FIXME: locking needed ? */ - if (tty->read_head == tty->canon_head) { + if (ldata->read_head == ldata->canon_head) { /* process_output('\a', tty); */ /* what do you think? */ return; } @@ -900,17 +915,17 @@ static void eraser(unsigned char c, struct tty_struct *tty) else { if (!L_ECHO(tty)) { spin_lock_irqsave(&tty->read_lock, flags); - tty->read_cnt -= ((tty->read_head - tty->canon_head) & + ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & (N_TTY_BUF_SIZE - 1)); - tty->read_head = tty->canon_head; + ldata->read_head = ldata->canon_head; spin_unlock_irqrestore(&tty->read_lock, flags); return; } if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { spin_lock_irqsave(&tty->read_lock, flags); - tty->read_cnt -= ((tty->read_head - tty->canon_head) & + ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & (N_TTY_BUF_SIZE - 1)); - tty->read_head = tty->canon_head; + ldata->read_head = ldata->canon_head; spin_unlock_irqrestore(&tty->read_lock, flags); finish_erasing(tty); echo_char(KILL_CHAR(tty), tty); @@ -924,14 +939,14 @@ static void eraser(unsigned char c, struct tty_struct *tty) seen_alnums = 0; /* FIXME: Locking ?? */ - while (tty->read_head != tty->canon_head) { - head = tty->read_head; + while (ldata->read_head != ldata->canon_head) { + head = ldata->read_head; /* erase a single possibly multibyte character */ do { head = (head - 1) & (N_TTY_BUF_SIZE-1); - c = tty->read_buf[head]; - } while (is_continuation(c, tty) && head != tty->canon_head); + c = ldata->read_buf[head]; + } while (is_continuation(c, tty) && head != ldata->canon_head); /* do not partially erase */ if (is_continuation(c, tty)) @@ -944,10 +959,10 @@ static void eraser(unsigned char c, struct tty_struct *tty) else if (seen_alnums) break; } - cnt = (tty->read_head - head) & (N_TTY_BUF_SIZE-1); + cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1); spin_lock_irqsave(&tty->read_lock, flags); - tty->read_head = head; - tty->read_cnt -= cnt; + ldata->read_head = head; + ldata->read_cnt -= cnt; spin_unlock_irqrestore(&tty->read_lock, flags); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { @@ -959,7 +974,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) echo_char(c, tty); while (--cnt > 0) { head = (head+1) & (N_TTY_BUF_SIZE-1); - echo_char_raw(tty->read_buf[head], tty); + echo_char_raw(ldata->read_buf[head], tty); echo_move_back_col(tty); } } else if (kill_type == ERASE && !L_ECHOE(tty)) { @@ -967,7 +982,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) } else if (c == '\t') { unsigned int num_chars = 0; int after_tab = 0; - unsigned long tail = tty->read_head; + unsigned long tail = ldata->read_head; /* * Count the columns used for characters @@ -976,9 +991,9 @@ static void eraser(unsigned char c, struct tty_struct *tty) * This info is used to go back the correct * number of columns. */ - while (tail != tty->canon_head) { + while (tail != ldata->canon_head) { tail = (tail-1) & (N_TTY_BUF_SIZE-1); - c = tty->read_buf[tail]; + c = ldata->read_buf[tail]; if (c == '\t') { after_tab = 1; break; @@ -1006,7 +1021,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) if (kill_type == ERASE) break; } - if (tty->read_head == tty->canon_head && L_ECHO(tty)) + if (ldata->read_head == ldata->canon_head && L_ECHO(tty)) finish_erasing(tty); } @@ -1171,7 +1186,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) if (!test_bit(c, ldata->process_char_map) || ldata->lnext) { ldata->lnext = 0; parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { + if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { /* beep if no space */ if (L_ECHO(tty)) process_output('\a', tty); @@ -1180,7 +1195,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) if (L_ECHO(tty)) { finish_erasing(tty); /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) + if (ldata->canon_head == ldata->read_head) echo_set_canon_col(tty); echo_char(c, tty); process_echoes(tty); @@ -1264,20 +1279,20 @@ send_signal: } if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { - unsigned long tail = tty->canon_head; + unsigned long tail = ldata->canon_head; finish_erasing(tty); echo_char(c, tty); echo_char_raw('\n', tty); - while (tail != tty->read_head) { - echo_char(tty->read_buf[tail], tty); + while (tail != ldata->read_head) { + echo_char(ldata->read_buf[tail], tty); tail = (tail+1) & (N_TTY_BUF_SIZE-1); } process_echoes(tty); return; } if (c == '\n') { - if (tty->read_cnt >= N_TTY_BUF_SIZE) { + if (ldata->read_cnt >= N_TTY_BUF_SIZE) { if (L_ECHO(tty)) process_output('\a', tty); return; @@ -1289,9 +1304,9 @@ send_signal: goto handle_newline; } if (c == EOF_CHAR(tty)) { - if (tty->read_cnt >= N_TTY_BUF_SIZE) + if (ldata->read_cnt >= N_TTY_BUF_SIZE) return; - if (tty->canon_head != tty->read_head) + if (ldata->canon_head != ldata->read_head) set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; @@ -1300,7 +1315,7 @@ send_signal: (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) { + if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) { if (L_ECHO(tty)) process_output('\a', tty); return; @@ -1310,7 +1325,7 @@ send_signal: */ if (L_ECHO(tty)) { /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) + if (ldata->canon_head == ldata->read_head) echo_set_canon_col(tty); echo_char(c, tty); process_echoes(tty); @@ -1324,10 +1339,10 @@ send_signal: handle_newline: spin_lock_irqsave(&tty->read_lock, flags); - set_bit(tty->read_head, ldata->read_flags); + set_bit(ldata->read_head, ldata->read_flags); put_tty_queue_nolock(c, tty); - tty->canon_head = tty->read_head; - tty->canon_data++; + ldata->canon_head = ldata->read_head; + ldata->canon_data++; spin_unlock_irqrestore(&tty->read_lock, flags); kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) @@ -1337,7 +1352,7 @@ handle_newline: } parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { + if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { /* beep if no space */ if (L_ECHO(tty)) process_output('\a', tty); @@ -1349,7 +1364,7 @@ handle_newline: echo_char_raw('\n', tty); else { /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) + if (ldata->canon_head == ldata->read_head) echo_set_canon_col(tty); echo_char(c, tty); } @@ -1403,21 +1418,21 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, if (ldata->real_raw) { spin_lock_irqsave(&tty->read_lock, cpuflags); - i = min(N_TTY_BUF_SIZE - tty->read_cnt, - N_TTY_BUF_SIZE - tty->read_head); + i = min(N_TTY_BUF_SIZE - ldata->read_cnt, + N_TTY_BUF_SIZE - ldata->read_head); i = min(count, i); - 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; + memcpy(ldata->read_buf + ldata->read_head, cp, i); + ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt += i; cp += i; count -= i; - i = min(N_TTY_BUF_SIZE - tty->read_cnt, - N_TTY_BUF_SIZE - tty->read_head); + i = min(N_TTY_BUF_SIZE - ldata->read_cnt, + N_TTY_BUF_SIZE - ldata->read_head); i = min(count, i); - 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; + memcpy(ldata->read_buf + ldata->read_head, cp, i); + ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt += i; spin_unlock_irqrestore(&tty->read_lock, cpuflags); } else { for (i = count, p = cp, f = fp; i; i--, p++) { @@ -1449,7 +1464,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, n_tty_set_room(tty); - if ((!ldata->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || + if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) @@ -1500,12 +1515,12 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON; if (canon_change) { bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); - tty->canon_head = tty->read_tail; - tty->canon_data = 0; + ldata->canon_head = ldata->read_tail; + ldata->canon_data = 0; ldata->erasing = 0; } - if (canon_change && !L_ICANON(tty) && tty->read_cnt) + if (canon_change && !L_ICANON(tty) && ldata->read_cnt) wake_up_interruptible(&tty->read_wait); ldata->icanon = (L_ICANON(tty) != 0); @@ -1586,11 +1601,9 @@ static void n_tty_close(struct tty_struct *tty) struct n_tty_data *ldata = tty->disc_data; n_tty_flush_buffer(tty); - kfree(tty->read_buf); - kfree(tty->echo_buf); + kfree(ldata->read_buf); + kfree(ldata->echo_buf); kfree(ldata); - tty->read_buf = NULL; - tty->echo_buf = NULL; tty->disc_data = NULL; } @@ -1615,9 +1628,9 @@ static int n_tty_open(struct tty_struct *tty) ldata->overrun_time = jiffies; /* These are ugly. Currently a malloc failure here can panic */ - tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - if (!tty->read_buf || !tty->echo_buf) + ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + ldata->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + if (!ldata->read_buf || !ldata->echo_buf) goto err_free_bufs; tty->disc_data = ldata; @@ -1630,8 +1643,8 @@ static int n_tty_open(struct tty_struct *tty) return 0; err_free_bufs: - kfree(tty->read_buf); - kfree(tty->echo_buf); + kfree(ldata->read_buf); + kfree(ldata->echo_buf); kfree(ldata); err: return -ENOMEM; @@ -1643,9 +1656,9 @@ static inline int input_available_p(struct tty_struct *tty, int amt) tty_flush_to_ldisc(tty); if (ldata->icanon && !L_EXTPROC(tty)) { - if (tty->canon_data) + if (ldata->canon_data) return 1; - } else if (tty->read_cnt >= (amt ? amt : 1)) + } else if (ldata->read_cnt >= (amt ? amt : 1)) return 1; return 0; @@ -1681,21 +1694,21 @@ static int copy_from_read_buf(struct tty_struct *tty, retval = 0; spin_lock_irqsave(&tty->read_lock, flags); - n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail); + n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail); n = min(*nr, n); spin_unlock_irqrestore(&tty->read_lock, flags); if (n) { - retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n); + retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n); n -= retval; is_eof = n == 1 && - tty->read_buf[tty->read_tail] == EOF_CHAR(tty); - tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n, + ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty); + tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n, ldata->icanon); spin_lock_irqsave(&tty->read_lock, flags); - tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); - tty->read_cnt -= n; + ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt -= n; /* Turn single EOF into zero-length read */ - if (L_EXTPROC(tty) && ldata->icanon && is_eof && !tty->read_cnt) + if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt) n = 0; spin_unlock_irqrestore(&tty->read_lock, flags); *b += n; @@ -1878,22 +1891,22 @@ do_it_again: if (ldata->icanon && !L_EXTPROC(tty)) { /* N.B. avoid overrun if nr == 0 */ spin_lock_irqsave(&tty->read_lock, flags); - while (nr && tty->read_cnt) { + while (nr && ldata->read_cnt) { int eol; - eol = test_and_clear_bit(tty->read_tail, + eol = test_and_clear_bit(ldata->read_tail, ldata->read_flags); - c = tty->read_buf[tty->read_tail]; - tty->read_tail = ((tty->read_tail+1) & + c = ldata->read_buf[ldata->read_tail]; + ldata->read_tail = ((ldata->read_tail+1) & (N_TTY_BUF_SIZE-1)); - tty->read_cnt--; + ldata->read_cnt--; if (eol) { /* this test should be redundant: * we shouldn't be reading data if * canon_data is 0 */ - if (--tty->canon_data < 0) - tty->canon_data = 0; + if (--ldata->canon_data < 0) + ldata->canon_data = 0; } spin_unlock_irqrestore(&tty->read_lock, flags); @@ -2111,15 +2124,15 @@ static unsigned long inq_canon(struct tty_struct *tty) struct n_tty_data *ldata = tty->disc_data; int nr, head, tail; - if (!tty->canon_data) + if (!ldata->canon_data) return 0; - head = tty->canon_head; - tail = tty->read_tail; + head = ldata->canon_head; + tail = ldata->read_tail; nr = (head - tail) & (N_TTY_BUF_SIZE-1); /* Skip EOF-chars.. */ while (head != tail) { if (test_bit(tail, ldata->read_flags) && - tty->read_buf[tail] == __DISABLED_CHAR) + ldata->read_buf[tail] == __DISABLED_CHAR) nr--; tail = (tail+1) & (N_TTY_BUF_SIZE-1); } @@ -2129,6 +2142,7 @@ static unsigned long inq_canon(struct tty_struct *tty) static int n_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct n_tty_data *ldata = tty->disc_data; int retval; switch (cmd) { @@ -2136,7 +2150,7 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, return put_user(tty_chars_in_buffer(tty), (int __user *) arg); case TIOCINQ: /* FIXME: Locking */ - retval = tty->read_cnt; + retval = ldata->read_cnt; if (L_ICANON(tty)) retval = inq_canon(tty); return put_user(retval, (unsigned int __user *) arg); diff --git a/include/linux/tty.h b/include/linux/tty.h index 2161e6b5a94..226cf20e015 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -272,16 +272,6 @@ struct tty_struct { */ unsigned char closing:1; unsigned short minimum_to_wake; - char *read_buf; - int read_head; - int read_tail; - int read_cnt; - unsigned char *echo_buf; - unsigned int echo_pos; - unsigned int echo_cnt; - int canon_data; - unsigned long canon_head; - unsigned int canon_column; struct mutex atomic_read_lock; struct mutex atomic_write_lock; struct mutex output_lock; From bddc7152f68bc1e0ee1f55a8055e33531f384101 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:42 +0200 Subject: [PATCH 018/146] TTY: move ldisc data from tty_struct: locks atomic_write_lock is not n_tty specific, so move it up in the tty_struct. And since these are the last ones to move, remove also the comment saying there are some ldisc' members. There are none now. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 123 +++++++++++++++++++++++++------------------ drivers/tty/tty_io.c | 4 -- include/linux/tty.h | 11 +--- 3 files changed, 73 insertions(+), 65 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 4794537a50f..0a6fcda9615 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -96,6 +96,11 @@ struct n_tty_data { int canon_data; unsigned long canon_head; unsigned int canon_column; + + struct mutex atomic_read_lock; + struct mutex output_lock; + struct mutex echo_lock; + spinlock_t read_lock; }; static inline int tty_put_user(struct tty_struct *tty, unsigned char x, @@ -171,14 +176,15 @@ static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) static void put_tty_queue(unsigned char c, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; /* * The problem of stomping on the buffers ends here. * Why didn't anyone see this one coming? --AJK */ - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); put_tty_queue_nolock(c, tty); - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); } /** @@ -212,13 +218,13 @@ static void reset_buffer_flags(struct tty_struct *tty) struct n_tty_data *ldata = tty->disc_data; unsigned long flags; - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); ldata->read_head = ldata->read_tail = ldata->read_cnt = 0; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); - mutex_lock(&tty->echo_lock); + mutex_lock(&ldata->echo_lock); ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0; - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); ldata->canon_head = ldata->canon_data = ldata->erasing = 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); @@ -270,7 +276,7 @@ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) unsigned long flags; ssize_t n = 0; - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); if (!ldata->icanon) { n = ldata->read_cnt; } else if (ldata->canon_data) { @@ -278,7 +284,7 @@ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) ldata->canon_head - ldata->read_tail : ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail); } - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); return n; } @@ -408,14 +414,15 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) static int process_output(unsigned char c, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int space, retval; - mutex_lock(&tty->output_lock); + mutex_lock(&ldata->output_lock); space = tty_write_room(tty); retval = do_output_char(c, tty, space); - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->output_lock); if (retval < 0) return -1; else @@ -449,11 +456,11 @@ static ssize_t process_output_block(struct tty_struct *tty, int i; const unsigned char *cp; - mutex_lock(&tty->output_lock); + mutex_lock(&ldata->output_lock); space = tty_write_room(tty); if (!space) { - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->output_lock); return 0; } if (nr > space) @@ -496,7 +503,7 @@ static ssize_t process_output_block(struct tty_struct *tty, break_out: i = tty->ops->write(tty, buf, i); - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->output_lock); return i; } @@ -536,8 +543,8 @@ static void process_echoes(struct tty_struct *tty) if (!ldata->echo_cnt) return; - mutex_lock(&tty->output_lock); - mutex_lock(&tty->echo_lock); + mutex_lock(&ldata->output_lock); + mutex_lock(&ldata->echo_lock); space = tty_write_room(tty); @@ -682,8 +689,8 @@ static void process_echoes(struct tty_struct *tty) ldata->echo_overrun = 0; } - mutex_unlock(&tty->echo_lock); - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->echo_lock); + mutex_unlock(&ldata->output_lock); if (tty->ops->flush_chars) tty->ops->flush_chars(tty); @@ -748,12 +755,14 @@ static void add_echo_byte(unsigned char c, struct tty_struct *tty) static void echo_move_back_col(struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); add_echo_byte(ECHO_OP_START, tty); add_echo_byte(ECHO_OP_MOVE_BACK_COL, tty); - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -768,12 +777,14 @@ static void echo_move_back_col(struct tty_struct *tty) static void echo_set_canon_col(struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); add_echo_byte(ECHO_OP_START, tty); add_echo_byte(ECHO_OP_SET_CANON_COL, tty); - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -796,7 +807,9 @@ static void echo_set_canon_col(struct tty_struct *tty) static void echo_erase_tab(unsigned int num_chars, int after_tab, struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); add_echo_byte(ECHO_OP_START, tty); add_echo_byte(ECHO_OP_ERASE_TAB, tty); @@ -810,7 +823,7 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, add_echo_byte(num_chars, tty); - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -828,7 +841,9 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, static void echo_char_raw(unsigned char c, struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); if (c == ECHO_OP_START) { add_echo_byte(ECHO_OP_START, tty); @@ -837,7 +852,7 @@ static void echo_char_raw(unsigned char c, struct tty_struct *tty) add_echo_byte(c, tty); } - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -856,7 +871,9 @@ static void echo_char_raw(unsigned char c, struct tty_struct *tty) static void echo_char(unsigned char c, struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); if (c == ECHO_OP_START) { add_echo_byte(ECHO_OP_START, tty); @@ -867,7 +884,7 @@ static void echo_char(unsigned char c, struct tty_struct *tty) add_echo_byte(c, tty); } - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -914,19 +931,19 @@ static void eraser(unsigned char c, struct tty_struct *tty) kill_type = WERASE; else { if (!L_ECHO(tty)) { - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & (N_TTY_BUF_SIZE - 1)); ldata->read_head = ldata->canon_head; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); return; } if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & (N_TTY_BUF_SIZE - 1)); ldata->read_head = ldata->canon_head; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); finish_erasing(tty); echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ @@ -960,10 +977,10 @@ static void eraser(unsigned char c, struct tty_struct *tty) break; } cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1); - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); ldata->read_head = head; ldata->read_cnt -= cnt; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!ldata->erasing) { @@ -1338,12 +1355,12 @@ send_signal: put_tty_queue(c, tty); handle_newline: - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); set_bit(ldata->read_head, ldata->read_flags); put_tty_queue_nolock(c, tty); ldata->canon_head = ldata->read_head; ldata->canon_data++; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); @@ -1417,7 +1434,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, unsigned long cpuflags; if (ldata->real_raw) { - spin_lock_irqsave(&tty->read_lock, cpuflags); + spin_lock_irqsave(&ldata->read_lock, cpuflags); i = min(N_TTY_BUF_SIZE - ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_head); i = min(count, i); @@ -1433,7 +1450,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, memcpy(ldata->read_buf + ldata->read_head, cp, i); ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1); ldata->read_cnt += i; - spin_unlock_irqrestore(&tty->read_lock, cpuflags); + spin_unlock_irqrestore(&ldata->read_lock, cpuflags); } else { for (i = count, p = cp, f = fp; i; i--, p++) { if (f) @@ -1626,6 +1643,10 @@ static int n_tty_open(struct tty_struct *tty) goto err; ldata->overrun_time = jiffies; + mutex_init(&ldata->atomic_read_lock); + mutex_init(&ldata->output_lock); + mutex_init(&ldata->echo_lock); + spin_lock_init(&ldata->read_lock); /* These are ugly. Currently a malloc failure here can panic */ ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); @@ -1677,7 +1698,7 @@ static inline int input_available_p(struct tty_struct *tty, int amt) * buffer, and once to drain the space from the (physical) beginning of * the buffer to head pointer. * - * Called under the tty->atomic_read_lock sem + * Called under the ldata->atomic_read_lock sem * */ @@ -1693,10 +1714,10 @@ static int copy_from_read_buf(struct tty_struct *tty, bool is_eof; retval = 0; - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail); n = min(*nr, n); - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (n) { retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n); n -= retval; @@ -1704,13 +1725,13 @@ static int copy_from_read_buf(struct tty_struct *tty, ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty); tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n, ldata->icanon); - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1); ldata->read_cnt -= n; /* Turn single EOF into zero-length read */ if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt) n = 0; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); *b += n; *nr -= n; } @@ -1818,10 +1839,10 @@ do_it_again: * Internal serialization of reads. */ if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&tty->atomic_read_lock)) + if (!mutex_trylock(&ldata->atomic_read_lock)) return -EAGAIN; } else { - if (mutex_lock_interruptible(&tty->atomic_read_lock)) + if (mutex_lock_interruptible(&ldata->atomic_read_lock)) return -ERESTARTSYS; } packet = tty->packet; @@ -1890,7 +1911,7 @@ do_it_again: if (ldata->icanon && !L_EXTPROC(tty)) { /* N.B. avoid overrun if nr == 0 */ - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); while (nr && ldata->read_cnt) { int eol; @@ -1908,25 +1929,25 @@ do_it_again: if (--ldata->canon_data < 0) ldata->canon_data = 0; } - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (!eol || (c != __DISABLED_CHAR)) { if (tty_put_user(tty, c, b++)) { retval = -EFAULT; b--; - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); break; } nr--; } if (eol) { tty_audit_push(tty); - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); break; } - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); } - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (retval) break; } else { @@ -1958,7 +1979,7 @@ do_it_again: if (time) timeout = time; } - mutex_unlock(&tty->atomic_read_lock); + mutex_unlock(&ldata->atomic_read_lock); remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 67b024ca16e..f90b6217b3b 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2939,11 +2939,7 @@ void initialize_tty_struct(struct tty_struct *tty, init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup); - mutex_init(&tty->atomic_read_lock); mutex_init(&tty->atomic_write_lock); - mutex_init(&tty->output_lock); - mutex_init(&tty->echo_lock); - spin_lock_init(&tty->read_lock); spin_lock_init(&tty->ctrl_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); diff --git a/include/linux/tty.h b/include/linux/tty.h index 226cf20e015..08787ece3fd 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -235,6 +235,7 @@ struct tty_struct { struct mutex ldisc_mutex; struct tty_ldisc *ldisc; + struct mutex atomic_write_lock; struct mutex legacy_mutex; struct mutex termios_mutex; spinlock_t ctrl_lock; @@ -265,20 +266,10 @@ struct tty_struct { #define N_TTY_BUF_SIZE 4096 - /* - * The following is data for the N_TTY line discipline. For - * historical reasons, this is included in the tty structure. - * Mostly locked by the BKL. - */ unsigned char closing:1; unsigned short minimum_to_wake; - struct mutex atomic_read_lock; - struct mutex atomic_write_lock; - struct mutex output_lock; - struct mutex echo_lock; unsigned char *write_buf; int write_cnt; - spinlock_t read_lock; /* If the tty has a pending do_SAK, queue it here - akpm */ struct work_struct SAK_work; struct tty_port *port; From 57c941212d203979720081198ebda41f51812635 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:43 +0200 Subject: [PATCH 019/146] TTY: n_tty, propagate n_tty_data In some funtions we need only n_tty_data, so pass it down directly in case tty is not needed there. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 171 ++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 93 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 0a6fcda9615..531e539dbfc 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -153,10 +153,8 @@ static void n_tty_set_room(struct tty_struct *tty) schedule_work(&tty->buf.work); } -static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) +static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; - if (ldata->read_cnt < N_TTY_BUF_SIZE) { ldata->read_buf[ldata->read_head] = c; ldata->read_head = (ldata->read_head + 1) & (N_TTY_BUF_SIZE-1); @@ -167,23 +165,22 @@ static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) /** * put_tty_queue - add character to tty * @c: character - * @tty: tty device + * @ldata: n_tty data * * Add a character to the tty read_buf queue. This is done under the * read_lock to serialize character addition and also to protect us * against parallel reads or flushes */ -static void put_tty_queue(unsigned char c, struct tty_struct *tty) +static void put_tty_queue(unsigned char c, struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; unsigned long flags; /* * The problem of stomping on the buffers ends here. * Why didn't anyone see this one coming? --AJK */ spin_lock_irqsave(&ldata->read_lock, flags); - put_tty_queue_nolock(c, tty); + put_tty_queue_nolock(c, ldata); spin_unlock_irqrestore(&ldata->read_lock, flags); } @@ -699,16 +696,15 @@ static void process_echoes(struct tty_struct *tty) /** * add_echo_byte - add a byte to the echo buffer * @c: unicode byte to echo - * @tty: terminal device + * @ldata: n_tty data * * Add a character or operation byte to the echo buffer. * * Should be called under the echo lock to protect the echo buffer. */ -static void add_echo_byte(unsigned char c, struct tty_struct *tty) +static void add_echo_byte(unsigned char c, struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; int new_byte_pos; if (ldata->echo_cnt == N_TTY_BUF_SIZE) { @@ -746,28 +742,24 @@ static void add_echo_byte(unsigned char c, struct tty_struct *tty) /** * echo_move_back_col - add operation to move back a column - * @tty: terminal device + * @ldata: n_tty data * * Add an operation to the echo buffer to move back one column. * * Locking: echo_lock to protect the echo buffer */ -static void echo_move_back_col(struct tty_struct *tty) +static void echo_move_back_col(struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; - mutex_lock(&ldata->echo_lock); - - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_MOVE_BACK_COL, tty); - + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata); mutex_unlock(&ldata->echo_lock); } /** * echo_set_canon_col - add operation to set the canon column - * @tty: terminal device + * @ldata: n_tty data * * Add an operation to the echo buffer to set the canon column * to the current column. @@ -775,15 +767,11 @@ static void echo_move_back_col(struct tty_struct *tty) * Locking: echo_lock to protect the echo buffer */ -static void echo_set_canon_col(struct tty_struct *tty) +static void echo_set_canon_col(struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; - mutex_lock(&ldata->echo_lock); - - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_SET_CANON_COL, tty); - + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_SET_CANON_COL, ldata); mutex_unlock(&ldata->echo_lock); } @@ -791,7 +779,7 @@ static void echo_set_canon_col(struct tty_struct *tty) * echo_erase_tab - add operation to erase a tab * @num_chars: number of character columns already used * @after_tab: true if num_chars starts after a previous tab - * @tty: terminal device + * @ldata: n_tty data * * Add an operation to the echo buffer to erase a tab. * @@ -805,14 +793,12 @@ static void echo_set_canon_col(struct tty_struct *tty) */ static void echo_erase_tab(unsigned int num_chars, int after_tab, - struct tty_struct *tty) + struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; - mutex_lock(&ldata->echo_lock); - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_ERASE_TAB, tty); + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_ERASE_TAB, ldata); /* We only need to know this modulo 8 (tab spacing) */ num_chars &= 7; @@ -821,7 +807,7 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, if (after_tab) num_chars |= 0x80; - add_echo_byte(num_chars, tty); + add_echo_byte(num_chars, ldata); mutex_unlock(&ldata->echo_lock); } @@ -839,19 +825,15 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, * Locking: echo_lock to protect the echo buffer */ -static void echo_char_raw(unsigned char c, struct tty_struct *tty) +static void echo_char_raw(unsigned char c, struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; - mutex_lock(&ldata->echo_lock); - if (c == ECHO_OP_START) { - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_START, tty); + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_START, ldata); } else { - add_echo_byte(c, tty); + add_echo_byte(c, ldata); } - mutex_unlock(&ldata->echo_lock); } @@ -876,12 +858,12 @@ static void echo_char(unsigned char c, struct tty_struct *tty) mutex_lock(&ldata->echo_lock); if (c == ECHO_OP_START) { - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_START, tty); + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_START, ldata); } else { if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(c, tty); + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(c, ldata); } mutex_unlock(&ldata->echo_lock); @@ -889,14 +871,13 @@ static void echo_char(unsigned char c, struct tty_struct *tty) /** * finish_erasing - complete erase - * @tty: tty doing the erase + * @ldata: n_tty data */ -static inline void finish_erasing(struct tty_struct *tty) +static inline void finish_erasing(struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; if (ldata->erasing) { - echo_char_raw('/', tty); + echo_char_raw('/', ldata); ldata->erasing = 0; } } @@ -944,11 +925,11 @@ static void eraser(unsigned char c, struct tty_struct *tty) (N_TTY_BUF_SIZE - 1)); ldata->read_head = ldata->canon_head; spin_unlock_irqrestore(&ldata->read_lock, flags); - finish_erasing(tty); + finish_erasing(ldata); echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ if (L_ECHOK(tty)) - echo_char_raw('\n', tty); + echo_char_raw('\n', ldata); return; } kill_type = KILL; @@ -984,15 +965,16 @@ static void eraser(unsigned char c, struct tty_struct *tty) if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!ldata->erasing) { - echo_char_raw('\\', tty); + echo_char_raw('\\', ldata); ldata->erasing = 1; } /* if cnt > 1, output a multi-byte character */ echo_char(c, tty); while (--cnt > 0) { head = (head+1) & (N_TTY_BUF_SIZE-1); - echo_char_raw(ldata->read_buf[head], tty); - echo_move_back_col(tty); + echo_char_raw(ldata->read_buf[head], + ldata); + echo_move_back_col(ldata); } } else if (kill_type == ERASE && !L_ECHOE(tty)) { echo_char(ERASE_CHAR(tty), tty); @@ -1021,17 +1003,17 @@ static void eraser(unsigned char c, struct tty_struct *tty) num_chars++; } } - echo_erase_tab(num_chars, after_tab, tty); + echo_erase_tab(num_chars, after_tab, ldata); } else { if (iscntrl(c) && L_ECHOCTL(tty)) { - echo_char_raw('\b', tty); - echo_char_raw(' ', tty); - echo_char_raw('\b', tty); + echo_char_raw('\b', ldata); + echo_char_raw(' ', ldata); + echo_char_raw('\b', ldata); } if (!iscntrl(c) || L_ECHOCTL(tty)) { - echo_char_raw('\b', tty); - echo_char_raw(' ', tty); - echo_char_raw('\b', tty); + echo_char_raw('\b', ldata); + echo_char_raw(' ', ldata); + echo_char_raw('\b', ldata); } } } @@ -1039,7 +1021,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) break; } if (ldata->read_head == ldata->canon_head && L_ECHO(tty)) - finish_erasing(tty); + finish_erasing(ldata); } /** @@ -1078,6 +1060,8 @@ static inline void isig(int sig, struct tty_struct *tty, int flush) static inline void n_tty_receive_break(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; + if (I_IGNBRK(tty)) return; if (I_BRKINT(tty)) { @@ -1085,10 +1069,10 @@ static inline void n_tty_receive_break(struct tty_struct *tty) return; } if (I_PARMRK(tty)) { - put_tty_queue('\377', tty); - put_tty_queue('\0', tty); + put_tty_queue('\377', ldata); + put_tty_queue('\0', ldata); } - put_tty_queue('\0', tty); + put_tty_queue('\0', ldata); wake_up_interruptible(&tty->read_wait); } @@ -1132,16 +1116,18 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty) static inline void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) { + struct n_tty_data *ldata = tty->disc_data; + if (I_IGNPAR(tty)) return; if (I_PARMRK(tty)) { - put_tty_queue('\377', tty); - put_tty_queue('\0', tty); - put_tty_queue(c, tty); + put_tty_queue('\377', ldata); + put_tty_queue('\0', ldata); + put_tty_queue(c, ldata); } else if (I_INPCK(tty)) - put_tty_queue('\0', tty); + put_tty_queue('\0', ldata); else - put_tty_queue(c, tty); + put_tty_queue(c, ldata); wake_up_interruptible(&tty->read_wait); } @@ -1162,7 +1148,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) int parmrk; if (ldata->raw) { - put_tty_queue(c, tty); + put_tty_queue(c, ldata); return; } @@ -1172,7 +1158,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) c = tolower(c); if (L_EXTPROC(tty)) { - put_tty_queue(c, tty); + put_tty_queue(c, ldata); return; } @@ -1210,16 +1196,16 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) return; } if (L_ECHO(tty)) { - finish_erasing(tty); + finish_erasing(ldata); /* Record the column of first canon char. */ if (ldata->canon_head == ldata->read_head) - echo_set_canon_col(tty); + echo_set_canon_col(ldata); echo_char(c, tty); process_echoes(tty); } if (parmrk) - put_tty_queue(c, tty); - put_tty_queue(c, tty); + put_tty_queue(c, ldata); + put_tty_queue(c, ldata); return; } @@ -1285,10 +1271,10 @@ send_signal: if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { ldata->lnext = 1; if (L_ECHO(tty)) { - finish_erasing(tty); + finish_erasing(ldata); if (L_ECHOCTL(tty)) { - echo_char_raw('^', tty); - echo_char_raw('\b', tty); + echo_char_raw('^', ldata); + echo_char_raw('\b', ldata); process_echoes(tty); } } @@ -1298,9 +1284,9 @@ send_signal: L_IEXTEN(tty)) { unsigned long tail = ldata->canon_head; - finish_erasing(tty); + finish_erasing(ldata); echo_char(c, tty); - echo_char_raw('\n', tty); + echo_char_raw('\n', ldata); while (tail != ldata->read_head) { echo_char(ldata->read_buf[tail], tty); tail = (tail+1) & (N_TTY_BUF_SIZE-1); @@ -1315,7 +1301,7 @@ send_signal: return; } if (L_ECHO(tty) || L_ECHONL(tty)) { - echo_char_raw('\n', tty); + echo_char_raw('\n', ldata); process_echoes(tty); } goto handle_newline; @@ -1343,7 +1329,7 @@ send_signal: if (L_ECHO(tty)) { /* Record the column of first canon char. */ if (ldata->canon_head == ldata->read_head) - echo_set_canon_col(tty); + echo_set_canon_col(ldata); echo_char(c, tty); process_echoes(tty); } @@ -1352,12 +1338,12 @@ send_signal: * EOL_CHAR and EOL2_CHAR? */ if (parmrk) - put_tty_queue(c, tty); + put_tty_queue(c, ldata); handle_newline: spin_lock_irqsave(&ldata->read_lock, flags); set_bit(ldata->read_head, ldata->read_flags); - put_tty_queue_nolock(c, tty); + put_tty_queue_nolock(c, ldata); ldata->canon_head = ldata->read_head; ldata->canon_data++; spin_unlock_irqrestore(&ldata->read_lock, flags); @@ -1376,22 +1362,22 @@ handle_newline: return; } if (L_ECHO(tty)) { - finish_erasing(tty); + finish_erasing(ldata); if (c == '\n') - echo_char_raw('\n', tty); + echo_char_raw('\n', ldata); else { /* Record the column of first canon char. */ if (ldata->canon_head == ldata->read_head) - echo_set_canon_col(tty); + echo_set_canon_col(ldata); echo_char(c, tty); } process_echoes(tty); } if (parmrk) - put_tty_queue(c, tty); + put_tty_queue(c, ldata); - put_tty_queue(c, tty); + put_tty_queue(c, ldata); } @@ -2140,9 +2126,8 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, return mask; } -static unsigned long inq_canon(struct tty_struct *tty) +static unsigned long inq_canon(struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; int nr, head, tail; if (!ldata->canon_data) @@ -2173,7 +2158,7 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, /* FIXME: Locking */ retval = ldata->read_cnt; if (L_ICANON(tty)) - retval = inq_canon(tty); + retval = inq_canon(ldata); return put_user(retval, (unsigned int __user *) arg); default: return n_tty_ioctl_helper(tty, file, cmd, arg); From 2fc20661e3171d45e8e58a61eb5c6b7d8d614fde Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:44 +0200 Subject: [PATCH 020/146] TTY: move TTY_FLUSH* flags to tty_port They are only TTY buffers specific. And the buffers will go to tty_port in the next patches. So to remove the need to have both tty_port and tty_struct at some places, let us move the flags to tty_port. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 18 ++++++++++-------- include/linux/tty.h | 5 +++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 8b00f6a34a7..6f366f257fb 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -134,17 +134,18 @@ static void __tty_buffer_flush(struct tty_struct *tty) void tty_buffer_flush(struct tty_struct *tty) { + struct tty_port *port = tty->port; unsigned long flags; spin_lock_irqsave(&tty->buf.lock, flags); /* If the data is being pushed to the tty layer then we can't process it here. Instead set a flag and the flush_to_ldisc path will process the flush request before it exits */ - if (test_bit(TTY_FLUSHING, &tty->flags)) { - set_bit(TTY_FLUSHPENDING, &tty->flags); + if (test_bit(TTYP_FLUSHING, &port->iflags)) { + set_bit(TTYP_FLUSHPENDING, &port->iflags); spin_unlock_irqrestore(&tty->buf.lock, flags); wait_event(tty->read_wait, - test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); + test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); return; } else __tty_buffer_flush(tty); @@ -450,6 +451,7 @@ static void flush_to_ldisc(struct work_struct *work) { struct tty_struct *tty = container_of(work, struct tty_struct, buf.work); + struct tty_port *port = tty->port; unsigned long flags; struct tty_ldisc *disc; @@ -459,7 +461,7 @@ static void flush_to_ldisc(struct work_struct *work) spin_lock_irqsave(&tty->buf.lock, flags); - if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { + if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { struct tty_buffer *head; while ((head = tty->buf.head) != NULL) { int count; @@ -477,7 +479,7 @@ static void flush_to_ldisc(struct work_struct *work) /* Ldisc or user is trying to flush the buffers we are feeding to the ldisc, stop feeding the line discipline as we want to empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) + if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) break; if (!tty->receive_room) break; @@ -491,14 +493,14 @@ static void flush_to_ldisc(struct work_struct *work) flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); } - clear_bit(TTY_FLUSHING, &tty->flags); + clear_bit(TTYP_FLUSHING, &port->iflags); } /* We may have a deferred request to flush the input buffer, if so pull the chain under the lock and empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { + if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { __tty_buffer_flush(tty); - clear_bit(TTY_FLUSHPENDING, &tty->flags); + clear_bit(TTYP_FLUSHPENDING, &port->iflags); wake_up(&tty->read_wait); } spin_unlock_irqrestore(&tty->buf.lock, flags); diff --git a/include/linux/tty.h b/include/linux/tty.h index 08787ece3fd..b4b3c568d24 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -197,6 +197,9 @@ struct tty_port { wait_queue_head_t close_wait; /* Close waiters */ wait_queue_head_t delta_msr_wait; /* Modem status change */ unsigned long flags; /* TTY flags ASY_*/ + unsigned long iflags; /* TTYP_ internal flags */ +#define TTYP_FLUSHING 1 /* Flushing to ldisc in progress */ +#define TTYP_FLUSHPENDING 2 /* Queued buffer flush pending */ unsigned char console:1; /* port is a console */ struct mutex mutex; /* Locking */ struct mutex buf_mutex; /* Buffer alloc lock */ @@ -309,8 +312,6 @@ struct tty_file_private { #define TTY_PTY_LOCK 16 /* pty private */ #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ #define TTY_HUPPED 18 /* Post driver->hangup() */ -#define TTY_FLUSHING 19 /* Flushing to ldisc in progress */ -#define TTY_FLUSHPENDING 20 /* Queued buffer flush pending */ #define TTY_HUPPING 21 /* ->hangup() in progress */ #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty)) From 5cff39c69b57df6d7bf4e87f2963571aa4ea6336 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:45 +0200 Subject: [PATCH 021/146] TTY: tty_buffer, cache pointer to tty->buf During the move of tty buffers from tty_struct to tty_port, we will need to switch all users of buf to tty->port->buf. There are many functions where this is accessed directly in their code many times. Cache the tty->buf pointer in such functions now and change only single lines in each function in the next patch. Not that it is convenient for the next patch, but the code is now also more readable. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 132 ++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 56 deletions(-) diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 6f366f257fb..ddd74d41cbb 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -29,17 +29,19 @@ void tty_buffer_free_all(struct tty_struct *tty) { + struct tty_bufhead *buf = &tty->buf; struct tty_buffer *thead; - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; + + while ((thead = buf->head) != NULL) { + buf->head = thead->next; kfree(thead); } - while ((thead = tty->buf.free) != NULL) { - tty->buf.free = thead->next; + while ((thead = buf->free) != NULL) { + buf->free = thead->next; kfree(thead); } - tty->buf.tail = NULL; - tty->buf.memory_used = 0; + buf->tail = NULL; + buf->memory_used = 0; } /** @@ -87,15 +89,17 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) { + struct tty_bufhead *buf = &tty->buf; + /* Dumb strategy for now - should keep some stats */ - tty->buf.memory_used -= b->size; - WARN_ON(tty->buf.memory_used < 0); + buf->memory_used -= b->size; + WARN_ON(buf->memory_used < 0); if (b->size >= 512) kfree(b); else { - b->next = tty->buf.free; - tty->buf.free = b; + b->next = buf->free; + buf->free = b; } } @@ -112,13 +116,14 @@ static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) static void __tty_buffer_flush(struct tty_struct *tty) { + struct tty_bufhead *buf = &tty->buf; struct tty_buffer *thead; - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; + while ((thead = buf->head) != NULL) { + buf->head = thead->next; tty_buffer_free(tty, thead); } - tty->buf.tail = NULL; + buf->tail = NULL; } /** @@ -135,21 +140,23 @@ static void __tty_buffer_flush(struct tty_struct *tty) void tty_buffer_flush(struct tty_struct *tty) { struct tty_port *port = tty->port; + struct tty_bufhead *buf = &tty->buf; unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); + + spin_lock_irqsave(&buf->lock, flags); /* If the data is being pushed to the tty layer then we can't process it here. Instead set a flag and the flush_to_ldisc path will process the flush request before it exits */ if (test_bit(TTYP_FLUSHING, &port->iflags)) { set_bit(TTYP_FLUSHPENDING, &port->iflags); - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); wait_event(tty->read_wait, test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); return; } else __tty_buffer_flush(tty); - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); } /** @@ -197,12 +204,14 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) */ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) { + struct tty_bufhead *buf = &tty->buf; struct tty_buffer *b, *n; int left; /* OPTIMISATION: We could keep a per tty "zero" sized buffer to remove this conditional if its worth it. This would be invisible to the callers */ - if ((b = tty->buf.tail) != NULL) + b = buf->tail; + if (b != NULL) left = b->size - b->used; else left = 0; @@ -214,8 +223,8 @@ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) b->next = n; b->commit = b->used; } else - tty->buf.head = n; - tty->buf.tail = n; + buf->head = n; + buf->tail = n; } else size = left; } @@ -262,6 +271,7 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, const unsigned char *chars, char flag, size_t size) { + struct tty_bufhead *buf = &tty->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); @@ -269,18 +279,18 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, unsigned long flags; struct tty_buffer *tb; - spin_lock_irqsave(&tty->buf.lock, flags); + spin_lock_irqsave(&buf->lock, flags); space = __tty_buffer_request_room(tty, goal); - tb = tty->buf.tail; + tb = buf->tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); break; } memcpy(tb->char_buf_ptr + tb->used, chars, space); memset(tb->flag_buf_ptr + tb->used, flag, space); tb->used += space; - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); copied += space; chars += space; /* There is a small chance that we need to split the data over @@ -307,6 +317,7 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *chars, const char *flags, size_t size) { + struct tty_bufhead *buf = &tty->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); @@ -314,18 +325,18 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, unsigned long __flags; struct tty_buffer *tb; - spin_lock_irqsave(&tty->buf.lock, __flags); + spin_lock_irqsave(&buf->lock, __flags); space = __tty_buffer_request_room(tty, goal); - tb = tty->buf.tail; + tb = buf->tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { - spin_unlock_irqrestore(&tty->buf.lock, __flags); + spin_unlock_irqrestore(&buf->lock, __flags); break; } memcpy(tb->char_buf_ptr + tb->used, chars, space); memcpy(tb->flag_buf_ptr + tb->used, flags, space); tb->used += space; - spin_unlock_irqrestore(&tty->buf.lock, __flags); + spin_unlock_irqrestore(&buf->lock, __flags); copied += space; chars += space; flags += space; @@ -351,12 +362,14 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); void tty_schedule_flip(struct tty_struct *tty) { + struct tty_bufhead *buf = &tty->buf; unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); - schedule_work(&tty->buf.work); + + spin_lock_irqsave(&buf->lock, flags); + if (buf->tail != NULL) + buf->tail->commit = buf->tail->used; + spin_unlock_irqrestore(&buf->lock, flags); + schedule_work(&buf->work); } EXPORT_SYMBOL(tty_schedule_flip); @@ -378,20 +391,21 @@ EXPORT_SYMBOL(tty_schedule_flip); int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, size_t size) { + struct tty_bufhead *buf = &tty->buf; int space; unsigned long flags; struct tty_buffer *tb; - spin_lock_irqsave(&tty->buf.lock, flags); + spin_lock_irqsave(&buf->lock, flags); space = __tty_buffer_request_room(tty, size); - tb = tty->buf.tail; + tb = buf->tail; if (likely(space)) { *chars = tb->char_buf_ptr + tb->used; memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); tb->used += space; } - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); return space; } EXPORT_SYMBOL_GPL(tty_prepare_flip_string); @@ -415,20 +429,21 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size) { + struct tty_bufhead *buf = &tty->buf; int space; unsigned long __flags; struct tty_buffer *tb; - spin_lock_irqsave(&tty->buf.lock, __flags); + spin_lock_irqsave(&buf->lock, __flags); space = __tty_buffer_request_room(tty, size); - tb = tty->buf.tail; + tb = buf->tail; if (likely(space)) { *chars = tb->char_buf_ptr + tb->used; *flags = tb->flag_buf_ptr + tb->used; tb->used += space; } - spin_unlock_irqrestore(&tty->buf.lock, __flags); + spin_unlock_irqrestore(&buf->lock, __flags); return space; } EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); @@ -452,6 +467,7 @@ static void flush_to_ldisc(struct work_struct *work) struct tty_struct *tty = container_of(work, struct tty_struct, buf.work); struct tty_port *port = tty->port; + struct tty_bufhead *buf = &tty->buf; unsigned long flags; struct tty_ldisc *disc; @@ -459,11 +475,11 @@ static void flush_to_ldisc(struct work_struct *work) if (disc == NULL) /* !TTY_LDISC */ return; - spin_lock_irqsave(&tty->buf.lock, flags); + spin_lock_irqsave(&buf->lock, flags); if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { struct tty_buffer *head; - while ((head = tty->buf.head) != NULL) { + while ((head = buf->head) != NULL) { int count; char *char_buf; unsigned char *flag_buf; @@ -472,7 +488,7 @@ static void flush_to_ldisc(struct work_struct *work) if (!count) { if (head->next == NULL) break; - tty->buf.head = head->next; + buf->head = head->next; tty_buffer_free(tty, head); continue; } @@ -488,10 +504,10 @@ static void flush_to_ldisc(struct work_struct *work) 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); + spin_unlock_irqrestore(&buf->lock, flags); disc->ops->receive_buf(tty, char_buf, flag_buf, count); - spin_lock_irqsave(&tty->buf.lock, flags); + spin_lock_irqsave(&buf->lock, flags); } clear_bit(TTYP_FLUSHING, &port->iflags); } @@ -503,7 +519,7 @@ static void flush_to_ldisc(struct work_struct *work) clear_bit(TTYP_FLUSHPENDING, &port->iflags); wake_up(&tty->read_wait); } - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_unlock_irqrestore(&buf->lock, flags); tty_ldisc_deref(disc); } @@ -537,16 +553,18 @@ void tty_flush_to_ldisc(struct tty_struct *tty) void tty_flip_buffer_push(struct tty_struct *tty) { + struct tty_bufhead *buf = &tty->buf; unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); + + spin_lock_irqsave(&buf->lock, flags); + if (buf->tail != NULL) + buf->tail->commit = buf->tail->used; + spin_unlock_irqrestore(&buf->lock, flags); if (tty->low_latency) - flush_to_ldisc(&tty->buf.work); + flush_to_ldisc(&buf->work); else - schedule_work(&tty->buf.work); + schedule_work(&buf->work); } EXPORT_SYMBOL(tty_flip_buffer_push); @@ -562,11 +580,13 @@ EXPORT_SYMBOL(tty_flip_buffer_push); void tty_buffer_init(struct tty_struct *tty) { - spin_lock_init(&tty->buf.lock); - tty->buf.head = NULL; - tty->buf.tail = NULL; - tty->buf.free = NULL; - tty->buf.memory_used = 0; - INIT_WORK(&tty->buf.work, flush_to_ldisc); + struct tty_bufhead *buf = &tty->buf; + + spin_lock_init(&buf->lock); + buf->head = NULL; + buf->tail = NULL; + buf->free = NULL; + buf->memory_used = 0; + INIT_WORK(&buf->work, flush_to_ldisc); } From 967fab6916681e5ab131fdef1226327b02454f19 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:46 +0200 Subject: [PATCH 022/146] TTY: add port -> tty link For that purpose we have to temporarily introduce a second tty back pointer into tty_port. It is because serial layer, and maybe others, still do not use tty_port_tty_set/get. So that we cannot set the tty_port->tty to NULL at will now. Yes, the fix would be to convert whole serial layer and all its users to tty_port_tty_set/get. However we are in the process of removing the need of tty in most of the call sites, so this would lead to a duplicated work. Instead we have now tty_port->itty (internal tty) which will be used only in flush_to_ldisc. For that one it is ensured that itty is valid wherever the work is run. IOW, the work is synchronously cancelled before we set itty to NULL and also before hangup is processed. After we need only tty_port and not tty_struct in most code, this shall be changed to tty_port_tty_set/get and itty removed completely. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 2 ++ drivers/tty/tty_io.c | 3 +++ include/linux/tty.h | 1 + 3 files changed, 6 insertions(+) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 2728afe52ee..c3269086267 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -345,6 +345,7 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, tty_port_init(ports[1]); o_tty->port = ports[0]; tty->port = ports[1]; + o_tty->port->itty = o_tty; tty_driver_kref_get(driver); tty->count++; @@ -371,6 +372,7 @@ static void pty_unix98_shutdown(struct tty_struct *tty) static void pty_cleanup(struct tty_struct *tty) { + tty->port->itty = NULL; kfree(tty->port); } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index f90b6217b3b..202008f38ca 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1417,6 +1417,8 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n", __func__, tty->driver->name); + tty->port->itty = tty; + /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need @@ -1552,6 +1554,7 @@ static void release_tty(struct tty_struct *tty, int idx) tty->ops->shutdown(tty); tty_free_termios(tty); tty_driver_remove_tty(tty->driver, tty); + tty->port->itty = NULL; if (tty->link) tty_kref_put(tty->link); diff --git a/include/linux/tty.h b/include/linux/tty.h index b4b3c568d24..9be74d649a5 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -189,6 +189,7 @@ struct tty_port_operations { struct tty_port { struct tty_struct *tty; /* Back pointer */ + struct tty_struct *itty; /* internal back ptr */ const struct tty_port_operations *ops; /* Port operations */ spinlock_t lock; /* Lock protecting tty field */ int blocked_open; /* Waiting to open */ From ecbbfd44a08fa80e0d664814efd4c187721b85f6 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:47 +0200 Subject: [PATCH 023/146] TTY: move tty buffers to tty_port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So this is it. The big step why we did all the work over the past kernel releases. Now everything is prepared, so nothing protects us from doing that big step. | | \ \ nnnn/^l | | | | \ / / | | | '-,.__ => \/ ,-` => | '-,.__ | O __.´´) ( .` | O __.´´) ~~~ ~~ `` ~~~ ~~ The buffers are now in the tty_port structure and we can start teaching the buffer helpers (insert char/string, flip etc.) to use tty_port instead of tty_struct all around. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 7 ++- drivers/tty/pty.c | 2 +- drivers/tty/tty_buffer.c | 102 ++++++++++++++++++++------------------- drivers/tty/tty_io.c | 2 - drivers/tty/tty_ldisc.c | 10 ++-- drivers/tty/tty_port.c | 2 + include/linux/tty.h | 6 +-- include/linux/tty_flip.h | 2 +- 8 files changed, 70 insertions(+), 63 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 531e539dbfc..60b076cc4e2 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -149,8 +149,11 @@ static void n_tty_set_room(struct tty_struct *tty) 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); + if (left && !old_left) { + WARN_RATELIMIT(tty->port->itty == NULL, + "scheduling with invalid itty"); + schedule_work(&tty->port->buf.work); + } } static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index c3269086267..4219f040adb 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -93,7 +93,7 @@ static void pty_unthrottle(struct tty_struct *tty) static int pty_space(struct tty_struct *to) { - int n = 8192 - to->buf.memory_used; + int n = 8192 - to->port->buf.memory_used; if (n < 0) return 0; return n; diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index ddd74d41cbb..06725f5cc81 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -27,9 +27,9 @@ * Locking: none */ -void tty_buffer_free_all(struct tty_struct *tty) +void tty_buffer_free_all(struct tty_port *port) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; struct tty_buffer *thead; while ((thead = buf->head) != NULL) { @@ -56,11 +56,11 @@ void tty_buffer_free_all(struct tty_struct *tty) * Locking: Caller must hold tty->buf.lock */ -static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) +static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) { struct tty_buffer *p; - if (tty->buf.memory_used + size > 65536) + if (port->buf.memory_used + size > 65536) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) @@ -72,7 +72,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) p->read = 0; p->char_buf_ptr = (char *)(p->data); p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; - tty->buf.memory_used += size; + port->buf.memory_used += size; return p; } @@ -87,9 +87,9 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) * Locking: Caller must hold tty->buf.lock */ -static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) +static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; /* Dumb strategy for now - should keep some stats */ buf->memory_used -= b->size; @@ -114,14 +114,14 @@ static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) * Locking: Caller must hold tty->buf.lock */ -static void __tty_buffer_flush(struct tty_struct *tty) +static void __tty_buffer_flush(struct tty_port *port) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; struct tty_buffer *thead; while ((thead = buf->head) != NULL) { buf->head = thead->next; - tty_buffer_free(tty, thead); + tty_buffer_free(port, thead); } buf->tail = NULL; } @@ -140,7 +140,7 @@ static void __tty_buffer_flush(struct tty_struct *tty) void tty_buffer_flush(struct tty_struct *tty) { struct tty_port *port = tty->port; - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); @@ -155,7 +155,7 @@ void tty_buffer_flush(struct tty_struct *tty) test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); return; } else - __tty_buffer_flush(tty); + __tty_buffer_flush(port); spin_unlock_irqrestore(&buf->lock, flags); } @@ -171,9 +171,9 @@ void tty_buffer_flush(struct tty_struct *tty) * Locking: Caller must hold tty->buf.lock */ -static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) +static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) { - struct tty_buffer **tbh = &tty->buf.free; + struct tty_buffer **tbh = &port->buf.free; while ((*tbh) != NULL) { struct tty_buffer *t = *tbh; if (t->size >= size) { @@ -182,14 +182,14 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) t->used = 0; t->commit = 0; t->read = 0; - tty->buf.memory_used += t->size; + port->buf.memory_used += t->size; return t; } tbh = &((*tbh)->next); } /* Round the buffer size out */ size = (size + 0xFF) & ~0xFF; - return tty_buffer_alloc(tty, size); + return tty_buffer_alloc(port, size); /* Should possibly check if this fails for the largest buffer we have queued and recycle that ? */ } @@ -200,11 +200,11 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) * * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. - * Locking: Caller must hold tty->buf.lock + * Locking: Caller must hold port->buf.lock */ -static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) +static int __tty_buffer_request_room(struct tty_port *port, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; struct tty_buffer *b, *n; int left; /* OPTIMISATION: We could keep a per tty "zero" sized buffer to @@ -218,7 +218,7 @@ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) if (left < size) { /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(tty, size)) != NULL) { + if ((n = tty_buffer_find(port, size)) != NULL) { if (b != NULL) { b->next = n; b->commit = b->used; @@ -241,16 +241,17 @@ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. * - * Locking: Takes tty->buf.lock + * Locking: Takes port->buf.lock */ int tty_buffer_request_room(struct tty_struct *tty, size_t size) { + struct tty_port *port = tty->port; unsigned long flags; int length; - spin_lock_irqsave(&tty->buf.lock, flags); - length = __tty_buffer_request_room(tty, size); - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_lock_irqsave(&port->buf.lock, flags); + length = __tty_buffer_request_room(port, size); + spin_unlock_irqrestore(&port->buf.lock, flags); return length; } EXPORT_SYMBOL_GPL(tty_buffer_request_room); @@ -265,13 +266,13 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); * Queue a series of bytes to the tty buffering. All the characters * passed are marked with the supplied flag. Returns the number added. * - * Locking: Called functions may take tty->buf.lock + * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, const unsigned char *chars, char flag, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); @@ -280,7 +281,7 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, flags); - space = __tty_buffer_request_room(tty, goal); + space = __tty_buffer_request_room(tty->port, goal); tb = buf->tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { @@ -311,13 +312,13 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); * the flags array indicates the status of the character. Returns the * number added. * - * Locking: Called functions may take tty->buf.lock + * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *chars, const char *flags, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); @@ -326,7 +327,7 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, __flags); - space = __tty_buffer_request_room(tty, goal); + space = __tty_buffer_request_room(tty->port, goal); tb = buf->tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { @@ -357,12 +358,12 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); * Note that this function can only be used when the low_latency flag * is unset. Otherwise the workqueue won't be flushed. * - * Locking: Takes tty->buf.lock + * Locking: Takes port->buf.lock */ void tty_schedule_flip(struct tty_struct *tty) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); @@ -385,19 +386,19 @@ EXPORT_SYMBOL(tty_schedule_flip); * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! * - * Locking: May call functions taking tty->buf.lock + * Locking: May call functions taking port->buf.lock */ int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, - size_t size) + size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int space; unsigned long flags; struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, flags); - space = __tty_buffer_request_room(tty, size); + space = __tty_buffer_request_room(tty->port, size); tb = buf->tail; if (likely(space)) { @@ -423,19 +424,19 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! * - * Locking: May call functions taking tty->buf.lock + * Locking: May call functions taking port->buf.lock */ int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int space; unsigned long __flags; struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, __flags); - space = __tty_buffer_request_room(tty, size); + space = __tty_buffer_request_room(tty->port, size); tb = buf->tail; if (likely(space)) { @@ -464,13 +465,16 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); static void flush_to_ldisc(struct work_struct *work) { - struct tty_struct *tty = - container_of(work, struct tty_struct, buf.work); - struct tty_port *port = tty->port; - struct tty_bufhead *buf = &tty->buf; + struct tty_port *port = container_of(work, struct tty_port, buf.work); + struct tty_bufhead *buf = &port->buf; + struct tty_struct *tty; unsigned long flags; struct tty_ldisc *disc; + tty = port->itty; + if (WARN_RATELIMIT(tty == NULL, "tty is NULL")) + return; + disc = tty_ldisc_ref(tty); if (disc == NULL) /* !TTY_LDISC */ return; @@ -489,7 +493,7 @@ static void flush_to_ldisc(struct work_struct *work) if (head->next == NULL) break; buf->head = head->next; - tty_buffer_free(tty, head); + tty_buffer_free(port, head); continue; } /* Ldisc or user is trying to flush the buffers @@ -515,7 +519,7 @@ static void flush_to_ldisc(struct work_struct *work) /* We may have a deferred request to flush the input buffer, if so pull the chain under the lock and empty the queue */ if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { - __tty_buffer_flush(tty); + __tty_buffer_flush(port); clear_bit(TTYP_FLUSHPENDING, &port->iflags); wake_up(&tty->read_wait); } @@ -535,7 +539,7 @@ static void flush_to_ldisc(struct work_struct *work) void tty_flush_to_ldisc(struct tty_struct *tty) { if (!tty->low_latency) - flush_work(&tty->buf.work); + flush_work(&tty->port->buf.work); } /** @@ -553,7 +557,7 @@ void tty_flush_to_ldisc(struct tty_struct *tty) void tty_flip_buffer_push(struct tty_struct *tty) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); @@ -578,9 +582,9 @@ EXPORT_SYMBOL(tty_flip_buffer_push); * Locking: none */ -void tty_buffer_init(struct tty_struct *tty) +void tty_buffer_init(struct tty_port *port) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; spin_lock_init(&buf->lock); buf->head = NULL; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 202008f38ca..a3eba7f359e 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -186,7 +186,6 @@ void free_tty_struct(struct tty_struct *tty) if (tty->dev) put_device(tty->dev); kfree(tty->write_buf); - tty_buffer_free_all(tty); tty->magic = 0xDEADDEAD; kfree(tty); } @@ -2935,7 +2934,6 @@ void initialize_tty_struct(struct tty_struct *tty, tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; - tty_buffer_init(tty); mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 47e3968df10..f4e6754525d 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -512,7 +512,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) static int tty_ldisc_halt(struct tty_struct *tty) { clear_bit(TTY_LDISC, &tty->flags); - return cancel_work_sync(&tty->buf.work); + return cancel_work_sync(&tty->port->buf.work); } /** @@ -525,7 +525,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty) { flush_work(&tty->hangup_work); flush_work(&tty->SAK_work); - flush_work(&tty->buf.work); + flush_work(&tty->port->buf.work); } /** @@ -704,9 +704,9 @@ enable: /* Restart the work queue in case no characters kick it off. Safe if already running */ if (work) - schedule_work(&tty->buf.work); + schedule_work(&tty->port->buf.work); if (o_work) - schedule_work(&o_tty->buf.work); + schedule_work(&o_tty->port->buf.work); mutex_unlock(&tty->ldisc_mutex); tty_unlock(tty); return retval; @@ -817,7 +817,7 @@ void tty_ldisc_hangup(struct tty_struct *tty) */ clear_bit(TTY_LDISC, &tty->flags); tty_unlock(tty); - cancel_work_sync(&tty->buf.work); + cancel_work_sync(&tty->port->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: tty_lock(tty); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index d7bdd8d0c23..416b42f7c34 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -21,6 +21,7 @@ void tty_port_init(struct tty_port *port) { memset(port, 0, sizeof(*port)); + tty_buffer_init(port); init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); init_waitqueue_head(&port->delta_msr_wait); @@ -126,6 +127,7 @@ static void tty_port_destructor(struct kref *kref) struct tty_port *port = container_of(kref, struct tty_port, kref); if (port->xmit_buf) free_page((unsigned long)port->xmit_buf); + tty_buffer_free_all(port); if (port->ops->destruct) port->ops->destruct(port); else diff --git a/include/linux/tty.h b/include/linux/tty.h index 9be74d649a5..d7ff88fb896 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -188,6 +188,7 @@ struct tty_port_operations { }; struct tty_port { + struct tty_bufhead buf; /* Locked internally */ struct tty_struct *tty; /* Back pointer */ struct tty_struct *itty; /* internal back ptr */ const struct tty_port_operations *ops; /* Port operations */ @@ -259,7 +260,6 @@ struct tty_struct { struct tty_struct *link; struct fasync_struct *fasync; - struct tty_bufhead buf; /* Locked internally */ int alt_speed; /* For magic substitution of 38400 bps */ wait_queue_head_t write_wait; wait_queue_head_t read_wait; @@ -388,9 +388,9 @@ extern void disassociate_ctty(int priv); extern void no_tty(void); extern void tty_flip_buffer_push(struct tty_struct *tty); extern void tty_flush_to_ldisc(struct tty_struct *tty); -extern void tty_buffer_free_all(struct tty_struct *tty); +extern void tty_buffer_free_all(struct tty_port *port); extern void tty_buffer_flush(struct tty_struct *tty); -extern void tty_buffer_init(struct tty_struct *tty); +extern void tty_buffer_init(struct tty_port *port); extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 9239d033a0a..2002344ed36 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -11,7 +11,7 @@ void tty_schedule_flip(struct tty_struct *tty); static inline int tty_insert_flip_char(struct tty_struct *tty, unsigned char ch, char flag) { - struct tty_buffer *tb = tty->buf.tail; + struct tty_buffer *tb = tty->port->buf.tail; if (tb && tb->used < tb->size) { tb->flag_buf_ptr[tb->used] = flag; tb->char_buf_ptr[tb->used++] = ch; From 6b898c07cb1d5bd8344a8044288bb4ae3873da74 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 17 Sep 2012 23:03:31 +0000 Subject: [PATCH 024/146] console: use might_sleep in console_lock Instead of BUG_ON(in_interrupt()), since that doesn't check for all the newfangled stuff like preempt. Note that this is valid since the console_sem is essentially used like a real mutex with only two twists: - we allow trylock from hardirq context - across suspend/resume we lock the logical console_lock, but drop the semaphore protecting the locking state. Now that doesn't guarantee that no one is playing tricks in single-thread atomic contexts at suspend/resume/boot time, but - I couldn't find anything suspicious with some grepping, - might_sleep shouldn't die, - and I think the upside of catching more potential issues is worth the risk of getting a might_sleep backtrace that would have been save (and then dealing with that fallout). Cc: Dave Airlie Cc: Thomas Gleixner Cc: Alan Cox Cc: Peter Zijlstra Signed-off-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- kernel/printk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/printk.c b/kernel/printk.c index ee79f14db35..22e070f3470 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1914,7 +1914,8 @@ static int __cpuinit console_cpu_notify(struct notifier_block *self, */ void console_lock(void) { - BUG_ON(in_interrupt()); + might_sleep(); + down(&console_sem); if (console_suspended) return; From b8b345bae8cb6745f2afdd28bb2d93f9cf0d7f2c Mon Sep 17 00:00:00 2001 From: Ivo Sieben Date: Wed, 24 Oct 2012 14:35:42 +0200 Subject: [PATCH 025/146] TTY: Report warning when low_latency flag is wrongly used When a driver has the low_latency flag set and uses the schedule_flip() function to initiate copying data to the line discipline, a workqueue is scheduled in but never actually flushed. This is incorrect use of the low_latency flag (driver should not support the low_latency flag, or use the tty_flip_buffer_push() function instead). Make sure a warning is reported to catch incorrect use of the low_latency flag. This patch goes with: cee4ad1ed90a0959fc29f9d30a2526e5e9522cfa Signed-off-by: Ivo Sieben Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 06725f5cc81..6cf87d7afb7 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -365,6 +365,7 @@ void tty_schedule_flip(struct tty_struct *tty) { struct tty_bufhead *buf = &tty->port->buf; unsigned long flags; + WARN_ON(tty->low_latency); spin_lock_irqsave(&buf->lock, flags); if (buf->tail != NULL) From 9484b009b57b6523a5c7477a899f4438942febde Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Wed, 3 Oct 2012 07:40:04 +0900 Subject: [PATCH 026/146] serial: samsung: use clk_prepare_enable and clk_disable_unprepare Convert clk_enable/clk_disable to clk_prepare_enable/clk_disable_unprepare calls as required by common clock framework. Signed-off-by: Thomas Abraham Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 7f04717176a..740458ca62c 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -530,16 +530,16 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, switch (level) { case 3: if (!IS_ERR(ourport->baudclk)) - clk_disable(ourport->baudclk); + clk_disable_unprepare(ourport->baudclk); - clk_disable(ourport->clk); + clk_disable_unprepare(ourport->clk); break; case 0: - clk_enable(ourport->clk); + clk_prepare_enable(ourport->clk); if (!IS_ERR(ourport->baudclk)) - clk_enable(ourport->baudclk); + clk_prepare_enable(ourport->baudclk); break; default: @@ -713,11 +713,11 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, s3c24xx_serial_setsource(port, clk_sel); if (!IS_ERR(ourport->baudclk)) { - clk_disable(ourport->baudclk); + clk_disable_unprepare(ourport->baudclk); ourport->baudclk = ERR_PTR(-EINVAL); } - clk_enable(clk); + clk_prepare_enable(clk); ourport->baudclk = clk; ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; @@ -1287,9 +1287,9 @@ static int s3c24xx_serial_resume(struct device *dev) struct s3c24xx_uart_port *ourport = to_ourport(port); if (port) { - clk_enable(ourport->clk); + clk_prepare_enable(ourport->clk); s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port)); - clk_disable(ourport->clk); + clk_disable_unprepare(ourport->clk); uart_resume_port(&s3c24xx_uart_drv, port); } From b15d5380e471f9ce27180b14d5080abc2e2f30ec Mon Sep 17 00:00:00 2001 From: Alexey Brodkin Date: Wed, 3 Oct 2012 16:27:43 +0400 Subject: [PATCH 027/146] serial/8250/8250_early: Prevent rounding error in uartclk Modify divisor to select the nearest baud rate divider rather than the lowest. It minimizes baud rate errors especially on low UART clock frequencies. For example, if uartclk is 33000000 and baud is 115200 the ratio is about 17.9 The current code selects 17 (5% error) but should select 18 (0.5% error). This 5% error in baud rate leads to garbage on receiving end, while 0.5% doesn't. The issue showed up when using the stock 8250 driver for Synopsys DW UART. This was on a FPGA with ~12MHz UART clock. When we enabled early serial, we saw garbage which was narrowed down to the rounding error. So the bug had been latent and it only showed up with such low clock rates. Signed-off-by: Alexey Brodkin Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_early.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index eaafb98debe..843a150ba10 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -140,7 +140,7 @@ static void __init init_port(struct early_serial8250_device *device) serial_out(port, UART_FCR, 0); /* no fifo */ serial_out(port, UART_MCR, 0x3); /* DTR + RTS */ - divisor = port->uartclk / (16 * device->baud); + divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); c = serial_in(port, UART_LCR); serial_out(port, UART_LCR, c | UART_LCR_DLAB); serial_out(port, UART_DLL, divisor & 0xff); From 54ec52b6dd3b0ba4bc4eb97e7e1b2534705b326c Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 3 Oct 2012 15:31:58 -0700 Subject: [PATCH 028/146] tty/serial/8250: Make omap hardware workarounds local to 8250.h This allows us to get rid of the ifdefs in 8250.c. Cc: Alan Cox Signed-off-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- arch/arm/plat-omap/include/plat/serial.h | 9 ------ drivers/tty/serial/8250/8250.c | 9 ++---- drivers/tty/serial/8250/8250.h | 36 ++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/arch/arm/plat-omap/include/plat/serial.h b/arch/arm/plat-omap/include/plat/serial.h index 65fce44dce3..b780470d03e 100644 --- a/arch/arm/plat-omap/include/plat/serial.h +++ b/arch/arm/plat-omap/include/plat/serial.h @@ -109,15 +109,6 @@ #define OMAP5UART4 OMAP4UART4 #define ZOOM_UART 95 /* Only on zoom2/3 */ -/* This is only used by 8250.c for omap1510 */ -#define is_omap_port(pt) ({int __ret = 0; \ - if ((pt)->port.mapbase == OMAP1_UART1_BASE || \ - (pt)->port.mapbase == OMAP1_UART2_BASE || \ - (pt)->port.mapbase == OMAP1_UART3_BASE) \ - __ret = 1; \ - __ret; \ - }) - #ifndef __ASSEMBLER__ struct omap_board_data; diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 3ba4234592b..5ccbd90540c 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -2349,16 +2349,14 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial_port_out(port, UART_EFR, efr); } -#ifdef CONFIG_ARCH_OMAP1 /* Workaround to enable 115200 baud on OMAP1510 internal ports */ - if (cpu_is_omap1510() && is_omap_port(up)) { + if (is_omap1510_8250(up)) { if (baud == 115200) { quot = 1; serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1); } else serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0); } -#endif /* * For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2, @@ -2439,10 +2437,9 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt) { if (pt->port.iotype == UPIO_AU) return 0x1000; -#ifdef CONFIG_ARCH_OMAP1 - if (is_omap_port(pt)) + if (is_omap1_8250(pt)) return 0x16 << pt->port.regshift; -#endif + return 8 << pt->port.regshift; } diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 5a76f9c8d36..3b4ea84898c 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -106,3 +106,39 @@ static inline int serial8250_pnp_init(void) { return 0; } static inline void serial8250_pnp_exit(void) { } #endif +#ifdef CONFIG_ARCH_OMAP1 +static inline int is_omap1_8250(struct uart_8250_port *pt) +{ + int res; + + switch (pt->port.mapbase) { + case OMAP1_UART1_BASE: + case OMAP1_UART2_BASE: + case OMAP1_UART3_BASE: + res = 1; + break; + default: + res = 0; + break; + } + + return res; +} + +static inline int is_omap1510_8250(struct uart_8250_port *pt) +{ + if (!cpu_is_omap1510()) + return 0; + + return is_omap1_8250(pt); +} +#else +static inline int is_omap1_8250(struct uart_8250_port *pt) +{ + return 0; +} +static inline int is_omap1510_8250(struct uart_8250_port *pt) +{ + return 0; +} +#endif From 59c2e855e43735f4ab93b8b8db96206219f6c1d4 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 8 Oct 2012 10:35:46 +0800 Subject: [PATCH 029/146] serial: vt8500: fix possible memory leak in vt8500_serial_probe() vt8500_port is malloced in vt8500_serial_probe() and should be freed before leaving from the error handling cases, otherwise it will cause memory leak. Fix it by move the allocation of vt8500_port after those test. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Acked-by: Tony Prisk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/vt8500_serial.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 205d4cf4a06..4354fe565f6 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -567,10 +567,6 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev) if (!mmres || !irqres) return -ENODEV; - vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL); - if (!vt8500_port) - return -ENOMEM; - if (np) port = of_alias_get_id(np, "serial"); if (port > VT8500_MAX_PORTS) @@ -593,6 +589,10 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev) return -EBUSY; } + vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL); + if (!vt8500_port) + return -ENOMEM; + vt8500_port->uart.type = PORT_VT8500; vt8500_port->uart.iotype = UPIO_MEM; vt8500_port->uart.mapbase = mmres->start; From b61c5ed57195ec97006d8d3ede1f583f6618b79e Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 15 Oct 2012 10:25:58 +0100 Subject: [PATCH 030/146] tty: serial: 8250_dw: Implement suspend/resume Implement suspend and resume callbacks for DesignWare 8250 driver. They're simple wrappers around serial8250_{suspend,resume}_port. Signed-off-by: James Hogan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index c3b2ec0c8c0..b19b8c54780 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -161,6 +161,29 @@ static int __devexit dw8250_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int dw8250_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct dw8250_data *data = platform_get_drvdata(pdev); + + serial8250_suspend_port(data->line); + + return 0; +} + +static int dw8250_resume(struct platform_device *pdev) +{ + struct dw8250_data *data = platform_get_drvdata(pdev); + + serial8250_resume_port(data->line); + + return 0; +} +#else +#define dw8250_suspend NULL +#define dw8250_resume NULL +#endif /* CONFIG_PM */ + static const struct of_device_id dw8250_match[] = { { .compatible = "snps,dw-apb-uart" }, { /* Sentinel */ } @@ -175,6 +198,8 @@ static struct platform_driver dw8250_platform_driver = { }, .probe = dw8250_probe, .remove = __devexit_p(dw8250_remove), + .suspend = dw8250_suspend, + .resume = dw8250_resume, }; module_platform_driver(dw8250_platform_driver); From 8ae763cd7e88a6bc552a6615ba6c1dcaa4828cbf Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 16 Oct 2012 11:53:44 +0100 Subject: [PATCH 031/146] audit: remove bogus tty name check tty name is an array not a pointer Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- kernel/auditsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 2f186ed80c4..fc7376bf86e 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1159,7 +1159,7 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) cred = current_cred(); spin_lock_irq(&tsk->sighand->siglock); - if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) + if (tsk->signal && tsk->signal->tty) tty = tsk->signal->tty->name; else tty = "(none)"; From 7a0c4edae99da6ab3d402deb0d88410251c6ac63 Mon Sep 17 00:00:00 2001 From: Sangho Yi Date: Thu, 18 Oct 2012 00:15:13 +0900 Subject: [PATCH 032/146] tty: tty_mutex.c: Fixed coding style warning (using printk) Here I fixed from printk(KERN_ERR, ... to pr_err(... on tty_mutex.c Signed-off-by: Sangho Yi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_mutex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 67feac9e6eb..2e41abebbcb 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -19,7 +19,7 @@ static void __lockfunc tty_lock_nested(struct tty_struct *tty, unsigned int subclass) { if (tty->magic != TTY_MAGIC) { - printk(KERN_ERR "L Bad %p\n", tty); + pr_err("L Bad %p\n", tty); WARN_ON(1); return; } @@ -36,7 +36,7 @@ EXPORT_SYMBOL(tty_lock); void __lockfunc tty_unlock(struct tty_struct *tty) { if (tty->magic != TTY_MAGIC) { - printk(KERN_ERR "U Bad %p\n", tty); + pr_err("U Bad %p\n", tty); WARN_ON(1); return; } From 08ec212c0f92cbf30e3ecc7349f18151714041d6 Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Fri, 19 Oct 2012 10:45:07 +0200 Subject: [PATCH 033/146] x86: ce4100: allow second UART usage The current CE4100 and 8250_pci code have both a limitation preventing the registration and usage of CE4100's second UART. This patch changes the platform code fixing up the UART port to work on a relative UART port base address, as well as the 8250_pci code to make it register 2 UART ports for CE4100 and pass the port index down to all consumers. Signed-off-by: Florian Fainelli Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- arch/x86/platform/ce4100/ce4100.c | 3 +++ drivers/tty/serial/8250/8250_pci.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/x86/platform/ce4100/ce4100.c b/arch/x86/platform/ce4100/ce4100.c index 4c61b52191e..0dcc30e9df8 100644 --- a/arch/x86/platform/ce4100/ce4100.c +++ b/arch/x86/platform/ce4100/ce4100.c @@ -92,8 +92,11 @@ static void ce4100_serial_fixup(int port, struct uart_port *up, up->membase = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); up->membase += up->mapbase & ~PAGE_MASK; + up->mapbase += port * 0x100; + up->membase += port * 0x100; up->iotype = UPIO_MEM32; up->regshift = 2; + up->irq = 4; } #endif up->iobase = 0; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 17b7d26abf4..cec8852dd1b 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1068,7 +1068,7 @@ ce4100_serial_setup(struct serial_private *priv, { int ret; - ret = setup_port(priv, port, 0, 0, board->reg_shift); + ret = setup_port(priv, port, idx, 0, board->reg_shift); port->port.iotype = UPIO_MEM32; port->port.type = PORT_XSCALE; port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); @@ -2658,8 +2658,8 @@ static struct pciserial_board pci_boards[] __devinitdata = { .first_offset = 0x1000, }, [pbn_ce4100_1_115200] = { - .flags = FL_BASE0, - .num_ports = 1, + .flags = FL_BASE_BARS, + .num_ports = 2, .base_baud = 921600, .reg_shift = 2, }, From ad3d1e5fc94e1d617298cf9b8fb522e2d219521a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 22 Oct 2012 12:42:59 +0800 Subject: [PATCH 034/146] TTY: hvcs: fix missing unlock on error in hvcs_initialize() Add the missing unlock on the error handling path in function hvcs_initialize(). Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvcs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index cab5c7adf8e..744c3b8eea4 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -1496,8 +1496,10 @@ static int __devinit hvcs_initialize(void) num_ttys_to_alloc = hvcs_parm_num_devs; hvcs_tty_driver = alloc_tty_driver(num_ttys_to_alloc); - if (!hvcs_tty_driver) + if (!hvcs_tty_driver) { + mutex_unlock(&hvcs_init_mutex); return -ENOMEM; + } if (hvcs_alloc_index_list(num_ttys_to_alloc)) { rc = -ENOMEM; From c97399418a25b18943c9910fb28e0ee5ecc3c316 Mon Sep 17 00:00:00 2001 From: Ivo Sieben Date: Wed, 17 Oct 2012 14:03:14 +0200 Subject: [PATCH 035/146] tty: Use raw spin lock to protect TTY ldisc administration The global "normal" spin lock that guards the line discipline administration is replaced by a raw spin lock. On a PREEMPT_RT system this prevents unwanted scheduling overhead around the line discipline administration. On a 200 MHz AT91SAM9261 processor setup this fixes about 100us of scheduling overhead on a TTY read or write call. Signed-off-by: Ivo Sieben Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index f4e6754525d..c5782294e53 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -26,7 +26,7 @@ * callers who will do ldisc lookups and cannot sleep. */ -static DEFINE_SPINLOCK(tty_ldisc_lock); +static DEFINE_RAW_SPINLOCK(tty_ldisc_lock); static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); /* Line disc dispatch table */ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; @@ -49,21 +49,21 @@ static void put_ldisc(struct tty_ldisc *ld) * If this is the last user, free the ldisc, and * release the ldisc ops. * - * We really want an "atomic_dec_and_lock_irqsave()", + * We really want an "atomic_dec_and_raw_lock_irqsave()", * but we don't have it, so this does it by hand. */ - local_irq_save(flags); - if (atomic_dec_and_lock(&ld->users, &tty_ldisc_lock)) { + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + if (atomic_dec_and_test(&ld->users)) { struct tty_ldisc_ops *ldo = ld->ops; ldo->refcount--; module_put(ldo->owner); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); kfree(ld); return; } - local_irq_restore(flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); wake_up(&ld->wq_idle); } @@ -88,11 +88,11 @@ int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; - spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); tty_ldiscs[disc] = new_ldisc; new_ldisc->num = disc; new_ldisc->refcount = 0; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; } @@ -118,12 +118,12 @@ int tty_unregister_ldisc(int disc) if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; - spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); if (tty_ldiscs[disc]->refcount) ret = -EBUSY; else tty_ldiscs[disc] = NULL; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; } @@ -134,7 +134,7 @@ static struct tty_ldisc_ops *get_ldops(int disc) unsigned long flags; struct tty_ldisc_ops *ldops, *ret; - spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); ret = ERR_PTR(-EINVAL); ldops = tty_ldiscs[disc]; if (ldops) { @@ -144,7 +144,7 @@ static struct tty_ldisc_ops *get_ldops(int disc) ret = ldops; } } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; } @@ -152,10 +152,10 @@ static void put_ldops(struct tty_ldisc_ops *ldops) { unsigned long flags; - spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); ldops->refcount--; module_put(ldops->owner); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); } /** @@ -287,11 +287,11 @@ static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) unsigned long flags; struct tty_ldisc *ld; - spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); ld = NULL; if (test_bit(TTY_LDISC, &tty->flags)) ld = get_ldisc(tty->ldisc); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ld; } From e1a9c17969f0aa60cb00f1f777b33a07f4e84883 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Sun, 21 Oct 2012 12:00:30 +0800 Subject: [PATCH 036/146] tty: serial: KGDB support for PXA Actually, in order to support KGDB over serial console one must implement two callbacks for character polling. Clone them from 8250 driver with a bit of tuning. Signed-off-by: Denis V. Lunev Signed-off-by: Marko Katic CC: Eric Miao CC: Russell King Acked-by: Haojian Zhuang drivers/tty/serial/pxa.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 55 insertions(+), 0 deletions(-) Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pxa.c | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 9033fc6e0e4..2764828251f 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -705,6 +705,57 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) clk_disable_unprepare(up->clk); } +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int serial_pxa_get_poll_char(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned char lsr = serial_in(up, UART_LSR); + + while (!(lsr & UART_LSR_DR)) + lsr = serial_in(up, UART_LSR); + + return serial_in(up, UART_RX); +} + + +static void serial_pxa_put_poll_char(struct uart_port *port, + unsigned char c) +{ + unsigned int ier; + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, UART_IER_UUE); + + wait_for_xmitr(up); + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(up, UART_TX, c); + if (c == 10) { + wait_for_xmitr(up); + serial_out(up, UART_TX, 13); + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); +} + +#endif /* CONFIG_CONSOLE_POLL */ + static int __init serial_pxa_console_setup(struct console *co, char *options) { @@ -759,6 +810,10 @@ struct uart_ops serial_pxa_pops = { .request_port = serial_pxa_request_port, .config_port = serial_pxa_config_port, .verify_port = serial_pxa_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = serial_pxa_get_poll_char, + .poll_put_char = serial_pxa_put_poll_char, +#endif }; static struct uart_driver serial_pxa_reg = { From 95113728f03cc6775ae895133c7fc420221cc8a4 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:23 +0400 Subject: [PATCH 037/146] serial: clps711x: Add platform_driver interface to clps711x driver Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 39 +++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index d0f719fafc8..07fef1cbc11 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -37,10 +37,13 @@ #include #include #include +#include #include #include +#define UART_CLPS711X_NAME "uart-clps711x" + #define UART_NR 2 #define SERIAL_CLPS711X_MAJOR 204 @@ -543,7 +546,7 @@ static struct uart_driver clps711x_reg = { .cons = CLPS711X_CONSOLE, }; -static int __init clps711xuart_init(void) +static int __devinit uart_clps711x_probe(struct platform_device *pdev) { int ret, i; @@ -559,7 +562,7 @@ static int __init clps711xuart_init(void) return 0; } -static void __exit clps711xuart_exit(void) +static int __devexit uart_clps711x_remove(struct platform_device *pdev) { int i; @@ -567,12 +570,36 @@ static void __exit clps711xuart_exit(void) uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); uart_unregister_driver(&clps711x_reg); + + return 0; } -module_init(clps711xuart_init); -module_exit(clps711xuart_exit); +static struct platform_driver clps711x_uart_driver = { + .driver = { + .name = UART_CLPS711X_NAME, + .owner = THIS_MODULE, + }, + .probe = uart_clps711x_probe, + .remove = __devexit_p(uart_clps711x_remove), +}; +module_platform_driver(clps711x_uart_driver); + +static struct platform_device clps711x_uart_device = { + .name = UART_CLPS711X_NAME, +}; + +static int __init uart_clps711x_init(void) +{ + return platform_device_register(&clps711x_uart_device); +} +module_init(uart_clps711x_init); + +static void __exit uart_clps711x_exit(void) +{ + platform_device_unregister(&clps711x_uart_device); +} +module_exit(uart_clps711x_exit); MODULE_AUTHOR("Deep Blue Solutions Ltd"); -MODULE_DESCRIPTION("CLPS-711x generic serial driver"); +MODULE_DESCRIPTION("CLPS711X serial driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR); From 117d5d424a1ee7aedaf6ad8b0ba6eff163c57815 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:24 +0400 Subject: [PATCH 038/146] serial: clps711x: Convert all static variables to dynamic This patch converts all static variables of clps711x serial driver to dynamic allocating. In this case we are should remove console_initcall() and declare console during driver registration. Early kernel messages can be retrieved by add "earlyprintk" option to the kernel command line. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 211 ++++++++++++++-------------------- 1 file changed, 89 insertions(+), 122 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 07fef1cbc11..de6aa33c305 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -43,28 +43,29 @@ #include #define UART_CLPS711X_NAME "uart-clps711x" +#define UART_CLPS711X_NR 2 +#define UART_CLPS711X_MAJOR 204 +#define UART_CLPS711X_MINOR 40 -#define UART_NR 2 - -#define SERIAL_CLPS711X_MAJOR 204 -#define SERIAL_CLPS711X_MINOR 40 -#define SERIAL_CLPS711X_NR UART_NR - -/* - * We use the relevant SYSCON register as a base address for these ports. - */ -#define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1) -#define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1) -#define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1) -#define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1) - -#define TX_IRQ(port) ((port)->irq) -#define RX_IRQ(port) ((port)->irq + 1) +#define UBRLCR(port) ((port)->line ? UBRLCR2 : UBRLCR1) +#define UARTDR(port) ((port)->line ? UARTDR2 : UARTDR1) +#define SYSFLG(port) ((port)->line ? SYSFLG2 : SYSFLG1) +#define SYSCON(port) ((port)->line ? SYSCON2 : SYSCON1) +#define TX_IRQ(port) ((port)->line ? IRQ_UTXINT2 : IRQ_UTXINT1) +#define RX_IRQ(port) ((port)->line ? IRQ_URXINT2 : IRQ_URXINT1) #define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) #define tx_enabled(port) ((port)->unused[0]) +struct clps711x_port { + struct uart_driver uart; + struct uart_port port[UART_CLPS711X_NR]; +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + struct console console; +#endif +}; + static void clps711xuart_stop_tx(struct uart_port *port) { if (tx_enabled(port)) { @@ -382,7 +383,7 @@ static int clps711xuart_request_port(struct uart_port *port) return 0; } -static struct uart_ops clps711x_pops = { +static struct uart_ops uart_clps711x_ops = { .tx_empty = clps711xuart_tx_empty, .set_mctrl = clps711xuart_set_mctrl_null, .get_mctrl = clps711xuart_get_mctrl, @@ -400,72 +401,39 @@ static struct uart_ops clps711x_pops = { .request_port = clps711xuart_request_port, }; -static struct uart_port clps711x_ports[UART_NR] = { - { - .iobase = SYSCON1, - .irq = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */ - .uartclk = 3686400, - .fifosize = 16, - .ops = &clps711x_pops, - .line = 0, - .flags = UPF_BOOT_AUTOCONF, - }, - { - .iobase = SYSCON2, - .irq = IRQ_UTXINT2, /* IRQ_URXINT2 */ - .uartclk = 3686400, - .fifosize = 16, - .ops = &clps711x_pops, - .line = 1, - .flags = UPF_BOOT_AUTOCONF, - } -}; - #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE -static void clps711xuart_console_putchar(struct uart_port *port, int ch) +static void uart_clps711x_console_putchar(struct uart_port *port, int ch) { while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF) barrier(); - clps_writel(ch, UARTDR(port)); + + clps_writew(ch, UARTDR(port)); } -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console_lock must be held when we get here. - * - * Note that this is called with interrupts already disabled - */ -static void -clps711xuart_console_write(struct console *co, const char *s, - unsigned int count) +static void uart_clps711x_console_write(struct console *co, const char *c, + unsigned n) { - struct uart_port *port = clps711x_ports + co->index; - unsigned int status, syscon; + struct clps711x_port *s = (struct clps711x_port *)co->data; + struct uart_port *port = &s->port[co->index]; + u32 syscon; - /* - * Ensure that the port is enabled. - */ + /* Ensure that the port is enabled */ syscon = clps_readl(SYSCON(port)); clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); - uart_console_write(port, s, count, clps711xuart_console_putchar); + uart_console_write(port, c, n, uart_clps711x_console_putchar); - /* - * Finally, wait for transmitter to become empty - * and restore the uart state. - */ - do { - status = clps_readl(SYSFLG(port)); - } while (status & SYSFLG_UBUSY); + /* Wait for transmitter to become empty */ + while (clps_readl(SYSFLG(port)) & SYSFLG_UBUSY) + barrier(); + /* Restore the uart state */ clps_writel(syscon, SYSCON(port)); } -static void __init -clps711xuart_console_get_options(struct uart_port *port, int *baud, - int *parity, int *bits) +static void uart_clps711x_console_get_options(struct uart_port *port, + int *baud, int *parity, + int *bits) { if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { unsigned int ubrlcr, quot; @@ -490,86 +458,85 @@ clps711xuart_console_get_options(struct uart_port *port, int *baud, } } -static int __init clps711xuart_console_setup(struct console *co, char *options) +static int uart_clps711x_console_setup(struct console *co, char *options) { - struct uart_port *port; - int baud = 38400; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - port = uart_get_console(clps711x_ports, UART_NR, co); + int baud = 38400, bits = 8, parity = 'n', flow = 'n'; + struct clps711x_port *s = (struct clps711x_port *)co->data; + struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0]; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - clps711xuart_console_get_options(port, &baud, &parity, &bits); + uart_clps711x_console_get_options(port, &baud, &parity, &bits); return uart_set_options(port, co, baud, parity, bits, flow); } - -static struct uart_driver clps711x_reg; -static struct console clps711x_console = { - .name = "ttyCL", - .write = clps711xuart_console_write, - .device = uart_console_device, - .setup = clps711xuart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &clps711x_reg, -}; - -static int __init clps711xuart_console_init(void) -{ - register_console(&clps711x_console); - return 0; -} -console_initcall(clps711xuart_console_init); - -#define CLPS711X_CONSOLE &clps711x_console -#else -#define CLPS711X_CONSOLE NULL #endif -static struct uart_driver clps711x_reg = { - .driver_name = "ttyCL", - .dev_name = "ttyCL", - .major = SERIAL_CLPS711X_MAJOR, - .minor = SERIAL_CLPS711X_MINOR, - .nr = UART_NR, - - .cons = CLPS711X_CONSOLE, -}; - static int __devinit uart_clps711x_probe(struct platform_device *pdev) { + struct clps711x_port *s; int ret, i; - printk(KERN_INFO "Serial: CLPS711x driver\n"); + s = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_port), GFP_KERNEL); + if (!s) { + dev_err(&pdev->dev, "Error allocating port structure\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, s); - ret = uart_register_driver(&clps711x_reg); - if (ret) - return ret; + s->uart.owner = THIS_MODULE; + s->uart.dev_name = "ttyCL"; + s->uart.major = UART_CLPS711X_MAJOR; + s->uart.minor = UART_CLPS711X_MINOR; + s->uart.nr = UART_CLPS711X_NR; +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + s->uart.cons = &s->console; + s->uart.cons->device = uart_console_device; + s->uart.cons->write = uart_clps711x_console_write; + s->uart.cons->setup = uart_clps711x_console_setup; + s->uart.cons->flags = CON_PRINTBUFFER; + s->uart.cons->index = -1; + s->uart.cons->data = s; + strcpy(s->uart.cons->name, "ttyCL"); +#endif + ret = uart_register_driver(&s->uart); + if (ret) { + dev_err(&pdev->dev, "Registering UART driver failed\n"); + goto err_out; + } - for (i = 0; i < UART_NR; i++) - uart_add_one_port(&clps711x_reg, &clps711x_ports[i]); + for (i = 0; i < UART_CLPS711X_NR; i++) { + s->port[i].line = i; + s->port[i].dev = &pdev->dev; + s->port[i].irq = TX_IRQ(&s->port[i]); + s->port[i].iobase = SYSCON(&s->port[i]); + s->port[i].type = PORT_CLPS711X; + s->port[i].fifosize = 16; + s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; + s->port[i].uartclk = 3686400; + s->port[i].ops = &uart_clps711x_ops; + WARN_ON(uart_add_one_port(&s->uart, &s->port[i])); + } return 0; + +err_out: + platform_set_drvdata(pdev, NULL); + + return ret; } static int __devexit uart_clps711x_remove(struct platform_device *pdev) { + struct clps711x_port *s = platform_get_drvdata(pdev); int i; - for (i = 0; i < UART_NR; i++) - uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); + for (i = 0; i < UART_CLPS711X_NR; i++) + uart_remove_one_port(&s->uart, &s->port[i]); - uart_unregister_driver(&clps711x_reg); + uart_unregister_driver(&s->uart); + platform_set_drvdata(pdev, NULL); return 0; } From 3c7e9eb1603d21659a38b92f5b764d41d88fa652 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:25 +0400 Subject: [PATCH 039/146] serial: clps711x: Do not use "uart_port->unused" field Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index de6aa33c305..3b17d193248 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -56,11 +56,10 @@ #define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) -#define tx_enabled(port) ((port)->unused[0]) - struct clps711x_port { struct uart_driver uart; struct uart_port port[UART_CLPS711X_NR]; + int tx_enabled[UART_CLPS711X_NR]; #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE struct console console; #endif @@ -68,17 +67,21 @@ struct clps711x_port { static void clps711xuart_stop_tx(struct uart_port *port) { - if (tx_enabled(port)) { + struct clps711x_port *s = dev_get_drvdata(port->dev); + + if (s->tx_enabled[port->line]) { disable_irq(TX_IRQ(port)); - tx_enabled(port) = 0; + s->tx_enabled[port->line] = 0; } } static void clps711xuart_start_tx(struct uart_port *port) { - if (!tx_enabled(port)) { + struct clps711x_port *s = dev_get_drvdata(port->dev); + + if (!s->tx_enabled[port->line]) { enable_irq(TX_IRQ(port)); - tx_enabled(port) = 1; + s->tx_enabled[port->line] = 1; } } @@ -148,6 +151,7 @@ static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id) static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) { struct uart_port *port = dev_id; + struct clps711x_port *s = dev_get_drvdata(port->dev); struct circ_buf *xmit = &port->state->xmit; int count; @@ -158,8 +162,11 @@ static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) return IRQ_HANDLED; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) - goto disable_tx_irq; + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + disable_irq_nosync(TX_IRQ(port)); + s->tx_enabled[port->line] = 0; + return IRQ_HANDLED; + } count = port->fifosize >> 1; do { @@ -173,12 +180,6 @@ static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { - disable_tx_irq: - disable_irq_nosync(TX_IRQ(port)); - tx_enabled(port) = 0; - } - return IRQ_HANDLED; } @@ -230,10 +231,11 @@ static void clps711xuart_break_ctl(struct uart_port *port, int break_state) static int clps711xuart_startup(struct uart_port *port) { + struct clps711x_port *s = dev_get_drvdata(port->dev); unsigned int syscon; int retval; - tx_enabled(port) = 1; + s->tx_enabled[port->line] = 1; /* * Allocate the IRQs From c08f0153f54ee0a7f3eeaf7f48dce1a71b3f8c7d Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:26 +0400 Subject: [PATCH 040/146] serial: clps711x: Using CPU clock subsystem for getting base UART speed Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 3b17d193248..0884939d29c 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -58,6 +59,7 @@ struct clps711x_port { struct uart_driver uart; + struct clk *uart_clk; struct uart_port port[UART_CLPS711X_NR]; int tx_enabled[UART_CLPS711X_NR]; #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE @@ -299,10 +301,9 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, */ termios->c_cflag |= CREAD; - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + /* Ask the core to calculate the divisor for us */ + baud = uart_get_baud_rate(port, termios, old, port->uartclk / 4096, + port->uartclk / 16); quot = uart_get_divisor(port, baud); switch (termios->c_cflag & CSIZE) { @@ -487,6 +488,13 @@ static int __devinit uart_clps711x_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, s); + s->uart_clk = devm_clk_get(&pdev->dev, "uart"); + if (IS_ERR(s->uart_clk)) { + dev_err(&pdev->dev, "Can't get UART clocks\n"); + ret = PTR_ERR(s->uart_clk); + goto err_out; + } + s->uart.owner = THIS_MODULE; s->uart.dev_name = "ttyCL"; s->uart.major = UART_CLPS711X_MAJOR; @@ -505,6 +513,7 @@ static int __devinit uart_clps711x_probe(struct platform_device *pdev) ret = uart_register_driver(&s->uart); if (ret) { dev_err(&pdev->dev, "Registering UART driver failed\n"); + devm_clk_put(&pdev->dev, s->uart_clk); goto err_out; } @@ -516,7 +525,7 @@ static int __devinit uart_clps711x_probe(struct platform_device *pdev) s->port[i].type = PORT_CLPS711X; s->port[i].fifosize = 16; s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; - s->port[i].uartclk = 3686400; + s->port[i].uartclk = clk_get_rate(s->uart_clk); s->port[i].ops = &uart_clps711x_ops; WARN_ON(uart_add_one_port(&s->uart, &s->port[i])); } @@ -537,6 +546,7 @@ static int __devexit uart_clps711x_remove(struct platform_device *pdev) for (i = 0; i < UART_CLPS711X_NR; i++) uart_remove_one_port(&s->uart, &s->port[i]); + devm_clk_put(&pdev->dev, s->uart_clk); uart_unregister_driver(&s->uart); platform_set_drvdata(pdev, NULL); From cf03a884b9f4a63d4bcf29614fe03ca3f8299138 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:27 +0400 Subject: [PATCH 041/146] serial: clps711x: Improved TX FIFO handling Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 0884939d29c..d37460965ba 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -155,7 +155,6 @@ static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) struct uart_port *port = dev_id; struct clps711x_port *s = dev_get_drvdata(port->dev); struct circ_buf *xmit = &port->state->xmit; - int count; if (port->x_char) { clps_writel(port->x_char, UARTDR(port)); @@ -170,14 +169,13 @@ static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) return IRQ_HANDLED; } - count = port->fifosize >> 1; - do { - clps_writel(xmit->buf[xmit->tail], UARTDR(port)); + while (!uart_circ_empty(xmit)) { + clps_writew(xmit->buf[xmit->tail], UARTDR(port)); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; - if (uart_circ_empty(xmit)) + if (clps_readl(SYSFLG(port) & SYSFLG_UTXFF)) break; - } while (--count > 0); + } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); @@ -327,8 +325,9 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, if (!(termios->c_cflag & PARODD)) ubrlcr |= UBRLCR_EVENPRT; } - if (port->fifosize > 1) - ubrlcr |= UBRLCR_FIFOEN; + + /* Enable FIFO */ + ubrlcr |= UBRLCR_FIFOEN; spin_lock_irqsave(&port->lock, flags); From 1593daf9a84f4b29e90027e0999c93da1d25478b Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:28 +0400 Subject: [PATCH 042/146] serial: clps711x: Return valid modem controls for port that not support it Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index d37460965ba..7b0e539ee9c 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -191,12 +191,9 @@ static unsigned int clps711xuart_tx_empty(struct uart_port *port) static unsigned int clps711xuart_get_mctrl(struct uart_port *port) { - unsigned int port_addr; - unsigned int result = 0; - unsigned int status; + unsigned int status, result = 0; - port_addr = SYSFLG(port); - if (port_addr == SYSFLG1) { + if (port->line == 0) { status = clps_readl(SYSFLG1); if (status & SYSFLG1_DCD) result |= TIOCM_CAR; @@ -204,7 +201,8 @@ static unsigned int clps711xuart_get_mctrl(struct uart_port *port) result |= TIOCM_DSR; if (status & SYSFLG1_CTS) result |= TIOCM_CTS; - } + } else + result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; return result; } From ec335526b4bce21f6777d3917d6d67c16009ec63 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:29 +0400 Subject: [PATCH 043/146] serial: clps711x: Fix break control handling Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 7b0e539ee9c..73505c1edc7 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -218,12 +218,14 @@ static void clps711xuart_break_ctl(struct uart_port *port, int break_state) unsigned int ubrlcr; spin_lock_irqsave(&port->lock, flags); + ubrlcr = clps_readl(UBRLCR(port)); - if (break_state == -1) + if (break_state) ubrlcr |= UBRLCR_BREAK; else ubrlcr &= ~UBRLCR_BREAK; clps_writel(ubrlcr, UBRLCR(port)); + spin_unlock_irqrestore(&port->lock, flags); } From f27de95c2a2120526c8cb051d741b7eb09e78cc9 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:30 +0400 Subject: [PATCH 044/146] serial: clps711x: Check for valid TTY in RX-interrupt Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 59 +++++++++++++++++------------------ 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 73505c1edc7..6039ebe349c 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -55,8 +55,6 @@ #define TX_IRQ(port) ((port)->line ? IRQ_UTXINT2 : IRQ_UTXINT1) #define RX_IRQ(port) ((port)->line ? IRQ_URXINT2 : IRQ_URXINT1) -#define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) - struct clps711x_port { struct uart_driver uart; struct clk *uart_clk; @@ -99,54 +97,55 @@ static void clps711xuart_enable_ms(struct uart_port *port) static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct tty_struct *tty = port->state->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); unsigned int status, ch, flg; - status = clps_readl(SYSFLG(port)); - while (!(status & SYSFLG_URXFE)) { - ch = clps_readl(UARTDR(port)); + if (!tty) + return IRQ_HANDLED; + + for (;;) { + status = clps_readl(SYSFLG(port)); + if (status & SYSFLG_URXFE) + break; + + ch = clps_readw(UARTDR(port)); + status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR); + ch &= 0xff; port->icount.rx++; - flg = TTY_NORMAL; - /* - * Note that the error handling code is - * out of the main execution path - */ - if (unlikely(ch & UART_ANY_ERR)) { - if (ch & UARTDR_PARERR) + if (unlikely(status)) { + if (status & UARTDR_PARERR) port->icount.parity++; - else if (ch & UARTDR_FRMERR) + else if (status & UARTDR_FRMERR) port->icount.frame++; - if (ch & UARTDR_OVERR) + else if (status & UARTDR_OVERR) port->icount.overrun++; - ch &= port->read_status_mask; + status &= port->read_status_mask; - if (ch & UARTDR_PARERR) + if (status & UARTDR_PARERR) flg = TTY_PARITY; - else if (ch & UARTDR_FRMERR) + else if (status & UARTDR_FRMERR) flg = TTY_FRAME; - -#ifdef SUPPORT_SYSRQ - port->sysrq = 0; -#endif + else if (status & UARTDR_OVERR) + flg = TTY_OVERRUN; } if (uart_handle_sysrq_char(port, ch)) - goto ignore_char; + continue; - /* - * CHECK: does overrun affect the current character? - * ASSUMPTION: it does not. - */ - uart_insert_char(port, ch, UARTDR_OVERR, ch, flg); + if (status & port->ignore_status_mask) + continue; - ignore_char: - status = clps_readl(SYSFLG(port)); + uart_insert_char(port, status, UARTDR_OVERR, ch, flg); } + tty_flip_buffer_push(tty); + + tty_kref_put(tty); + return IRQ_HANDLED; } From 135cc7903593af78c45dc3f8e6a1f528f083e002 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:31 +0400 Subject: [PATCH 045/146] serial: clps711x: Using resource-managed functions Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 37 +++++++++++++++-------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 6039ebe349c..7cf392829ff 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -94,7 +94,7 @@ static void clps711xuart_enable_ms(struct uart_port *port) { } -static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id) +static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id) { struct uart_port *port = dev_id; struct tty_struct *tty = tty_port_tty_get(&port->state->port); @@ -149,7 +149,7 @@ static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id) return IRQ_HANDLED; } -static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) +static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) { struct uart_port *port = dev_id; struct clps711x_port *s = dev_get_drvdata(port->dev); @@ -232,23 +232,20 @@ static int clps711xuart_startup(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); unsigned int syscon; - int retval; + int ret; s->tx_enabled[port->line] = 1; + /* Allocate the IRQs */ + ret = devm_request_irq(port->dev, TX_IRQ(port), uart_clps711x_int_tx, + 0, UART_CLPS711X_NAME " TX", port); + if (ret) + return ret; - /* - * Allocate the IRQs - */ - retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0, - "clps711xuart_tx", port); - if (retval) - return retval; - - retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0, - "clps711xuart_rx", port); - if (retval) { - free_irq(TX_IRQ(port), port); - return retval; + ret = devm_request_irq(port->dev, RX_IRQ(port), uart_clps711x_int_rx, + 0, UART_CLPS711X_NAME " RX", port); + if (ret) { + devm_free_irq(port->dev, TX_IRQ(port), port); + return ret; } /* @@ -265,11 +262,9 @@ static void clps711xuart_shutdown(struct uart_port *port) { unsigned int ubrlcr, syscon; - /* - * Free the interrupt - */ - free_irq(TX_IRQ(port), port); /* TX interrupt */ - free_irq(RX_IRQ(port), port); /* RX interrupt */ + /* Free the interrupts */ + devm_free_irq(port->dev, TX_IRQ(port), port); + devm_free_irq(port->dev, RX_IRQ(port), port); /* * disable the port From f52ede2ac1159f844994519ae0386def308a296b Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:32 +0400 Subject: [PATCH 046/146] serial: clps711x: Disable "break"-state before port startup Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 7cf392829ff..e71508767e1 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -231,7 +231,6 @@ static void clps711xuart_break_ctl(struct uart_port *port, int break_state) static int clps711xuart_startup(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); - unsigned int syscon; int ret; s->tx_enabled[port->line] = 1; @@ -248,37 +247,23 @@ static int clps711xuart_startup(struct uart_port *port) return ret; } - /* - * enable the port - */ - syscon = clps_readl(SYSCON(port)); - syscon |= SYSCON_UARTEN; - clps_writel(syscon, SYSCON(port)); + /* Disable break */ + clps_writel(clps_readl(UBRLCR(port)) & ~UBRLCR_BREAK, UBRLCR(port)); + + /* Enable the port */ + clps_writel(clps_readl(SYSCON(port)) | SYSCON_UARTEN, SYSCON(port)); return 0; } static void clps711xuart_shutdown(struct uart_port *port) { - unsigned int ubrlcr, syscon; - /* Free the interrupts */ devm_free_irq(port->dev, TX_IRQ(port), port); devm_free_irq(port->dev, RX_IRQ(port), port); - /* - * disable the port - */ - syscon = clps_readl(SYSCON(port)); - syscon &= ~SYSCON_UARTEN; - clps_writel(syscon, SYSCON(port)); - - /* - * disable break condition and fifos - */ - ubrlcr = clps_readl(UBRLCR(port)); - ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK); - clps_writel(ubrlcr, UBRLCR(port)); + /* Disable the port */ + clps_writel(clps_readl(SYSCON(port)) & ~SYSCON_UARTEN, SYSCON(port)); } static void From 7ae75e94ec1128598f91dc56ca2919c45701ec32 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:33 +0400 Subject: [PATCH 047/146] serial: clps711x: Fix TERMIOS-flags handling Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 36 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index e71508767e1..90efe059571 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -273,10 +273,9 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, unsigned int ubrlcr, baud, quot; unsigned long flags; - /* - * We don't implement CREAD. - */ - termios->c_cflag |= CREAD; + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + termios->c_iflag &= ~(BRKINT | IGNBRK); /* Ask the core to calculate the divisor for us */ baud = uart_get_baud_rate(port, termios, old, port->uartclk / 4096, @@ -297,8 +296,10 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, ubrlcr = UBRLCR_WRDLEN8; break; } + if (termios->c_cflag & CSTOPB) ubrlcr |= UBRLCR_XSTOP; + if (termios->c_cflag & PARENB) { ubrlcr |= UBRLCR_PRTEN; if (!(termios->c_cflag & PARODD)) @@ -310,33 +311,20 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, spin_lock_irqsave(&port->lock, flags); - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - + /* Set read status mask */ port->read_status_mask = UARTDR_OVERR; if (termios->c_iflag & INPCK) port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR; - /* - * Characters to ignore - */ + /* Set status ignore mask */ port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR; - if (termios->c_iflag & IGNBRK) { - /* - * If we're ignoring parity and break indicators, - * ignore overruns to (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= UARTDR_OVERR; - } + if (!(termios->c_cflag & CREAD)) + port->ignore_status_mask |= UARTDR_OVERR | UARTDR_PARERR | + UARTDR_FRMERR; - quot -= 1; + uart_update_timeout(port, termios->c_cflag, baud); - clps_writel(ubrlcr | quot, UBRLCR(port)); + clps_writel(ubrlcr | (quot - 1), UBRLCR(port)); spin_unlock_irqrestore(&port->lock, flags); } From a1c25f2b98e10038f7d198704d033eb73bad0677 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 14 Oct 2012 11:05:34 +0400 Subject: [PATCH 048/146] serial: clps711x: Cleanup driver This patch performs cleanup on clps711x serial driver. This include: - Change functions naming style. - Removed unused includes. - Removed unneeded comments. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/clps711x.c | 105 +++++++++++++++------------------- 1 file changed, 46 insertions(+), 59 deletions(-) diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 90efe059571..a0a6db5c32f 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -26,22 +17,18 @@ #endif #include -#include -#include -#include -#include -#include #include -#include -#include +#include #include #include #include #include +#include +#include +#include #include #include -#include #define UART_CLPS711X_NAME "uart-clps711x" #define UART_CLPS711X_NR 2 @@ -65,7 +52,7 @@ struct clps711x_port { #endif }; -static void clps711xuart_stop_tx(struct uart_port *port) +static void uart_clps711x_stop_tx(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); @@ -75,7 +62,7 @@ static void clps711xuart_stop_tx(struct uart_port *port) } } -static void clps711xuart_start_tx(struct uart_port *port) +static void uart_clps711x_start_tx(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); @@ -85,13 +72,14 @@ static void clps711xuart_start_tx(struct uart_port *port) } } -static void clps711xuart_stop_rx(struct uart_port *port) +static void uart_clps711x_stop_rx(struct uart_port *port) { disable_irq(RX_IRQ(port)); } -static void clps711xuart_enable_ms(struct uart_port *port) +static void uart_clps711x_enable_ms(struct uart_port *port) { + /* Do nothing */ } static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id) @@ -156,7 +144,7 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) struct circ_buf *xmit = &port->state->xmit; if (port->x_char) { - clps_writel(port->x_char, UARTDR(port)); + clps_writew(port->x_char, UARTDR(port)); port->icount.tx++; port->x_char = 0; return IRQ_HANDLED; @@ -182,13 +170,12 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) return IRQ_HANDLED; } -static unsigned int clps711xuart_tx_empty(struct uart_port *port) +static unsigned int uart_clps711x_tx_empty(struct uart_port *port) { - unsigned int status = clps_readl(SYSFLG(port)); - return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT; + return (clps_readl(SYSFLG(port) & SYSFLG_UBUSY)) ? 0 : TIOCSER_TEMT; } -static unsigned int clps711xuart_get_mctrl(struct uart_port *port) +static unsigned int uart_clps711x_get_mctrl(struct uart_port *port) { unsigned int status, result = 0; @@ -206,12 +193,12 @@ static unsigned int clps711xuart_get_mctrl(struct uart_port *port) return result; } -static void -clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl) +static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl) { + /* Do nothing */ } -static void clps711xuart_break_ctl(struct uart_port *port, int break_state) +static void uart_clps711x_break_ctl(struct uart_port *port, int break_state) { unsigned long flags; unsigned int ubrlcr; @@ -228,7 +215,7 @@ static void clps711xuart_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&port->lock, flags); } -static int clps711xuart_startup(struct uart_port *port) +static int uart_clps711x_startup(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); int ret; @@ -256,7 +243,7 @@ static int clps711xuart_startup(struct uart_port *port) return 0; } -static void clps711xuart_shutdown(struct uart_port *port) +static void uart_clps711x_shutdown(struct uart_port *port) { /* Free the interrupts */ devm_free_irq(port->dev, TX_IRQ(port), port); @@ -266,9 +253,9 @@ static void clps711xuart_shutdown(struct uart_port *port) clps_writel(clps_readl(SYSCON(port)) & ~SYSCON_UARTEN, SYSCON(port)); } -static void -clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) +static void uart_clps711x_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) { unsigned int ubrlcr, baud, quot; unsigned long flags; @@ -292,7 +279,8 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, case CS7: ubrlcr = UBRLCR_WRDLEN7; break; - default: // CS8 + case CS8: + default: ubrlcr = UBRLCR_WRDLEN8; break; } @@ -329,45 +317,44 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, spin_unlock_irqrestore(&port->lock, flags); } -static const char *clps711xuart_type(struct uart_port *port) +static const char *uart_clps711x_type(struct uart_port *port) { - return port->type == PORT_CLPS711X ? "CLPS711x" : NULL; + return (port->type == PORT_CLPS711X) ? "CLPS711X" : NULL; } -/* - * Configure/autoconfigure the port. - */ -static void clps711xuart_config_port(struct uart_port *port, int flags) +static void uart_clps711x_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) port->type = PORT_CLPS711X; } -static void clps711xuart_release_port(struct uart_port *port) +static void uart_clps711x_release_port(struct uart_port *port) { + /* Do nothing */ } -static int clps711xuart_request_port(struct uart_port *port) +static int uart_clps711x_request_port(struct uart_port *port) { + /* Do nothing */ return 0; } -static struct uart_ops uart_clps711x_ops = { - .tx_empty = clps711xuart_tx_empty, - .set_mctrl = clps711xuart_set_mctrl_null, - .get_mctrl = clps711xuart_get_mctrl, - .stop_tx = clps711xuart_stop_tx, - .start_tx = clps711xuart_start_tx, - .stop_rx = clps711xuart_stop_rx, - .enable_ms = clps711xuart_enable_ms, - .break_ctl = clps711xuart_break_ctl, - .startup = clps711xuart_startup, - .shutdown = clps711xuart_shutdown, - .set_termios = clps711xuart_set_termios, - .type = clps711xuart_type, - .config_port = clps711xuart_config_port, - .release_port = clps711xuart_release_port, - .request_port = clps711xuart_request_port, +static const struct uart_ops uart_clps711x_ops = { + .tx_empty = uart_clps711x_tx_empty, + .set_mctrl = uart_clps711x_set_mctrl, + .get_mctrl = uart_clps711x_get_mctrl, + .stop_tx = uart_clps711x_stop_tx, + .start_tx = uart_clps711x_start_tx, + .stop_rx = uart_clps711x_stop_rx, + .enable_ms = uart_clps711x_enable_ms, + .break_ctl = uart_clps711x_break_ctl, + .startup = uart_clps711x_startup, + .shutdown = uart_clps711x_shutdown, + .set_termios = uart_clps711x_set_termios, + .type = uart_clps711x_type, + .config_port = uart_clps711x_config_port, + .release_port = uart_clps711x_release_port, + .request_port = uart_clps711x_request_port, }; #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE From e3224111b3a527eb8f9b9b6deed83b727522941e Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Thu, 25 Oct 2012 12:46:21 -0400 Subject: [PATCH 049/146] staging: dgrp: remove use of real_raw and read_cnt in dgrp_input dgrp_input used real_raw and read_cnt from struct tty_struct. Those members have gone away. Rework the code to not use them. Reported-by: Fengguang Wu Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/staging/dgrp/dgrp_net_ops.c | 67 ++++------------------------- 1 file changed, 8 insertions(+), 59 deletions(-) diff --git a/drivers/staging/dgrp/dgrp_net_ops.c b/drivers/staging/dgrp/dgrp_net_ops.c index ab839ea3b44..0788357fd3c 100644 --- a/drivers/staging/dgrp/dgrp_net_ops.c +++ b/drivers/staging/dgrp/dgrp_net_ops.c @@ -151,20 +151,15 @@ static void dgrp_read_data_block(struct ch_struct *ch, u8 *flipbuf, * Copys the rbuf to the flipbuf and sends to line discipline. * Sends input buffer data to the line discipline. * - * There are several modes to consider here: - * rawreadok, tty->real_raw, and IF_PARMRK */ static void dgrp_input(struct ch_struct *ch) { struct nd_struct *nd; struct tty_struct *tty; - int remain; int data_len; int len; - int flip_len; int tty_count; ulong lock_flags; - struct tty_ldisc *ld; u8 *myflipbuf; u8 *myflipflagbuf; @@ -212,37 +207,11 @@ static void dgrp_input(struct ch_struct *ch) spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - /* Decide how much data we can send into the tty layer */ - if (dgrp_rawreadok && tty->real_raw) - flip_len = MYFLIPLEN; - else - flip_len = TTY_FLIPBUF_SIZE; - /* data_len should be the number of chars that we read in */ data_len = (ch->ch_rin - ch->ch_rout) & RBUF_MASK; - remain = data_len; /* len is the amount of data we are going to transfer here */ - len = min(data_len, flip_len); - - /* take into consideration length of ldisc */ - len = min(len, (N_TTY_BUF_SIZE - 1) - tty->read_cnt); - - ld = tty_ldisc_ref(tty); - - /* - * If we were unable to get a reference to the ld, - * don't flush our buffer, and act like the ld doesn't - * have any space to put the data right now. - */ - if (!ld) { - len = 0; - } else if (!ld->ops->receive_buf) { - spin_lock_irqsave(&nd->nd_lock, lock_flags); - ch->ch_rout = ch->ch_rin; - spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - len = 0; - } + len = tty_buffer_request_room(tty, data_len); /* Check DPA flow control */ if ((nd->nd_dpa_debug) && @@ -254,42 +223,22 @@ static void dgrp_input(struct ch_struct *ch) dgrp_read_data_block(ch, myflipbuf, len); - /* - * In high performance mode, we don't have to update - * flag_buf or any of the counts or pointers into flip buf. - */ - if (!dgrp_rawreadok || !tty->real_raw) { - if (I_PARMRK(tty) || I_BRKINT(tty) || I_INPCK(tty)) - parity_scan(ch, myflipbuf, myflipflagbuf, &len); - else - memset(myflipflagbuf, TTY_NORMAL, len); - } + if (I_PARMRK(tty) || I_BRKINT(tty) || I_INPCK(tty)) + parity_scan(ch, myflipbuf, myflipflagbuf, &len); + else + memset(myflipflagbuf, TTY_NORMAL, len); if ((nd->nd_dpa_debug) && (nd->nd_dpa_port == PORT_NUM(MINOR(tty_devnum(tty))))) dgrp_dpa_data(nd, 1, myflipbuf, len); - /* - * If we're doing raw reads, jam it right into the - * line disc bypassing the flip buffers. - */ - if (dgrp_rawreadok && tty->real_raw) - ld->ops->receive_buf(tty, myflipbuf, NULL, len); - else { - len = tty_buffer_request_room(tty, len); - tty_insert_flip_string_flags(tty, myflipbuf, - myflipflagbuf, len); - - /* Tell the tty layer its okay to "eat" the data now */ - tty_flip_buffer_push(tty); - } + tty_insert_flip_string_flags(tty, myflipbuf, + myflipflagbuf, len); + tty_flip_buffer_push(tty); ch->ch_rxcount += len; } - if (ld) - tty_ldisc_deref(ld); - /* * Wake up any sleepers (maybe dgrp close) that might be waiting * for a channel flag state change. From 57cf50acbf5b153f331a966ecc08836324c1cd8d Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Thu, 25 Oct 2012 12:46:22 -0400 Subject: [PATCH 050/146] staging: dgrp: remove rawreadok module option The functionality behind this option has been removed in the driver so remove the config option to set/unset it. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/staging/dgrp/dgrp_common.h | 1 - drivers/staging/dgrp/dgrp_driver.c | 4 ---- drivers/staging/dgrp/dgrp_specproc.c | 2 -- drivers/staging/dgrp/dgrp_sysfs.c | 18 ------------------ 4 files changed, 25 deletions(-) diff --git a/drivers/staging/dgrp/dgrp_common.h b/drivers/staging/dgrp/dgrp_common.h index 05ff338471a..0583fe9c7b0 100644 --- a/drivers/staging/dgrp/dgrp_common.h +++ b/drivers/staging/dgrp/dgrp_common.h @@ -31,7 +31,6 @@ * All global storage allocation. ************************************************************************/ -extern int dgrp_rawreadok; /* Allow raw writing of input */ extern int dgrp_register_cudevices; /* enable legacy cu devices */ extern int dgrp_register_prdevices; /* enable transparent print devices */ extern int dgrp_poll_tick; /* Poll interval - in ms */ diff --git a/drivers/staging/dgrp/dgrp_driver.c b/drivers/staging/dgrp/dgrp_driver.c index 6e4a0ebc074..aa262588e9b 100644 --- a/drivers/staging/dgrp/dgrp_driver.c +++ b/drivers/staging/dgrp/dgrp_driver.c @@ -39,14 +39,10 @@ MODULE_VERSION(DIGI_VERSION); struct list_head nd_struct_list; struct dgrp_poll_data dgrp_poll_data; -int dgrp_rawreadok = 1; /* Bypass flipbuf on input */ int dgrp_register_cudevices = 1;/* Turn on/off registering legacy cu devices */ int dgrp_register_prdevices = 1;/* Turn on/off registering transparent print */ int dgrp_poll_tick = 20; /* Poll interval - in ms */ -module_param_named(rawreadok, dgrp_rawreadok, int, 0644); -MODULE_PARM_DESC(rawreadok, "Bypass flip buffers on input"); - module_param_named(register_cudevices, dgrp_register_cudevices, int, 0644); MODULE_PARM_DESC(register_cudevices, "Turn on/off registering legacy cu devices"); diff --git a/drivers/staging/dgrp/dgrp_specproc.c b/drivers/staging/dgrp/dgrp_specproc.c index 24327c3bad8..db91f676508 100644 --- a/drivers/staging/dgrp/dgrp_specproc.c +++ b/drivers/staging/dgrp/dgrp_specproc.c @@ -629,8 +629,6 @@ static int info_proc_show(struct seq_file *m, void *v) { seq_printf(m, "version: %s\n", DIGI_VERSION); seq_puts(m, "register_with_sysfs: 1\n"); - seq_printf(m, "rawreadok: 0x%08x\t(%d)\n", - dgrp_rawreadok, dgrp_rawreadok); seq_printf(m, "pollrate: 0x%08x\t(%d)\n", dgrp_poll_tick, dgrp_poll_tick); diff --git a/drivers/staging/dgrp/dgrp_sysfs.c b/drivers/staging/dgrp/dgrp_sysfs.c index e5a3c88d016..8b513e9111c 100644 --- a/drivers/staging/dgrp/dgrp_sysfs.c +++ b/drivers/staging/dgrp/dgrp_sysfs.c @@ -55,23 +55,6 @@ static DEVICE_ATTR(register_with_sysfs, 0400, dgrp_class_register_with_sysfs_show, NULL); -static ssize_t dgrp_class_rawreadok_show(struct device *c, - struct device_attribute *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", dgrp_rawreadok); -} -static ssize_t dgrp_class_rawreadok_store(struct device *c, - struct device_attribute *attr, - const char *buf, size_t count) -{ - sscanf(buf, "0x%x\n", &dgrp_rawreadok); - return count; -} -static DEVICE_ATTR(rawreadok, 0600, dgrp_class_rawreadok_show, - dgrp_class_rawreadok_store); - - static ssize_t dgrp_class_pollrate_show(struct device *c, struct device_attribute *attr, char *buf) @@ -91,7 +74,6 @@ static DEVICE_ATTR(pollrate, 0600, dgrp_class_pollrate_show, static struct attribute *dgrp_sysfs_global_settings_entries[] = { &dev_attr_pollrate.attr, - &dev_attr_rawreadok.attr, &dev_attr_register_with_sysfs.attr, NULL }; From 39aee51d439d8ad7339ee49dc3ccaf91ca61d8f0 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Wed, 3 Oct 2012 17:24:36 +0530 Subject: [PATCH 051/146] serial: omap: Make context_loss_cnt signed get_context_loss_count returns an int however it is stored in unsigned integer context_loss_cnt . This patch tries to make context_loss_cnt int. So that in case of errors the value (which may be negative) is not interpreted wrongly. In serial_omap_runtime_resume in case of errors returned by get_context_loss_count print a warning and do a restore. Signed-off-by: Shubhrajyoti D Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 6ede6fd92b4..fd0fb8cf7cb 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -96,7 +96,7 @@ struct uart_omap_port { unsigned char msr_saved_flags; char name[20]; unsigned long port_activity; - u32 context_loss_cnt; + int context_loss_cnt; u32 errata; u8 wakeups_enabled; unsigned int irq_pending:1; @@ -1556,11 +1556,15 @@ static int serial_omap_runtime_resume(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); - u32 loss_cnt = serial_omap_get_context_loss_count(up); + int loss_cnt = serial_omap_get_context_loss_count(up); - if (up->context_loss_cnt != loss_cnt) + if (loss_cnt < 0) { + dev_err(dev, "serial_omap_get_context_loss_count failed : %d\n", + loss_cnt); serial_omap_restore_context(up); - + } else if (up->context_loss_cnt != loss_cnt) { + serial_omap_restore_context(up); + } up->latency = up->calc_latency; schedule_work(&up->qos_work); From 7ba897d77ce2df4538c2d3e5bcf3640bde3a54cd Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Wed, 3 Oct 2012 17:24:37 +0530 Subject: [PATCH 052/146] serial: omap: Remove the default setting of special character Special character detect enable if enabled by default.Received data comparison with XOFF2 data happens by default. tty provides only XOFF1 no X0FF2 is provided so no need to enable check for XOFF2. Keeping this enabled might give some slow transfers due to dummy xoff2 comparison with xoff2 reset value. Since not all want the XOFF2 support lets not enable it by default. Signed-off-by: Shubhrajyoti D Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index fd0fb8cf7cb..caf49a60916 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -702,11 +702,7 @@ serial_omap_configure_xonxoff serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); - /* Enable special char function UARTi.EFR_REG[5] and - * load the new software flow control mode IXON or IXOFF - * and restore the UARTi.EFR_REG[4] ENHANCED_EN value. - */ - serial_out(up, UART_EFR, up->efr | UART_EFR_SCD); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); From 40477d0e04fd8d004ee9c70ad57e397a6b6ead63 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Wed, 3 Oct 2012 17:24:38 +0530 Subject: [PATCH 053/146] serial: omap: Remove the hardcode serial_omap_console_ports array. Currently the array serial_omap_console_ports is hard coded to 4. Make it depend on the maximum uart count. Post to [cfc55bc ARM: OMAP2+: serial: Change MAX_HSUART_PORTS to 6] the max ports is 6. Cc: AnilKumar Ch Signed-off-by: Shubhrajyoti D Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index caf49a60916..478383d3d9c 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1077,7 +1077,7 @@ out: #ifdef CONFIG_SERIAL_OMAP_CONSOLE -static struct uart_omap_port *serial_omap_console_ports[4]; +static struct uart_omap_port *serial_omap_console_ports[OMAP_MAX_HSUART_PORTS]; static struct uart_driver serial_omap_reg; From de609582072af195673e72d9e7647d5cba8eb7e4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 15 Oct 2012 13:36:01 +0200 Subject: [PATCH 054/146] serial/amba-pl011: use devm_* managed resources This switches a bunch of allocation and remapping to use the devm_* garbage collected methods and cleans up the error path and remove() paths consequently. devm_ioremap() is only in so fix up the erroneous include as well. Signed-off-by: Linus Walleij Tested-by: Domenico Andreoli Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index d7e1edec50b..7fca4022a8b 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -56,8 +56,7 @@ #include #include #include - -#include +#include #define UART_NR 14 @@ -1973,7 +1972,8 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) goto out; } - uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL); + uap = devm_kzalloc(&dev->dev, sizeof(struct uart_amba_port), + GFP_KERNEL); if (uap == NULL) { ret = -ENOMEM; goto out; @@ -1981,16 +1981,17 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) i = pl011_probe_dt_alias(i, &dev->dev); - base = ioremap(dev->res.start, resource_size(&dev->res)); + base = devm_ioremap(&dev->dev, dev->res.start, + resource_size(&dev->res)); if (!base) { ret = -ENOMEM; - goto free; + goto out; } uap->pinctrl = devm_pinctrl_get(&dev->dev); if (IS_ERR(uap->pinctrl)) { ret = PTR_ERR(uap->pinctrl); - goto unmap; + goto out; } uap->pins_default = pinctrl_lookup_state(uap->pinctrl, PINCTRL_STATE_DEFAULT); @@ -2002,10 +2003,10 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) if (IS_ERR(uap->pins_sleep)) dev_dbg(&dev->dev, "could not get sleep pinstate\n"); - uap->clk = clk_get(&dev->dev, NULL); + uap->clk = devm_clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); - goto unmap; + goto out; } uap->vendor = vendor; @@ -2038,11 +2039,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) amba_set_drvdata(dev, NULL); amba_ports[i] = NULL; pl011_dma_remove(uap); - clk_put(uap->clk); - unmap: - iounmap(base); - free: - kfree(uap); } out: return ret; @@ -2062,9 +2058,6 @@ static int pl011_remove(struct amba_device *dev) amba_ports[i] = NULL; pl011_dma_remove(uap); - iounmap(uap->port.membase); - clk_put(uap->clk); - kfree(uap); return 0; } From 319fb0d21907f771d8ccdb6177fb2fcafabc6ab4 Mon Sep 17 00:00:00 2001 From: chao bi Date: Thu, 25 Oct 2012 09:02:32 +0800 Subject: [PATCH 055/146] serial: ifx6x60: different SPI word width configure requires different swap process SPI protocol driver only provide one function (swap_buf()) to swap SPI data into big endian format, which is only available when SPI controller's word width is 16 bits. But word width could be configured as 8/16/32 bits, different word width configure should be mapped to different swap methods.This patch is to make SPI protocol driver choose the right swap function corresponding to SPI word width configuration. cc: liu chuansheng cc: Chen Jun Signed-off-by: channing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 70 ++++++++++++++++++++++++++++++------ drivers/tty/serial/ifx6x60.h | 1 + 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 5b9bc19ed13..2d2bcbd8067 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -152,26 +152,67 @@ ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val) } /** - * swap_buf + * swap_buf_8 * @buf: our buffer * @len : number of bytes (not words) in the buffer * @end: end of buffer * * Swap the contents of a buffer into big endian format */ -static inline void swap_buf(u16 *buf, int len, void *end) +static inline void swap_buf_8(unsigned char *buf, int len, void *end) +{ + /* don't swap buffer if SPI word width is 8 bits */ + return; +} + +/** + * swap_buf_16 + * @buf: our buffer + * @len : number of bytes (not words) in the buffer + * @end: end of buffer + * + * Swap the contents of a buffer into big endian format + */ +static inline void swap_buf_16(unsigned char *buf, int len, void *end) { int n; + u16 *buf_16 = (u16 *)buf; len = ((len + 1) >> 1); - if ((void *)&buf[len] > end) { - pr_err("swap_buf: swap exceeds boundary (%p > %p)!", - &buf[len], end); + if ((void *)&buf_16[len] > end) { + pr_err("swap_buf_16: swap exceeds boundary (%p > %p)!", + &buf_16[len], end); return; } for (n = 0; n < len; n++) { - *buf = cpu_to_be16(*buf); - buf++; + *buf_16 = cpu_to_be16(*buf_16); + buf_16++; + } +} + +/** + * swap_buf_32 + * @buf: our buffer + * @len : number of bytes (not words) in the buffer + * @end: end of buffer + * + * Swap the contents of a buffer into big endian format + */ +static inline void swap_buf_32(unsigned char *buf, int len, void *end) +{ + int n; + + u32 *buf_32 = (u32 *)buf; + len = (len + 3) >> 2; + + if ((void *)&buf_32[len] > end) { + pr_err("swap_buf_32: swap exceeds boundary (%p > %p)!\n", + &buf_32[len], end); + return; + } + for (n = 0; n < len; n++) { + *buf_32 = cpu_to_be32(*buf_32); + buf_32++; } } @@ -449,7 +490,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) tx_count-IFX_SPI_HEADER_OVERHEAD, ifx_dev->spi_more); /* swap actual data in the buffer */ - swap_buf((u16 *)(ifx_dev->tx_buffer), tx_count, + ifx_dev->swap_buf((ifx_dev->tx_buffer), tx_count, &ifx_dev->tx_buffer[IFX_SPI_TRANSFER_SIZE]); return tx_count; } @@ -617,7 +658,7 @@ static void ifx_spi_complete(void *ctx) if (!ifx_dev->spi_msg.status) { /* check header validity, get comm flags */ - swap_buf((u16 *)ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD, + ifx_dev->swap_buf(ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD, &ifx_dev->rx_buffer[IFX_SPI_HEADER_OVERHEAD]); decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer, &length, &more, &cts); @@ -636,7 +677,8 @@ static void ifx_spi_complete(void *ctx) actual_length = min((unsigned int)length, ifx_dev->spi_msg.actual_length); - swap_buf((u16 *)(ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD), + ifx_dev->swap_buf( + (ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD), actual_length, &ifx_dev->rx_buffer[IFX_SPI_TRANSFER_SIZE]); ifx_spi_insert_flip_string( @@ -1001,6 +1043,14 @@ static int ifx_spi_spi_probe(struct spi_device *spi) return -ENODEV; } + /* init swap_buf function according to word width configuration */ + if (spi->bits_per_word == 32) + ifx_dev->swap_buf = swap_buf_32; + else if (spi->bits_per_word == 16) + ifx_dev->swap_buf = swap_buf_16; + else + ifx_dev->swap_buf = swap_buf_8; + /* ensure SPI protocol flags are initialized to enable transfer */ ifx_dev->spi_more = 0; ifx_dev->spi_slave_cts = 0; diff --git a/drivers/tty/serial/ifx6x60.h b/drivers/tty/serial/ifx6x60.h index e8464baf9e7..d8869f5a463 100644 --- a/drivers/tty/serial/ifx6x60.h +++ b/drivers/tty/serial/ifx6x60.h @@ -124,6 +124,7 @@ struct ifx_spi_device { #define MR_INPROGRESS 1 #define MR_COMPLETE 2 wait_queue_head_t mdm_reset_wait; + void (*swap_buf)(unsigned char *buf, int len, void *end); }; #endif /* _IFX6X60_H */ From c73ba2ae43fd4d69589caeecc5260a41be87b759 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Mon, 22 Oct 2012 10:23:07 -0400 Subject: [PATCH 056/146] serial: ifx6x60: add_timer is not safe in the mrdy_assert function This patch make use of mod_timer instead of add_timer in the mrdy_assert function. Because the srdy interrupter can go high when we are running function mrdy_assert and mrdy_assert can be called by multi-entry. In our medfield platform, spi stress test can encounter this error logs triggered by the BUG_ON of add_timer function.This patch had been tested on our medfield platform. the scenario: CPU0 CPU1 mrdy_assert set_bit(IFX_SPI_STATE_TIMER_PENDING) ifx_spi_handle_srdy ... clear_bit(IFX_SPI_STATE_TIMER_PENDING) ... mrdy_assert set_bit(IFX_SPI_STATE_TIMER_PENDING) ... add_timer ... add_timer Cc: liu chuansheng Cc: Bi Chao Signed-off-by: Chen Jun Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 2d2bcbd8067..4b001ea4b0b 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -231,9 +231,7 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev) if (!val) { if (!test_and_set_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) { - ifx_dev->spi_timer.expires = - jiffies + IFX_SPI_TIMEOUT_SEC*HZ; - add_timer(&ifx_dev->spi_timer); + mod_timer(&ifx_dev->spi_timer,jiffies + IFX_SPI_TIMEOUT_SEC*HZ); } } From 2e30802625f5754e9a0ce478a447ed0f2376d4d4 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Fri, 19 Oct 2012 09:51:30 -0400 Subject: [PATCH 057/146] serial: ifx6x60: del_timer_sync must not be called in interrupt context. This patch make use of del_timer instead of del_timer_sync in the interrupt context. The spi_timer function don't use any resources that may release after running del_timer, so using the del_timer is also safe and enough in this context. Signed-off-by: Chen Jun Acked-by: Alan Cox Tested-by: Chuansheng Liu Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 4b001ea4b0b..e595c832be2 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -866,7 +866,7 @@ error_ret: static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev) { if (test_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) { - del_timer_sync(&ifx_dev->spi_timer); + del_timer(&ifx_dev->spi_timer); clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); } From cadf74869013dc309bde50ed446f56d33a6a9806 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Thu, 25 Oct 2012 14:26:35 -0400 Subject: [PATCH 058/146] tty: add missing newlines to WARN_RATELIMIT WARN_RATELIMIT() expects the warning to end with a newline if one is needed. Not doing so results in odd looking warnings such as: [ 1339.454272] tty is NULLPid: 7147, comm: kworker/4:0 Tainted: G W 3.7.0-rc2-next-20121025-sasha-00001-g673f98e-dirty #75 Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 2 +- drivers/tty/tty_buffer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 60b076cc4e2..19083efa231 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -151,7 +151,7 @@ static void n_tty_set_room(struct tty_struct *tty) /* Did this open up the receive buffer? We may need to flip */ if (left && !old_left) { WARN_RATELIMIT(tty->port->itty == NULL, - "scheduling with invalid itty"); + "scheduling with invalid itty\n"); schedule_work(&tty->port->buf.work); } } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 6cf87d7afb7..45d916198f7 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -473,7 +473,7 @@ static void flush_to_ldisc(struct work_struct *work) struct tty_ldisc *disc; tty = port->itty; - if (WARN_RATELIMIT(tty == NULL, "tty is NULL")) + if (WARN_RATELIMIT(tty == NULL, "tty is NULL\n")) return; disc = tty_ldisc_ref(tty); From 0bbeb3c3e84bc963d1c66661e082d207023b0e5c Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 22 Oct 2012 11:58:01 -0400 Subject: [PATCH 059/146] of serial port driver - add clk_get_rate() support Currently this driver expects the clock-frequency attribute. This patch allows getting clock-frequency through clk driver API clk_get_rate() if clock-frequency attribute is not defined. So in the device bindings for serial device, one can add clocks phandle to refer to the clk device to get the rate. Signed-off-by: Murali Karicheri Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/of_serial.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index df443b908ca..533ccfe7709 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -21,8 +21,10 @@ #include #include #include +#include struct of_serial_info { + struct clk *clk; int type; int line; }; @@ -51,7 +53,8 @@ EXPORT_SYMBOL_GPL(tegra_serial_handle_break); * Fill a struct uart_port for a given device node */ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, - int type, struct uart_port *port) + int type, struct uart_port *port, + struct of_serial_info *info) { struct resource resource; struct device_node *np = ofdev->dev.of_node; @@ -60,8 +63,17 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, memset(port, 0, sizeof *port); if (of_property_read_u32(np, "clock-frequency", &clk)) { - dev_warn(&ofdev->dev, "no clock-frequency property set\n"); - return -ENODEV; + + /* Get clk rate through clk driver if present */ + info->clk = clk_get(&ofdev->dev, NULL); + if (info->clk == NULL) { + dev_warn(&ofdev->dev, + "clk or clock-frequency not defined\n"); + return -ENODEV; + } + + clk_prepare_enable(info->clk); + clk = clk_get_rate(info->clk); } /* If current-speed was set, then try not to change it. */ if (of_property_read_u32(np, "current-speed", &spd) == 0) @@ -70,7 +82,7 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, ret = of_address_to_resource(np, 0, &resource); if (ret) { dev_warn(&ofdev->dev, "invalid address\n"); - return ret; + goto out; } spin_lock_init(&port->lock); @@ -97,7 +109,8 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, default: dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n", prop); - return -EINVAL; + ret = -EINVAL; + goto out; } } @@ -115,6 +128,10 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, port->handle_break = tegra_serial_handle_break; return 0; +out: + if (info->clk) + clk_disable_unprepare(info->clk); + return ret; } /* @@ -141,7 +158,7 @@ static int __devinit of_platform_serial_probe(struct platform_device *ofdev) return -ENOMEM; port_type = (unsigned long)match->data; - ret = of_platform_serial_setup(ofdev, port_type, &port); + ret = of_platform_serial_setup(ofdev, port_type, &port, info); if (ret) goto out; @@ -204,6 +221,9 @@ static int of_platform_serial_remove(struct platform_device *ofdev) /* need to add code for these */ break; } + + if (info->clk) + clk_disable_unprepare(info->clk); kfree(info); return 0; } From ab72fa5523866cb93681abb9f9a401d43a93b7be Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 22 Oct 2012 11:58:02 -0400 Subject: [PATCH 060/146] Documentation: of-serial.txt - update for clocks phandle for clk Signed-off-by: Murali Karicheri Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/tty/serial/of-serial.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/tty/serial/of-serial.txt b/Documentation/devicetree/bindings/tty/serial/of-serial.txt index ba385f2e0dd..1e1145ca4f3 100644 --- a/Documentation/devicetree/bindings/tty/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/tty/serial/of-serial.txt @@ -14,7 +14,10 @@ Required properties: - "serial" if the port type is unknown. - reg : offset and length of the register set for the device. - interrupts : should contain uart interrupt. -- clock-frequency : the input clock frequency for the UART. +- clock-frequency : the input clock frequency for the UART + or + clocks phandle to refer to the clk used as per Documentation/devicetree + /bindings/clock/clock-bindings.txt Optional properties: - current-speed : the current active speed of the UART. From 06026d911c31dfa602e14e635a3489b8d67cc786 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 24 Oct 2012 23:43:20 +0400 Subject: [PATCH 061/146] tty: pty - Move TIOCPKT handling into pty.c Since this ioctl is for pty devices only move it to pty.c. v2: - drop PTY_TYPE_MASTER test since it's master peer ioctl anyway (by jslaby@) Suggested-by: Alan Cox Signed-off-by: Cyrill Gorcunov CC: "H. Peter Anvin" CC: Pavel Emelyanov CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 26 ++++++++++++++++++++++++++ drivers/tty/tty_ioctl.c | 21 --------------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 4219f040adb..df3c64272d2 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -171,6 +171,28 @@ static int pty_set_lock(struct tty_struct *tty, int __user *arg) return 0; } +/* Set the packet mode on a pty */ +static int pty_set_pktmode(struct tty_struct *tty, int __user *arg) +{ + unsigned long flags; + int pktmode; + + if (get_user(pktmode, arg)) + return -EFAULT; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (pktmode) { + if (!tty->packet) { + tty->packet = 1; + tty->link->ctrl_status = 0; + } + } else + tty->packet = 0; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + + return 0; +} + /* Send a signal to the slave */ static int pty_signal(struct tty_struct *tty, int sig) { @@ -398,6 +420,8 @@ static int pty_bsd_ioctl(struct tty_struct *tty, switch (cmd) { case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ return pty_set_lock(tty, (int __user *) arg); + case TIOCPKT: /* Set PT packet mode */ + return pty_set_pktmode(tty, (int __user *)arg); case TIOCSIG: /* Send signal to other side of pty */ return pty_signal(tty, (int) arg); } @@ -512,6 +536,8 @@ static int pty_unix98_ioctl(struct tty_struct *tty, switch (cmd) { case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ return pty_set_lock(tty, (int __user *)arg); + case TIOCPKT: /* Set PT packet mode */ + return pty_set_pktmode(tty, (int __user *)arg); case TIOCGPTN: /* Get PT Number */ return put_user(tty->index, (unsigned int __user *)arg); case TIOCSIG: /* Send signal to other side of pty */ diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 12b1fa0f4f8..8481b29d5b3 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -1118,7 +1118,6 @@ EXPORT_SYMBOL_GPL(tty_perform_flush); int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - unsigned long flags; int retval; switch (cmd) { @@ -1153,26 +1152,6 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, return 0; case TCFLSH: return tty_perform_flush(tty, arg); - case TIOCPKT: - { - int pktmode; - - if (tty->driver->type != TTY_DRIVER_TYPE_PTY || - tty->driver->subtype != PTY_TYPE_MASTER) - return -ENOTTY; - if (get_user(pktmode, (int __user *) arg)) - return -EFAULT; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (pktmode) { - if (!tty->packet) { - tty->packet = 1; - tty->link->ctrl_status = 0; - } - } else - tty->packet = 0; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - return 0; - } default: /* Try the mode commands */ return tty_mode_ioctl(tty, file, cmd, arg); From c6298038bcfc20710430a4ad069bb1f3f069997c Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 24 Oct 2012 23:43:21 +0400 Subject: [PATCH 062/146] tty, ioctls -- Add new ioctl definitions for tty flags fetching This patch defines new ioctl codes TIOCGPKT, TIOCGPTLCK, TIOCGEXCL for fetching pty's packet mode and locking state, and exclusive mode of tty. [ No real handlers for the codes though, this will be addressed in another patch for easier review and bisectability ] Signed-off-by: Cyrill Gorcunov CC: Alan Cox CC: "H. Peter Anvin" CC: Pavel Emelyanov CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- arch/alpha/include/asm/ioctls.h | 3 +++ arch/mips/include/uapi/asm/ioctls.h | 3 +++ arch/parisc/include/uapi/asm/ioctls.h | 3 +++ arch/powerpc/include/uapi/asm/ioctls.h | 3 +++ arch/sh/include/uapi/asm/ioctls.h | 3 +++ arch/sparc/include/uapi/asm/ioctls.h | 3 +++ arch/xtensa/include/uapi/asm/ioctls.h | 3 +++ fs/compat_ioctl.c | 3 +++ include/uapi/asm-generic/ioctls.h | 3 +++ 9 files changed, 27 insertions(+) diff --git a/arch/alpha/include/asm/ioctls.h b/arch/alpha/include/asm/ioctls.h index 80e1cee90f1..92c557be49f 100644 --- a/arch/alpha/include/asm/ioctls.h +++ b/arch/alpha/include/asm/ioctls.h @@ -95,6 +95,9 @@ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/mips/include/uapi/asm/ioctls.h b/arch/mips/include/uapi/asm/ioctls.h index 92403c3d600..addd56b6069 100644 --- a/arch/mips/include/uapi/asm/ioctls.h +++ b/arch/mips/include/uapi/asm/ioctls.h @@ -86,6 +86,9 @@ #define TIOCGDEV _IOR('T', 0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T', 0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ /* I hope the range from 0x5480 on is free ... */ #define TIOCSCTTY 0x5480 /* become controlling tty */ diff --git a/arch/parisc/include/uapi/asm/ioctls.h b/arch/parisc/include/uapi/asm/ioctls.h index 054ec06f9e2..66719c38a36 100644 --- a/arch/parisc/include/uapi/asm/ioctls.h +++ b/arch/parisc/include/uapi/asm/ioctls.h @@ -55,6 +55,9 @@ #define TIOCGDEV _IOR('T',0x32, int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 diff --git a/arch/powerpc/include/uapi/asm/ioctls.h b/arch/powerpc/include/uapi/asm/ioctls.h index e9b78870aaa..49a25796a61 100644 --- a/arch/powerpc/include/uapi/asm/ioctls.h +++ b/arch/powerpc/include/uapi/asm/ioctls.h @@ -97,6 +97,9 @@ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/sh/include/uapi/asm/ioctls.h b/arch/sh/include/uapi/asm/ioctls.h index a6769f352bf..34224107976 100644 --- a/arch/sh/include/uapi/asm/ioctls.h +++ b/arch/sh/include/uapi/asm/ioctls.h @@ -88,6 +88,9 @@ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP _IO('T', 0x37) +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */ #define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */ diff --git a/arch/sparc/include/uapi/asm/ioctls.h b/arch/sparc/include/uapi/asm/ioctls.h index 9155f7041d4..897d1723fa1 100644 --- a/arch/sparc/include/uapi/asm/ioctls.h +++ b/arch/sparc/include/uapi/asm/ioctls.h @@ -21,6 +21,9 @@ #define TCSETSF2 _IOW('T', 15, struct termios2) #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCVHANGUP _IO('T', 0x37) +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ /* Note that all the ioctls that are not available in Linux have a * double underscore on the front to: a) avoid some programs to diff --git a/arch/xtensa/include/uapi/asm/ioctls.h b/arch/xtensa/include/uapi/asm/ioctls.h index 2aa4cd9f0ce..b4cb1100c0f 100644 --- a/arch/xtensa/include/uapi/asm/ioctls.h +++ b/arch/xtensa/include/uapi/asm/ioctls.h @@ -101,6 +101,9 @@ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP _IO('T', 0x37) +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCSERCONFIG _IO('T', 83) #define TIOCSERGWILD _IOR('T', 84, int) diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index f5054025f9d..89cf6014a96 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -842,6 +842,9 @@ COMPATIBLE_IOCTL(TIOCGDEV) COMPATIBLE_IOCTL(TIOCCBRK) COMPATIBLE_IOCTL(TIOCGSID) COMPATIBLE_IOCTL(TIOCGICOUNT) +COMPATIBLE_IOCTL(TIOCGPKT) +COMPATIBLE_IOCTL(TIOCGPTLCK) +COMPATIBLE_IOCTL(TIOCGEXCL) /* Little t */ COMPATIBLE_IOCTL(TIOCGETD) COMPATIBLE_IOCTL(TIOCSETD) diff --git a/include/uapi/asm-generic/ioctls.h b/include/uapi/asm-generic/ioctls.h index 199975fac39..143dacbb7d9 100644 --- a/include/uapi/asm-generic/ioctls.h +++ b/include/uapi/asm-generic/ioctls.h @@ -74,6 +74,9 @@ #define TCSETXW 0x5435 #define TIOCSIG _IOW('T', 0x36, int) /* pty: generate signal */ #define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 From 84fd7bdf1266ee6228319903af7e58702745024d Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 24 Oct 2012 23:43:22 +0400 Subject: [PATCH 063/146] tty: Add get- ioctls to fetch tty status v3 For checkpoint/restore we need to know if tty has exclusive or packet mode set, as well as if pty is currently locked. Just to be able to restore this characteristics. For this sake the following ioctl codes are introduced - TIOCGPKT to get packet mode state - TIOCGPTLCK to get Pty locked state - TIOCGEXCL to get Exclusive mode state Note this ioctls are a bit unsafe in terms of data obtained consistency. The tty characteristics might be changed right after ioctl complete. Keep it in mind and use this ioctl carefully. v2: - Use TIOC prefix for ioctl codes (by jslaby@) Signed-off-by: Cyrill Gorcunov CC: Alan Cox CC: "H. Peter Anvin" CC: Pavel Emelyanov CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 21 +++++++++++++++++++++ drivers/tty/tty_io.c | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index df3c64272d2..0ce0b3ec2bb 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -171,6 +171,12 @@ static int pty_set_lock(struct tty_struct *tty, int __user *arg) return 0; } +static int pty_get_lock(struct tty_struct *tty, int __user *arg) +{ + int locked = test_bit(TTY_PTY_LOCK, &tty->flags); + return put_user(locked, arg); +} + /* Set the packet mode on a pty */ static int pty_set_pktmode(struct tty_struct *tty, int __user *arg) { @@ -193,6 +199,13 @@ static int pty_set_pktmode(struct tty_struct *tty, int __user *arg) return 0; } +/* Get the packet mode of a pty */ +static int pty_get_pktmode(struct tty_struct *tty, int __user *arg) +{ + int pktmode = tty->packet; + return put_user(pktmode, arg); +} + /* Send a signal to the slave */ static int pty_signal(struct tty_struct *tty, int sig) { @@ -420,8 +433,12 @@ static int pty_bsd_ioctl(struct tty_struct *tty, switch (cmd) { case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ return pty_set_lock(tty, (int __user *) arg); + case TIOCGPTLCK: /* Get PT Lock status */ + return pty_get_lock(tty, (int __user *)arg); case TIOCPKT: /* Set PT packet mode */ return pty_set_pktmode(tty, (int __user *)arg); + case TIOCGPKT: /* Get PT packet mode */ + return pty_get_pktmode(tty, (int __user *)arg); case TIOCSIG: /* Send signal to other side of pty */ return pty_signal(tty, (int) arg); } @@ -536,8 +553,12 @@ static int pty_unix98_ioctl(struct tty_struct *tty, switch (cmd) { case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ return pty_set_lock(tty, (int __user *)arg); + case TIOCGPTLCK: /* Get PT Lock status */ + return pty_get_lock(tty, (int __user *)arg); case TIOCPKT: /* Set PT packet mode */ return pty_set_pktmode(tty, (int __user *)arg); + case TIOCGPKT: /* Get PT packet mode */ + return pty_get_pktmode(tty, (int __user *)arg); case TIOCGPTN: /* Get PT Number */ return put_user(tty->index, (unsigned int __user *)arg); case TIOCSIG: /* Send signal to other side of pty */ diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index a3eba7f359e..739ea86c1cf 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2687,6 +2687,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCNXCL: clear_bit(TTY_EXCLUSIVE, &tty->flags); return 0; + case TIOCGEXCL: + { + int excl = test_bit(TTY_EXCLUSIVE, &tty->flags); + return put_user(excl, (int __user *)p); + } case TIOCNOTTY: if (current->signal->tty != tty) return -ENOTTY; From d2ffc740f33d432949eee7479ac49bafd9f5108a Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 25 Oct 2012 16:40:59 +0200 Subject: [PATCH 064/146] UM: TTY: fix build errors now that tty->raw is gone Signed-off-by: Jiri Slaby --- arch/um/drivers/chan_kern.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c index c3bba73e4be..e9a0abc6a32 100644 --- a/arch/um/drivers/chan_kern.c +++ b/arch/um/drivers/chan_kern.c @@ -83,21 +83,8 @@ static const struct chan_ops not_configged_ops = { static void tty_receive_char(struct tty_struct *tty, char ch) { - if (tty == NULL) - return; - - if (I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) { - if (ch == STOP_CHAR(tty)) { - stop_tty(tty); - return; - } - else if (ch == START_CHAR(tty)) { - start_tty(tty); - return; - } - } - - tty_insert_flip_char(tty, ch, TTY_NORMAL); + if (tty) + tty_insert_flip_char(tty, ch, TTY_NORMAL); } static int open_one_chan(struct chan *chan) From 669fef464468d3f02d60a5cf725fc097e03c5cb8 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 28 Oct 2012 03:48:57 -0700 Subject: [PATCH 065/146] serial: jsm: Convert jsm_printk to jsm_dbg These printks should all be emitted at KERN_DEBUG level. Make them dependent on CONFIG_DEBUG or (#define DEBUG) simplify the code a bit. Add missing newlines where appropriate. Most all of these messages could be deleted too. Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/jsm/jsm.h | 8 +- drivers/tty/serial/jsm/jsm_driver.c | 3 +- drivers/tty/serial/jsm/jsm_neo.c | 116 +++++++++++++++------------- drivers/tty/serial/jsm/jsm_tty.c | 102 ++++++++++++------------ 4 files changed, 120 insertions(+), 109 deletions(-) diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 529bec6edaf..844d5e4eb1a 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -57,9 +57,11 @@ enum { DBG_CARR = 0x10000, }; -#define jsm_printk(nlevel, klevel, pdev, fmt, args...) \ - if ((DBG_##nlevel & jsm_debug)) \ - dev_printk(KERN_##klevel, pdev->dev, fmt, ## args) +#define jsm_dbg(nlevel, pdev, fmt, ...) \ +do { \ + if (DBG_##nlevel & jsm_debug) \ + dev_dbg(pdev->dev, fmt, ##__VA_ARGS__); \ +} while (0) #define MAXLINES 256 #define MAXPORTS 8 diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 5ab3c3b595e..8e05ce94bd4 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -107,8 +107,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device brd->irq = pdev->irq; - jsm_printk(INIT, INFO, &brd->pci_dev, - "jsm_found_board - NEO adapter\n"); + jsm_dbg(INIT, &brd->pci_dev, "jsm_found_board - NEO adapter\n"); /* get the PCI Base Address Registers */ brd->membase = pci_resource_start(pdev, 0); diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 81dfafa11b0..dfaf4882641 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -52,7 +52,7 @@ static void neo_set_cts_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n"); /* Turn on auto CTS flow control */ ier |= (UART_17158_IER_CTSDSR); @@ -83,7 +83,7 @@ static void neo_set_rts_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n"); /* Turn on auto RTS flow control */ ier |= (UART_17158_IER_RTSDTR); @@ -123,7 +123,7 @@ static void neo_set_ixon_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n"); /* Turn off auto CTS flow control */ ier &= ~(UART_17158_IER_CTSDSR); @@ -160,7 +160,7 @@ static void neo_set_ixoff_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n"); /* Turn off auto RTS flow control */ ier &= ~(UART_17158_IER_RTSDTR); @@ -198,7 +198,7 @@ static void neo_set_no_input_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n"); /* Turn off auto RTS flow control */ ier &= ~(UART_17158_IER_RTSDTR); @@ -237,7 +237,7 @@ static void neo_set_no_output_flow_control(struct jsm_channel *ch) ier = readb(&ch->ch_neo_uart->ier); efr = readb(&ch->ch_neo_uart->efr); - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n"); /* Turn off auto CTS flow control */ ier &= ~(UART_17158_IER_CTSDSR); @@ -276,7 +276,7 @@ static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch) if (ch->ch_c_cflag & CRTSCTS) return; - jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "start\n"); + jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "start\n"); /* Tell UART what start/stop chars it should be looking for */ writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1); @@ -455,7 +455,7 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch) * I hope thats okay with everyone? Yes? Good. */ while (qleft < 1) { - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, + jsm_dbg(READ, &ch->ch_bd->pci_dev, "Queue full, dropping DATA:%x LSR:%x\n", ch->ch_rqueue[tail], ch->ch_equeue[tail]); @@ -467,8 +467,8 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch) memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1); ch->ch_equeue[head] = (u8) linestatus; - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]); + jsm_dbg(READ, &ch->ch_bd->pci_dev, "DATA/LSR pair: %x %x\n", + ch->ch_rqueue[head], ch->ch_equeue[head]); /* Ditch any remaining linestatus value. */ linestatus = 0; @@ -521,8 +521,8 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) ch->ch_cached_lsr &= ~(UART_LSR_THRE); writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx); - jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev, - "Tx data: %x\n", circ->buf[circ->tail]); + jsm_dbg(WRITE, &ch->ch_bd->pci_dev, + "Tx data: %x\n", circ->buf[circ->tail]); circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1); ch->ch_txcount++; } @@ -575,8 +575,9 @@ static void neo_parse_modem(struct jsm_channel *ch, u8 signals) { u8 msignals = signals; - jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev, - "neo_parse_modem: port: %d msignals: %x\n", ch->ch_portnum, msignals); + jsm_dbg(MSIGS, &ch->ch_bd->pci_dev, + "neo_parse_modem: port: %d msignals: %x\n", + ch->ch_portnum, msignals); /* Scrub off lower bits. They signify delta's, which I don't care about */ /* Keep DDCD and DDSR though */ @@ -606,8 +607,8 @@ static void neo_parse_modem(struct jsm_channel *ch, u8 signals) else ch->ch_mistat &= ~UART_MSR_CTS; - jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev, - "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n", + jsm_dbg(MSIGS, &ch->ch_bd->pci_dev, + "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n", ch->ch_portnum, !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR), !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS), @@ -649,8 +650,8 @@ static void neo_flush_uart_write(struct jsm_channel *ch) /* Check to see if the UART feels it completely flushed the FIFO. */ tmp = readb(&ch->ch_neo_uart->isr_fcr); if (tmp & 4) { - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, - "Still flushing TX UART... i: %d\n", i); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, + "Still flushing TX UART... i: %d\n", i); udelay(10); } else @@ -681,8 +682,8 @@ static void neo_flush_uart_read(struct jsm_channel *ch) /* Check to see if the UART feels it completely flushed the FIFO. */ tmp = readb(&ch->ch_neo_uart->isr_fcr); if (tmp & 2) { - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, - "Still flushing RX UART... i: %d\n", i); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, + "Still flushing RX UART... i: %d\n", i); udelay(10); } else @@ -705,8 +706,9 @@ static void neo_clear_break(struct jsm_channel *ch, int force) writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr); ch->ch_flags &= ~(CH_BREAK_SENDING); - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, - "clear break Finishing UART_LCR_SBC! finished: %lx\n", jiffies); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, + "clear break Finishing UART_LCR_SBC! finished: %lx\n", + jiffies); /* flush write operation */ neo_pci_posting_flush(ch->ch_bd); @@ -748,8 +750,8 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port) */ isr &= ~(UART_17158_IIR_FIFO_ENABLED); - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "%s:%d isr: %x\n", __FILE__, __LINE__, isr); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d isr: %x\n", + __FILE__, __LINE__, isr); if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) { /* Read data from uart -> queue */ @@ -772,8 +774,9 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port) if (isr & UART_17158_IIR_XONXOFF) { cause = readb(&ch->ch_neo_uart->xoffchar1); - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "Port %d. Got ISR_XONXOFF: cause:%x\n", port, cause); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "Port %d. Got ISR_XONXOFF: cause:%x\n", + port, cause); /* * Since the UART detected either an XON or @@ -786,17 +789,19 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port) if (brd->channels[port]->ch_flags & CH_STOP) { ch->ch_flags &= ~(CH_STOP); } - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "Port %d. XON detected in incoming data\n", port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "Port %d. XON detected in incoming data\n", + port); } else if (cause == UART_17158_XOFF_DETECT) { if (!(brd->channels[port]->ch_flags & CH_STOP)) { ch->ch_flags |= CH_STOP; - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "Setting CH_STOP\n"); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "Setting CH_STOP\n"); } - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "Port: %d. XOFF detected in incoming data\n", port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "Port: %d. XOFF detected in incoming data\n", + port); } spin_unlock_irqrestore(&ch->ch_lock, lock_flags); } @@ -825,8 +830,8 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port) } /* Parse any modem signal changes */ - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "MOD_STAT: sending to parse_modem_sigs\n"); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "MOD_STAT: sending to parse_modem_sigs\n"); neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr)); } } @@ -849,8 +854,8 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port) linestatus = readb(&ch->ch_neo_uart->lsr); - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "%s:%d port: %d linestatus: %x\n", __FILE__, __LINE__, port, linestatus); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d port: %d linestatus: %x\n", + __FILE__, __LINE__, port, linestatus); ch->ch_cached_lsr |= linestatus; @@ -869,7 +874,7 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port) *to do the special RX+LSR read for this FIFO load. */ if (linestatus & UART_17158_RX_FIFO_DATA_ERROR) - jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, + jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d Port: %d Got an RX error, need to parse LSR\n", __FILE__, __LINE__, port); @@ -880,20 +885,21 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port) if (linestatus & UART_LSR_PE) { ch->ch_err_parity++; - jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, - "%s:%d Port: %d. PAR ERR!\n", __FILE__, __LINE__, port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d Port: %d. PAR ERR!\n", + __FILE__, __LINE__, port); } if (linestatus & UART_LSR_FE) { ch->ch_err_frame++; - jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, - "%s:%d Port: %d. FRM ERR!\n", __FILE__, __LINE__, port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d Port: %d. FRM ERR!\n", + __FILE__, __LINE__, port); } if (linestatus & UART_LSR_BI) { ch->ch_err_break++; - jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, - "%s:%d Port: %d. BRK INTR!\n", __FILE__, __LINE__, port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "%s:%d Port: %d. BRK INTR!\n", + __FILE__, __LINE__, port); } if (linestatus & UART_LSR_OE) { @@ -904,8 +910,9 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port) * Probably we should eventually have an orun stat in our driver... */ ch->ch_err_overrun++; - jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, - "%s:%d Port: %d. Rx Overrun!\n", __FILE__, __LINE__, port); + jsm_dbg(INTR, &ch->ch_bd->pci_dev, + "%s:%d Port: %d. Rx Overrun!\n", + __FILE__, __LINE__, port); } if (linestatus & UART_LSR_THRE) { @@ -1128,11 +1135,11 @@ static irqreturn_t neo_intr(int irq, void *voidbrd) */ uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET); - jsm_printk(INTR, INFO, &brd->pci_dev, - "%s:%d uart_poll: %x\n", __FILE__, __LINE__, uart_poll); + jsm_dbg(INTR, &brd->pci_dev, "%s:%d uart_poll: %x\n", + __FILE__, __LINE__, uart_poll); if (!uart_poll) { - jsm_printk(INTR, INFO, &brd->pci_dev, + jsm_dbg(INTR, &brd->pci_dev, "Kernel interrupted to me, but no pending interrupts...\n"); spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); return IRQ_NONE; @@ -1158,15 +1165,15 @@ static irqreturn_t neo_intr(int irq, void *voidbrd) continue; } - jsm_printk(INTR, INFO, &brd->pci_dev, - "%s:%d port: %x type: %x\n", __FILE__, __LINE__, port, type); + jsm_dbg(INTR, &brd->pci_dev, "%s:%d port: %x type: %x\n", + __FILE__, __LINE__, port, type); /* Remove this port + type from uart_poll */ uart_poll &= ~(jsm_offset_table[port]); if (!type) { /* If no type, just ignore it, and move onto next port */ - jsm_printk(INTR, ERR, &brd->pci_dev, + jsm_dbg(INTR, &brd->pci_dev, "Interrupt with no type! port: %d\n", port); continue; } @@ -1231,15 +1238,16 @@ static irqreturn_t neo_intr(int irq, void *voidbrd) * these once and awhile. * Its harmless, just ignore it and move on. */ - jsm_printk(INTR, ERR, &brd->pci_dev, - "%s:%d Unknown Interrupt type: %x\n", __FILE__, __LINE__, type); + jsm_dbg(INTR, &brd->pci_dev, + "%s:%d Unknown Interrupt type: %x\n", + __FILE__, __LINE__, type); continue; } } spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); - jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n"); + jsm_dbg(INTR, &brd->pci_dev, "finish\n"); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 71397961773..7d2c1f3aa36 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -43,7 +43,7 @@ static inline int jsm_get_mstat(struct jsm_channel *ch) unsigned char mstat; unsigned result; - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "start\n"); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "start\n"); mstat = (ch->ch_mostat | ch->ch_mistat); @@ -62,7 +62,7 @@ static inline int jsm_get_mstat(struct jsm_channel *ch) if (mstat & UART_MSR_DCD) result |= TIOCM_CD; - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n"); return result; } @@ -79,14 +79,14 @@ static unsigned int jsm_tty_get_mctrl(struct uart_port *port) int result; struct jsm_channel *channel = (struct jsm_channel *)port; - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); result = jsm_get_mstat(channel); if (result < 0) return -ENXIO; - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); return result; } @@ -100,7 +100,7 @@ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct jsm_channel *channel = (struct jsm_channel *)port; - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); if (mctrl & TIOCM_RTS) channel->ch_mostat |= UART_MCR_RTS; @@ -114,7 +114,7 @@ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl) channel->ch_bd->bd_ops->assert_modem_signals(channel); - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); udelay(10); } @@ -135,23 +135,23 @@ static void jsm_tty_start_tx(struct uart_port *port) { struct jsm_channel *channel = (struct jsm_channel *)port; - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); channel->ch_flags &= ~(CH_STOP); jsm_tty_write(port); - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); } static void jsm_tty_stop_tx(struct uart_port *port) { struct jsm_channel *channel = (struct jsm_channel *)port; - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n"); channel->ch_flags |= (CH_STOP); - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n"); } static void jsm_tty_send_xchar(struct uart_port *port, char ch) @@ -216,16 +216,16 @@ static int jsm_tty_open(struct uart_port *port) if (!channel->ch_rqueue) { channel->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL); if (!channel->ch_rqueue) { - jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, - "unable to allocate read queue buf"); + jsm_dbg(INIT, &channel->ch_bd->pci_dev, + "unable to allocate read queue buf\n"); return -ENOMEM; } } if (!channel->ch_equeue) { channel->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL); if (!channel->ch_equeue) { - jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, - "unable to allocate error queue buf"); + jsm_dbg(INIT, &channel->ch_bd->pci_dev, + "unable to allocate error queue buf\n"); return -ENOMEM; } } @@ -234,7 +234,7 @@ static int jsm_tty_open(struct uart_port *port) /* * Initialize if neither terminal is open. */ - jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, + jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "jsm_open: initializing channel in open...\n"); /* @@ -270,7 +270,7 @@ static int jsm_tty_open(struct uart_port *port) channel->ch_open_count++; - jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "finish\n"); return 0; } @@ -280,7 +280,7 @@ static void jsm_tty_close(struct uart_port *port) struct ktermios *ts; struct jsm_channel *channel = (struct jsm_channel *)port; - jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n"); + jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "start\n"); bd = channel->ch_bd; ts = &port->state->port.tty->termios; @@ -293,7 +293,7 @@ static void jsm_tty_close(struct uart_port *port) * If we have HUPCL set, lower DTR and RTS */ if (channel->ch_c_cflag & HUPCL) { - jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, + jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "Close. HUPCL set, dropping DTR/RTS\n"); /* Drop RTS/DTR */ @@ -304,7 +304,7 @@ static void jsm_tty_close(struct uart_port *port) /* Turn off UART interrupts for this port */ channel->ch_bd->bd_ops->uart_off(channel); - jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "finish\n"); + jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "finish\n"); } static void jsm_tty_set_termios(struct uart_port *port, @@ -380,7 +380,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd) if (!brd) return -ENXIO; - jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); + jsm_dbg(INIT, &brd->pci_dev, "start\n"); /* * Initialize board structure elements. @@ -401,9 +401,9 @@ int __devinit jsm_tty_init(struct jsm_board *brd) */ brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL); if (!brd->channels[i]) { - jsm_printk(CORE, ERR, &brd->pci_dev, + jsm_dbg(CORE, &brd->pci_dev, "%s:%d Unable to allocate memory for channel struct\n", - __FILE__, __LINE__); + __FILE__, __LINE__); } } } @@ -431,7 +431,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd) init_waitqueue_head(&ch->ch_flags_wait); } - jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); + jsm_dbg(INIT, &brd->pci_dev, "finish\n"); return 0; } @@ -444,7 +444,7 @@ int jsm_uart_port_init(struct jsm_board *brd) if (!brd) return -ENXIO; - jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); + jsm_dbg(INIT, &brd->pci_dev, "start\n"); /* * Initialize board structure elements. @@ -481,7 +481,7 @@ int jsm_uart_port_init(struct jsm_board *brd) printk(KERN_INFO "jsm: Port %d added\n", i); } - jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); + jsm_dbg(INIT, &brd->pci_dev, "finish\n"); return 0; } @@ -493,7 +493,7 @@ int jsm_remove_uart_port(struct jsm_board *brd) if (!brd) return -ENXIO; - jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); + jsm_dbg(INIT, &brd->pci_dev, "start\n"); /* * Initialize board structure elements. @@ -513,7 +513,7 @@ int jsm_remove_uart_port(struct jsm_board *brd) uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port); } - jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); + jsm_dbg(INIT, &brd->pci_dev, "finish\n"); return 0; } @@ -531,7 +531,7 @@ void jsm_input(struct jsm_channel *ch) int s = 0; int i = 0; - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n"); + jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n"); if (!ch) return; @@ -560,7 +560,7 @@ void jsm_input(struct jsm_channel *ch) return; } - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n"); + jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n"); /* *If the device is not open, or CREAD is off, flush @@ -569,8 +569,9 @@ void jsm_input(struct jsm_channel *ch) if (!tp || !(tp->termios.c_cflag & CREAD) ) { - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum); + jsm_dbg(READ, &ch->ch_bd->pci_dev, + "input. dropping %d bytes on port %d...\n", + data_len, ch->ch_portnum); ch->ch_r_head = tail; /* Force queue flow control to be released, if needed */ @@ -585,17 +586,17 @@ void jsm_input(struct jsm_channel *ch) */ if (ch->ch_flags & CH_STOPI) { spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, + jsm_dbg(READ, &ch->ch_bd->pci_dev, "Port %d throttled, not reading any data. head: %x tail: %x\n", ch->ch_portnum, head, tail); return; } - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start 2\n"); + jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n"); if (data_len <= 0) { spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "jsm_input 1\n"); + jsm_dbg(READ, &ch->ch_bd->pci_dev, "jsm_input 1\n"); return; } @@ -653,7 +654,7 @@ void jsm_input(struct jsm_channel *ch) /* Tell the tty layer its okay to "eat" the data now */ tty_flip_buffer_push(tp); - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n"); + jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n"); } static void jsm_carrier(struct jsm_channel *ch) @@ -663,7 +664,7 @@ static void jsm_carrier(struct jsm_channel *ch) int virt_carrier = 0; int phys_carrier = 0; - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n"); + jsm_dbg(CARR, &ch->ch_bd->pci_dev, "start\n"); if (!ch) return; @@ -673,16 +674,16 @@ static void jsm_carrier(struct jsm_channel *ch) return; if (ch->ch_mistat & UART_MSR_DCD) { - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, - "mistat: %x D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD); + jsm_dbg(CARR, &ch->ch_bd->pci_dev, "mistat: %x D_CD: %x\n", + ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD); phys_carrier = 1; } if (ch->ch_c_cflag & CLOCAL) virt_carrier = 1; - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, - "DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier); + jsm_dbg(CARR, &ch->ch_bd->pci_dev, "DCD: physical: %d virt: %d\n", + phys_carrier, virt_carrier); /* * Test for a VIRTUAL carrier transition to HIGH. @@ -694,8 +695,7 @@ static void jsm_carrier(struct jsm_channel *ch) * for carrier in the open routine. */ - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, - "carrier: virt DCD rose\n"); + jsm_dbg(CARR, &ch->ch_bd->pci_dev, "carrier: virt DCD rose\n"); if (waitqueue_active(&(ch->ch_flags_wait))) wake_up_interruptible(&ch->ch_flags_wait); @@ -711,7 +711,7 @@ static void jsm_carrier(struct jsm_channel *ch) * for carrier in the open routine. */ - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, + jsm_dbg(CARR, &ch->ch_bd->pci_dev, "carrier: physical DCD rose\n"); if (waitqueue_active(&(ch->ch_flags_wait))) @@ -790,8 +790,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) if(!(ch->ch_flags & CH_RECEIVER_OFF)) { bd_ops->disable_receiver(ch); ch->ch_flags |= (CH_RECEIVER_OFF); - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "Internal queue hit hilevel mark (%d)! Turning off interrupts.\n", + jsm_dbg(READ, &ch->ch_bd->pci_dev, + "Internal queue hit hilevel mark (%d)! Turning off interrupts\n", qleft); } } @@ -800,8 +800,9 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) if (ch->ch_stops_sent <= MAX_STOPS_SENT) { bd_ops->send_stop_character(ch); ch->ch_stops_sent++; - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "Sending stop char! Times sent: %x\n", ch->ch_stops_sent); + jsm_dbg(READ, &ch->ch_bd->pci_dev, + "Sending stop char! Times sent: %x\n", + ch->ch_stops_sent); } } } @@ -827,8 +828,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) if (ch->ch_flags & CH_RECEIVER_OFF) { bd_ops->enable_receiver(ch); ch->ch_flags &= ~(CH_RECEIVER_OFF); - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n", + jsm_dbg(READ, &ch->ch_bd->pci_dev, + "Internal queue hit lowlevel mark (%d)! Turning on interrupts\n", qleft); } } @@ -836,7 +837,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) { ch->ch_stops_sent = 0; bd_ops->send_start_character(ch); - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n"); + jsm_dbg(READ, &ch->ch_bd->pci_dev, + "Sending start char!\n"); } } } From 15a12e83da812e9116577d46b048923587da5000 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 26 Oct 2012 23:04:22 +0800 Subject: [PATCH 066/146] serial: 8250_pci: use module_pci_driver to simplify the code Use the module_pci_driver() macro to make the code simpler by eliminating module_init and module_exit calls. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index cec8852dd1b..508063b2a4b 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -4332,18 +4332,7 @@ static struct pci_driver serial_pci_driver = { .err_handler = &serial8250_err_handler, }; -static int __init serial8250_pci_init(void) -{ - return pci_register_driver(&serial_pci_driver); -} - -static void __exit serial8250_pci_exit(void) -{ - pci_unregister_driver(&serial_pci_driver); -} - -module_init(serial8250_pci_init); -module_exit(serial8250_pci_exit); +module_pci_driver(serial_pci_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); From bebe73e31d98845c8b63e624c25a5da2d819345a Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 29 Oct 2012 15:19:57 +0000 Subject: [PATCH 067/146] uart: update the sysfs handler to use uart_get_info The two patches needed are now in the tree. The first added the sysfs interface and directly accesses the uartclk. The second provides a proper interface for getting the values. Wire them together. This formes a basis for both get and set methods for any of the other uart properties and we can now fill them out further. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0fcfd98a956..477e0790ddf 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2329,15 +2329,15 @@ struct tty_driver *uart_console_device(struct console *co, int *index) static ssize_t uart_get_attr_uartclk(struct device *dev, struct device_attribute *attr, char *buf) { - int ret; + struct serial_struct tmp; struct tty_port *port = dev_get_drvdata(dev); struct uart_state *state = container_of(port, struct uart_state, port); - mutex_lock(&state->port.mutex); - ret = snprintf(buf, PAGE_SIZE, "%d\n", state->uart_port->uartclk); - mutex_unlock(&state->port.mutex); + mutex_lock(&port->mutex); + uart_get_info(port, state, &tmp); + mutex_unlock(&port->mutex); - return ret; + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.baud_base * 16); } static DEVICE_ATTR(uartclk, S_IRUSR | S_IRGRP, uart_get_attr_uartclk, NULL); From 9f1096943a56c35cc85a0729ec759fd8a25e552f Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 29 Oct 2012 15:20:25 +0000 Subject: [PATCH 068/146] uart: tidy the uart_get_info API We pass both port and state because the original caller had both to hand. With all the attribute callers this won't be true so do the conversion in the function itself. The current callers all do lock/query/unlock. This won't be true for future set based cases but there are plenty of get ones that will exist so split the code with a helper for the future cases. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 477e0790ddf..0c4304ef66d 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -634,10 +634,10 @@ static void uart_unthrottle(struct tty_struct *tty) uart_set_mctrl(port, TIOCM_RTS); } -static void uart_get_info(struct tty_port *port, - struct uart_state *state, +static void do_uart_get_info(struct tty_port *port, struct serial_struct *retinfo) { + struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = state->uart_port; memset(retinfo, 0, sizeof(*retinfo)); @@ -662,17 +662,21 @@ static void uart_get_info(struct tty_port *port, retinfo->iomem_base = (void *)(unsigned long)uport->mapbase; } -static int uart_get_info_user(struct uart_state *state, - struct serial_struct __user *retinfo) +static void uart_get_info(struct tty_port *port, + struct serial_struct *retinfo) { - struct tty_port *port = &state->port; - struct serial_struct tmp; - /* Ensure the state we copy is consistent and no hardware changes occur as we go */ mutex_lock(&port->mutex); - uart_get_info(port, state, &tmp); + do_uart_get_info(port, retinfo); mutex_unlock(&port->mutex); +} + +static int uart_get_info_user(struct tty_port *port, + struct serial_struct __user *retinfo) +{ + struct serial_struct tmp; + uart_get_info(port, &tmp); if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -1131,7 +1135,7 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, */ switch (cmd) { case TIOCGSERIAL: - ret = uart_get_info_user(state, uarg); + ret = uart_get_info_user(port, uarg); break; case TIOCSSERIAL: @@ -2331,12 +2335,8 @@ static ssize_t uart_get_attr_uartclk(struct device *dev, { struct serial_struct tmp; struct tty_port *port = dev_get_drvdata(dev); - struct uart_state *state = container_of(port, struct uart_state, port); - - mutex_lock(&port->mutex); - uart_get_info(port, state, &tmp); - mutex_unlock(&port->mutex); + uart_get_info(port, &tmp); return snprintf(buf, PAGE_SIZE, "%d\n", tmp.baud_base * 16); } @@ -2356,6 +2356,7 @@ static const struct attribute_group *tty_dev_attr_groups[] = { NULL }; + /** * uart_add_one_port - attach a driver-defined port structure * @drv: pointer to the uart low level driver structure for this port From 373bac4cf4c3198cc6d6b9aec7c5d576a06f1f1c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 29 Oct 2012 15:20:40 +0000 Subject: [PATCH 069/146] uart: add other serial core layer get attributes Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-tty | 112 +++++++++++++++++++++ drivers/tty/serial/serial_core.c | 145 ++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-tty b/Documentation/ABI/testing/sysfs-tty index 0c430150d92..ad22fb0ee76 100644 --- a/Documentation/ABI/testing/sysfs-tty +++ b/Documentation/ABI/testing/sysfs-tty @@ -26,3 +26,115 @@ Description: UART port in serial_core, that is bound to TTY like ttyS0. uartclk = 16 * baud_base + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/type +Date: October 2012 +Contact: Alan Cox +Description: + Shows the current tty type for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/line +Date: October 2012 +Contact: Alan Cox +Description: + Shows the current tty line number for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/port +Date: October 2012 +Contact: Alan Cox +Description: + Shows the current tty port I/O address for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/irq +Date: October 2012 +Contact: Alan Cox +Description: + Shows the current primary interrupt for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/flags +Date: October 2012 +Contact: Alan Cox +Description: + Show the tty port status flags for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/xmit_fifo_size +Date: October 2012 +Contact: Alan Cox +Description: + Show the transmit FIFO size for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/close_delay +Date: October 2012 +Contact: Alan Cox +Description: + Show the closing delay time for this port in ms. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/closing_wait +Date: October 2012 +Contact: Alan Cox +Description: + Show the close wait time for this port in ms. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/custom_divisor +Date: October 2012 +Contact: Alan Cox +Description: + Show the custom divisor if any that is set on this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/io_type +Date: October 2012 +Contact: Alan Cox +Description: + Show the I/O type that is to be used with the iomem base + address. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/iomem_base +Date: October 2012 +Contact: Alan Cox +Description: + The I/O memory base for this port. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/iomem_reg_shift +Date: October 2012 +Contact: Alan Cox +Description: + Show the register shift indicating the spacing to be used + for accesses on this iomem address. + + These sysfs values expose the TIOCGSERIAL interface via + sysfs rather than via ioctls. diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0c4304ef66d..d3dd4ad984f 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2340,10 +2340,155 @@ static ssize_t uart_get_attr_uartclk(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", tmp.baud_base * 16); } +static ssize_t uart_get_attr_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.type); +} +static ssize_t uart_get_attr_line(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.line); +} + +static ssize_t uart_get_attr_port(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)(tmp.port | (tmp.port_high << HIGH_BITS_OFFSET))); +} + +static ssize_t uart_get_attr_irq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.irq); +} + +static ssize_t uart_get_attr_flags(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "0x%X\n", tmp.flags); +} + +static ssize_t uart_get_attr_xmit_fifo_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.xmit_fifo_size); +} + + +static ssize_t uart_get_attr_close_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.close_delay); +} + + +static ssize_t uart_get_attr_closing_wait(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.closing_wait); +} + +static ssize_t uart_get_attr_custom_divisor(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.custom_divisor); +} + +static ssize_t uart_get_attr_io_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.io_type); +} + +static ssize_t uart_get_attr_iomem_base(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)tmp.iomem_base); +} + +static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct serial_struct tmp; + struct tty_port *port = dev_get_drvdata(dev); + + uart_get_info(port, &tmp); + return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift); +} + +static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL); +static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL); +static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL); +static DEVICE_ATTR(irq, S_IRUSR | S_IRGRP, uart_get_attr_irq, NULL); +static DEVICE_ATTR(flags, S_IRUSR | S_IRGRP, uart_get_attr_flags, NULL); +static DEVICE_ATTR(xmit_fifo_size, S_IRUSR | S_IRGRP, uart_get_attr_xmit_fifo_size, NULL); static DEVICE_ATTR(uartclk, S_IRUSR | S_IRGRP, uart_get_attr_uartclk, NULL); +static DEVICE_ATTR(close_delay, S_IRUSR | S_IRGRP, uart_get_attr_close_delay, NULL); +static DEVICE_ATTR(closing_wait, S_IRUSR | S_IRGRP, uart_get_attr_closing_wait, NULL); +static DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP, uart_get_attr_custom_divisor, NULL); +static DEVICE_ATTR(io_type, S_IRUSR | S_IRGRP, uart_get_attr_io_type, NULL); +static DEVICE_ATTR(iomem_base, S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL); +static DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP, uart_get_attr_iomem_reg_shift, NULL); static struct attribute *tty_dev_attrs[] = { + &dev_attr_type.attr, + &dev_attr_line.attr, + &dev_attr_port.attr, + &dev_attr_irq.attr, + &dev_attr_flags.attr, + &dev_attr_xmit_fifo_size.attr, &dev_attr_uartclk.attr, + &dev_attr_close_delay.attr, + &dev_attr_closing_wait.attr, + &dev_attr_custom_divisor.attr, + &dev_attr_io_type.attr, + &dev_attr_iomem_base.attr, + &dev_attr_iomem_reg_shift.attr, NULL, }; From 2ac4ad2a1468123f6bb439a547880a9c0d302e0a Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Sat, 27 Oct 2012 12:47:12 +0530 Subject: [PATCH 070/146] serial/arc-uart: Add new driver Driver for non-standard on-chip UART, instantiated in the ARC (Synopsys) FPGA Boards such as ARCAngel4/ML50x Signed-off-by: Vineet Gupta Reviewed-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 23 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/arc_uart.c | 746 +++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 2 + 4 files changed, 772 insertions(+) create mode 100644 drivers/tty/serial/arc_uart.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2a53be5f010..b1768012ed2 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1423,4 +1423,27 @@ config SERIAL_EFM32_UART_CONSOLE depends on SERIAL_EFM32_UART=y select SERIAL_CORE_CONSOLE +config SERIAL_ARC + tristate "ARC UART driver support" + select SERIAL_CORE + help + Driver for on-chip UART for ARC(Synopsys) for the legacy + FPGA Boards (ML50x/ARCAngel4) + +config SERIAL_ARC_CONSOLE + bool "Console on ARC UART" + depends on SERIAL_ARC=y + select SERIAL_CORE_CONSOLE + help + Enable system Console on ARC UART + +config SERIAL_ARC_NR_PORTS + int "Number of ARC UART ports" + depends on SERIAL_ARC + range 1 3 + default "1" + help + Set this to the number of serial ports you want the driver + to support. + endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 4f694dafa71..df1b998c436 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -82,3 +82,4 @@ obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o +obj-$(CONFIG_SERIAL_ARC) += arc_uart.o diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c new file mode 100644 index 00000000000..e9c61d1b1c7 --- /dev/null +++ b/drivers/tty/serial/arc_uart.c @@ -0,0 +1,746 @@ +/* + * ARC On-Chip(fpga) UART Driver + * + * Copyright (C) 2010-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: July 10th 2012 + * -Decoupled the driver from arch/arc + * +Using platform_get_resource() for irq/membase (thx to bfin_uart.c) + * +Using early_platform_xxx() for early console (thx to mach-shmobile/xxx) + * + * Vineetg: Aug 21st 2010 + * -Is uart_tx_stopped() not done in tty write path as it has already been + * taken care of, in serial core + * + * Vineetg: Aug 18th 2010 + * -New Serial Core based ARC UART driver + * -Derived largely from blackfin driver albiet with some major tweaks + * + * TODO: + * -check if sysreq works + */ + +#if defined(CONFIG_SERIAL_ARC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/************************************* + * ARC UART Hardware Specs + ************************************/ +#define ARC_UART_TX_FIFO_SIZE 1 + +/* + * UART Register set (this is not a Standards Compliant IP) + * Also each reg is Word aligned, but only 8 bits wide + */ +#define R_ID0 0 +#define R_ID1 4 +#define R_ID2 8 +#define R_ID3 12 +#define R_DATA 16 +#define R_STS 20 +#define R_BAUDL 24 +#define R_BAUDH 28 + +/* Bits for UART Status Reg (R/W) */ +#define RXIENB 0x04 /* Receive Interrupt Enable */ +#define TXIENB 0x40 /* Transmit Interrupt Enable */ + +#define RXEMPTY 0x20 /* Receive FIFO Empty: No char receivede */ +#define TXEMPTY 0x80 /* Transmit FIFO Empty, thus char can be written into */ + +#define RXFULL 0x08 /* Receive FIFO full */ +#define RXFULL1 0x10 /* Receive FIFO has space for 1 char (tot space=4) */ + +#define RXFERR 0x01 /* Frame Error: Stop Bit not detected */ +#define RXOERR 0x02 /* OverFlow Err: Char recv but RXFULL still set */ + +/* Uart bit fiddling helpers: lowest level */ +#define RBASE(uart, reg) (uart->port.membase + reg) +#define UART_REG_SET(u, r, v) writeb((v), RBASE(u, r)) +#define UART_REG_GET(u, r) readb(RBASE(u, r)) + +#define UART_REG_OR(u, r, v) UART_REG_SET(u, r, UART_REG_GET(u, r) | (v)) +#define UART_REG_CLR(u, r, v) UART_REG_SET(u, r, UART_REG_GET(u, r) & ~(v)) + +/* Uart bit fiddling helpers: API level */ +#define UART_SET_DATA(uart, val) UART_REG_SET(uart, R_DATA, val) +#define UART_GET_DATA(uart) UART_REG_GET(uart, R_DATA) + +#define UART_SET_BAUDH(uart, val) UART_REG_SET(uart, R_BAUDH, val) +#define UART_SET_BAUDL(uart, val) UART_REG_SET(uart, R_BAUDL, val) + +#define UART_CLR_STATUS(uart, val) UART_REG_CLR(uart, R_STS, val) +#define UART_GET_STATUS(uart) UART_REG_GET(uart, R_STS) + +#define UART_ALL_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, RXIENB|TXIENB) +#define UART_RX_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, RXIENB) +#define UART_TX_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, TXIENB) + +#define UART_ALL_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, RXIENB|TXIENB) +#define UART_RX_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, RXIENB) +#define UART_TX_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, TXIENB) + +#define ARC_SERIAL_DEV_NAME "ttyARC" + +struct arc_uart_port { + struct uart_port port; + unsigned long baud; + int is_emulated; /* H/w vs. Instruction Set Simulator */ +}; + +#define to_arc_port(uport) container_of(uport, struct arc_uart_port, port) + +static struct arc_uart_port arc_uart_ports[CONFIG_SERIAL_ARC_NR_PORTS]; + +#ifdef CONFIG_SERIAL_ARC_CONSOLE +static struct console arc_console; +#endif + +#define DRIVER_NAME "arc-uart" + +static struct uart_driver arc_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = ARC_SERIAL_DEV_NAME, + .major = 0, + .minor = 0, + .nr = CONFIG_SERIAL_ARC_NR_PORTS, +#ifdef CONFIG_SERIAL_ARC_CONSOLE + .cons = &arc_console, +#endif +}; + +static void arc_serial_stop_rx(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + UART_RX_IRQ_DISABLE(uart); +} + +static void arc_serial_stop_tx(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + while (!(UART_GET_STATUS(uart) & TXEMPTY)) + cpu_relax(); + + UART_TX_IRQ_DISABLE(uart); +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int arc_serial_tx_empty(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + unsigned int stat; + + stat = UART_GET_STATUS(uart); + if (stat & TXEMPTY) + return TIOCSER_TEMT; + + return 0; +} + +/* + * Driver internal routine, used by both tty(serial core) as well as tx-isr + * -Called under spinlock in either cases + * -also tty->stopped / tty->hw_stopped has already been checked + * = by uart_start( ) before calling us + * = tx_ist checks that too before calling + */ +static void arc_serial_tx_chars(struct arc_uart_port *uart) +{ + struct circ_buf *xmit = &uart->port.state->xmit; + int sent = 0; + unsigned char ch; + + if (unlikely(uart->port.x_char)) { + UART_SET_DATA(uart, uart->port.x_char); + uart->port.icount.tx++; + uart->port.x_char = 0; + sent = 1; + } else if (xmit->tail != xmit->head) { /* TODO: uart_circ_empty */ + ch = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + uart->port.icount.tx++; + while (!(UART_GET_STATUS(uart) & TXEMPTY)) + cpu_relax(); + UART_SET_DATA(uart, ch); + sent = 1; + } + + /* + * If num chars in xmit buffer are too few, ask tty layer for more. + * By Hard ISR to schedule processing in software interrupt part + */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uart->port); + + if (sent) + UART_TX_IRQ_ENABLE(uart); +} + +/* + * port is locked and interrupts are disabled + * uart_start( ) calls us under the port spinlock irqsave + */ +static void arc_serial_start_tx(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + arc_serial_tx_chars(uart); +} + +static void arc_serial_rx_chars(struct arc_uart_port *uart) +{ + struct tty_struct *tty = tty_port_tty_get(&uart->port.state->port); + unsigned int status, ch, flg = 0; + + if (!tty) + return; + + /* + * UART has 4 deep RX-FIFO. Driver's recongnition of this fact + * is very subtle. Here's how ... + * Upon getting a RX-Intr, such that RX-EMPTY=0, meaning data available, + * driver reads the DATA Reg and keeps doing that in a loop, until + * RX-EMPTY=1. Multiple chars being avail, with a single Interrupt, + * before RX-EMPTY=0, implies some sort of buffering going on in the + * controller, which is indeed the Rx-FIFO. + */ + while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)) { + + ch = UART_GET_DATA(uart); + uart->port.icount.rx++; + + if (unlikely(status & (RXOERR | RXFERR))) { + if (status & RXOERR) { + uart->port.icount.overrun++; + flg = TTY_OVERRUN; + UART_CLR_STATUS(uart, RXOERR); + } + + if (status & RXFERR) { + uart->port.icount.frame++; + flg = TTY_FRAME; + UART_CLR_STATUS(uart, RXFERR); + } + } else + flg = TTY_NORMAL; + + if (unlikely(uart_handle_sysrq_char(&uart->port, ch))) + goto done; + + uart_insert_char(&uart->port, status, RXOERR, ch, flg); + +done: + tty_flip_buffer_push(tty); + } + + tty_kref_put(tty); +} + +/* + * A note on the Interrupt handling state machine of this driver + * + * kernel printk writes funnel thru the console driver framework and in order + * to keep things simple as well as efficient, it writes to UART in polled + * mode, in one shot, and exits. + * + * OTOH, Userland output (via tty layer), uses interrupt based writes as there + * can be undeterministic delay between char writes. + * + * Thus Rx-interrupts are always enabled, while tx-interrupts are by default + * disabled. + * + * When tty has some data to send out, serial core calls driver's start_tx + * which + * -checks-if-tty-buffer-has-char-to-send + * -writes-data-to-uart + * -enable-tx-intr + * + * Once data bits are pushed out, controller raises the Tx-room-avail-Interrupt. + * The first thing Tx ISR does is disable further Tx interrupts (as this could + * be the last char to send, before settling down into the quiet polled mode). + * It then calls the exact routine used by tty layer write to send out any + * more char in tty buffer. In case of sending, it re-enables Tx-intr. In case + * of no data, it remains disabled. + * This is how the transmit state machine is dynamically switched on/off + */ + +static irqreturn_t arc_serial_isr(int irq, void *dev_id) +{ + struct arc_uart_port *uart = dev_id; + unsigned int status; + + status = UART_GET_STATUS(uart); + + /* + * Single IRQ for both Rx (data available) Tx (room available) Interrupt + * notifications from the UART Controller. + * To demultiplex between the two, we check the relevant bits + */ + if ((status & RXIENB) && !(status & RXEMPTY)) { + + /* already in ISR, no need of xx_irqsave */ + spin_lock(&uart->port.lock); + arc_serial_rx_chars(uart); + spin_unlock(&uart->port.lock); + } + + if ((status & TXIENB) && (status & TXEMPTY)) { + + /* Unconditionally disable further Tx-Interrupts. + * will be enabled by tx_chars() if needed. + */ + UART_TX_IRQ_DISABLE(uart); + + spin_lock(&uart->port.lock); + + if (!uart_tx_stopped(&uart->port)) + arc_serial_tx_chars(uart); + + spin_unlock(&uart->port.lock); + } + + return IRQ_HANDLED; +} + +static unsigned int arc_serial_get_mctrl(struct uart_port *port) +{ + /* + * Pretend we have a Modem status reg and following bits are + * always set, to satify the serial core state machine + * (DSR) Data Set Ready + * (CTS) Clear To Send + * (CAR) Carrier Detect + */ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void arc_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* MCR not present */ +} + +/* Enable Modem Status Interrupts */ + +static void arc_serial_enable_ms(struct uart_port *port) +{ + /* MSR not present */ +} + +static void arc_serial_break_ctl(struct uart_port *port, int break_state) +{ + /* ARC UART doesn't support sending Break signal */ +} + +static int arc_serial_startup(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + /* Before we hook up the ISR, Disable all UART Interrupts */ + UART_ALL_IRQ_DISABLE(uart); + + if (request_irq(uart->port.irq, arc_serial_isr, 0, "arc uart rx-tx", + uart)) { + dev_warn(uart->port.dev, "Unable to attach ARC UART intr\n"); + return -EBUSY; + } + + UART_RX_IRQ_ENABLE(uart); /* Only Rx IRQ enabled to begin with */ + + return 0; +} + +/* This is not really needed */ +static void arc_serial_shutdown(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + free_irq(uart->port.irq, uart); +} + +static void +arc_serial_set_termios(struct uart_port *port, struct ktermios *new, + struct ktermios *old) +{ + struct arc_uart_port *uart = to_arc_port(port); + unsigned int baud, uartl, uarth, hw_val; + unsigned long flags; + + /* + * Use the generic handler so that any specially encoded baud rates + * such as SPD_xx flags or "%B0" can be handled + * Max Baud I suppose will not be more than current 115K * 4 + * Formula for ARC UART is: hw-val = ((CLK/(BAUD*4)) -1) + * spread over two 8-bit registers + */ + baud = uart_get_baud_rate(port, new, old, 0, 460800); + + hw_val = port->uartclk / (uart->baud * 4) - 1; + uartl = hw_val & 0xFF; + uarth = (hw_val >> 8) & 0xFF; + + /* + * UART ISS(Instruction Set simulator) emulation has a subtle bug: + * A existing value of Baudh = 0 is used as a indication to startup + * it's internal state machine. + * Thus if baudh is set to 0, 2 times, it chokes. + * This happens with BAUD=115200 and the formaula above + * Until that is fixed, when running on ISS, we will set baudh to !0 + */ + if (uart->is_emulated) + uarth = 1; + + spin_lock_irqsave(&port->lock, flags); + + UART_ALL_IRQ_DISABLE(uart); + + UART_SET_BAUDL(uart, uartl); + UART_SET_BAUDH(uart, uarth); + + UART_RX_IRQ_ENABLE(uart); + + /* + * UART doesn't support Parity/Hardware Flow Control; + * Only supports 8N1 character size + */ + new->c_cflag &= ~(CMSPAR|CRTSCTS|CSIZE); + new->c_cflag |= CS8; + + if (old) + tty_termios_copy_hw(new, old); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(new)) + tty_termios_encode_baud_rate(new, baud, baud); + + uart_update_timeout(port, new->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *arc_serial_type(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + return uart->port.type == PORT_ARC ? DRIVER_NAME : NULL; +} + +static void arc_serial_release_port(struct uart_port *port) +{ +} + +static int arc_serial_request_port(struct uart_port *port) +{ + return 0; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + */ +static int +arc_serial_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (port->type != PORT_UNKNOWN && ser->type != PORT_ARC) + return -EINVAL; + + return 0; +} + +/* + * Configure/autoconfigure the port. + */ +static void arc_serial_config_port(struct uart_port *port, int flags) +{ + struct arc_uart_port *uart = to_arc_port(port); + + if (flags & UART_CONFIG_TYPE) + uart->port.type = PORT_ARC; +} + +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE) + +static void arc_serial_poll_putchar(struct uart_port *port, unsigned char chr) +{ + struct arc_uart_port *uart = to_arc_port(port); + + while (!(UART_GET_STATUS(uart) & TXEMPTY)) + cpu_relax(); + + UART_SET_DATA(uart, chr); +} +#endif + +#ifdef CONFIG_CONSOLE_POLL +static int arc_serial_poll_getchar(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + unsigned char chr; + + while (!(UART_GET_STATUS(uart) & RXEMPTY)) + cpu_relax(); + + chr = UART_GET_DATA(uart); + return chr; +} +#endif + +static struct uart_ops arc_serial_pops = { + .tx_empty = arc_serial_tx_empty, + .set_mctrl = arc_serial_set_mctrl, + .get_mctrl = arc_serial_get_mctrl, + .stop_tx = arc_serial_stop_tx, + .start_tx = arc_serial_start_tx, + .stop_rx = arc_serial_stop_rx, + .enable_ms = arc_serial_enable_ms, + .break_ctl = arc_serial_break_ctl, + .startup = arc_serial_startup, + .shutdown = arc_serial_shutdown, + .set_termios = arc_serial_set_termios, + .type = arc_serial_type, + .release_port = arc_serial_release_port, + .request_port = arc_serial_request_port, + .config_port = arc_serial_config_port, + .verify_port = arc_serial_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char = arc_serial_poll_putchar, + .poll_get_char = arc_serial_poll_getchar, +#endif +}; + +static int __devinit +arc_uart_init_one(struct platform_device *pdev, struct arc_uart_port *uart) +{ + struct resource *res, *res2; + unsigned long *plat_data; + + if (pdev->id < 0 || pdev->id >= CONFIG_SERIAL_ARC_NR_PORTS) { + dev_err(&pdev->dev, "Wrong uart platform device id.\n"); + return -ENOENT; + } + + plat_data = ((unsigned long *)(pdev->dev.platform_data)); + uart->baud = plat_data[0]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res2) + return -ENODEV; + + uart->port.mapbase = res->start; + uart->port.membase = ioremap_nocache(res->start, resource_size(res)); + if (!uart->port.membase) + /* No point of dev_err since UART itself is hosed here */ + return -ENXIO; + + uart->port.irq = res2->start; + uart->port.dev = &pdev->dev; + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF; + uart->port.line = pdev->id; + uart->port.ops = &arc_serial_pops; + + uart->port.uartclk = plat_data[1]; + uart->port.fifosize = ARC_UART_TX_FIFO_SIZE; + + /* + * uart_insert_char( ) uses it in decideding whether to ignore a + * char or not. Explicitly setting it here, removes the subtelty + */ + uart->port.ignore_status_mask = 0; + + /* Real Hardware vs. emulated to work around a bug */ + uart->is_emulated = !!plat_data[2]; + + return 0; +} + +#ifdef CONFIG_SERIAL_ARC_CONSOLE + +static int __devinit arc_serial_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= CONFIG_SERIAL_ARC_NR_PORTS) + return -ENODEV; + + /* + * The uart port backing the console (e.g. ttyARC1) might not have been + * init yet. If so, defer the console setup to after the port. + */ + port = &arc_uart_ports[co->index].port; + if (!port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + /* + * Serial core will call port->ops->set_termios( ) + * which will set the baud reg + */ + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static void arc_serial_console_putchar(struct uart_port *port, int ch) +{ + arc_serial_poll_putchar(port, (unsigned char)ch); +} + +/* + * Interrupts are disabled on entering + */ +static void arc_serial_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &arc_uart_ports[co->index].port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + uart_console_write(port, s, count, arc_serial_console_putchar); + spin_unlock_irqrestore(&port->lock, flags); +} + +static struct console arc_console = { + .name = ARC_SERIAL_DEV_NAME, + .write = arc_serial_console_write, + .device = uart_console_device, + .setup = arc_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &arc_uart_driver +}; + +static __init void early_serial_write(struct console *con, const char *s, + unsigned int n) +{ + struct uart_port *port = &arc_uart_ports[con->index].port; + unsigned int i; + + for (i = 0; i < n; i++, s++) { + if (*s == '\n') + arc_serial_poll_putchar(port, '\r'); + arc_serial_poll_putchar(port, *s); + } +} + +static struct __initdata console arc_early_serial_console = { + .name = "early_ARCuart", + .write = early_serial_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1 +}; + +static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev) +{ + arc_early_serial_console.index = pdev->id; + + arc_uart_init_one(pdev, &arc_uart_ports[pdev->id]); + + arc_serial_console_setup(&arc_early_serial_console, NULL); + + register_console(&arc_early_serial_console); + return 0; +} +#else +static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif /* CONFIG_SERIAL_ARC_CONSOLE */ + +static int __devinit arc_serial_probe(struct platform_device *pdev) +{ + struct arc_uart_port *uart; + int rc; + + if (is_early_platform_device(pdev)) + return arc_serial_probe_earlyprintk(pdev); + + uart = &arc_uart_ports[pdev->id]; + rc = arc_uart_init_one(pdev, uart); + if (rc) + return rc; + + return uart_add_one_port(&arc_uart_driver, &uart->port); +} + +static int __devexit arc_serial_remove(struct platform_device *pdev) +{ + /* This will never be called */ + return 0; +} + +static struct platform_driver arc_platform_driver = { + .probe = arc_serial_probe, + .remove = __devexit_p(arc_serial_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +#ifdef CONFIG_SERIAL_ARC_CONSOLE +/* + * Register an early platform driver of "earlyprintk" class. + * ARCH platform code installs the driver and probes the early devices + * The installation could rely on user specifying earlyprintk=xyx in cmd line + * or it could be done independently, for all "earlyprintk" class drivers. + * [see arch/arc/plat-arcfpga/platform.c] + */ +early_platform_init("earlyprintk", &arc_platform_driver); + +#endif /* CONFIG_SERIAL_ARC_CONSOLE */ + +static int __init arc_serial_init(void) +{ + int ret; + + ret = uart_register_driver(&arc_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&arc_platform_driver); + if (ret) + uart_unregister_driver(&arc_uart_driver); + + return ret; +} + +static void __exit arc_serial_exit(void) +{ + platform_driver_unregister(&arc_platform_driver); + uart_unregister_driver(&arc_uart_driver); +} + +module_init(arc_serial_init); +module_exit(arc_serial_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("plat-arcfpga/uart"); +MODULE_AUTHOR("Vineet Gupta"); +MODULE_DESCRIPTION("ARC(Synopsys) On-Chip(fpga) serial driver"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 7e1ab20adc0..ebcc73f0418 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -215,5 +215,7 @@ /* Energy Micro efm32 SoC */ #define PORT_EFMUART 100 +/* ARC (Synopsys) on-chip UART */ +#define PORT_ARC 101 #endif /* _UAPILINUX_SERIAL_CORE_H */ From 31fe99048859b13a87f476016e7bb5c2b5220c36 Mon Sep 17 00:00:00 2001 From: chao bi Date: Wed, 31 Oct 2012 16:54:07 +0800 Subject: [PATCH 071/146] serial:ifx6x60:Prevent data transfer when IFX6x60 port is shutdown This patch is to implement following 2 places to avoid potential error when IFX6x60 port shutdown: 1) Clear Flag IFX_SPI_STATE_IO_AVAILABLE to disable data transfer when Modem port is shutdown; 2) Clear Flag IFX_SPI_STATE_IO_IN_PROGRESS and IFX_SPI_STATE_IO_READY when reopen port. This is because last port shutdown may happen when SPI/DMA transfer is in progress, if the last data transfer is not completed(for example due to modem reset), the Flag IFX_SPI_STATE_IO_IN_PROGRESS will be set forever, so when IFX port is activated again, IFX_SPI_STATE_IO_IN_PROGRESS will prevent transferring data forever. And if don't clear IFX_SPI_STATE_IO_READY, it may cause one more SPI frame transferring in spit there is not data need to be transfer. cc: liu chuansheng cc: Chen Jun Signed-off-by: channing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 11 ++++++++++- drivers/tty/serial/ifx6x60.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index e595c832be2..fbda37415f0 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -569,12 +569,19 @@ static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty) /* clear any old data; can't do this in 'close' */ kfifo_reset(&ifx_dev->tx_fifo); + /* clear any flag which may be set in port shutdown procedure */ + clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags); + clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags); + /* put port data into this tty */ tty->driver_data = ifx_dev; /* allows flip string push from int context */ tty->low_latency = 1; + /* set flag to allows data transfer */ + set_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags); + return 0; } @@ -590,6 +597,7 @@ static void ifx_port_shutdown(struct tty_port *port) struct ifx_spi_device *ifx_dev = container_of(port, struct ifx_spi_device, tty_port); + clear_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags); mrdy_set_low(ifx_dev); clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); tasklet_kill(&ifx_dev->io_work_tasklet); @@ -745,7 +753,8 @@ static void ifx_spi_io(unsigned long data) int retval; struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *) data; - if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags)) { + if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags) && + test_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags)) { if (ifx_dev->gpio.unack_srdy_int_nb > 0) ifx_dev->gpio.unack_srdy_int_nb--; diff --git a/drivers/tty/serial/ifx6x60.h b/drivers/tty/serial/ifx6x60.h index d8869f5a463..4fbddc29783 100644 --- a/drivers/tty/serial/ifx6x60.h +++ b/drivers/tty/serial/ifx6x60.h @@ -41,6 +41,7 @@ #define IFX_SPI_STATE_IO_IN_PROGRESS 1 #define IFX_SPI_STATE_IO_READY 2 #define IFX_SPI_STATE_TIMER_PENDING 3 +#define IFX_SPI_STATE_IO_AVAILABLE 4 /* flow control bitfields */ #define IFX_SPI_DCD 0 From 76cc43868c1e9d6344ad6c4992c4f6abd5204a8f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 1 Nov 2012 13:27:34 +0800 Subject: [PATCH 072/146] tty: of_serial: fix return value check in of_platform_serial_setup() In case of error, the function clk_get() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/of_serial.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 533ccfe7709..b9fdccb2259 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -66,10 +66,10 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, /* Get clk rate through clk driver if present */ info->clk = clk_get(&ofdev->dev, NULL); - if (info->clk == NULL) { + if (IS_ERR(info->clk)) { dev_warn(&ofdev->dev, "clk or clock-frequency not defined\n"); - return -ENODEV; + return PTR_ERR(info->clk); } clk_prepare_enable(info->clk); From 1b2f8a9550f92686fb76f9dd4d0ec7c0c3f1b027 Mon Sep 17 00:00:00 2001 From: chao bi Date: Tue, 6 Nov 2012 11:13:59 +0800 Subject: [PATCH 073/146] serial:ifx6x60:SPI header is decoded incorrectly This patch is to correct the bit mapping of "MORE" and "CTS" in SPI frame header. Per SPI protocol, SPI header is encoded with length of 4 byte, which is defined as below: bit 0 ~ 11: current data size; bit 12: "MORE" bit; bit 13: reserve bit 14 ~ 15: reserve bit 16 ~ 27: next data size bit 28: RI bit 29: DCD bit 30: CTS/RTS bit 31: DSR/DTR According to above SPI header structure, the bit mapping of "MORE" and "CTS" is incorrect in function ifx_spi_decode_spi_header(); Cc: Chen Jun Signed-off-by: channing Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index fbda37415f0..21eb70bcb6a 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -64,8 +64,8 @@ #include "ifx6x60.h" #define IFX_SPI_MORE_MASK 0x10 -#define IFX_SPI_MORE_BIT 12 /* bit position in u16 */ -#define IFX_SPI_CTS_BIT 13 /* bit position in u16 */ +#define IFX_SPI_MORE_BIT 4 /* bit position in u8 */ +#define IFX_SPI_CTS_BIT 6 /* bit position in u8 */ #define IFX_SPI_MODE SPI_MODE_1 #define IFX_SPI_TTY_ID 0 #define IFX_SPI_TIMEOUT_SEC 2 From e8823f1ca8d5a0c5d69a8862682ee8bde26990ca Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Wed, 7 Nov 2012 12:04:04 -0500 Subject: [PATCH 074/146] serial: ifx6x60: ifx_spi_write don't need to do mrdy_assert when fifo is not empty This patch check whether the fifo lenth is empty before writing new data to fifo.If condition is true,ifx_spi_write need to trigger one mrdy_assert. If condition is false,the mrdy_assert will be trigger by the next ifx_spi_io. Cc: Bi Chao Signed-off-by: Chen Jun Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 21eb70bcb6a..1754c147a94 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -508,9 +508,16 @@ static int ifx_spi_write(struct tty_struct *tty, const unsigned char *buf, { struct ifx_spi_device *ifx_dev = tty->driver_data; unsigned char *tmp_buf = (unsigned char *)buf; - int tx_count = kfifo_in_locked(&ifx_dev->tx_fifo, tmp_buf, count, - &ifx_dev->fifo_lock); - mrdy_assert(ifx_dev); + unsigned long flags; + bool is_fifo_empty; + + spin_lock_irqsave(&ifx_dev->fifo_lock, flags); + is_fifo_empty = kfifo_is_empty(&ifx_dev->tx_fifo); + int tx_count = kfifo_in(&ifx_dev->tx_fifo, tmp_buf, count); + spin_unlock_irqrestore(&ifx_dev->fifo_lock, flags); + if (is_fifo_empty) + mrdy_assert(ifx_dev); + return tx_count; } From 4bd82136cdf04f3a8d50e20c1b76da750f75f2db Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 1 Nov 2012 16:43:49 +0000 Subject: [PATCH 075/146] moxa: dcd handling of CLOCAL is backwards We should do hangup on dcd loss if CLOCAL is false not true. Signed-off-by: Alan Cox Resolves-bug: https://bugzilla.kernel.org/show_bug.cgi?id=49911 Signed-off-by: Greg Kroah-Hartman --- drivers/tty/moxa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 56e616b9109..9b57aae139f 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -1370,7 +1370,7 @@ static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) p->DCDState = dcd; spin_unlock_irqrestore(&p->port.lock, flags); tty = tty_port_tty_get(&p->port); - if (tty && C_CLOCAL(tty) && !dcd) + if (tty && !C_CLOCAL(tty) && !dcd) tty_hangup(tty); tty_kref_put(tty); } From d1519e23c2b3a518fb41daf3eceae43382433ceb Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 1 Nov 2012 16:45:49 +0000 Subject: [PATCH 076/146] ipwireless: don't oops if we run out of space Resolves-bug: https://bugzilla.kernel.org/show_bug.cgi?id=49851 Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/ipwireless/network.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c index 57102e66165..c0dfb642383 100644 --- a/drivers/tty/ipwireless/network.c +++ b/drivers/tty/ipwireless/network.c @@ -352,6 +352,8 @@ static struct sk_buff *ipw_packet_received_skb(unsigned char *data, } skb = dev_alloc_skb(length + 4); + if (skb == NULL) + return NULL; skb_reserve(skb, 2); memcpy(skb_put(skb, length), data, length); @@ -397,7 +399,8 @@ void ipwireless_network_packet_received(struct ipw_network *network, /* Send the data to the ppp_generic module. */ skb = ipw_packet_received_skb(data, length); - ppp_input(network->ppp_channel, skb); + if (skb) + ppp_input(network->ppp_channel, skb); } else spin_unlock_irqrestore(&network->lock, flags); From 4bb535d2b6fe1466d89037c95945cc7bf5ba2377 Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Mon, 5 Nov 2012 15:24:26 -0600 Subject: [PATCH 077/146] serial: xilinx_uartps: kill CONFIG_OF conditional The Zynq platform requires the use of CONFIG_OF. Remove the #ifdef conditionals in the uartps driver. Make dependency explicit in Kconfig. Signed-off-by: Josh Cartwright Tested-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 1 + drivers/tty/serial/xilinx_uartps.c | 9 --------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index b1768012ed2..6a69c88c502 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1376,6 +1376,7 @@ config SERIAL_MXS_AUART_CONSOLE config SERIAL_XILINX_PS_UART tristate "Xilinx PS UART support" + depends on OF select SERIAL_CORE help This driver supports the Xilinx PS UART port. diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index b627363352e..23efe17be44 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -946,15 +946,11 @@ static int __devinit xuartps_probe(struct platform_device *pdev) 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; @@ -1044,16 +1040,11 @@ static int xuartps_resume(struct platform_device *pdev) } /* 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 */ From 7a876b39b5bc94f67e8a3a7adfd270b8c21fc762 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 6 Nov 2012 14:30:28 +0000 Subject: [PATCH 078/146] serial: cast before shifting on port io Without this we will shift data into oblivion and give wrong results on some configurations Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index d3dd4ad984f..63b33889d51 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2366,7 +2366,7 @@ static ssize_t uart_get_attr_port(struct device *dev, struct tty_port *port = dev_get_drvdata(dev); uart_get_info(port, &tmp); - return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)(tmp.port | (tmp.port_high << HIGH_BITS_OFFSET))); + return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)(tmp.port | (((unsigned long)tmp.port_high) << HIGH_BITS_OFFSET))); } static ssize_t uart_get_attr_irq(struct device *dev, From 9642dbe73c8a3b55a812253fcb9add9eeed4ca81 Mon Sep 17 00:00:00 2001 From: Steven Miao Date: Wed, 7 Nov 2012 13:27:56 +0800 Subject: [PATCH 079/146] serial: bfin-uart: avoid dead lock in rx irq handler in smp kernel Disabing dma irq and lock bottom half in smp kernel doesn't ensure exclusive uart access. Call spin_lock_irqsave() instead. Signed-off-by: Steven Miao Signed-off-by: Sonic Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_uart.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 9242d56ba26..ca64c4a83ce 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -477,9 +477,9 @@ static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) { int x_pos, pos; + unsigned long flags; - dma_disable_irq_nosync(uart->rx_dma_channel); - spin_lock_bh(&uart->rx_lock); + spin_lock_irqsave(&uart->rx_lock, flags); /* 2D DMA RX buffer ring is used. Because curr_y_count and * curr_x_count can't be read as an atomic operation, @@ -510,8 +510,7 @@ void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) uart->rx_dma_buf.tail = uart->rx_dma_buf.head; } - spin_unlock_bh(&uart->rx_lock); - dma_enable_irq(uart->rx_dma_channel); + spin_unlock_irqrestore(&uart->rx_lock, flags); mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES); } From 50827fbde161a4ccb05a649752dd221b083f5ea5 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Thu, 15 Nov 2012 16:03:16 +0800 Subject: [PATCH 080/146] serial: mfd: Add nmi_touch_watchdog() into the console write function This is following what 8250 driver is doing in console write function, to avoid the hardware lockup case. v2: incldudes the Signed-off-by: Feng Tang Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mfd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index c4b50af46c4..79fe59b6cc4 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -1113,6 +1114,8 @@ serial_hsu_console_write(struct console *co, const char *s, unsigned int count) unsigned int ier; int locked = 1; + touch_nmi_watchdog(); + local_irq_save(flags); if (up->port.sysrq) locked = 0; From 18e0749aa825b8af12990521536f617d1405c37f Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 11 Nov 2012 10:24:19 +0400 Subject: [PATCH 081/146] serial: Unneeded ARCH dependencies are removed This patch performs a small cleanup tty/Serial/Kconfig file by removing unneeded ARCH dependencies. This dependencies already included in board/type symbols. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 6a69c88c502..59c23d03810 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -93,7 +93,7 @@ config SERIAL_SB1250_DUART_CONSOLE config SERIAL_ATMEL bool "AT91 / AT32 on-chip serial port support" - depends on (ARM && ARCH_AT91) || AVR32 + depends on ARCH_AT91 || AVR32 select SERIAL_CORE help This enables the driver for the on-chip UARTs of the Atmel @@ -198,7 +198,7 @@ config SERIAL_CLPS711X_CONSOLE config SERIAL_SAMSUNG tristate "Samsung SoC serial support" - depends on ARM && PLAT_SAMSUNG + depends on PLAT_SAMSUNG select SERIAL_CORE help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, @@ -208,14 +208,14 @@ config SERIAL_SAMSUNG config SERIAL_SAMSUNG_UARTS_4 bool - depends on ARM && PLAT_SAMSUNG + depends on PLAT_SAMSUNG default y if !(CPU_S3C2410 || SERIAL_S3C2412 || CPU_S3C2440 || CPU_S3C2442) help Internal node for the common case of 4 Samsung compatible UARTs config SERIAL_SAMSUNG_UARTS int - depends on ARM && PLAT_SAMSUNG + depends on PLAT_SAMSUNG default 6 if ARCH_S5P6450 default 4 if SERIAL_SAMSUNG_UARTS_4 || CPU_S3C2416 default 3 @@ -249,7 +249,7 @@ config SERIAL_SAMSUNG_CONSOLE config SERIAL_SIRFSOC tristate "SiRF SoC Platform Serial port support" - depends on ARM && ARCH_PRIMA2 + depends on ARCH_PRIMA2 select SERIAL_CORE help Support for the on-chip UART on the CSR SiRFprimaII series, @@ -347,7 +347,7 @@ config SERIAL_ZS_CONSOLE config SERIAL_21285 tristate "DC21285 serial port support" - depends on ARM && FOOTBRIDGE + depends on FOOTBRIDGE select SERIAL_CORE help If you have a machine based on a 21285 (Footbridge) StrongARM(R)/ @@ -371,7 +371,7 @@ config SERIAL_21285_CONSOLE config SERIAL_MPSC bool "Marvell MPSC serial port support" - depends on PPC32 && MV64X60 + depends on MV64X60 select SERIAL_CORE help Say Y here if you want to use the Marvell MPSC serial controller. @@ -408,7 +408,7 @@ config SERIAL_PXA_CONSOLE config SERIAL_SA1100 bool "SA1100 serial port support" - depends on ARM && ARCH_SA1100 + depends on ARCH_SA1100 select SERIAL_CORE help If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you @@ -716,7 +716,7 @@ config SERIAL_SH_SCI_DMA config SERIAL_PNX8XXX bool "Enable PNX8XXX SoCs' UART Support" - depends on MIPS && (SOC_PNX8550 || SOC_PNX833X) + depends on SOC_PNX8550 || SOC_PNX833X select SERIAL_CORE help If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 @@ -1013,7 +1013,7 @@ config SERIAL_SGI_IOC3 config SERIAL_MSM bool "MSM on-chip serial port support" - depends on ARM && ARCH_MSM + depends on ARCH_MSM select SERIAL_CORE config SERIAL_MSM_CONSOLE @@ -1035,7 +1035,7 @@ config SERIAL_MSM_HS config SERIAL_VT8500 bool "VIA VT8500 on-chip serial port support" - depends on ARM && ARCH_VT8500 + depends on ARCH_VT8500 select SERIAL_CORE config SERIAL_VT8500_CONSOLE @@ -1045,7 +1045,7 @@ config SERIAL_VT8500_CONSOLE config SERIAL_NETX tristate "NetX serial port support" - depends on ARM && ARCH_NETX + depends on ARCH_NETX select SERIAL_CORE help If you have a machine based on a Hilscher NetX SoC you From 5318609519800617323b5fdb17c1d4fe12c3d794 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Wed, 14 Nov 2012 01:15:19 -0800 Subject: [PATCH 082/146] mm, oom: ensure sysrq+f always passes valid zonelist With hotpluggable and memoryless nodes, it's possible that node 0 will not be online, so use the first online node's zonelist rather than hardcoding node 0 to pass a zonelist with all zones to the oom killer. Signed-off-by: David Rientjes Reviewed-by: Michal Hocko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/sysrq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 16ee6cee07d..b3c4a250ff8 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -346,7 +346,8 @@ static struct sysrq_key_op sysrq_term_op = { static void moom_callback(struct work_struct *ignored) { - out_of_memory(node_zonelist(0, GFP_KERNEL), GFP_KERNEL, 0, NULL, true); + out_of_memory(node_zonelist(first_online_node, GFP_KERNEL), GFP_KERNEL, + 0, NULL, true); } static DECLARE_WORK(moom_work, moom_callback); From 2dff8ad92661b6c99e9ba8b5918e43b522551556 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 14 Nov 2012 10:38:13 +0100 Subject: [PATCH 083/146] tty/serial/ar933x_uart: fix baud rate calculation The UART of the AR933x SoC implements a fractional divisor for generating the desired baud rate. The current code uses a fixed value for the fractional part of the divisor, and this leads to improperly calculated baud rates: baud scale step real baud diff 300 5207* 8192 17756 17456 5818.66% 600 2603* 8192 35511 34911 5818.50% 1200 1301* 8192 71023 69823 5818.58% 2400 650* 8192 11241 8841 368.37% 4800 324* 8192 22645 17845 371.77% 9600 161 8192 9645 45 0.46% 14400 107 8192 14468 68 0.47% 19200 80 8192 19290 90 0.46% 28800 53 8192 28935 135 0.46% 38400 39 8192 39063 663 1.72% 57600 26 8192 57870 270 0.46% 115200 12 8192 120192 4992 4.33% 230400 5 8192 260417 30017 13.02% 460800 2 8192 520833 60033 13.02% 921600 0 8192 1562500 640900 69.93% After the patch, the integer and fractional parts of the divisor will be calculated dynamically. This ensures that the UART will use correct baud rates: baud scale step real baud diff 300 6 11 300 0 0.00% 600 54 173 600 0 0.00% 1200 30 195 1200 0 0.00% 2400 30 390 2400 0 0.00% 4800 48 1233 4800 0 0.00% 9600 78 3976 9600 0 0.00% 14400 98 7474 14400 0 0.00% 19200 55 5637 19200 0 0.00% 28800 130 19780 28800 0 0.00% 38400 36 7449 38400 0 0.00% 57600 78 23857 57600 0 0.00% 115200 43 26575 115200 0 0.00% 230400 23 28991 230400 0 0.00% 460800 11 28991 460800 0 0.00% 921600 5 28991 921599 -1 0.00% Signed-off-by: Gabor Juhos Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ar933x_uart.c | 90 ++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index e4f60e2b87f..0f5b28afc2a 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -25,11 +25,19 @@ #include #include +#include + #include #include #define DRIVER_NAME "ar933x-uart" +#define AR933X_UART_MAX_SCALE 0xff +#define AR933X_UART_MAX_STEP 0xffff + +#define AR933X_UART_MIN_BAUD 300 +#define AR933X_UART_MAX_BAUD 3000000 + #define AR933X_DUMMY_STATUS_RD 0x01 static struct uart_driver ar933x_uart_driver; @@ -37,6 +45,8 @@ static struct uart_driver ar933x_uart_driver; struct ar933x_uart_port { struct uart_port port; unsigned int ier; /* shadow Interrupt Enable Register */ + unsigned int min_baud; + unsigned int max_baud; }; static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up, @@ -162,6 +172,57 @@ static void ar933x_uart_enable_ms(struct uart_port *port) { } +/* + * baudrate = (clk / (scale + 1)) * (step * (1 / 2^17)) + */ +static unsigned long ar933x_uart_get_baud(unsigned int clk, + unsigned int scale, + unsigned int step) +{ + u64 t; + u32 div; + + div = (2 << 16) * (scale + 1); + t = clk; + t *= step; + t += (div / 2); + do_div(t, div); + + return t; +} + +static void ar933x_uart_get_scale_step(unsigned int clk, + unsigned int baud, + unsigned int *scale, + unsigned int *step) +{ + unsigned int tscale; + long min_diff; + + *scale = 0; + *step = 0; + + min_diff = baud; + for (tscale = 0; tscale < AR933X_UART_MAX_SCALE; tscale++) { + u64 tstep; + int diff; + + tstep = baud * (tscale + 1); + tstep *= (2 << 16); + do_div(tstep, clk); + + if (tstep > AR933X_UART_MAX_STEP) + break; + + diff = abs(ar933x_uart_get_baud(clk, tscale, tstep) - baud); + if (diff < min_diff) { + min_diff = diff; + *scale = tscale; + *step = tstep; + } + } +} + static void ar933x_uart_set_termios(struct uart_port *port, struct ktermios *new, struct ktermios *old) @@ -169,7 +230,7 @@ static void ar933x_uart_set_termios(struct uart_port *port, struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; unsigned int cs; unsigned long flags; - unsigned int baud, scale; + unsigned int baud, scale, step; /* Only CS8 is supported */ new->c_cflag &= ~CSIZE; @@ -191,8 +252,8 @@ static void ar933x_uart_set_termios(struct uart_port *port, /* Mark/space parity is not supported */ new->c_cflag &= ~CMSPAR; - baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); - scale = (port->uartclk / (16 * baud)) - 1; + baud = uart_get_baud_rate(port, new, old, up->min_baud, up->max_baud); + ar933x_uart_get_scale_step(port->uartclk, baud, &scale, &step); /* * Ok, we're now changing the port state. Do it with @@ -200,6 +261,10 @@ static void ar933x_uart_set_termios(struct uart_port *port, */ spin_lock_irqsave(&up->port.lock, flags); + /* disable the UART */ + ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG, + AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S); + /* Update the per-port timeout. */ uart_update_timeout(port, new->c_cflag, baud); @@ -210,7 +275,7 @@ static void ar933x_uart_set_termios(struct uart_port *port, up->port.ignore_status_mask |= AR933X_DUMMY_STATUS_RD; ar933x_uart_write(up, AR933X_UART_CLOCK_REG, - scale << AR933X_UART_CLOCK_SCALE_S | 8192); + scale << AR933X_UART_CLOCK_SCALE_S | step); /* setup configuration register */ ar933x_uart_rmw(up, AR933X_UART_CS_REG, AR933X_UART_CS_PARITY_M, cs); @@ -219,6 +284,11 @@ static void ar933x_uart_set_termios(struct uart_port *port, ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, AR933X_UART_CS_HOST_INT_EN); + /* reenable the UART */ + ar933x_uart_rmw(up, AR933X_UART_CS_REG, + AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S, + AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S); + spin_unlock_irqrestore(&up->port.lock, flags); if (tty_termios_baud_rate(new)) @@ -401,6 +471,8 @@ static void ar933x_uart_config_port(struct uart_port *port, int flags) static int ar933x_uart_verify_port(struct uart_port *port, struct serial_struct *ser) { + struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_AR933X) return -EINVAL; @@ -408,7 +480,8 @@ static int ar933x_uart_verify_port(struct uart_port *port, if (ser->irq < 0 || ser->irq >= NR_IRQS) return -EINVAL; - if (ser->baud_base < 28800) + if (ser->baud_base < up->min_baud || + ser->baud_base > up->max_baud) return -EINVAL; return 0; @@ -561,6 +634,7 @@ static int __devinit ar933x_uart_probe(struct platform_device *pdev) struct uart_port *port; struct resource *mem_res; struct resource *irq_res; + unsigned int baud; int id; int ret; @@ -611,6 +685,12 @@ static int __devinit ar933x_uart_probe(struct platform_device *pdev) port->fifosize = AR933X_UART_FIFO_SIZE; port->ops = &ar933x_uart_ops; + baud = ar933x_uart_get_baud(port->uartclk, AR933X_UART_MAX_SCALE, 1); + up->min_baud = max_t(unsigned int, baud, AR933X_UART_MIN_BAUD); + + baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP); + up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD); + ar933x_uart_add_console_port(up); ret = uart_add_one_port(&ar933x_uart_driver, &up->port); From 7342c59a44ad9e5f30baaa2de84830f40b9f06c0 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 15 Nov 2012 09:49:48 +0100 Subject: [PATCH 084/146] TTY: isicom, stop using port->tty Do not access unsafe port->tty pointer when we have a safe tty already. Use the safe one. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/isicom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index d7492e18360..5f3ecbc2713 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -603,7 +603,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) if (tty_port_cts_enabled(&port->port)) { if (tty->hw_stopped) { if (header & ISI_CTS) { - port->port.tty->hw_stopped = 0; + tty->hw_stopped = 0; /* start tx ing */ port->status |= (ISI_TXOK | ISI_CTS); From 81c79838ca24f48e0e4bb96502d131d6af758ae0 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 15 Nov 2012 09:49:49 +0100 Subject: [PATCH 085/146] TTY: pty, fix tty buffers leak After commit "TTY: move tty buffers to tty_port", the tty buffers are not freed in some drivers. This is because tty_port_destructor is not called whenever a tty_port is freed. This was an assumption I counted with but was unfortunately untrue. So fix the drivers to fulfil this assumption. PTY is one of those, here we just need to use tty_port_put instead of kfree. (Assuming tty_port_destructor does not need port->ops to be set which we change here too.) Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 2 +- drivers/tty/tty_port.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 0ce0b3ec2bb..a541ec87593 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -408,7 +408,7 @@ static void pty_unix98_shutdown(struct tty_struct *tty) static void pty_cleanup(struct tty_struct *tty) { tty->port->itty = NULL; - kfree(tty->port); + tty_port_put(tty->port); } /* Traditional BSD devices */ diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 416b42f7c34..fdc42c2d565 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -128,7 +128,7 @@ static void tty_port_destructor(struct kref *kref) if (port->xmit_buf) free_page((unsigned long)port->xmit_buf); tty_buffer_free_all(port); - if (port->ops->destruct) + if (port->ops && port->ops->destruct) port->ops->destruct(port); else kfree(port); From 55bef83cc68bda76a14a23b1076a9a9a9e43af68 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 15 Nov 2012 09:49:50 +0100 Subject: [PATCH 086/146] ISDN: capi, use kref from tty_port After commit "TTY: move tty buffers to tty_port", the tty buffers are not freed in some drivers. This is because tty_port_destructor is not called whenever a tty_port is freed. This was an assumption I counted with but was unfortunately untrue. So fix the drivers to fulfil this assumption. Here it is enough to switch to refcounting in tty_port. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/capi/capi.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index c679867c2cc..89562a845f6 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -77,8 +77,6 @@ struct ackqueue_entry { }; struct capiminor { - struct kref kref; - unsigned int minor; struct capi20_appl *ap; @@ -190,7 +188,20 @@ static void capiminor_del_all_ack(struct capiminor *mp) /* -------- struct capiminor ---------------------------------------- */ -static const struct tty_port_operations capiminor_port_ops; /* we have none */ +static void capiminor_destroy(struct tty_port *port) +{ + struct capiminor *mp = container_of(port, struct capiminor, port); + + kfree_skb(mp->outskb); + skb_queue_purge(&mp->inqueue); + skb_queue_purge(&mp->outqueue); + capiminor_del_all_ack(mp); + kfree(mp); +} + +static const struct tty_port_operations capiminor_port_ops = { + .destruct = capiminor_destroy, +}; static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci) { @@ -204,8 +215,6 @@ static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci) return NULL; } - kref_init(&mp->kref); - mp->ap = ap; mp->ncci = ncci; INIT_LIST_HEAD(&mp->ackqueue); @@ -247,21 +256,10 @@ err_out2: spin_unlock(&capiminors_lock); err_out1: - kfree(mp); + tty_port_put(&mp->port); return NULL; } -static void capiminor_destroy(struct kref *kref) -{ - struct capiminor *mp = container_of(kref, struct capiminor, kref); - - kfree_skb(mp->outskb); - skb_queue_purge(&mp->inqueue); - skb_queue_purge(&mp->outqueue); - capiminor_del_all_ack(mp); - kfree(mp); -} - static struct capiminor *capiminor_get(unsigned int minor) { struct capiminor *mp; @@ -269,7 +267,7 @@ static struct capiminor *capiminor_get(unsigned int minor) spin_lock(&capiminors_lock); mp = capiminors[minor]; if (mp) - kref_get(&mp->kref); + tty_port_get(&mp->port); spin_unlock(&capiminors_lock); return mp; @@ -277,7 +275,7 @@ static struct capiminor *capiminor_get(unsigned int minor) static inline void capiminor_put(struct capiminor *mp) { - kref_put(&mp->kref, capiminor_destroy); + tty_port_put(&mp->port); } static void capiminor_free(struct capiminor *mp) From 9753eb8de8a36de41f3b5e217e995cccfbecdada Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 15 Nov 2012 09:49:51 +0100 Subject: [PATCH 087/146] MMC: sdio_uart, remove unused member from sdio_uart_port tty from struct sdio_uart_port is unused. Proper refcounted tty in tty_port->tty is used instead. So remove the member from that structure. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index d2339ea3781..369f7ba1d21 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -67,7 +67,6 @@ struct uart_icount { struct sdio_uart_port { struct tty_port port; struct kref kref; - struct tty_struct *tty; unsigned int index; struct sdio_func *func; struct mutex func_lock; From e70c67713cdabf55e05db7f1c4fc38085da333a5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 15 Nov 2012 09:49:52 +0100 Subject: [PATCH 088/146] MMC: sdio, use kref from tty_port After commit "TTY: move tty buffers to tty_port", the tty buffers are not freed in some drivers. This is because tty_port_destructor is not called whenever a tty_port is freed. This was an assumption I counted with but was unfortunately untrue. So fix the drivers to fulfil this assumption. Here it is enough to switch to refcounting in tty_port. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 369f7ba1d21..bd57a11acc7 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -66,7 +66,6 @@ struct uart_icount { struct sdio_uart_port { struct tty_port port; - struct kref kref; unsigned int index; struct sdio_func *func; struct mutex func_lock; @@ -92,7 +91,6 @@ static int sdio_uart_add_port(struct sdio_uart_port *port) { int index, ret = -EBUSY; - kref_init(&port->kref); mutex_init(&port->func_lock); spin_lock_init(&port->write_lock); if (kfifo_alloc(&port->xmit_fifo, FIFO_SIZE, GFP_KERNEL)) @@ -122,23 +120,15 @@ static struct sdio_uart_port *sdio_uart_port_get(unsigned index) spin_lock(&sdio_uart_table_lock); port = sdio_uart_table[index]; if (port) - kref_get(&port->kref); + tty_port_get(&port->port); spin_unlock(&sdio_uart_table_lock); return port; } -static void sdio_uart_port_destroy(struct kref *kref) -{ - struct sdio_uart_port *port = - container_of(kref, struct sdio_uart_port, kref); - kfifo_free(&port->xmit_fifo); - kfree(port); -} - static void sdio_uart_port_put(struct sdio_uart_port *port) { - kref_put(&port->kref, sdio_uart_port_destroy); + tty_port_put(&port->port); } static void sdio_uart_port_remove(struct sdio_uart_port *port) @@ -736,6 +726,14 @@ static void sdio_uart_shutdown(struct tty_port *tport) sdio_uart_release_func(port); } +static void sdio_uart_port_destroy(struct tty_port *tport) +{ + struct sdio_uart_port *port = + container_of(tport, struct sdio_uart_port, port); + kfifo_free(&port->xmit_fifo); + kfree(port); +} + /** * sdio_uart_install - install method * @driver: the driver in use (sdio_uart in our case) @@ -1044,6 +1042,7 @@ static const struct tty_port_operations sdio_uart_port_ops = { .carrier_raised = uart_carrier_raised, .shutdown = sdio_uart_shutdown, .activate = sdio_uart_activate, + .destruct = sdio_uart_port_destroy, }; static const struct tty_operations sdio_uart_ops = { From 9a8e62bc68832dc55a5e6868f812b65567fe66b5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 15 Nov 2012 09:49:53 +0100 Subject: [PATCH 089/146] TTY: n_gsm, use kref from tty_port After commit "TTY: move tty buffers to tty_port", the tty buffers are not freed in some drivers. This is because tty_port_destructor is not called whenever a tty_port is freed. This was an assumption I counted with but was unfortunately untrue. So fix the drivers to fulfil this assumption. Here it is enough to switch to refcounting in tty_port. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 1e8e8ce5595..dcc0430a49c 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -134,7 +134,6 @@ struct gsm_dlci { #define DLCI_OPENING 1 /* Sending SABM not seen UA */ #define DLCI_OPEN 2 /* SABM/UA complete */ #define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */ - struct kref ref; /* freed from port or mux close */ struct mutex mutex; /* Link layer */ @@ -1635,7 +1634,6 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) if (dlci == NULL) return NULL; spin_lock_init(&dlci->lock); - kref_init(&dlci->ref); mutex_init(&dlci->mutex); dlci->fifo = &dlci->_fifo; if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) { @@ -1669,9 +1667,9 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) * * Can sleep. */ -static void gsm_dlci_free(struct kref *ref) +static void gsm_dlci_free(struct tty_port *port) { - struct gsm_dlci *dlci = container_of(ref, struct gsm_dlci, ref); + struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); del_timer_sync(&dlci->t1); dlci->gsm->dlci[dlci->addr] = NULL; @@ -1683,12 +1681,12 @@ static void gsm_dlci_free(struct kref *ref) static inline void dlci_get(struct gsm_dlci *dlci) { - kref_get(&dlci->ref); + tty_port_get(&dlci->port); } static inline void dlci_put(struct gsm_dlci *dlci) { - kref_put(&dlci->ref, gsm_dlci_free); + tty_port_put(&dlci->port); } /** @@ -2874,6 +2872,7 @@ static void gsm_dtr_rts(struct tty_port *port, int onoff) static const struct tty_port_operations gsm_port_ops = { .carrier_raised = gsm_carrier_raised, .dtr_rts = gsm_dtr_rts, + .destruct = gsm_dlci_free, }; static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) From de274bfe0fc81def6ddb8a17020a9a4b56477cc4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 15 Nov 2012 09:49:54 +0100 Subject: [PATCH 090/146] TTY: introduce tty_port_destroy After commit "TTY: move tty buffers to tty_port", the tty buffers are not freed in some drivers. This is because tty_port_destructor is not called whenever a tty_port is freed. This was an assumption I counted with but was unfortunately untrue. Those using refcounting are safe now, but for those which do not we introduce a function to be called right before the tty_port is freed by the drivers. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 16 +++++++++++++++- include/linux/tty.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index fdc42c2d565..b7ff59d3db8 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -122,12 +122,26 @@ void tty_port_free_xmit_buf(struct tty_port *port) } EXPORT_SYMBOL(tty_port_free_xmit_buf); +/** + * tty_port_destroy -- destroy inited port + * @port: tty port to be doestroyed + * + * When a port was initialized using tty_port_init, one has to destroy the + * port by this function. Either indirectly by using tty_port refcounting + * (tty_port_put) or directly if refcounting is not used. + */ +void tty_port_destroy(struct tty_port *port) +{ + tty_buffer_free_all(port); +} +EXPORT_SYMBOL(tty_port_destroy); + static void tty_port_destructor(struct kref *kref) { struct tty_port *port = container_of(kref, struct tty_port, kref); if (port->xmit_buf) free_page((unsigned long)port->xmit_buf); - tty_buffer_free_all(port); + tty_port_destroy(port); if (port->ops && port->ops->destruct) port->ops->destruct(port); else diff --git a/include/linux/tty.h b/include/linux/tty.h index d7ff88fb896..8db1b569c37 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -455,6 +455,7 @@ extern struct device *tty_port_register_device_attr(struct tty_port *port, const struct attribute_group **attr_grp); extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); +extern void tty_port_destroy(struct tty_port *port); extern void tty_port_put(struct tty_port *port); static inline struct tty_port *tty_port_get(struct tty_port *port) From d0f59141ca40159c9d142c0f62e9aea61f846539 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 15 Nov 2012 09:49:55 +0100 Subject: [PATCH 091/146] TTY: isicom, fix tty buffers memory leak After commit "TTY: move tty buffers to tty_port", the tty buffers are not freed in some drivers. This is because tty_port_destructor is not called whenever a tty_port is freed. This was an assumption I counted with but was unfortunately untrue. So fix the drivers to fulfil this assumption. This one is special as we need more work to be done. Previously, the tty_port was initialized at module load time, but to be able to destroy the port and init it again, we now do the initialization in probe and destroy in remove. I.e. at more appropriate places for that. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/isicom.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index 5f3ecbc2713..67f288d7e76 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -1610,10 +1610,15 @@ static int __devinit isicom_probe(struct pci_dev *pdev, if (retval < 0) goto errunri; - for (index = 0; index < board->port_count; index++) - tty_port_register_device(&board->ports[index].port, - isicom_normal, board->index * 16 + index, - &pdev->dev); + for (index = 0; index < board->port_count; index++) { + struct tty_port *tport = &board->ports[index].port; + tty_port_init(tport); + tport->ops = &isicom_port_ops; + tport->close_delay = 50 * HZ/100; + tport->closing_wait = 3000 * HZ/100; + tty_port_register_device(tport, isicom_normal, + board->index * 16 + index, &pdev->dev); + } return 0; @@ -1635,8 +1640,10 @@ static void __devexit isicom_remove(struct pci_dev *pdev) struct isi_board *board = pci_get_drvdata(pdev); unsigned int i; - for (i = 0; i < board->port_count; i++) + for (i = 0; i < board->port_count; i++) { tty_unregister_device(isicom_normal, board->index * 16 + i); + tty_port_destroy(&board->ports[i].port); + } free_irq(board->irq, board); pci_release_region(pdev, 3); @@ -1655,13 +1662,9 @@ static int __init isicom_init(void) isi_card[idx].ports = port; spin_lock_init(&isi_card[idx].card_lock); for (channel = 0; channel < 16; channel++, port++) { - tty_port_init(&port->port); - port->port.ops = &isicom_port_ops; port->magic = ISICOM_MAGIC; port->card = &isi_card[idx]; port->channel = channel; - port->port.close_delay = 50 * HZ/100; - port->port.closing_wait = 3000 * HZ/100; port->status = 0; /* . . . */ } From 191c5f10275cfbb36802edadbdb10c73537327b4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 15 Nov 2012 09:49:56 +0100 Subject: [PATCH 092/146] TTY: call tty_port_destroy in the rest of drivers After commit "TTY: move tty buffers to tty_port", the tty buffers are not freed in some drivers. This is because tty_port_destructor is not called whenever a tty_port is freed. This was an assumption I counted with but was unfortunately untrue. So fix the drivers to fulfil this assumption. To be sure, the TTY buffers (and later some stuff) are gone along with the tty_port, we have to call tty_port_destroy at tear-down places. This is mostly where the structure containing a tty_port is freed. This patch does exactly that -- put tty_port_destroy at those places. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- arch/alpha/kernel/srmcons.c | 5 ++++- arch/ia64/hp/sim/simserial.c | 1 + arch/m68k/emu/nfcon.c | 6 ++++-- arch/parisc/kernel/pdc_cons.c | 5 +++-- arch/um/drivers/line.c | 2 ++ arch/xtensa/platforms/iss/console.c | 1 + drivers/char/pcmcia/synclink_cs.c | 5 ++++- drivers/char/ttyprintk.c | 4 +++- drivers/isdn/gigaset/common.c | 10 ++++++---- drivers/isdn/i4l/isdn_tty.c | 4 ++++ drivers/misc/pti.c | 7 +++++-- drivers/net/usb/hso.c | 5 +++-- drivers/s390/char/con3215.c | 1 + drivers/s390/char/sclp_tty.c | 4 +++- drivers/s390/char/sclp_vt220.c | 2 ++ drivers/s390/char/tty3270.c | 2 ++ drivers/staging/ccg/u_serial.c | 5 ++++- drivers/staging/dgrp/dgrp_specproc.c | 2 ++ drivers/staging/dgrp/dgrp_tty.c | 4 +++- drivers/staging/ipack/devices/ipoctal.c | 2 ++ drivers/tty/amiserial.c | 2 ++ drivers/tty/bfin_jtag_comm.c | 6 ++++-- drivers/tty/cyclades.c | 8 +++++--- drivers/tty/ehv_bytechan.c | 2 ++ drivers/tty/hvc/hvsi.c | 1 + drivers/tty/ipwireless/tty.c | 1 + drivers/tty/moxa.c | 4 ++++ drivers/tty/mxser.c | 25 +++++++++++++++++-------- drivers/tty/nozomi.c | 13 +++++++++---- drivers/tty/rocket.c | 2 ++ drivers/tty/serial/68328serial.c | 2 ++ drivers/tty/serial/ifx6x60.c | 5 ++++- drivers/tty/serial/kgdb_nmi.c | 2 ++ drivers/tty/serial/serial_core.c | 6 ++++++ drivers/tty/synclink.c | 1 + drivers/tty/synclink_gt.c | 5 ++++- drivers/tty/synclinkmp.c | 5 ++++- drivers/tty/vt/vt.c | 5 ++++- drivers/usb/gadget/u_serial.c | 5 ++++- drivers/usb/serial/usb-serial.c | 1 + net/irda/ircomm/ircomm_tty.c | 1 + 41 files changed, 139 insertions(+), 40 deletions(-) diff --git a/arch/alpha/kernel/srmcons.c b/arch/alpha/kernel/srmcons.c index 5d5865204a1..59b7bbad839 100644 --- a/arch/alpha/kernel/srmcons.c +++ b/arch/alpha/kernel/srmcons.c @@ -205,7 +205,6 @@ static const struct tty_operations srmcons_ops = { static int __init srmcons_init(void) { - tty_port_init(&srmcons_singleton.port); setup_timer(&srmcons_singleton.timer, srmcons_receive_chars, (unsigned long)&srmcons_singleton); if (srm_is_registered_console) { @@ -215,6 +214,9 @@ srmcons_init(void) driver = alloc_tty_driver(MAX_SRM_CONSOLE_DEVICES); if (!driver) return -ENOMEM; + + tty_port_init(&srmcons_singleton.port); + driver->driver_name = "srm"; driver->name = "srm"; driver->major = 0; /* dynamic */ @@ -227,6 +229,7 @@ srmcons_init(void) err = tty_register_driver(driver); if (err) { put_tty_driver(driver); + tty_port_destroy(&srmcons_singleton.port); return err; } srmcons_driver = driver; diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index ec536e4e36c..fc3924d18c1 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -555,6 +555,7 @@ static int __init simrs_init(void) return 0; err_free_tty: put_tty_driver(hp_simserial_driver); + tty_port_destroy(&state->port); return retval; } diff --git a/arch/m68k/emu/nfcon.c b/arch/m68k/emu/nfcon.c index 16d170f53bf..6685bf45c2c 100644 --- a/arch/m68k/emu/nfcon.c +++ b/arch/m68k/emu/nfcon.c @@ -120,8 +120,6 @@ static int __init nfcon_init(void) { int res; - tty_port_init(&nfcon_tty_port); - stderr_id = nf_get_id("NF_STDERR"); if (!stderr_id) return -ENODEV; @@ -130,6 +128,8 @@ static int __init nfcon_init(void) if (!nfcon_tty_driver) return -ENOMEM; + tty_port_init(&nfcon_tty_port); + nfcon_tty_driver->driver_name = "nfcon"; nfcon_tty_driver->name = "nfcon"; nfcon_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; @@ -143,6 +143,7 @@ static int __init nfcon_init(void) if (res) { pr_err("failed to register nfcon tty driver\n"); put_tty_driver(nfcon_tty_driver); + tty_port_destroy(&nfcon_tty_port); return res; } @@ -157,6 +158,7 @@ static void __exit nfcon_exit(void) unregister_console(&nf_console); tty_unregister_driver(nfcon_tty_driver); put_tty_driver(nfcon_tty_driver); + tty_port_destroy(&nfcon_tty_port); } module_init(nfcon_init); diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c index 88238638aee..efc5e7d3053 100644 --- a/arch/parisc/kernel/pdc_cons.c +++ b/arch/parisc/kernel/pdc_cons.c @@ -186,13 +186,13 @@ static int __init pdc_console_tty_driver_init(void) printk(KERN_INFO "The PDC console driver is still registered, removing CON_BOOT flag\n"); pdc_cons.flags &= ~CON_BOOT; - tty_port_init(&tty_port); - pdc_console_tty_driver = alloc_tty_driver(1); if (!pdc_console_tty_driver) return -ENOMEM; + tty_port_init(&tty_port); + pdc_console_tty_driver->driver_name = "pdc_cons"; pdc_console_tty_driver->name = "ttyB"; pdc_console_tty_driver->major = MUX_MAJOR; @@ -207,6 +207,7 @@ static int __init pdc_console_tty_driver_init(void) err = tty_register_driver(pdc_console_tty_driver); if (err) { printk(KERN_ERR "Unable to register the PDC console TTY driver\n"); + tty_port_destroy(&tty_port); return err; } diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index fd9a15b318a..9ffc28bd4b7 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -584,6 +584,8 @@ int register_lines(struct line_driver *line_driver, printk(KERN_ERR "register_lines : can't register %s driver\n", line_driver->name); put_tty_driver(driver); + for (i = 0; i < nlines; i++) + tty_port_destroy(&lines[i].port); return err; } diff --git a/arch/xtensa/platforms/iss/console.c b/arch/xtensa/platforms/iss/console.c index 7e74895eee0..8207a119eee 100644 --- a/arch/xtensa/platforms/iss/console.c +++ b/arch/xtensa/platforms/iss/console.c @@ -221,6 +221,7 @@ static __exit void rs_exit(void) printk("ISS_SERIAL: failed to unregister serial driver (%d)\n", error); put_tty_driver(serial_driver); + tty_port_destroy(&serial_port); } diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 21721d25e38..b66eaa04f8c 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -549,8 +549,10 @@ static int mgslpc_probe(struct pcmcia_device *link) /* Initialize the struct pcmcia_device structure */ ret = mgslpc_config(link); - if (ret) + if (ret) { + tty_port_destroy(&info->port); return ret; + } mgslpc_add_device(info); @@ -2757,6 +2759,7 @@ static void mgslpc_remove_device(MGSLPC_INFO *remove_info) hdlcdev_exit(info); #endif release_resources(info); + tty_port_destroy(&info->port); kfree(info); mgslpc_device_count--; return; diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index af98f6d6509..4945bd3d18d 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -179,7 +179,6 @@ static int __init ttyprintk_init(void) { int ret = -ENOMEM; - tty_port_init(&tpk_port.port); tpk_port.port.ops = &null_ops; mutex_init(&tpk_port.port_write_mutex); @@ -190,6 +189,8 @@ static int __init ttyprintk_init(void) if (IS_ERR(ttyprintk_driver)) return PTR_ERR(ttyprintk_driver); + tty_port_init(&tpk_port.port); + ttyprintk_driver->driver_name = "ttyprintk"; ttyprintk_driver->name = "ttyprintk"; ttyprintk_driver->major = TTYAUX_MAJOR; @@ -211,6 +212,7 @@ static int __init ttyprintk_init(void) error: tty_unregister_driver(ttyprintk_driver); put_tty_driver(ttyprintk_driver); + tty_port_destroy(&tpk_port.port); ttyprintk_driver = NULL; return ret; } diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index 30a6b174fbb..bc9d89a8c4f 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -518,6 +518,7 @@ f_bcs: gig_dbg(DEBUG_INIT, "freeing bcs[]"); kfree(cs->bcs); f_cs: gig_dbg(DEBUG_INIT, "freeing cs"); mutex_unlock(&cs->mutex); + tty_port_destroy(&cs->port); free_cs(cs); } EXPORT_SYMBOL_GPL(gigaset_freecs); @@ -751,14 +752,14 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, gig_dbg(DEBUG_INIT, "setting up iif"); if (gigaset_isdn_regdev(cs, modulename) < 0) { pr_err("error registering ISDN device\n"); - goto error; + goto error_port; } make_valid(cs, VALID_ID); ++cs->cs_init; gig_dbg(DEBUG_INIT, "setting up hw"); if (cs->ops->initcshw(cs) < 0) - goto error; + goto error_port; ++cs->cs_init; @@ -773,7 +774,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, gig_dbg(DEBUG_INIT, "setting up bcs[%d]", i); if (gigaset_initbcs(cs->bcs + i, cs, i) < 0) { pr_err("could not allocate channel %d data\n", i); - goto error; + goto error_port; } } @@ -786,7 +787,8 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, gig_dbg(DEBUG_INIT, "cs initialized"); return cs; - +error_port: + tty_port_destroy(&cs->port); error: gig_dbg(DEBUG_INIT, "failed"); gigaset_freecs(cs); diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index b817809f763..e09dc8a5e74 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1849,6 +1849,8 @@ err_unregister: kfree(info->fax); #endif kfree(info->port.xmit_buf - 4); + info->port.xmit_buf = NULL; + tty_port_destroy(&info->port); } tty_unregister_driver(m->tty_modem); err: @@ -1870,6 +1872,8 @@ isdn_tty_exit(void) kfree(info->fax); #endif kfree(info->port.xmit_buf - 4); + info->port.xmit_buf = NULL; + tty_port_destroy(&info->port); } tty_unregister_driver(dev->mdm.tty_modem); put_tty_driver(dev->mdm.tty_modem); diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c index 4999b34b7a6..a1f0d174e68 100644 --- a/drivers/misc/pti.c +++ b/drivers/misc/pti.c @@ -882,11 +882,14 @@ err: static void __devexit pti_pci_remove(struct pci_dev *pdev) { struct pti_dev *drv_data = pci_get_drvdata(pdev); + unsigned int a; unregister_console(&pti_console); - tty_unregister_device(pti_tty_driver, 0); - tty_unregister_device(pti_tty_driver, 1); + for (a = 0; a < PTITTY_MINOR_NUM; a++) { + tty_unregister_device(pti_tty_driver, a); + tty_port_destroy(&drv_data->port[a]); + } iounmap(drv_data->pti_ioaddr); pci_set_drvdata(pdev, NULL); diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 605a4baa9b7..cd8ccb240f4 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2274,6 +2274,7 @@ static void hso_serial_common_free(struct hso_serial *serial) /* unlink and free TX URB */ usb_free_urb(serial->tx_urb); kfree(serial->tx_data); + tty_port_destroy(&serial->port); } static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, @@ -2283,12 +2284,12 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, int minor; int i; + tty_port_init(&serial->port); + minor = get_free_serial_index(); if (minor < 0) goto exit; - tty_port_init(&serial->port); - /* register our minor number */ serial->parent->dev = tty_port_register_device(&serial->port, tty_drv, minor, &serial->parent->interface->dev); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 9ffb6d5f17a..8fb014f32e4 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -677,6 +677,7 @@ static void raw3215_free_info(struct raw3215_info *raw) { kfree(raw->inbuf); kfree(raw->buffer); + tty_port_destroy(&raw->port); kfree(raw); } diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 30ec09e3d03..877fbc37c1e 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -547,7 +547,6 @@ sclp_tty_init(void) sclp_tty_tolower = 1; } sclp_tty_chars_count = 0; - tty_port_init(&sclp_port); rc = sclp_register(&sclp_input_event); if (rc) { @@ -555,6 +554,8 @@ sclp_tty_init(void) return rc; } + tty_port_init(&sclp_port); + driver->driver_name = "sclp_line"; driver->name = "sclp_line"; driver->major = TTY_MAJOR; @@ -571,6 +572,7 @@ sclp_tty_init(void) rc = tty_register_driver(driver); if (rc) { put_tty_driver(driver); + tty_port_destroy(&sclp_port); return rc; } sclp_tty_driver = driver; diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 7e60f3d2f3f..effcc8756e0 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -615,6 +615,7 @@ static void __init __sclp_vt220_cleanup(void) return; sclp_unregister(&sclp_vt220_register); __sclp_vt220_free_pages(); + tty_port_destroy(&sclp_vt220_port); } /* Allocate buffer pages and register with sclp core. Controlled by init @@ -650,6 +651,7 @@ out: if (rc) { __sclp_vt220_free_pages(); sclp_vt220_init_count--; + tty_port_destroy(&sclp_vt220_port); } return rc; } diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 482ee028f84..43ea0593bdb 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -722,6 +722,7 @@ out_pages: while (pages--) free_pages((unsigned long) tp->freemem_pages[pages], 0); kfree(tp->freemem_pages); + tty_port_destroy(&tp->port); out_tp: kfree(tp); out_err: @@ -744,6 +745,7 @@ tty3270_free_view(struct tty3270 *tp) for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) free_pages((unsigned long) tp->freemem_pages[pages], 0); kfree(tp->freemem_pages); + tty_port_destroy(&tp->port); kfree(tp); } diff --git a/drivers/staging/ccg/u_serial.c b/drivers/staging/ccg/u_serial.c index 5b3f5fffea9..373c40656b5 100644 --- a/drivers/staging/ccg/u_serial.c +++ b/drivers/staging/ccg/u_serial.c @@ -1140,8 +1140,10 @@ int gserial_setup(struct usb_gadget *g, unsigned count) return status; fail: - while (count--) + while (count--) { + tty_port_destroy(&ports[count].port->port); kfree(ports[count].port); + } put_tty_driver(gs_tty_driver); gs_tty_driver = NULL; return status; @@ -1195,6 +1197,7 @@ void gserial_cleanup(void) WARN_ON(port->port_usb != NULL); + tty_port_destroy(&port->port); kfree(port); } n_ports = 0; diff --git a/drivers/staging/dgrp/dgrp_specproc.c b/drivers/staging/dgrp/dgrp_specproc.c index db91f676508..c214078a89e 100644 --- a/drivers/staging/dgrp/dgrp_specproc.c +++ b/drivers/staging/dgrp/dgrp_specproc.c @@ -752,6 +752,8 @@ static int dgrp_add_id(long id) return 0; + /* FIXME this guy should free the tty driver stored in nd and destroy + * all channel ports */ error_out: kfree(nd); return ret; diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c index e125b03598d..0db4c0514f6 100644 --- a/drivers/staging/dgrp/dgrp_tty.c +++ b/drivers/staging/dgrp/dgrp_tty.c @@ -3119,6 +3119,7 @@ static void dgrp_tty_hangup(struct tty_struct *tty) void dgrp_tty_uninit(struct nd_struct *nd) { + unsigned int i; char id[3]; ID_TO_CHAR(nd->nd_ID, id); @@ -3152,6 +3153,8 @@ dgrp_tty_uninit(struct nd_struct *nd) put_tty_driver(nd->nd_xprint_ttdriver); nd->nd_ttdriver_flags &= ~XPRINT_TTDRV_REG; } + for (i = 0; i < CHAN_MAX; i++) + tty_port_destroy(&nd->nd_chan[i].port); } @@ -3335,7 +3338,6 @@ dgrp_tty_init(struct nd_struct *nd) init_waitqueue_head(&(ch->ch_pun.un_open_wait)); init_waitqueue_head(&(ch->ch_pun.un_close_wait)); tty_port_init(&ch->port); - tty_port_init(&ch->port); } return 0; } diff --git a/drivers/staging/ipack/devices/ipoctal.c b/drivers/staging/ipack/devices/ipoctal.c index d751edfda83..729cb642840 100644 --- a/drivers/staging/ipack/devices/ipoctal.c +++ b/drivers/staging/ipack/devices/ipoctal.c @@ -446,6 +446,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, tty_dev = tty_port_register_device(&channel->tty_port, tty, i, NULL); if (IS_ERR(tty_dev)) { dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n"); + tty_port_destroy(&channel->tty_port); continue; } dev_set_drvdata(tty_dev, channel); @@ -741,6 +742,7 @@ static void __ipoctal_remove(struct ipoctal *ipoctal) struct ipoctal_channel *channel = &ipoctal->channel[i]; tty_unregister_device(ipoctal->tty_drv, i); tty_port_free_xmit_buf(&channel->tty_port); + tty_port_destroy(&channel->tty_port); } tty_unregister_driver(ipoctal->tty_drv); diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 42d0a2581a8..9d7d00cdfec 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1771,6 +1771,7 @@ fail_free_irq: fail_unregister: tty_unregister_driver(serial_driver); fail_put_tty_driver: + tty_port_destroy(&state->tport); put_tty_driver(serial_driver); return error; } @@ -1785,6 +1786,7 @@ static int __exit amiga_serial_remove(struct platform_device *pdev) printk("SERIAL: failed to unregister serial driver (%d)\n", error); put_tty_driver(serial_driver); + tty_port_destroy(&state->tport); free_irq(IRQ_AMIGA_TBE, state); free_irq(IRQ_AMIGA_RBF, state); diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c index 02b7d3a0969..1cfcdbf1d0c 100644 --- a/drivers/tty/bfin_jtag_comm.c +++ b/drivers/tty/bfin_jtag_comm.c @@ -240,8 +240,6 @@ static int __init bfin_jc_init(void) { int ret; - tty_port_init(&port); - bfin_jc_kthread = kthread_create(bfin_jc_emudat_manager, NULL, DRV_NAME); if (IS_ERR(bfin_jc_kthread)) return PTR_ERR(bfin_jc_kthread); @@ -257,6 +255,8 @@ static int __init bfin_jc_init(void) if (!bfin_jc_driver) goto err_driver; + tty_port_init(&port); + bfin_jc_driver->driver_name = DRV_NAME; bfin_jc_driver->name = DEV_NAME; bfin_jc_driver->type = TTY_DRIVER_TYPE_SERIAL; @@ -274,6 +274,7 @@ static int __init bfin_jc_init(void) return 0; err: + tty_port_destroy(&port); put_tty_driver(bfin_jc_driver); err_driver: kfree(bfin_jc_write_buf.buf); @@ -289,6 +290,7 @@ static void __exit bfin_jc_exit(void) kfree(bfin_jc_write_buf.buf); tty_unregister_driver(bfin_jc_driver); put_tty_driver(bfin_jc_driver); + tty_port_destroy(&port); } module_exit(bfin_jc_exit); diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 0a6a0bc1b59..de25774b5ec 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -3934,7 +3934,7 @@ err: static void __devexit cy_pci_remove(struct pci_dev *pdev) { struct cyclades_card *cinfo = pci_get_drvdata(pdev); - unsigned int i; + unsigned int i, channel; /* non-Z with old PLX */ if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) == @@ -3960,9 +3960,11 @@ static void __devexit cy_pci_remove(struct pci_dev *pdev) pci_release_regions(pdev); cinfo->base_addr = NULL; - for (i = cinfo->first_line; i < cinfo->first_line + - cinfo->nports; i++) + for (channel = 0, i = cinfo->first_line; i < cinfo->first_line + + cinfo->nports; i++, channel++) { tty_unregister_device(cy_serial_driver, i); + tty_port_destroy(&cinfo->ports[channel].port); + } cinfo->nports = 0; kfree(cinfo->ports); } diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 4ab936b7aac..4193afb74a0 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -757,6 +757,7 @@ static int __devinit ehv_bc_tty_probe(struct platform_device *pdev) return 0; error: + tty_port_destroy(&bc->port); irq_dispose_mapping(bc->tx_irq); irq_dispose_mapping(bc->rx_irq); @@ -770,6 +771,7 @@ static int ehv_bc_tty_remove(struct platform_device *pdev) tty_unregister_device(ehv_bc_driver, bc - bcs); + tty_port_destroy(&bc->port); irq_dispose_mapping(bc->tx_irq); irq_dispose_mapping(bc->rx_irq); diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 5b95b4f28cf..68357a6e4de 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -1218,6 +1218,7 @@ static int __init hvsi_console_init(void) if (hp->virq == 0) { printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", __func__, irq[0]); + tty_port_destroy(&hp->port); continue; } diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c index 160f0ad9589..2cde13ddf9f 100644 --- a/drivers/tty/ipwireless/tty.c +++ b/drivers/tty/ipwireless/tty.c @@ -566,6 +566,7 @@ void ipwireless_tty_free(struct ipw_tty *tty) ipwireless_disassociate_network_ttys(network, ttyj->channel_idx); tty_unregister_device(ipw_tty_driver, j); + tty_port_destroy(&ttyj->port); ttys[j] = NULL; mutex_unlock(&ttyj->ipw_tty_mutex); kfree(ttyj); diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 9b57aae139f..d628176fb6d 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -895,6 +895,8 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) return 0; err_free: + for (i = 0; i < MAX_PORTS_PER_BOARD; i++) + tty_port_destroy(&brd->ports[i].port); kfree(brd->ports); err: return ret; @@ -919,6 +921,8 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) tty_kref_put(tty); } } + for (a = 0; a < MAX_PORTS_PER_BOARD; a++) + tty_port_destroy(&brd->ports[a].port); while (1) { opened = 0; for (a = 0; a < brd->numPorts; a++) diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index cfda47dabd2..802a248e7ab 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -2411,14 +2411,27 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, retval = request_irq(brd->irq, mxser_interrupt, IRQF_SHARED, "mxser", brd); - if (retval) + if (retval) { + for (i = 0; i < brd->info->nports; i++) + tty_port_destroy(&brd->ports[i].port); printk(KERN_ERR "Board %s: Request irq failed, IRQ (%d) may " "conflict with another device.\n", brd->info->name, brd->irq); + } return retval; } +static void mxser_board_remove(struct mxser_board *brd) +{ + unsigned int i; + + for (i = 0; i < brd->info->nports; i++) { + tty_unregister_device(mxvar_sdriver, brd->idx + i); + tty_port_destroy(&brd->ports[i].port); + } +} + static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) { int id, i, bits, ret; @@ -2649,10 +2662,8 @@ static void __devexit mxser_remove(struct pci_dev *pdev) { #ifdef CONFIG_PCI struct mxser_board *brd = pci_get_drvdata(pdev); - unsigned int i; - for (i = 0; i < brd->info->nports; i++) - tty_unregister_device(mxvar_sdriver, brd->idx + i); + mxser_board_remove(brd); free_irq(pdev->irq, brd); pci_release_region(pdev, 2); @@ -2748,15 +2759,13 @@ err_put: static void __exit mxser_module_exit(void) { - unsigned int i, j; + unsigned int i; pci_unregister_driver(&mxser_driver); for (i = 0; i < MXSER_BOARDS; i++) /* ISA remains */ if (mxser_boards[i].info != NULL) - for (j = 0; j < mxser_boards[i].info->nports; j++) - tty_unregister_device(mxvar_sdriver, - mxser_boards[i].idx + j); + mxser_board_remove(&mxser_boards[i]); tty_unregister_driver(mxvar_sdriver); put_tty_driver(mxvar_sdriver); diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index b917c942495..cb764d29775 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -1479,6 +1479,7 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev, if (IS_ERR(tty_dev)) { ret = PTR_ERR(tty_dev); dev_err(&pdev->dev, "Could not allocate tty?\n"); + tty_port_destroy(&port->port); goto err_free_tty; } } @@ -1486,8 +1487,10 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev, return 0; err_free_tty: - for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i) - tty_unregister_device(ntty_driver, i); + for (i = 0; i < MAX_PORT; ++i) { + tty_unregister_device(ntty_driver, dc->index_start + i); + tty_port_destroy(&dc->port[i].port); + } err_free_kfifo: for (i = 0; i < MAX_PORT; i++) kfifo_free(&dc->port[i].fifo_ul); @@ -1520,8 +1523,10 @@ static void __devexit tty_exit(struct nozomi *dc) complete off a hangup method ? */ while (dc->open_ttys) msleep(1); - for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i) - tty_unregister_device(ntty_driver, i); + for (i = 0; i < MAX_PORT; ++i) { + tty_unregister_device(ntty_driver, dc->index_start + i); + tty_port_destroy(&dc->port[i].port); + } } /* Deallocate memory for one device */ diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 9700d34b20a..d9056dac4ea 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -673,6 +673,7 @@ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) { printk(KERN_ERR "RocketPort sInitChan(%d, %d, %d) failed!\n", board, aiop, chan); + tty_port_destroy(&info->port); kfree(info); return; } @@ -2357,6 +2358,7 @@ static void rp_cleanup_module(void) for (i = 0; i < MAX_RP_PORTS; i++) if (rp_table[i]) { tty_unregister_device(rocket_driver, i); + tty_port_destroy(&rp_table[i]->port); kfree(rp_table[i]); } diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index 66c38a3f74c..f99a84526f8 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -1225,6 +1225,8 @@ rs68328_init(void) if (tty_register_driver(serial_driver)) { put_tty_driver(serial_driver); + for (i = 0; i < NR_PORTS; i++) + tty_port_destroy(&m68k_soft[i].tport); printk(KERN_ERR "Couldn't register serial driver\n"); return -ENOMEM; } diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 1754c147a94..91125bb151c 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -829,6 +829,7 @@ static void ifx_spi_free_port(struct ifx_spi_device *ifx_dev) { if (ifx_dev->tty_dev) tty_unregister_device(tty_drv, ifx_dev->minor); + tty_port_destroy(&ifx_dev->tty_port); kfifo_free(&ifx_dev->tx_fifo); } @@ -862,10 +863,12 @@ static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev) dev_dbg(&ifx_dev->spi_dev->dev, "%s: registering tty device failed", __func__); ret = PTR_ERR(ifx_dev->tty_dev); - goto error_ret; + goto error_port; } return 0; +error_port: + tty_port_destroy(pport); error_ret: ifx_spi_free_port(ifx_dev); return ret; diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index d185247ba1a..6ac2b797a76 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -266,6 +266,7 @@ static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty) } return 0; err: + tty_port_destroy(&priv->port); kfree(priv); return ret; } @@ -275,6 +276,7 @@ static void kgdb_nmi_tty_cleanup(struct tty_struct *tty) struct kgdb_nmi_tty_priv *priv = tty->driver_data; tty->driver_data = NULL; + tty_port_destroy(&priv->port); kfree(priv); } diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 63b33889d51..61ba24089ef 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2297,6 +2297,8 @@ int uart_register_driver(struct uart_driver *drv) if (retval >= 0) return retval; + for (i = 0; i < drv->nr; i++) + tty_port_destroy(&drv->state[i].port); put_tty_driver(normal); out_kfree: kfree(drv->state); @@ -2316,8 +2318,12 @@ out: void uart_unregister_driver(struct uart_driver *drv) { struct tty_driver *p = drv->tty_driver; + unsigned int i; + tty_unregister_driver(p); put_tty_driver(p); + for (i = 0; i < drv->nr; i++) + tty_port_destroy(&drv->state[i].port); kfree(drv->state); drv->state = NULL; drv->tty_driver = NULL; diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 70e3a525bc8..e4b5c39fc4e 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -4425,6 +4425,7 @@ static void synclink_cleanup(void) mgsl_release_resources(info); tmp = info; info = info->next_device; + tty_port_destroy(&tmp->port); kfree(tmp); } diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index b38e954eedd..6e4c34011b7 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3645,8 +3645,10 @@ static void device_init(int adapter_num, struct pci_dev *pdev) for (i=0; i < port_count; ++i) { port_array[i] = alloc_dev(adapter_num, i, pdev); if (port_array[i] == NULL) { - for (--i; i >= 0; --i) + for (--i; i >= 0; --i) { + tty_port_destroy(&port_array[i]->port); kfree(port_array[i]); + } return; } } @@ -3773,6 +3775,7 @@ static void slgt_cleanup(void) release_resources(info); tmp = info; info = info->next_device; + tty_port_destroy(&tmp->port); kfree(tmp); } diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index f17d9f3d84a..40745beb258 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3843,8 +3843,10 @@ static void device_init(int adapter_num, struct pci_dev *pdev) for ( port = 0; port < SCA_MAX_PORTS; ++port ) { port_array[port] = alloc_dev(adapter_num,port,pdev); if( port_array[port] == NULL ) { - for ( --port; port >= 0; --port ) + for (--port; port >= 0; --port) { + tty_port_destroy(&port_array[port]->port); kfree(port_array[port]); + } return; } } @@ -3953,6 +3955,7 @@ static void synclinkmp_cleanup(void) } tmp = info; info = info->next_device; + tty_port_destroy(&tmp->port); kfree(tmp); } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index f87d7e8964b..607636b4734 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -779,6 +779,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ con_set_default_unimap(vc); vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); if (!vc->vc_screenbuf) { + tty_port_destroy(&vc->port); kfree(vc); vc_cons[currcons].d = NULL; return -ENOMEM; @@ -999,8 +1000,10 @@ void vc_deallocate(unsigned int currcons) put_pid(vc->vt_pid); module_put(vc->vc_sw->owner); kfree(vc->vc_screenbuf); - if (currcons >= MIN_NR_CONSOLES) + if (currcons >= MIN_NR_CONSOLES) { + tty_port_destroy(&vc->port); kfree(vc); + } vc_cons[currcons].d = NULL; } } diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index f1739526820..d0f95482f40 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -1145,8 +1145,10 @@ int gserial_setup(struct usb_gadget *g, unsigned count) return status; fail: - while (count--) + while (count--) { + tty_port_destroy(&ports[count].port->port); kfree(ports[count].port); + } put_tty_driver(gs_tty_driver); gs_tty_driver = NULL; return status; @@ -1200,6 +1202,7 @@ void gserial_cleanup(void) WARN_ON(port->port_usb != NULL); + tty_port_destroy(&port->port); kfree(port); } n_ports = 0; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 73b8e056916..64bda135ba7 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -597,6 +597,7 @@ static void port_release(struct device *dev) kfifo_free(&port->write_fifo); kfree(port->interrupt_in_buffer); kfree(port->interrupt_out_buffer); + tty_port_destroy(&port->port); kfree(port); } diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 496ce2cebcd..a68c88cdec6 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -183,6 +183,7 @@ static void __exit __ircomm_tty_cleanup(struct ircomm_tty_cb *self) ircomm_tty_shutdown(self); self->magic = 0; + tty_port_destroy(&self->port); kfree(self); } From cee31c52df6263555c11cf51ee6ea30637e0cfd1 Mon Sep 17 00:00:00 2001 From: Shinya Kuribayashi Date: Fri, 16 Nov 2012 10:50:03 +0900 Subject: [PATCH 093/146] Revert "sh-sci / PM: Avoid deadlocking runtime PM" This reverts commit 048be431e4 (sh-sci / PM: Avoid deadlocking runtime PM, from Rafael J. Wysocki , 2012-03-09). In order to get console PM work properly, we should implement uart_ops ->pm() operation, rather than sprinkle band-ading runtime PM calls in the driver. Signed-off-by: Shinya Kuribayashi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 6ee59001d61..3d27f4978dd 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1753,8 +1753,6 @@ static int sci_startup(struct uart_port *port) dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); - pm_runtime_put_noidle(port->dev); - sci_port_enable(s); ret = sci_request_irq(s); @@ -1782,8 +1780,6 @@ static void sci_shutdown(struct uart_port *port) sci_free_irq(s); sci_port_disable(s); - - pm_runtime_get_noresume(port->dev); } static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps, @@ -2122,7 +2118,6 @@ static int __devinit sci_init_single(struct platform_device *dev, sci_init_gpios(sci_port); pm_runtime_irq_safe(&dev->dev); - pm_runtime_get_noresume(&dev->dev); pm_runtime_enable(&dev->dev); } From 8807ec6c707802cabadc0fe1b035ffefa27f1719 Mon Sep 17 00:00:00 2001 From: Shinya Kuribayashi Date: Fri, 16 Nov 2012 10:51:01 +0900 Subject: [PATCH 094/146] Revert "sh-sci / PM: Use power.irq_safe" This reverts commit 5a50a01bf0 (sh-sci / PM: Use power.irq_safe, from Rafael J. Wysocki , 2011-08-24). In order to get console PM work properly, we should implement uart_ops ->pm() operation, rather than sprinkle band-ading runtime PM calls in the driver. Signed-off-by: Shinya Kuribayashi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 3d27f4978dd..fcac1360c78 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2117,7 +2117,6 @@ static int __devinit sci_init_single(struct platform_device *dev, sci_init_gpios(sci_port); - pm_runtime_irq_safe(&dev->dev); pm_runtime_enable(&dev->dev); } From 00cadbfd1e73fb9951da7d2358c39b561c017ea3 Mon Sep 17 00:00:00 2001 From: Shinya Kuribayashi Date: Fri, 16 Nov 2012 10:51:24 +0900 Subject: [PATCH 095/146] Partially revert "serial: sh-sci: console Runtime PM support" This partially reverts commit 1ba7622094 (serial: sh-sci: console Runtime PM support, from Magnus Damm , 2011-08-03). The generic 'serial_core' can take care of console PM maintenance, so all (or at least the first thing) we have to do to get console PM work properly, is to implement uart_ops ->pm() operation in the sh-sci serial client driver. This patch partially reverts the commit above, but leaving sci_reset() change in place, because sci_reset() is already part of another commit (73c3d53f38 serial: sh-sci: Avoid FIFO clear for MCE toggle.). A revised version of console PM support follows next. Signed-off-by: Shinya Kuribayashi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 53 +------------------------------------ 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index fcac1360c78..602d0781c6c 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -99,12 +99,6 @@ struct sci_port { #endif struct notifier_block freq_transition; - -#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE - unsigned short saved_smr; - unsigned short saved_fcr; - unsigned char saved_brr; -#endif }; /* Function prototypes */ @@ -2248,8 +2242,7 @@ static int __devinit serial_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - sci_port_disable(sci_port); - + /* TODO: disable clock */ return uart_set_options(port, co, baud, parity, bits, flow); } @@ -2292,46 +2285,6 @@ static int __devinit sci_probe_earlyprintk(struct platform_device *pdev) return 0; } -#define uart_console(port) ((port)->cons->index == (port)->line) - -static int sci_runtime_suspend(struct device *dev) -{ - struct sci_port *sci_port = dev_get_drvdata(dev); - struct uart_port *port = &sci_port->port; - - if (uart_console(port)) { - struct plat_sci_reg *reg; - - sci_port->saved_smr = serial_port_in(port, SCSMR); - sci_port->saved_brr = serial_port_in(port, SCBRR); - - reg = sci_getreg(port, SCFCR); - if (reg->size) - sci_port->saved_fcr = serial_port_in(port, SCFCR); - else - sci_port->saved_fcr = 0; - } - return 0; -} - -static int sci_runtime_resume(struct device *dev) -{ - struct sci_port *sci_port = dev_get_drvdata(dev); - struct uart_port *port = &sci_port->port; - - if (uart_console(port)) { - sci_reset(port); - serial_port_out(port, SCSMR, sci_port->saved_smr); - serial_port_out(port, SCBRR, sci_port->saved_brr); - - if (sci_port->saved_fcr) - serial_port_out(port, SCFCR, sci_port->saved_fcr); - - serial_port_out(port, SCSCR, sci_port->cfg->scscr); - } - return 0; -} - #define SCI_CONSOLE (&serial_console) #else @@ -2341,8 +2294,6 @@ static inline int __devinit sci_probe_earlyprintk(struct platform_device *pdev) } #define SCI_CONSOLE NULL -#define sci_runtime_suspend NULL -#define sci_runtime_resume NULL #endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ @@ -2460,8 +2411,6 @@ static int sci_resume(struct device *dev) } static const struct dev_pm_ops sci_dev_pm_ops = { - .runtime_suspend = sci_runtime_suspend, - .runtime_resume = sci_runtime_resume, .suspend = sci_suspend, .resume = sci_resume, }; From 0174e5ca82ba6bd62ab870e5781b72bd5397f1c3 Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Fri, 16 Nov 2012 10:51:55 +0900 Subject: [PATCH 096/146] serial: sh-sci: console runtime PM support (revisit) The commit 1ba7622094 (serial: sh-sci: console Runtime PM support, from Magnus Damm , 2011-08-03), tried to support console runtime PM, but unfortunately it didn't work for us for some reason. We did not investigated further at that time, instead would like to propose a different approach. In Linux tty/serial world, to get console PM work properly, a serial client driver does not have to maintain .runtime_suspend()/..resume() calls itself, but can leave console power power management handling to the serial core driver. This patch moves the sh-sci driver in that direction. Notes: * There is room to optimize console runtime PM more aggressively by maintaining additional local runtime PM calls, but as a first step having .pm() operation would suffice. * We still have a couple of direct calls to sci_port_enable/..disable left in the driver. We have to live with them, because they're out of serial core's help. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 602d0781c6c..8aade611d1a 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1747,8 +1747,6 @@ static int sci_startup(struct uart_port *port) dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); - sci_port_enable(s); - ret = sci_request_irq(s); if (unlikely(ret < 0)) return ret; @@ -1772,8 +1770,6 @@ static void sci_shutdown(struct uart_port *port) sci_free_dma(port); sci_free_irq(s); - - sci_port_disable(s); } static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps, @@ -1922,6 +1918,21 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_port_disable(s); } +static void sci_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct sci_port *sci_port = to_sci_port(port); + + switch (state) { + case 3: + sci_port_disable(sci_port); + break; + default: + sci_port_enable(sci_port); + break; + } +} + static const char *sci_type(struct uart_port *port) { switch (port->type) { @@ -2043,6 +2054,7 @@ static struct uart_ops sci_uart_ops = { .startup = sci_startup, .shutdown = sci_shutdown, .set_termios = sci_set_termios, + .pm = sci_pm, .type = sci_type, .release_port = sci_release_port, .request_port = sci_request_port, @@ -2196,16 +2208,12 @@ static void serial_console_write(struct console *co, const char *s, struct uart_port *port = &sci_port->port; unsigned short bits; - sci_port_enable(sci_port); - uart_console_write(port, s, count, serial_console_putchar); /* wait until fifo is empty and last bit has been transmitted */ bits = SCxSR_TDxE(port) | SCxSR_TEND(port); while ((serial_port_in(port, SCxSR) & bits) != bits) cpu_relax(); - - sci_port_disable(sci_port); } static int __devinit serial_console_setup(struct console *co, char *options) @@ -2237,12 +2245,9 @@ static int __devinit serial_console_setup(struct console *co, char *options) if (unlikely(ret != 0)) return ret; - sci_port_enable(sci_port); - if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - /* TODO: disable clock */ return uart_set_options(port, co, baud, parity, bits, flow); } From 4ffc3cdb642823ebee84538addac7cde1174e314 Mon Sep 17 00:00:00 2001 From: Takashi Yoshii Date: Fri, 16 Nov 2012 10:52:22 +0900 Subject: [PATCH 097/146] serial: sh-sci: fix condition test to set SCBRR SCBRR == 0 is valid value (divide by 1). Signed-off-by: Takashi Yoshii Signed-off-by: Shinya Kuribayashi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 8aade611d1a..6c1fddb0e20 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1854,7 +1854,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t, s->cfg->scscr); - if (t > 0) { + if (t >= 0) { if (t >= 256) { serial_port_out(port, SCSMR, (serial_port_in(port, SCSMR) & ~3) | 1); t >>= 2; From 9d482cc353bd0391730730b26e4c2938dc90e477 Mon Sep 17 00:00:00 2001 From: Takashi Yoshii Date: Fri, 16 Nov 2012 10:52:49 +0900 Subject: [PATCH 098/146] serial: sh-sci: support lower baud rate Support prescaler 1/16 and 1/64, in addition to current 1 and 1/4. Supporting below 2400bps was dropped long time ago in mainline. Since then, setting lower rate has been resulting in erroneous register value, without indicating any errors through API. This patch adds more prescaler to support lower rates again. This still doesn't check range, but we won't hit the case because even 50bps at 48MHz clock is now supported. Signed-off-by: Takashi Yoshii Signed-off-by: Shinya Kuribayashi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 6c1fddb0e20..a54c47d0fd6 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1815,7 +1815,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, { struct sci_port *s = to_sci_port(port); struct plat_sci_reg *reg; - unsigned int baud, smr_val, max_baud; + unsigned int baud, smr_val, max_baud, cks; int t = -1; /* @@ -1849,21 +1849,18 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, termios->c_cflag, baud); - serial_port_out(port, SCSMR, smr_val); + for (cks = 0; t >= 256 && cks <= 3; cks++) + t >>= 2; - dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t, - s->cfg->scscr); + dev_dbg(port->dev, "%s: SMR %x, cks %x, t %x, SCSCR %x\n", + __func__, smr_val, cks, t, s->cfg->scscr); if (t >= 0) { - if (t >= 256) { - serial_port_out(port, SCSMR, (serial_port_in(port, SCSMR) & ~3) | 1); - t >>= 2; - } else - serial_port_out(port, SCSMR, serial_port_in(port, SCSMR) & ~3); - + serial_port_out(port, SCSMR, (smr_val & ~3) | cks); serial_port_out(port, SCBRR, t); udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ - } + } else + serial_port_out(port, SCSMR, smr_val); sci_init_pins(port, termios->c_cflag); From 63f7ad115ef35b711f3ae2b46a07acbf1ca3bdfd Mon Sep 17 00:00:00 2001 From: Takashi Yoshii Date: Fri, 16 Nov 2012 10:53:11 +0900 Subject: [PATCH 099/146] serial: sh-sci: mask SCTFDR/RFDR according to fifosize Current mask 0xff to SCTFDR/RFDR damages SCIFB, because the registers on SCIFB have 9-bit data (0 to 256). This patch changes the mask according to port->fifosize. Though I'm not sure if the mask is really needed (I don't know if there are variants which have non-zero upper bits), it is safer. Signed-off-by: Takashi Yoshii Signed-off-by: Shinya Kuribayashi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index a54c47d0fd6..c2d359cba55 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -485,7 +485,7 @@ static int sci_txfill(struct uart_port *port) reg = sci_getreg(port, SCTFDR); if (reg->size) - return serial_port_in(port, SCTFDR) & 0xff; + return serial_port_in(port, SCTFDR) & ((port->fifosize << 1) - 1); reg = sci_getreg(port, SCFDR); if (reg->size) @@ -505,7 +505,7 @@ static int sci_rxfill(struct uart_port *port) reg = sci_getreg(port, SCRFDR); if (reg->size) - return serial_port_in(port, SCRFDR) & 0xff; + return serial_port_in(port, SCRFDR) & ((port->fifosize << 1) - 1); reg = sci_getreg(port, SCFDR); if (reg->size) From 8c66d6d2a1a572768616ddca2c3863384b14d846 Mon Sep 17 00:00:00 2001 From: Takashi Yoshii Date: Fri, 16 Nov 2012 10:53:31 +0900 Subject: [PATCH 100/146] serial: sh-sci: fix common SCIFB regmap definition About FIFO count, there are two variants of SCIFs which show a) TX count in upper, RX count in lower byte of FDR register b) TX count in TFDR register, RX count in RFDR register Common SCIFB regmap in current source code is defined as "a". At least 7372 and 73a0 HW manual say their SICFB are "b". This patch alters the definition to "b", considering the current one has come from a mistake. The reason is as follows. The flag SCIFB sh-sci driver means it has 256 byte FIFO. The count is from 0(empty) to 256(full), that makes 9-bit. Because FDR is 16-bit register, it can not hold two 9-bits. That's why, SCIFB can not be "a". Signed-off-by: Takashi Yoshii Signed-off-by: Shinya Kuribayashi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index c2d359cba55..107801b1279 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -196,9 +196,9 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCxSR] = { 0x14, 16 }, [SCxRDR] = { 0x60, 8 }, [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCTFDR] = sci_reg_invalid, - [SCRFDR] = sci_reg_invalid, + [SCFDR] = sci_reg_invalid, + [SCTFDR] = { 0x38, 16 }, + [SCRFDR] = { 0x3c, 16 }, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, }, From 40f70c03e33a1eed3f3fcd13418e76abad77d117 Mon Sep 17 00:00:00 2001 From: Shinya Kuribayashi Date: Fri, 16 Nov 2012 10:54:15 +0900 Subject: [PATCH 101/146] serial: sh-sci: add locking to console write function to avoid SMP lockup Symptom: When entering the suspend with Android logcat running, printk() call gets stuck and never returns. The issue can be observed at printk()s on nonboot CPUs when going to offline with their interrupts disabled, and never seen at boot CPU (core0 in our case). Details: serial_console_write() lacks of appropriate spinlock handling. In SMP systems, as long as sci_transmit_chars() is being processed at one CPU core, serial_console_write() can stuck at the other CPU core(s), when it tries to access to the same serial port _without_ a proper locking. serial_console_write() waits for the transmit FIFO getting empty, while sci_transmit_chars() writes data to the FIFO. In general, peripheral interrupts are routed to boot CPU (core0) by Linux ARM standard affinity settings. SCI(F) interrupts are handled by core0, so sci_transmit_chars() is processed on core0 as well. When logcat is running, it writes enormous log data to the kernel at every moment, forever. So core0 can repeatedly continue to process sci_transmit_chars() in its interrupt handler, which eventually makes the other CPU core(s) stuck at serial_console_write(). Looking at serial/8250.c, this is a known console write lockup issue with SMP kernels. Fix the sh-sci driver in the same way 8250.c does. Signed-off-by: Shinya Kuribayashi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 107801b1279..63a23eadd7e 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2203,7 +2203,21 @@ static void serial_console_write(struct console *co, const char *s, { struct sci_port *sci_port = &sci_ports[co->index]; struct uart_port *port = &sci_port->port; - unsigned short bits; + unsigned short bits, ctrl; + unsigned long flags; + int locked = 1; + + local_irq_save(flags); + if (port->sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&port->lock); + else + spin_lock(&port->lock); + + /* first save the SCSCR then disable the interrupts */ + ctrl = serial_port_in(port, SCSCR); + serial_port_out(port, SCSCR, sci_port->cfg->scscr); uart_console_write(port, s, count, serial_console_putchar); @@ -2211,6 +2225,13 @@ static void serial_console_write(struct console *co, const char *s, bits = SCxSR_TDxE(port) | SCxSR_TEND(port); while ((serial_port_in(port, SCxSR) & bits) != bits) cpu_relax(); + + /* restore the SCSCR */ + serial_port_out(port, SCSCR, ctrl); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); } static int __devinit serial_console_setup(struct console *co, char *options) From 33b48e1633f738c5ae78234c2dd5e3a9ba115437 Mon Sep 17 00:00:00 2001 From: Shinya Kuribayashi Date: Fri, 16 Nov 2012 10:54:49 +0900 Subject: [PATCH 102/146] serial: sh-sci: fix possible race cases on SCSCR register accesses In the previous commit, console write function (serial_console_write) is changed to disable SCI interrupts while printing console strings. This introduces possible race cases in the serial startup / shutdown functions on SMP systems. This patch fixes the sh-sci in the same way as commit 9ec1882df2 (tty: serial: imx: console write routing is unsafe on SMP, from Xinyu Chen , 2012-08-27) did. There could be several consumers of the console, * the kernel printk * the init process using /dev/kmsg to call printk to show log * shell, which opens /dev/console and writes with sys_write() The shell goes into the normal UART open() and write() system calls, while the other two go into the console operations. The open() call invokes serial startup function (sci_startup), which will write to the SCSCR register (to enable or disable SCI interrupts) without any locking. This will conflict with the console serial function. Add spinlock protections in sci_startup() and sci_shutdown() properly. Signed-off-by: Shinya Kuribayashi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 63a23eadd7e..d38c0f54603 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1743,6 +1743,7 @@ static inline void sci_free_dma(struct uart_port *port) static int sci_startup(struct uart_port *port) { struct sci_port *s = to_sci_port(port); + unsigned long flags; int ret; dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); @@ -1753,8 +1754,10 @@ static int sci_startup(struct uart_port *port) sci_request_dma(port); + spin_lock_irqsave(&port->lock, flags); sci_start_tx(port); sci_start_rx(port); + spin_unlock_irqrestore(&port->lock, flags); return 0; } @@ -1762,11 +1765,14 @@ static int sci_startup(struct uart_port *port) static void sci_shutdown(struct uart_port *port) { struct sci_port *s = to_sci_port(port); + unsigned long flags; dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + spin_lock_irqsave(&port->lock, flags); sci_stop_rx(port); sci_stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); sci_free_dma(port); sci_free_irq(s); From ed71871bed7198ca4aa6a79b7a93b73ad6408e98 Mon Sep 17 00:00:00 2001 From: Noam Camus Date: Fri, 16 Nov 2012 07:03:05 +0200 Subject: [PATCH 103/146] tty/8250_early: Turn serial_in/serial_out into weak symbols. Allows overriding default methods serial_in/serial_out. In such platform specific replacement it is possible to use other regshift, biased register offset, any other manipulation that is not covered with common default methods. Overriding default methods may be useful for platforms which got serial peripheral with registers represented in big endian. In this situation and assuming that 32 bit operations / alignment is required then it may be useful to swab words before/after accessing the serial registers. Signed-off-by: Noam Camus Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_early.c | 42 ++++++++++++++-------------- include/linux/serial_8250.h | 2 ++ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index 843a150ba10..f53a7db4350 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -48,7 +48,7 @@ struct early_serial8250_device { static struct early_serial8250_device early_device; -static unsigned int __init serial_in(struct uart_port *port, int offset) +unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset) { switch (port->iotype) { case UPIO_MEM: @@ -62,7 +62,7 @@ static unsigned int __init serial_in(struct uart_port *port, int offset) } } -static void __init serial_out(struct uart_port *port, int offset, int value) +void __weak __init serial8250_early_out(struct uart_port *port, int offset, int value) { switch (port->iotype) { case UPIO_MEM: @@ -84,7 +84,7 @@ static void __init wait_for_xmitr(struct uart_port *port) unsigned int status; for (;;) { - status = serial_in(port, UART_LSR); + status = serial8250_early_in(port, UART_LSR); if ((status & BOTH_EMPTY) == BOTH_EMPTY) return; cpu_relax(); @@ -94,7 +94,7 @@ static void __init wait_for_xmitr(struct uart_port *port) static void __init serial_putc(struct uart_port *port, int c) { wait_for_xmitr(port); - serial_out(port, UART_TX, c); + serial8250_early_out(port, UART_TX, c); } static void __init early_serial8250_write(struct console *console, @@ -104,14 +104,14 @@ static void __init early_serial8250_write(struct console *console, unsigned int ier; /* Save the IER and disable interrupts */ - ier = serial_in(port, UART_IER); - serial_out(port, UART_IER, 0); + ier = serial8250_early_in(port, UART_IER); + serial8250_early_out(port, UART_IER, 0); uart_console_write(port, s, count, serial_putc); /* Wait for transmitter to become empty and restore the IER */ wait_for_xmitr(port); - serial_out(port, UART_IER, ier); + serial8250_early_out(port, UART_IER, ier); } static unsigned int __init probe_baud(struct uart_port *port) @@ -119,11 +119,11 @@ static unsigned int __init probe_baud(struct uart_port *port) unsigned char lcr, dll, dlm; unsigned int quot; - lcr = serial_in(port, UART_LCR); - serial_out(port, UART_LCR, lcr | UART_LCR_DLAB); - dll = serial_in(port, UART_DLL); - dlm = serial_in(port, UART_DLM); - serial_out(port, UART_LCR, lcr); + lcr = serial8250_early_in(port, UART_LCR); + serial8250_early_out(port, UART_LCR, lcr | UART_LCR_DLAB); + dll = serial8250_early_in(port, UART_DLL); + dlm = serial8250_early_in(port, UART_DLM); + serial8250_early_out(port, UART_LCR, lcr); quot = (dlm << 8) | dll; return (port->uartclk / 16) / quot; @@ -135,17 +135,17 @@ static void __init init_port(struct early_serial8250_device *device) unsigned int divisor; unsigned char c; - serial_out(port, UART_LCR, 0x3); /* 8n1 */ - serial_out(port, UART_IER, 0); /* no interrupt */ - serial_out(port, UART_FCR, 0); /* no fifo */ - serial_out(port, UART_MCR, 0x3); /* DTR + RTS */ + serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */ + serial8250_early_out(port, UART_IER, 0); /* no interrupt */ + serial8250_early_out(port, UART_FCR, 0); /* no fifo */ + serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */ divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); - c = serial_in(port, UART_LCR); - serial_out(port, UART_LCR, c | UART_LCR_DLAB); - serial_out(port, UART_DLL, divisor & 0xff); - serial_out(port, UART_DLM, (divisor >> 8) & 0xff); - serial_out(port, UART_LCR, c & ~UART_LCR_DLAB); + c = serial8250_early_in(port, UART_LCR); + serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB); + serial8250_early_out(port, UART_DLL, divisor & 0xff); + serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff); + serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); } static int __init parse_options(struct early_serial8250_device *device, diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index c174c90fb3f..c490d20b3fb 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -105,6 +105,8 @@ extern int early_serial_setup(struct uart_port *port); extern int serial8250_find_port(struct uart_port *p); extern int serial8250_find_port_for_earlycon(void); +extern unsigned int serial8250_early_in(struct uart_port *port, int offset); +extern void serial8250_early_out(struct uart_port *port, int offset, int value); extern int setup_early_serial8250_console(char *cmdline); extern void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old); From f4b1f03b826ba22c9835e9e89a1ca03541313e04 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 16 Nov 2012 16:03:52 +0800 Subject: [PATCH 104/146] serial: mxs-auart: distinguish the different SOCs The current mxs-auart driver is used for both mx23 and mx28. But in mx23, the DMA has a bug(see errata:2836). We can not add the DMA support in mx23, but we can add DMA support to auart in mx28. So in order to add the DMA support for the auart in mx28, we should distinguish the distinguish SOCs. This patch adds a new platform_device_id table and a inline function is_imx28_auart() to distinguish the mx23 and mx28. Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 42 +++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 6db3baa39a9..06d72713fb9 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -114,11 +114,17 @@ static struct uart_driver auart_driver; +enum mxs_auart_type { + IMX23_AUART, + IMX28_AUART, +}; + struct mxs_auart_port { struct uart_port port; unsigned int flags; unsigned int ctrl; + enum mxs_auart_type devtype; unsigned int irq; @@ -126,6 +132,29 @@ struct mxs_auart_port { struct device *dev; }; +static struct platform_device_id mxs_auart_devtype[] = { + { .name = "mxs-auart-imx23", .driver_data = IMX23_AUART }, + { .name = "mxs-auart-imx28", .driver_data = IMX28_AUART }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, mxs_auart_devtype); + +static struct of_device_id mxs_auart_dt_ids[] = { + { + .compatible = "fsl,imx28-auart", + .data = &mxs_auart_devtype[IMX28_AUART] + }, { + .compatible = "fsl,imx23-auart", + .data = &mxs_auart_devtype[IMX23_AUART] + }, { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids); + +static inline int is_imx28_auart(struct mxs_auart_port *s) +{ + return s->devtype == IMX28_AUART; +} + static void mxs_auart_stop_tx(struct uart_port *u); #define to_auart_port(u) container_of(u, struct mxs_auart_port, port) @@ -706,6 +735,8 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, static int __devinit mxs_auart_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = + of_match_device(mxs_auart_dt_ids, &pdev->dev); struct mxs_auart_port *s; u32 version; int ret = 0; @@ -730,6 +761,11 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) goto out_free; } + if (of_id) { + pdev->id_entry = of_id->data; + s->devtype = pdev->id_entry->driver_data; + } + s->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(s->clk)) { ret = PTR_ERR(s->clk); @@ -805,12 +841,6 @@ static int __devexit mxs_auart_remove(struct platform_device *pdev) return 0; } -static struct of_device_id mxs_auart_dt_ids[] = { - { .compatible = "fsl,imx23-auart", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids); - static struct platform_driver mxs_auart_driver = { .probe = mxs_auart_probe, .remove = __devexit_p(mxs_auart_remove), From e8001632816e600ced7d9d4790930fd87935c654 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 16 Nov 2012 16:03:53 +0800 Subject: [PATCH 105/146] serial: mxs-auart: add the DMA support for mx28 Only we meet the following conditions, we can enable the DMA support for auart: (1) We enable the DMA support in the dts file, such as arch/arm/boot/dts/imx28.dtsi. (2) We enable the hardware flow control. (3) We use the mx28, not the mx23. Due to hardware bug(see errata: 2836), we can not add the DMA support to mx23. Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman --- .../bindings/tty/serial/fsl-mxs-auart.txt | 8 + drivers/tty/serial/mxs-auart.c | 322 +++++++++++++++++- 2 files changed, 325 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt b/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt index 2ee903fad25..273a8d5b330 100644 --- a/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt +++ b/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt @@ -6,11 +6,19 @@ Required properties: - reg : Address and length of the register set for the device - interrupts : Should contain the auart interrupt numbers +Optional properties: +- fsl,auart-dma-channel : The DMA channels, the first is for RX, the other + is for TX. If you add this property, it also means that you + will enable the DMA support for the auart. + Note: due to the hardware bug in imx23(see errata : 2836), + only the imx28 can enable the DMA support for the auart. + Example: auart0: serial@8006a000 { compatible = "fsl,imx28-auart", "fsl,imx23-auart"; reg = <0x8006a000 0x2000>; interrupts = <112 70 71>; + fsl,auart-dma-channel = <8 9>; }; Note: Each auart port should have an alias correctly numbered in "aliases" diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 06d72713fb9..d5b9e303901 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include @@ -71,6 +73,15 @@ #define AUART_CTRL0_SFTRST (1 << 31) #define AUART_CTRL0_CLKGATE (1 << 30) +#define AUART_CTRL0_RXTO_ENABLE (1 << 27) +#define AUART_CTRL0_RXTIMEOUT(v) (((v) & 0x7ff) << 16) +#define AUART_CTRL0_XFER_COUNT(v) ((v) & 0xffff) + +#define AUART_CTRL1_XFER_COUNT(v) ((v) & 0xffff) + +#define AUART_CTRL2_DMAONERR (1 << 26) +#define AUART_CTRL2_TXDMAE (1 << 25) +#define AUART_CTRL2_RXDMAE (1 << 24) #define AUART_CTRL2_CTSEN (1 << 15) #define AUART_CTRL2_RTSEN (1 << 14) @@ -111,6 +122,7 @@ #define AUART_STAT_BERR (1 << 18) #define AUART_STAT_PERR (1 << 17) #define AUART_STAT_FERR (1 << 16) +#define AUART_STAT_RXCOUNT_MASK 0xffff static struct uart_driver auart_driver; @@ -122,7 +134,11 @@ enum mxs_auart_type { struct mxs_auart_port { struct uart_port port; - unsigned int flags; +#define MXS_AUART_DMA_CONFIG 0x1 +#define MXS_AUART_DMA_ENABLED 0x2 +#define MXS_AUART_DMA_TX_SYNC 2 /* bit 2 */ +#define MXS_AUART_DMA_RX_READY 3 /* bit 3 */ + unsigned long flags; unsigned int ctrl; enum mxs_auart_type devtype; @@ -130,6 +146,20 @@ struct mxs_auart_port { struct clk *clk; struct device *dev; + + /* for DMA */ + struct mxs_dma_data dma_data; + int dma_channel_rx, dma_channel_tx; + int dma_irq_rx, dma_irq_tx; + int dma_channel; + + struct scatterlist tx_sgl; + struct dma_chan *tx_dma_chan; + void *tx_dma_buf; + + struct scatterlist rx_sgl; + struct dma_chan *rx_dma_chan; + void *rx_dma_buf; }; static struct platform_device_id mxs_auart_devtype[] = { @@ -155,14 +185,107 @@ static inline int is_imx28_auart(struct mxs_auart_port *s) return s->devtype == IMX28_AUART; } +static inline bool auart_dma_enabled(struct mxs_auart_port *s) +{ + return s->flags & MXS_AUART_DMA_ENABLED; +} + static void mxs_auart_stop_tx(struct uart_port *u); #define to_auart_port(u) container_of(u, struct mxs_auart_port, port) -static inline void mxs_auart_tx_chars(struct mxs_auart_port *s) +static void mxs_auart_tx_chars(struct mxs_auart_port *s); + +static void dma_tx_callback(void *param) +{ + struct mxs_auart_port *s = param; + struct circ_buf *xmit = &s->port.state->xmit; + + dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE); + + /* clear the bit used to serialize the DMA tx. */ + clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); + smp_mb__after_clear_bit(); + + /* wake up the possible processes. */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + + mxs_auart_tx_chars(s); +} + +static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size) +{ + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl = &s->tx_sgl; + struct dma_chan *channel = s->tx_dma_chan; + u32 pio; + + /* [1] : send PIO. Note, the first pio word is CTRL1. */ + pio = AUART_CTRL1_XFER_COUNT(size); + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)&pio, + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(s->dev, "step 1 error\n"); + return -EINVAL; + } + + /* [2] : set DMA buffer. */ + sg_init_one(sgl, s->tx_dma_buf, size); + dma_map_sg(s->dev, sgl, 1, DMA_TO_DEVICE); + desc = dmaengine_prep_slave_sg(channel, sgl, + 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(s->dev, "step 2 error\n"); + return -EINVAL; + } + + /* [3] : submit the DMA */ + desc->callback = dma_tx_callback; + desc->callback_param = s; + dmaengine_submit(desc); + dma_async_issue_pending(channel); + return 0; +} + +static void mxs_auart_tx_chars(struct mxs_auart_port *s) { struct circ_buf *xmit = &s->port.state->xmit; + if (auart_dma_enabled(s)) { + int i = 0; + int size; + void *buffer = s->tx_dma_buf; + + if (test_and_set_bit(MXS_AUART_DMA_TX_SYNC, &s->flags)) + return; + + while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { + size = min_t(u32, UART_XMIT_SIZE - i, + CIRC_CNT_TO_END(xmit->head, + xmit->tail, + UART_XMIT_SIZE)); + memcpy(buffer + i, xmit->buf + xmit->tail, size); + xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1); + + i += size; + if (i >= UART_XMIT_SIZE) + break; + } + + if (uart_tx_stopped(&s->port)) + mxs_auart_stop_tx(&s->port); + + if (i) { + mxs_auart_dma_tx(s, i); + } else { + clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); + smp_mb__after_clear_bit(); + } + return; + } + + while (!(readl(s->port.membase + AUART_STAT) & AUART_STAT_TXFF)) { if (s->port.x_char) { @@ -316,10 +439,157 @@ static u32 mxs_auart_get_mctrl(struct uart_port *u) return mctrl; } +static bool mxs_auart_dma_filter(struct dma_chan *chan, void *param) +{ + struct mxs_auart_port *s = param; + + if (!mxs_dma_is_apbx(chan)) + return false; + + if (s->dma_channel == chan->chan_id) { + chan->private = &s->dma_data; + return true; + } + return false; +} + +static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s); +static void dma_rx_callback(void *arg) +{ + struct mxs_auart_port *s = (struct mxs_auart_port *) arg; + struct tty_struct *tty = s->port.state->port.tty; + int count; + u32 stat; + + stat = readl(s->port.membase + AUART_STAT); + stat &= ~(AUART_STAT_OERR | AUART_STAT_BERR | + AUART_STAT_PERR | AUART_STAT_FERR); + + count = stat & AUART_STAT_RXCOUNT_MASK; + tty_insert_flip_string(tty, s->rx_dma_buf, count); + + writel(stat, s->port.membase + AUART_STAT); + tty_flip_buffer_push(tty); + + /* start the next DMA for RX. */ + mxs_auart_dma_prep_rx(s); +} + +static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s) +{ + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl = &s->rx_sgl; + struct dma_chan *channel = s->rx_dma_chan; + u32 pio[1]; + + /* [1] : send PIO */ + pio[0] = AUART_CTRL0_RXTO_ENABLE + | AUART_CTRL0_RXTIMEOUT(0x80) + | AUART_CTRL0_XFER_COUNT(UART_XMIT_SIZE); + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(s->dev, "step 1 error\n"); + return -EINVAL; + } + + /* [2] : send DMA request */ + sg_init_one(sgl, s->rx_dma_buf, UART_XMIT_SIZE); + dma_map_sg(s->dev, sgl, 1, DMA_FROM_DEVICE); + desc = dmaengine_prep_slave_sg(channel, sgl, 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(s->dev, "step 2 error\n"); + return -1; + } + + /* [3] : submit the DMA, but do not issue it. */ + desc->callback = dma_rx_callback; + desc->callback_param = s; + dmaengine_submit(desc); + dma_async_issue_pending(channel); + return 0; +} + +static void mxs_auart_dma_exit_channel(struct mxs_auart_port *s) +{ + if (s->tx_dma_chan) { + dma_release_channel(s->tx_dma_chan); + s->tx_dma_chan = NULL; + } + if (s->rx_dma_chan) { + dma_release_channel(s->rx_dma_chan); + s->rx_dma_chan = NULL; + } + + kfree(s->tx_dma_buf); + kfree(s->rx_dma_buf); + s->tx_dma_buf = NULL; + s->rx_dma_buf = NULL; +} + +static void mxs_auart_dma_exit(struct mxs_auart_port *s) +{ + + writel(AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR, + s->port.membase + AUART_CTRL2_CLR); + + mxs_auart_dma_exit_channel(s); + s->flags &= ~MXS_AUART_DMA_ENABLED; + clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); + clear_bit(MXS_AUART_DMA_RX_READY, &s->flags); +} + +static int mxs_auart_dma_init(struct mxs_auart_port *s) +{ + dma_cap_mask_t mask; + + if (auart_dma_enabled(s)) + return 0; + + /* We do not get the right DMA channels. */ + if (s->dma_channel_rx == -1 || s->dma_channel_rx == -1) + return -EINVAL; + + /* init for RX */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + s->dma_channel = s->dma_channel_rx; + s->dma_data.chan_irq = s->dma_irq_rx; + s->rx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + if (!s->rx_dma_chan) + goto err_out; + s->rx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); + if (!s->rx_dma_buf) + goto err_out; + + /* init for TX */ + s->dma_channel = s->dma_channel_tx; + s->dma_data.chan_irq = s->dma_irq_tx; + s->tx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + if (!s->tx_dma_chan) + goto err_out; + s->tx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); + if (!s->tx_dma_buf) + goto err_out; + + /* set the flags */ + s->flags |= MXS_AUART_DMA_ENABLED; + dev_dbg(s->dev, "enabled the DMA support."); + + return 0; + +err_out: + mxs_auart_dma_exit_channel(s); + return -EINVAL; + +} + static void mxs_auart_settermios(struct uart_port *u, struct ktermios *termios, struct ktermios *old) { + struct mxs_auart_port *s = to_auart_port(u); u32 bm, ctrl, ctrl2, div; unsigned int cflag, baud; @@ -391,10 +661,23 @@ static void mxs_auart_settermios(struct uart_port *u, ctrl |= AUART_LINECTRL_STP2; /* figure out the hardware flow control settings */ - if (cflag & CRTSCTS) + if (cflag & CRTSCTS) { + /* + * The DMA has a bug(see errata:2836) in mx23. + * So we can not implement the DMA for auart in mx23, + * we can only implement the DMA support for auart + * in mx28. + */ + if (is_imx28_auart(s) && (s->flags & MXS_AUART_DMA_CONFIG)) { + if (!mxs_auart_dma_init(s)) + /* enable DMA tranfer */ + ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE + | AUART_CTRL2_DMAONERR; + } ctrl2 |= AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN; - else + } else { ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN); + } /* set baud rate */ baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk); @@ -406,6 +689,18 @@ static void mxs_auart_settermios(struct uart_port *u, writel(ctrl2, u->membase + AUART_CTRL2); uart_update_timeout(u, termios->c_cflag, baud); + + /* prepare for the DMA RX. */ + if (auart_dma_enabled(s) && + !test_and_set_bit(MXS_AUART_DMA_RX_READY, &s->flags)) { + if (!mxs_auart_dma_prep_rx(s)) { + /* Disable the normal RX interrupt. */ + writel(AUART_INTR_RXIEN, u->membase + AUART_INTR_CLR); + } else { + mxs_auart_dma_exit(s); + dev_err(s->dev, "We can not start up the DMA.\n"); + } + } } static irqreturn_t mxs_auart_irq_handle(int irq, void *context) @@ -484,6 +779,9 @@ static void mxs_auart_shutdown(struct uart_port *u) { struct mxs_auart_port *s = to_auart_port(u); + if (auart_dma_enabled(s)) + mxs_auart_dma_exit(s); + writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR); writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, @@ -717,6 +1015,7 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + u32 dma_channel[2]; int ret; if (!np) @@ -730,6 +1029,20 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, } s->port.line = ret; + s->dma_irq_rx = platform_get_irq(pdev, 1); + s->dma_irq_tx = platform_get_irq(pdev, 2); + + ret = of_property_read_u32_array(np, "fsl,auart-dma-channel", + dma_channel, 2); + if (ret == 0) { + s->dma_channel_rx = dma_channel[0]; + s->dma_channel_tx = dma_channel[1]; + + s->flags |= MXS_AUART_DMA_CONFIG; + } else { + s->dma_channel_rx = -1; + s->dma_channel_tx = -1; + } return 0; } @@ -787,7 +1100,6 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) s->port.type = PORT_IMX; s->port.dev = s->dev = get_device(&pdev->dev); - s->flags = 0; s->ctrl = 0; s->irq = platform_get_irq(pdev, 0); From 77a807dcebc1be1e96dc8b47db2e598034e8a7c7 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 16 Nov 2012 16:03:54 +0800 Subject: [PATCH 106/146] ARM: dts: enable dma support for auart0 in mx28 enable the dma support for auart0 in mx28. Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/imx28.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi index 55c57ea6169..b4587b27ae4 100644 --- a/arch/arm/boot/dts/imx28.dtsi +++ b/arch/arm/boot/dts/imx28.dtsi @@ -799,6 +799,7 @@ compatible = "fsl,imx28-auart", "fsl,imx23-auart"; reg = <0x8006a000 0x2000>; interrupts = <112 70 71>; + fsl,auart-dma-channel = <8 9>; clocks = <&clks 45>; status = "disabled"; }; From 68a81291ff6650f3ff409ebfc58ef97dfe85a2e4 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 16 Nov 2012 09:28:49 -0500 Subject: [PATCH 107/146] staging: Add SystemBase Multi-2/PCI driver I ported the driver supplied by SystemBase to mainline. As the driver had MODULE_LICENSE("GPL") it is declared as a GPL module and thus I have the right to distribute it upstream. Note, I did the bare minimum to get it working. It still needs a lot of loving. Cc: hjchoi Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/sb105x/Kconfig | 9 + drivers/staging/sb105x/Makefile | 3 + drivers/staging/sb105x/sb_mp_register.h | 295 +++ drivers/staging/sb105x/sb_pci_mp.c | 3195 +++++++++++++++++++++++ drivers/staging/sb105x/sb_pci_mp.h | 293 +++ drivers/staging/sb105x/sb_ser_core.h | 368 +++ 8 files changed, 4166 insertions(+) create mode 100644 drivers/staging/sb105x/Kconfig create mode 100644 drivers/staging/sb105x/Makefile create mode 100644 drivers/staging/sb105x/sb_mp_register.h create mode 100644 drivers/staging/sb105x/sb_pci_mp.c create mode 100644 drivers/staging/sb105x/sb_pci_mp.h create mode 100644 drivers/staging/sb105x/sb_ser_core.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index d805eef1191..f245fd3153d 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -144,4 +144,6 @@ source "drivers/staging/imx-drm/Kconfig" source "drivers/staging/dgrp/Kconfig" +source "drivers/staging/sb105x/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 76e2ebd596f..94cc3fa5ef8 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -64,3 +64,4 @@ obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/ obj-$(CONFIG_CED1401) += ced1401/ obj-$(CONFIG_DRM_IMX) += imx-drm/ obj-$(CONFIG_DGRP) += dgrp/ +obj-$(CONFIG_SB105X) += sb105x/ diff --git a/drivers/staging/sb105x/Kconfig b/drivers/staging/sb105x/Kconfig new file mode 100644 index 00000000000..ac87c5e38de --- /dev/null +++ b/drivers/staging/sb105x/Kconfig @@ -0,0 +1,9 @@ +config SB105X + tristate "SystemBase PCI Multiport UART" + select SERIAL_CORE + depends on PCI + help + A driver for the SystemBase Multi-2/PCI serial card + + To compile this driver a module, choose M here: the module + will be called "sb105x". diff --git a/drivers/staging/sb105x/Makefile b/drivers/staging/sb105x/Makefile new file mode 100644 index 00000000000..b1bf3779aca --- /dev/null +++ b/drivers/staging/sb105x/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_SB105X) += sb105x.o + +sb105x-y := sb_pci_mp.o diff --git a/drivers/staging/sb105x/sb_mp_register.h b/drivers/staging/sb105x/sb_mp_register.h new file mode 100644 index 00000000000..5480ae11368 --- /dev/null +++ b/drivers/staging/sb105x/sb_mp_register.h @@ -0,0 +1,295 @@ + +/* + * SB105X_UART.h + * + * Copyright (C) 2008 systembase + * + * UART registers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef UART_SB105X_H +#define UART_SB105X_H + +/* + * option register + */ + +/* Device Infomation Register */ +#define MP_OPTR_DIR0 0x04 /* port0 ~ port8 */ +#define MP_OPTR_DIR1 0x05 /* port8 ~ port15 */ +#define MP_OPTR_DIR2 0x06 /* port16 ~ port23 */ +#define MP_OPTR_DIR3 0x07 /* port24 ~ port31 */ + +#define DIR_UART_16C550 0 +#define DIR_UART_16C1050 1 +#define DIR_UART_16C1050A 2 + +#define DIR_CLK_1843200 0x0 /* input clock 1843200 Hz */ +#define DIR_CLK_3686400 0x1 /* input clock 3686400 Hz */ +#define DIR_CLK_7372800 0x2 /* input clock 7372800 Hz */ +#define DIR_CLK_14745600 0x3 /* input clock 14745600 Hz */ +#define DIR_CLK_29491200 0x4 /* input clock 29491200 Hz */ +#define DIR_CLK_58985400 0x5 /* input clock 58985400 Hz */ + +/* Interface Information Register */ +#define MP_OPTR_IIR0 0x08 /* port0 ~ port8 */ +#define MP_OPTR_IIR1 0x09 /* port8 ~ port15 */ +#define MP_OPTR_IIR2 0x0A /* port16 ~ port23 */ +#define MP_OPTR_IIR3 0x0B /* port24 ~ port31 */ + +#define IIR_RS232 0x00 /* RS232 type */ +#define IIR_RS422 0x10 /* RS422 type */ +#define IIR_RS485 0x20 /* RS485 type */ +#define IIR_UNKNOWN 0x30 /* unknown type */ + +/* Interrrupt Mask Register */ +#define MP_OPTR_IMR0 0x0C /* port0 ~ port8 */ +#define MP_OPTR_IMR1 0x0D /* port8 ~ port15 */ +#define MP_OPTR_IMR2 0x0E /* port16 ~ port23 */ +#define MP_OPTR_IMR3 0x0F /* port24 ~ port31 */ + +/* Interrupt Poll Register */ +#define MP_OPTR_IPR0 0x10 /* port0 ~ port8 */ +#define MP_OPTR_IPR1 0x11 /* port8 ~ port15 */ +#define MP_OPTR_IPR2 0x12 /* port16 ~ port23 */ +#define MP_OPTR_IPR3 0x13 /* port24 ~ port31 */ + +/* General Purpose Output Control Register */ +#define MP_OPTR_GPOCR 0x20 + +/* General Purpose Output Data Register */ +#define MP_OPTR_GPODR 0x21 + +/* Parallel Additional Function Register */ +#define MP_OPTR_PAFR 0x23 + +/* + * systembase 16c105x UART register + */ + +#define PAGE_0 0 +#define PAGE_1 1 +#define PAGE_2 2 +#define PAGE_3 3 +#define PAGE_4 4 + +/* + * ****************************************************************** + * * DLAB=0 =============== Page 0 Registers * + * ****************************************************************** + */ + +#define SB105X_RX 0 /* In: Receive buffer */ +#define SB105X_TX 0 /* Out: Transmit buffer */ + +#define SB105X_IER 1 /* Out: Interrupt Enable Register */ + +#define SB105X_IER_CTSI 0x80 /* CTS# Interrupt Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_RTSI 0x40 /* RTS# Interrupt Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_XOI 0x20 /* Xoff Interrupt Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_SME 0x10 /* Sleep Mode Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define SB105X_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define SB105X_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define SB105X_IER_RDI 0x01 /* Enable receiver data interrupt */ + +#define SB105X_ISR 2 /* In: Interrupt ID Register */ + +#define SB105X_ISR_NOINT 0x01 /* No interrupts pending */ +#define SB105X_ISR_RLSI 0x06 /* Receiver line status interrupt (Priority = 1)*/ +#define SB105X_ISR_RDAI 0x0c /* Receive Data Available interrupt */ +#define SB105X_ISR_CTII 0x04 /* Character Timeout Indication interrupt */ +#define SB105X_ISR_THRI 0x02 /* Transmitter holding register empty */ +#define SB105X_ISR_MSI 0x00 /* Modem status interrupt */ +#define SB105X_ISR_RXCI 0x10 /* Receive Xoff or Special Character interrupt */ +#define SB105X_ISR_RCSI 0x20 /* RTS#, CTS# status interrupt during Auto RTS/CTS flow control */ + +#define SB105X_FCR 2 /* Out: FIFO Control Register */ + +#define SB105X_FCR_FEN 0x01 /* FIFO Enable */ +#define SB105X_FCR_RXFR 0x02 /* RX FIFO Reset */ +#define SB105X_FCR_TXFR 0x04 /* TX FIFO Reset */ +#define SB105X_FCR_DMS 0x08 /* DMA Mode Select */ + +#define SB105X_FCR_RTR08 0x00 /* Receice Trigger Level set at 8 */ +#define SB105X_FCR_RTR16 0x40 /* Receice Trigger Level set at 16 */ +#define SB105X_FCR_RTR56 0x80 /* Receice Trigger Level set at 56 */ +#define SB105X_FCR_RTR60 0xc0 /* Receice Trigger Level set at 60 */ +#define SB105X_FCR_TTR08 0x00 /* Transmit Trigger Level set at 8 */ +#define SB105X_FCR_TTR16 0x10 /* Transmit Trigger Level set at 16 */ +#define SB105X_FCR_TTR32 0x20 /* Transmit Trigger Level set at 32 */ +#define SB105X_FCR_TTR56 0x30 /* Transmit Trigger Level set at 56 */ + +#define SB105X_LCR 3 /* Out: Line Control Register */ +/* + * * Note: if the word length is 5 bits (SB105X_LCR_WLEN5), then setting + * * SB105X_LCR_STOP will select 1.5 stop bits, not 2 stop bits. + */ +#define SB105X_LCR_DLAB 0x80 /* Divisor Latch Enable */ +#define SB105X_LCR_SBC 0x40 /* Break Enable*/ +#define SB105X_LCR_SPAR 0x20 /* Set Stick parity */ +#define SB105X_LCR_EPAR 0x10 /* Even parity select */ +#define SB105X_LCR_PAREN 0x08 /* Parity Enable */ +#define SB105X_LCR_STOP 0x04 /* Stop bits: 0->1 bit, 1->2 bits, 1 and SB105X_LCR_WLEN5 -> 1.5 bit */ +#define SB105X_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define SB105X_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define SB105X_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define SB105X_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + +#define SB105X_LCR_BF 0xBF + +#define SB105X_MCR 4 /* Out: Modem Control Register */ +#define SB105X_MCR_CPS 0x80 /* Clock Prescaler Select */ +#define SB105X_MCR_P2S 0x40 /* Page 2 Select /Xoff Re-Transmit Access Enable */ +#define SB105X_MCR_XOA 0x20 /* Xon Any Enable */ +#define SB105X_MCR_ILB 0x10 /* Internal Loopback Enable */ +#define SB105X_MCR_OUT2 0x08 /* Out2/Interrupt Output Enable*/ +#define SB105X_MCR_OUT1 0x04 /* Out1/Interrupt Output Enable */ +#define SB105X_MCR_RTS 0x02 /* RTS# Output */ +#define SB105X_MCR_DTR 0x01 /* DTR# Output */ + +#define SB105X_LSR 5 /* In: Line Status Register */ +#define SB105X_LSR_RFEI 0x80 /* Receive FIFO data error Indicator */ +#define SB105X_LSR_TEMI 0x40 /* THR and TSR Empty Indicator */ +#define SB105X_LSR_THRE 0x20 /* THR Empty Indicator */ +#define SB105X_LSR_BII 0x10 /* Break interrupt indicator */ +#define SB105X_LSR_FEI 0x08 /* Frame error indicator */ +#define SB105X_LSR_PEI 0x04 /* Parity error indicator */ +#define SB105X_LSR_OEI 0x02 /* Overrun error indicator */ +#define SB105X_LSR_RDRI 0x01 /* Receive data ready Indicator*/ + +#define SB105X_MSR 6 /* In: Modem Status Register */ +#define SB105X_MSR_DCD 0x80 /* Data Carrier Detect */ +#define SB105X_MSR_RI 0x40 /* Ring Indicator */ +#define SB105X_MSR_DSR 0x20 /* Data Set Ready */ +#define SB105X_MSR_CTS 0x10 /* Clear to Send */ +#define SB105X_MSR_DDCD 0x08 /* Delta DCD */ +#define SB105X_MSR_DRI 0x04 /* Delta ring indicator */ +#define SB105X_MSR_DDSR 0x02 /* Delta DSR */ +#define SB105X_MSR_DCTS 0x01 /* Delta CTS */ + +#define SB105XA_MDR 6 /* Out: Multi Drop mode Register */ +#define SB105XA_MDR_NPS 0x08 /* 9th Bit Polarity Select */ +#define SB105XA_MDR_AME 0x02 /* Auto Multi-drop Enable */ +#define SB105XA_MDR_MDE 0x01 /* Multi Drop Enable */ + +#define SB105X_SPR 7 /* I/O: Scratch Register */ + +/* + * DLAB=1 + */ +#define SB105X_DLL 0 /* Out: Divisor Latch Low */ +#define SB105X_DLM 1 /* Out: Divisor Latch High */ + +/* + * ****************************************************************** + * * DLAB(LCR[7]) = 0 , MCR[6] = 1 ============= Page 2 Registers * + * ****************************************************************** + */ +#define SB105X_GICR 1 /* Global Interrupt Control Register */ +#define SB105X_GICR_GIM 0x01 /* Global Interrupt Mask */ + +#define SB105X_GISR 2 /* Global Interrupt Status Register */ +#define SB105X_GISR_MGICR0 0x80 /* Mirror the content of GICR[0] */ +#define SB105X_GISR_CS3IS 0x08 /* SB105X of CS3# Interrupt Status */ +#define SB105X_GISR_CS2IS 0x04 /* SB105X of CS2# Interrupt Status */ +#define SB105X_GISR_CS1IS 0x02 /* SB105X of CS1# Interrupt Status */ +#define SB105X_GISR_CS0IS 0x01 /* SB105X of CS0# Interrupt Status */ + +#define SB105X_TFCR 5 /* Transmit FIFO Count Register */ + +#define SB105X_RFCR 6 /* Receive FIFO Count Register */ + +#define SB105X_FSR 7 /* Flow Control Status Register */ +#define SB105X_FSR_THFS 0x20 /* Transmit Hardware Flow Control Status */ +#define SB105X_FSR_TSFS 0x10 /* Transmit Software Flow Control Status */ +#define SB105X_FSR_RHFS 0x02 /* Receive Hardware Flow Control Status */ +#define SB105X_FSR_RSFS 0x01 /* Receive Software Flow Control Status */ + +/* + * ****************************************************************** + * * LCR = 0xBF, PSR[0] = 0 ============= Page 3 Registers * + * ****************************************************************** + */ + +#define SB105X_PSR 0 /* Page Select Register */ +#define SB105X_PSR_P3KEY 0xA4 /* Page 3 Select Key */ +#define SB105X_PSR_P4KEY 0xA5 /* Page 5 Select Key */ + +#define SB105X_ATR 1 /* Auto Toggle Control Register */ +#define SB105X_ATR_RPS 0x80 /* RXEN Polarity Select */ +#define SB105X_ATR_RCMS 0x40 /* RXEN Control Mode Select */ +#define SB105X_ATR_TPS 0x20 /* TXEN Polarity Select */ +#define SB105X_ATR_TCMS 0x10 /* TXEN Control Mode Select */ +#define SB105X_ATR_ATDIS 0x00 /* Auto Toggle is disabled */ +#define SB105X_ATR_ART 0x01 /* RTS#/TXEN pin operates as TXEN */ +#define SB105X_ATR_ADT 0x02 /* DTR#/TXEN pin operates as TXEN */ +#define SB105X_ATR_A80 0x03 /* only in 80 pin use */ + +#define SB105X_EFR 2 /* (Auto) Enhanced Feature Register */ +#define SB105X_EFR_ACTS 0x80 /* Auto-CTS Flow Control Enable */ +#define SB105X_EFR_ARTS 0x40 /* Auto-RTS Flow Control Enable */ +#define SB105X_EFR_SCD 0x20 /* Special Character Detect */ +#define SB105X_EFR_EFBEN 0x10 /* Enhanced Function Bits Enable */ + +#define SB105X_XON1 4 /* Xon1 Character Register */ +#define SB105X_XON2 5 /* Xon2 Character Register */ +#define SB105X_XOFF1 6 /* Xoff1 Character Register */ +#define SB105X_XOFF2 7 /* Xoff2 Character Register */ + +/* + * ****************************************************************** + * * LCR = 0xBF, PSR[0] = 1 ============ Page 4 Registers * + * ****************************************************************** + */ + +#define SB105X_AFR 1 /* Additional Feature Register */ +#define SB105X_AFR_GIPS 0x20 /* Global Interrupt Polarity Select */ +#define SB105X_AFR_GIEN 0x10 /* Global Interrupt Enable */ +#define SB105X_AFR_AFEN 0x01 /* 256-byte FIFO Enable */ + +#define SB105X_XRCR 2 /* Xoff Re-transmit Count Register */ +#define SB105X_XRCR_NRC1 0x00 /* Transmits Xoff Character whenever the number of received data is 1 during XOFF status */ +#define SB105X_XRCR_NRC4 0x01 /* Transmits Xoff Character whenever the number of received data is 4 during XOFF status */ +#define SB105X_XRCR_NRC8 0x02 /* Transmits Xoff Character whenever the number of received data is 8 during XOFF status */ +#define SB105X_XRCR_NRC16 0x03 /* Transmits Xoff Character whenever the number of received data is 16 during XOFF status */ + +#define SB105X_TTR 4 /* Transmit FIFO Trigger Level Register */ +#define SB105X_RTR 5 /* Receive FIFO Trigger Level Register */ +#define SB105X_FUR 6 /* Flow Control Upper Threshold Register */ +#define SB105X_FLR 7 /* Flow Control Lower Threshold Register */ + + +/* page 0 */ + +#define SB105X_GET_CHAR(port) inb((port)->iobase + SB105X_RX) +#define SB105X_GET_IER(port) inb((port)->iobase + SB105X_IER) +#define SB105X_GET_ISR(port) inb((port)->iobase + SB105X_ISR) +#define SB105X_GET_LCR(port) inb((port)->iobase + SB105X_LCR) +#define SB105X_GET_MCR(port) inb((port)->iobase + SB105X_MCR) +#define SB105X_GET_LSR(port) inb((port)->iobase + SB105X_LSR) +#define SB105X_GET_MSR(port) inb((port)->iobase + SB105X_MSR) +#define SB105X_GET_SPR(port) inb((port)->iobase + SB105X_SPR) + +#define SB105X_PUT_CHAR(port,v) outb((v),(port)->iobase + SB105X_TX ) +#define SB105X_PUT_IER(port,v) outb((v),(port)->iobase + SB105X_IER ) +#define SB105X_PUT_FCR(port,v) outb((v),(port)->iobase + SB105X_FCR ) +#define SB105X_PUT_LCR(port,v) outb((v),(port)->iobase + SB105X_LCR ) +#define SB105X_PUT_MCR(port,v) outb((v),(port)->iobase + SB105X_MCR ) +#define SB105X_PUT_SPR(port,v) outb((v),(port)->iobase + SB105X_SPR ) + + +/* page 1 */ +#define SB105X_GET_REG(port,reg) inb((port)->iobase + (reg)) +#define SB105X_PUT_REG(port,reg,v) outb((v),(port)->iobase + (reg)) + +/* page 2 */ + +#define SB105X_PUT_PSR(port,v) outb((v),(port)->iobase + SB105X_PSR ) + +#endif diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c new file mode 100644 index 00000000000..fbebf88226d --- /dev/null +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -0,0 +1,3195 @@ +#include "sb_pci_mp.h" +#include +#include + +extern struct parport *parport_pc_probe_port(unsigned long base_lo, + unsigned long base_hi, + int irq, int dma, + struct device *dev, + int irqflags); + +static struct mp_device_t mp_devs[MAX_MP_DEV]; +static int mp_nrpcibrds = sizeof(mp_pciboards)/sizeof(mppcibrd_t); +static int NR_BOARD=0; +static int NR_PORTS=0; +static struct mp_port multi_ports[MAX_MP_PORT]; +static struct irq_info irq_lists[NR_IRQS]; + +static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset); +static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value); +static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset); +static int sb1054_get_register(struct sb_uart_port * port, int page, int reg); +static int sb1054_set_register(struct sb_uart_port * port, int page, int reg, int value); +static void SendATCommand(struct mp_port * mtpt); +static int set_deep_fifo(struct sb_uart_port * port, int status); +static int get_deep_fifo(struct sb_uart_port * port); +static int get_device_type(int arg); +static int set_auto_rts(struct sb_uart_port *port, int status); +static void mp_stop(struct tty_struct *tty); +static void __mp_start(struct tty_struct *tty); +static void mp_start(struct tty_struct *tty); +static void mp_tasklet_action(unsigned long data); +static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear); +static int mp_startup(struct sb_uart_state *state, int init_hw); +static void mp_shutdown(struct sb_uart_state *state); +static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios); + +static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c); +static int mp_put_char(struct tty_struct *tty, unsigned char ch); + +static void mp_put_chars(struct tty_struct *tty); +static int mp_write(struct tty_struct *tty, const unsigned char * buf, int count); +static int mp_write_room(struct tty_struct *tty); +static int mp_chars_in_buffer(struct tty_struct *tty); +static void mp_flush_buffer(struct tty_struct *tty); +static void mp_send_xchar(struct tty_struct *tty, char ch); +static void mp_throttle(struct tty_struct *tty); +static void mp_unthrottle(struct tty_struct *tty); +static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo); +static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo); +static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value); + +static int mp_tiocmget(struct tty_struct *tty); +static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int mp_break_ctl(struct tty_struct *tty, int break_state); +static int mp_do_autoconfig(struct sb_uart_state *state); +static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg); +static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt); +static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); +static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios); +static void mp_close(struct tty_struct *tty, struct file *filp); +static void mp_wait_until_sent(struct tty_struct *tty, int timeout); +static void mp_hangup(struct tty_struct *tty); +static void mp_update_termios(struct sb_uart_state *state); +static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state); +static struct sb_uart_state *uart_get(struct uart_driver *drv, int line); +static int mp_open(struct tty_struct *tty, struct file *filp); +static const char *mp_type(struct sb_uart_port *port); +static void mp_change_pm(struct sb_uart_state *state, int pm_state); +static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port); +static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port); +static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state); +static int mp_register_driver(struct uart_driver *drv); +static void mp_unregister_driver(struct uart_driver *drv); +static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port); +static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port); +static void autoconfig(struct mp_port *mtpt, unsigned int probeflags); +static void autoconfig_irq(struct mp_port *mtpt); +static void multi_stop_tx(struct sb_uart_port *port); +static void multi_start_tx(struct sb_uart_port *port); +static void multi_stop_rx(struct sb_uart_port *port); +static void multi_enable_ms(struct sb_uart_port *port); +static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status ); +static _INLINE_ void transmit_chars(struct mp_port *mtpt); +static _INLINE_ void check_modem_status(struct mp_port *mtpt); +static inline void multi_handle_port(struct mp_port *mtpt); +static irqreturn_t multi_interrupt(int irq, void *dev_id); +static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt); +static int serial_link_irq_chain(struct mp_port *mtpt); +static void serial_unlink_irq_chain(struct mp_port *mtpt); +static void multi_timeout(unsigned long data); +static unsigned int multi_tx_empty(struct sb_uart_port *port); +static unsigned int multi_get_mctrl(struct sb_uart_port *port); +static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl); +static void multi_break_ctl(struct sb_uart_port *port, int break_state); +static int multi_startup(struct sb_uart_port *port); +static void multi_shutdown(struct sb_uart_port *port); +static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud); +static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old); +static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate); +static void multi_release_std_resource(struct mp_port *mtpt); +static void multi_release_port(struct sb_uart_port *port); +static int multi_request_port(struct sb_uart_port *port); +static void multi_config_port(struct sb_uart_port *port, int flags); +static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser); +static const char * multi_type(struct sb_uart_port *port); +static void __init multi_init_ports(void); +static void __init multi_register_ports(struct uart_driver *drv); +static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd); + +static int deep[256]; +static int deep_count; +static int fcr_arr[256]; +static int fcr_count; +static int ttr[256]; +static int ttr_count; +static int rtr[256]; +static int rtr_count; + +module_param_array(deep,int,&deep_count,0); +module_param_array(fcr_arr,int,&fcr_count,0); +module_param_array(ttr,int,&ttr_count,0); +module_param_array(rtr,int,&rtr_count,0); + +static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset) +{ + return inb(mtpt->port.iobase + offset); +} + +static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value) +{ + outb(value, mtpt->port.iobase + offset); +} + +static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset) +{ + return inb(mtpt->option_base_addr + offset); +} + +static int sb1053a_get_interface(struct mp_port *mtpt, int port_num) +{ + unsigned long option_base_addr = mtpt->option_base_addr; + unsigned int interface = 0; + + switch (port_num) + { + case 0: + case 1: + /* set GPO[1:0] = 00 */ + outb(0x00, option_base_addr + MP_OPTR_GPODR); + break; + case 2: + case 3: + /* set GPO[1:0] = 01 */ + outb(0x01, option_base_addr + MP_OPTR_GPODR); + break; + case 4: + case 5: + /* set GPO[1:0] = 10 */ + outb(0x02, option_base_addr + MP_OPTR_GPODR); + break; + default: + break; + } + + port_num &= 0x1; + + /* get interface */ + interface = inb(option_base_addr + MP_OPTR_IIR0 + port_num); + + /* set GPO[1:0] = 11 */ + outb(0x03, option_base_addr + MP_OPTR_GPODR); + + return (interface); +} + +static int sb1054_get_register(struct sb_uart_port * port, int page, int reg) +{ + int ret = 0; + unsigned int lcr = 0; + unsigned int mcr = 0; + unsigned int tmp = 0; + + if( page <= 0) + { + printk(" page 0 can not use this fuction\n"); + return -1; + } + + switch(page) + { + case 1: + lcr = SB105X_GET_LCR(port); + tmp = lcr | SB105X_LCR_DLAB; + SB105X_PUT_LCR(port, tmp); + + tmp = SB105X_GET_LCR(port); + + ret = SB105X_GET_REG(port,reg); + SB105X_PUT_LCR(port,lcr); + break; + case 2: + mcr = SB105X_GET_MCR(port); + tmp = mcr | SB105X_MCR_P2S; + SB105X_PUT_MCR(port,tmp); + + ret = SB105X_GET_REG(port,reg); + + SB105X_PUT_MCR(port,mcr); + break; + case 3: + lcr = SB105X_GET_LCR(port); + tmp = lcr | SB105X_LCR_BF; + SB105X_PUT_LCR(port,tmp); + SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P3KEY); + + ret = SB105X_GET_REG(port,reg); + + SB105X_PUT_LCR(port,lcr); + break; + case 4: + lcr = SB105X_GET_LCR(port); + tmp = lcr | SB105X_LCR_BF; + SB105X_PUT_LCR(port,tmp); + SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P4KEY); + + ret = SB105X_GET_REG(port,reg); + + SB105X_PUT_LCR(port,lcr); + break; + default: + printk(" error invalid page number \n"); + return -1; + } + + return ret; +} + +static int sb1054_set_register(struct sb_uart_port * port, int page, int reg, int value) +{ + int lcr = 0; + int mcr = 0; + int ret = 0; + + if( page <= 0) + { + printk(" page 0 can not use this fuction\n"); + return -1; + } + switch(page) + { + case 1: + lcr = SB105X_GET_LCR(port); + SB105X_PUT_LCR(port, lcr | SB105X_LCR_DLAB); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_LCR(port, lcr); + ret = 1; + break; + case 2: + mcr = SB105X_GET_MCR(port); + SB105X_PUT_MCR(port, mcr | SB105X_MCR_P2S); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_MCR(port, mcr); + ret = 1; + break; + case 3: + lcr = SB105X_GET_LCR(port); + SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF); + SB105X_PUT_PSR(port, SB105X_PSR_P3KEY); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_LCR(port, lcr); + ret = 1; + break; + case 4: + lcr = SB105X_GET_LCR(port); + SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF); + SB105X_PUT_PSR(port, SB105X_PSR_P4KEY); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_LCR(port, lcr); + ret = 1; + break; + default: + printk(" error invalid page number \n"); + return -1; + } + + return ret; +} + +static int set_multidrop_mode(struct sb_uart_port *port, unsigned int mode) +{ + int mdr = SB105XA_MDR_NPS; + + if (mode & MDMODE_ENABLE) + { + mdr |= SB105XA_MDR_MDE; + } + + if (1) //(mode & MDMODE_AUTO) + { + int efr = 0; + mdr |= SB105XA_MDR_AME; + efr = sb1054_get_register(port, PAGE_3, SB105X_EFR); + efr |= SB105X_EFR_SCD; + sb1054_set_register(port, PAGE_3, SB105X_EFR, efr); + } + + sb1054_set_register(port, PAGE_1, SB105XA_MDR, mdr); + port->mdmode &= ~0x6; + port->mdmode |= mode; + printk("[%d] multidrop init: %x\n", port->line, port->mdmode); + + return 0; +} + +static int get_multidrop_addr(struct sb_uart_port *port) +{ + return sb1054_get_register(port, PAGE_3, SB105X_XOFF2); +} + +static int set_multidrop_addr(struct sb_uart_port *port, unsigned int addr) +{ + sb1054_set_register(port, PAGE_3, SB105X_XOFF2, addr); + + return 0; +} + +static void SendATCommand(struct mp_port * mtpt) +{ + // a t cr lf + unsigned char ch[] = {0x61,0x74,0x0d,0x0a,0x0}; + unsigned char lineControl; + unsigned char i=0; + unsigned char Divisor = 0xc; + + lineControl = serial_inp(mtpt,UART_LCR); + serial_outp(mtpt,UART_LCR,(lineControl | UART_LCR_DLAB)); + serial_outp(mtpt,UART_DLL,(Divisor & 0xff)); + serial_outp(mtpt,UART_DLM,(Divisor & 0xff00)>>8); //baudrate is 4800 + + + serial_outp(mtpt,UART_LCR,lineControl); + serial_outp(mtpt,UART_LCR,0x03); // N-8-1 + serial_outp(mtpt,UART_FCR,7); + serial_outp(mtpt,UART_MCR,0x3); + while(ch[i]){ + while((serial_inp(mtpt,UART_LSR) & 0x60) !=0x60){ + ; + } + serial_outp(mtpt,0,ch[i++]); + } + + +}// end of SendATCommand() + +static int set_deep_fifo(struct sb_uart_port * port, int status) +{ + int afr_status = 0; + afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); + + if(status == ENABLE) + { + afr_status |= SB105X_AFR_AFEN; + } + else + { + afr_status &= ~SB105X_AFR_AFEN; + } + + sb1054_set_register(port,PAGE_4,SB105X_AFR,afr_status); + sb1054_set_register(port,PAGE_4,SB105X_TTR,ttr[port->line]); + sb1054_set_register(port,PAGE_4,SB105X_RTR,rtr[port->line]); + afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); + + return afr_status; +} + +static int get_device_type(int arg) +{ + int ret; + ret = inb(mp_devs[arg].option_reg_addr+MP_OPTR_DIR0); + ret = (ret & 0xf0) >> 4; + switch (ret) + { + case DIR_UART_16C550: + return PORT_16C55X; + case DIR_UART_16C1050: + return PORT_16C105X; + case DIR_UART_16C1050A: + /* + if (mtpt->port.line < 2) + { + return PORT_16C105XA; + } + else + { + if (mtpt->device->device_id & 0x50) + { + return PORT_16C55X; + } + else + { + return PORT_16C105X; + } + }*/ + return PORT_16C105XA; + default: + return PORT_UNKNOWN; + } + +} +static int get_deep_fifo(struct sb_uart_port * port) +{ + int afr_status = 0; + afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); + return afr_status; +} + +static int set_auto_rts(struct sb_uart_port *port, int status) +{ + int atr_status = 0; + +#if 0 + int efr_status = 0; + + efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR); + if(status == ENABLE) + efr_status |= SB105X_EFR_ARTS; + else + efr_status &= ~SB105X_EFR_ARTS; + sb1054_set_register(port,PAGE_3,SB105X_EFR,efr_status); + efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR); +#endif + +//ATR + atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR); + switch(status) + { + case RS422PTP: + atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_A80); + break; + case RS422MD: + atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); + break; + case RS485NE: + atr_status = (SB105X_ATR_RCMS) | (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); + break; + case RS485ECHO: + atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); + break; + } + + sb1054_set_register(port,PAGE_3,SB105X_ATR,atr_status); + atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR); + + return atr_status; +} + +static void mp_stop(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void __mp_start(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + + if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && + !tty->stopped && !tty->hw_stopped) + port->ops->start_tx(port); +} + +static void mp_start(struct tty_struct *tty) +{ + __mp_start(tty); +} + +static void mp_tasklet_action(unsigned long data) +{ + struct sb_uart_state *state = (struct sb_uart_state *)data; + struct tty_struct *tty; + + printk("tasklet is called!\n"); + tty = state->info->tty; + tty_wakeup(tty); +} + +static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear) +{ + unsigned int old; + + old = port->mctrl; + port->mctrl = (old & ~clear) | set; + if (old != port->mctrl) + port->ops->set_mctrl(port, port->mctrl); +} + +#define uart_set_mctrl(port,set) mp_update_mctrl(port,set,0) +#define uart_clear_mctrl(port,clear) mp_update_mctrl(port,0,clear) + +static int mp_startup(struct sb_uart_state *state, int init_hw) +{ + struct sb_uart_info *info = state->info; + struct sb_uart_port *port = state->port; + unsigned long page; + int retval = 0; + + if (info->flags & UIF_INITIALIZED) + return 0; + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + if (port->type == PORT_UNKNOWN) + return 0; + + if (!info->xmit.buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + info->xmit.buf = (unsigned char *) page; + + uart_circ_clear(&info->xmit); + } + + retval = port->ops->startup(port); + if (retval == 0) { + if (init_hw) { + mp_change_speed(state, NULL); + + if (info->tty->termios.c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); + } + + info->flags |= UIF_INITIALIZED; + + + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + if (retval && capable(CAP_SYS_ADMIN)) + retval = 0; + + return retval; +} + +static void mp_shutdown(struct sb_uart_state *state) +{ + struct sb_uart_info *info = state->info; + struct sb_uart_port *port = state->port; + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + if (info->flags & UIF_INITIALIZED) { + info->flags &= ~UIF_INITIALIZED; + + if (!info->tty || (info->tty->termios.c_cflag & HUPCL)) + uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + + wake_up_interruptible(&info->delta_msr_wait); + + port->ops->shutdown(port); + + synchronize_irq(port->irq); + } + tasklet_kill(&info->tlet); + + if (info->xmit.buf) { + free_page((unsigned long)info->xmit.buf); + info->xmit.buf = NULL; + } +} + +static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios) +{ + struct tty_struct *tty = state->info->tty; + struct sb_uart_port *port = state->port; + + if (!tty || port->type == PORT_UNKNOWN) + return; + + if (tty->termios.c_cflag & CRTSCTS) + state->info->flags |= UIF_CTS_FLOW; + else + state->info->flags &= ~UIF_CTS_FLOW; + + if (tty->termios.c_cflag & CLOCAL) + state->info->flags &= ~UIF_CHECK_CD; + else + state->info->flags |= UIF_CHECK_CD; + + port->ops->set_termios(port, &tty->termios, old_termios); +} + +static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c) +{ + unsigned long flags; + int ret = 0; + + if (!circ->buf) + return 0; + + spin_lock_irqsave(&port->lock, flags); + if (uart_circ_chars_free(circ) != 0) { + circ->buf[circ->head] = c; + circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); + ret = 1; + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +static int mp_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct sb_uart_state *state = tty->driver_data; + + return __mp_put_char(state->port, &state->info->xmit, ch); +} + +static void mp_put_chars(struct tty_struct *tty) +{ + mp_start(tty); +} + +static int mp_write(struct tty_struct *tty, const unsigned char * buf, int count) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port; + struct circ_buf *circ; + int c, ret = 0; + + if (!state || !state->info) { + return -EL3HLT; + } + + port = state->port; + circ = &state->info->xmit; + + if (!circ->buf) + return 0; + + while (1) { + c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(circ->buf + circ->head, buf, c); + + circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; + } + mp_start(tty); + return ret; +} + +static int mp_write_room(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + return uart_circ_chars_free(&state->info->xmit); +} + +static int mp_chars_in_buffer(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + return uart_circ_chars_pending(&state->info->xmit); +} + +static void mp_flush_buffer(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long flags; + + if (!state || !state->info) { + return; + } + + spin_lock_irqsave(&port->lock, flags); + uart_circ_clear(&state->info->xmit); + spin_unlock_irqrestore(&port->lock, flags); + wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); +} + +static void mp_send_xchar(struct tty_struct *tty, char ch) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long flags; + + if (port->ops->send_xchar) + port->ops->send_xchar(port, ch); + else { + port->x_char = ch; + if (ch) { + spin_lock_irqsave(&port->lock, flags); + port->ops->start_tx(port); + spin_unlock_irqrestore(&port->lock, flags); + } + } +} + +static void mp_throttle(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + if (I_IXOFF(tty)) + mp_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios.c_cflag & CRTSCTS) + uart_clear_mctrl(state->port, TIOCM_RTS); +} + +static void mp_unthrottle(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + + if (I_IXOFF(tty)) { + if (port->x_char) + port->x_char = 0; + else + mp_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios.c_cflag & CRTSCTS) + uart_set_mctrl(port, TIOCM_RTS); +} + +static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo) +{ + struct sb_uart_port *port = state->port; + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = port->type; + tmp.line = port->line; + tmp.port = port->iobase; + if (HIGH_BITS_OFFSET) + tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; + tmp.irq = port->irq; + tmp.flags = port->flags; + tmp.xmit_fifo_size = port->fifosize; + tmp.baud_base = port->uartclk / 16; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait == USF_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : + state->closing_wait; + tmp.custom_divisor = port->custom_divisor; + tmp.hub6 = port->hub6; + tmp.io_type = port->iotype; + tmp.iomem_reg_shift = port->regshift; + tmp.iomem_base = (void *)port->mapbase; + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo) +{ + struct serial_struct new_serial; + struct sb_uart_port *port = state->port; + unsigned long new_port; + unsigned int change_irq, change_port, closing_wait; + unsigned int old_custom_divisor; + unsigned int old_flags, new_flags; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + new_serial.irq = irq_canonicalize(new_serial.irq); + + closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + USF_CLOSING_WAIT_NONE : new_serial.closing_wait; + MP_STATE_LOCK(state); + + change_irq = new_serial.irq != port->irq; + change_port = new_port != port->iobase || + (unsigned long)new_serial.iomem_base != port->mapbase || + new_serial.hub6 != port->hub6 || + new_serial.io_type != port->iotype || + new_serial.iomem_reg_shift != port->regshift || + new_serial.type != port->type; + old_flags = port->flags; + new_flags = new_serial.flags; + old_custom_divisor = port->custom_divisor; + + if (!capable(CAP_SYS_ADMIN)) { + retval = -EPERM; + if (change_irq || change_port || + (new_serial.baud_base != port->uartclk / 16) || + (new_serial.close_delay != state->close_delay) || + (closing_wait != state->closing_wait) || + (new_serial.xmit_fifo_size != port->fifosize) || + (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) + goto exit; + port->flags = ((port->flags & ~UPF_USR_MASK) | + (new_flags & UPF_USR_MASK)); + port->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + if (port->ops->verify_port) + retval = port->ops->verify_port(port, &new_serial); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)) + retval = -EINVAL; + + if (retval) + goto exit; + + if (change_port || change_irq) { + retval = -EBUSY; + + if (uart_users(state) > 1) + goto exit; + + mp_shutdown(state); + } + + if (change_port) { + unsigned long old_iobase, old_mapbase; + unsigned int old_type, old_iotype, old_hub6, old_shift; + + old_iobase = port->iobase; + old_mapbase = port->mapbase; + old_type = port->type; + old_hub6 = port->hub6; + old_iotype = port->iotype; + old_shift = port->regshift; + + if (old_type != PORT_UNKNOWN) + port->ops->release_port(port); + + port->iobase = new_port; + port->type = new_serial.type; + port->hub6 = new_serial.hub6; + port->iotype = new_serial.io_type; + port->regshift = new_serial.iomem_reg_shift; + port->mapbase = (unsigned long)new_serial.iomem_base; + + if (port->type != PORT_UNKNOWN) { + retval = port->ops->request_port(port); + } else { + retval = 0; + } + + if (retval && old_type != PORT_UNKNOWN) { + port->iobase = old_iobase; + port->type = old_type; + port->hub6 = old_hub6; + port->iotype = old_iotype; + port->regshift = old_shift; + port->mapbase = old_mapbase; + retval = port->ops->request_port(port); + if (retval) + port->type = PORT_UNKNOWN; + + retval = -EBUSY; + } + } + + port->irq = new_serial.irq; + port->uartclk = new_serial.baud_base * 16; + port->flags = (port->flags & ~UPF_CHANGE_MASK) | + (new_flags & UPF_CHANGE_MASK); + port->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay; + state->closing_wait = closing_wait; + port->fifosize = new_serial.xmit_fifo_size; + if (state->info->tty) + state->info->tty->low_latency = + (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + +check_and_exit: + retval = 0; + if (port->type == PORT_UNKNOWN) + goto exit; + if (state->info->flags & UIF_INITIALIZED) { + if (((old_flags ^ port->flags) & UPF_SPD_MASK) || + old_custom_divisor != port->custom_divisor) { + if (port->flags & UPF_SPD_MASK) { + printk(KERN_NOTICE + "%s sets custom speed on ttyMP%d. This " + "is deprecated.\n", current->comm, + port->line); + } + mp_change_speed(state, NULL); + } + } else + retval = mp_startup(state, 1); +exit: + MP_STATE_UNLOCK(state); + return retval; +} + + +static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value) +{ + struct sb_uart_port *port = state->port; + unsigned int result; + + result = port->ops->tx_empty(port); + + if (port->x_char || + ((uart_circ_chars_pending(&state->info->xmit) > 0) && + !state->info->tty->stopped && !state->info->tty->hw_stopped)) + result &= ~TIOCSER_TEMT; + + return put_user(result, value); +} + +static int mp_tiocmget(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + int result = -EIO; + + MP_STATE_LOCK(state); + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + result = port->mctrl; + spin_lock_irq(&port->lock); + result |= port->ops->get_mctrl(port); + spin_unlock_irq(&port->lock); + } + MP_STATE_UNLOCK(state); + return result; +} + +static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + int ret = -EIO; + + + MP_STATE_LOCK(state); + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + mp_update_mctrl(port, set, clear); + ret = 0; + } + MP_STATE_UNLOCK(state); + + return ret; +} + +static int mp_break_ctl(struct tty_struct *tty, int break_state) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + + MP_STATE_LOCK(state); + + if (port->type != PORT_UNKNOWN) + port->ops->break_ctl(port, break_state); + + MP_STATE_UNLOCK(state); + return 0; +} + +static int mp_do_autoconfig(struct sb_uart_state *state) +{ + struct sb_uart_port *port = state->port; + int flags, ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (mutex_lock_interruptible(&state->mutex)) + return -ERESTARTSYS; + ret = -EBUSY; + if (uart_users(state) == 1) { + mp_shutdown(state); + + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + + port->ops->config_port(port, flags); + + ret = mp_startup(state, 1); + } + MP_STATE_UNLOCK(state); + return ret; +} + +static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg) +{ + struct sb_uart_port *port = state->port; + DECLARE_WAITQUEUE(wait, current); + struct sb_uart_icount cprev, cnow; + int ret; + + spin_lock_irq(&port->lock); + memcpy(&cprev, &port->icount, sizeof(struct sb_uart_icount)); + + port->ops->enable_ms(port); + spin_unlock_irq(&port->lock); + + add_wait_queue(&state->info->delta_msr_wait, &wait); + for (;;) { + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount)); + spin_unlock_irq(&port->lock); + + set_current_state(TASK_INTERRUPTIBLE); + + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { + ret = 0; + break; + } + + schedule(); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + cprev = cnow; + } + + current->state = TASK_RUNNING; + remove_wait_queue(&state->info->delta_msr_wait, &wait); + + return ret; +} + +static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt) +{ + struct serial_icounter_struct icount; + struct sb_uart_icount cnow; + struct sb_uart_port *port = state->port; + + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount)); + spin_unlock_irq(&port->lock); + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; +} + +static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) +{ + struct sb_uart_state *state = tty->driver_data; + struct mp_port *info = (struct mp_port *)state->port; + int ret = -ENOIOCTLCMD; + + + switch (cmd) { + case TIOCSMULTIDROP: + /* set multi-drop mode enable or disable, and default operation mode is H/W mode */ + if (info->port.type == PORT_16C105XA) + { + //arg &= ~0x6; + //state->port->mdmode = 0; + return set_multidrop_mode((struct sb_uart_port *)info, (unsigned int)arg); + } + ret = -ENOTSUPP; + break; + case GETDEEPFIFO: + ret = get_deep_fifo(state->port); + return ret; + case SETDEEPFIFO: + ret = set_deep_fifo(state->port,arg); + deep[state->port->line] = arg; + return ret; + case SETTTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_set_register(state->port,PAGE_4,SB105X_TTR,arg); + ttr[state->port->line] = arg; + } + return ret; + case SETRTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_set_register(state->port,PAGE_4,SB105X_RTR,arg); + rtr[state->port->line] = arg; + } + return ret; + case GETTTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_get_register(state->port,PAGE_4,SB105X_TTR); + } + return ret; + case GETRTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_get_register(state->port,PAGE_4,SB105X_RTR); + } + return ret; + + case SETFCR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_set_register(state->port,PAGE_1,SB105X_FCR,arg); + } + else{ + serial_out(info,2,arg); + } + + return ret; + case TIOCSMDADDR: + /* set multi-drop address */ + if (info->port.type == PORT_16C105XA) + { + state->port->mdmode |= MDMODE_ADDR; + return set_multidrop_addr((struct sb_uart_port *)info, (unsigned int)arg); + } + ret = -ENOTSUPP; + break; + + case TIOCGMDADDR: + /* set multi-drop address */ + if ((info->port.type == PORT_16C105XA) && (state->port->mdmode & MDMODE_ADDR)) + { + return get_multidrop_addr((struct sb_uart_port *)info); + } + ret = -ENOTSUPP; + break; + + case TIOCSENDADDR: + /* send address in multi-drop mode */ + if ((info->port.type == PORT_16C105XA) + && (state->port->mdmode & (MDMODE_ENABLE))) + { + if (mp_chars_in_buffer(tty) > 0) + { + tty_wait_until_sent(tty, 0); + } + //while ((serial_in(info, UART_LSR) & 0x60) != 0x60); + //while (sb1054_get_register(state->port, PAGE_2, SB105X_TFCR) != 0); + while ((serial_in(info, UART_LSR) & 0x60) != 0x60); + serial_out(info, UART_SCR, (int)arg); + } + break; + + case TIOCGSERIAL: + ret = mp_get_info(state, (struct serial_struct *)arg); + break; + + case TIOCSSERIAL: + ret = mp_set_info(state, (struct serial_struct *)arg); + break; + + case TIOCSERCONFIG: + ret = mp_do_autoconfig(state); + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; + /* for Multiport */ + case TIOCGNUMOFPORT: /* Get number of ports */ + return NR_PORTS; + case TIOCGGETDEVID: + return mp_devs[arg].device_id; + case TIOCGGETREV: + return mp_devs[arg].revision; + case TIOCGGETNRPORTS: + return mp_devs[arg].nr_ports; + case TIOCGGETBDNO: + return NR_BOARD; + case TIOCGGETINTERFACE: + if (mp_devs[arg].revision == 0xc0) + { + /* for SB16C1053APCI */ + return (sb1053a_get_interface(info, info->port.line)); + } + else + { + return (inb(mp_devs[arg].option_reg_addr+MP_OPTR_IIR0+(state->port->line/8))); + } + case TIOCGGETPORTTYPE: + ret = get_device_type(arg);; + return ret; + case TIOCSMULTIECHO: /* set to multi-drop mode(RS422) or echo mode(RS485)*/ + outb( ( inb(info->interface_config_addr) & ~0x03 ) | 0x01 , + info->interface_config_addr); + return 0; + case TIOCSPTPNOECHO: /* set to multi-drop mode(RS422) or echo mode(RS485) */ + outb( ( inb(info->interface_config_addr) & ~0x03 ) , + info->interface_config_addr); + return 0; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + if (tty->flags & (1 << TTY_IO_ERROR)) { + ret = -EIO; + goto out; + } + + switch (cmd) { + case TIOCMIWAIT: + ret = mp_wait_modem_status(state, arg); + break; + + case TIOCGICOUNT: + ret = mp_get_count(state, (struct serial_icounter_struct *)arg); + break; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + MP_STATE_LOCK(state); + switch (cmd) { + case TIOCSERGETLSR: /* Get line status register */ + ret = mp_get_lsr_info(state, (unsigned int *)arg); + break; + + default: { + struct sb_uart_port *port = state->port; + if (port->ops->ioctl) + ret = port->ops->ioctl(port, cmd, arg); + break; + } + } + + MP_STATE_UNLOCK(state); +out: + return ret; +} + +static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios) +{ + struct sb_uart_state *state = tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios.c_cflag; + +#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + if ((cflag ^ old_termios->c_cflag) == 0 && + RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0) + return; + + mp_change_speed(state, old_termios); + + if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) + uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); + + if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { + unsigned int mask = TIOCM_DTR; + if (!(cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) + mask |= TIOCM_RTS; + uart_set_mctrl(state->port, mask); + } + + if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { + spin_lock_irqsave(&state->port->lock, flags); + tty->hw_stopped = 0; + __mp_start(tty); + spin_unlock_irqrestore(&state->port->lock, flags); + } + + if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { + spin_lock_irqsave(&state->port->lock, flags); + if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) { + tty->hw_stopped = 1; + state->port->ops->stop_tx(state->port); + } + spin_unlock_irqrestore(&state->port->lock, flags); + } +} + +static void mp_close(struct tty_struct *tty, struct file *filp) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port; + + printk("mp_close!\n"); + if (!state || !state->port) + return; + + port = state->port; + + printk("close1 %d\n", __LINE__); + MP_STATE_LOCK(state); + + printk("close2 %d\n", __LINE__); + if (tty_hung_up_p(filp)) + goto done; + + printk("close3 %d\n", __LINE__); + if ((tty->count == 1) && (state->count != 1)) { + printk("mp_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + printk("close4 %d\n", __LINE__); + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttyMP%d: %d\n", + port->line, state->count); + state->count = 0; + } + if (state->count) + goto done; + + tty->closing = 1; + + printk("close5 %d\n", __LINE__); + if (state->closing_wait != USF_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, state->closing_wait); + + printk("close6 %d\n", __LINE__); + if (state->info->flags & UIF_INITIALIZED) { + unsigned long flags; + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_rx(port); + spin_unlock_irqrestore(&port->lock, flags); + mp_wait_until_sent(tty, port->timeout); + } + printk("close7 %d\n", __LINE__); + + mp_shutdown(state); + printk("close8 %d\n", __LINE__); + mp_flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; + state->info->tty = NULL; + if (state->info->blocked_open) + { + if (state->close_delay) + { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(state->close_delay); + } + } + else + { + mp_change_pm(state, 3); + } + printk("close8 %d\n", __LINE__); + + state->info->flags &= ~UIF_NORMAL_ACTIVE; + wake_up_interruptible(&state->info->open_wait); + +done: + printk("close done\n"); + MP_STATE_UNLOCK(state); + module_put(THIS_MODULE); +} + +static void mp_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long char_time, expire; + + if (port->type == PORT_UNKNOWN || port->fifosize == 0) + return; + + char_time = (port->timeout - HZ/50) / port->fifosize; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + + if (timeout == 0 || timeout > 2 * port->timeout) + timeout = 2 * port->timeout; + + expire = jiffies + timeout; + + while (!port->ops->tx_empty(port)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); /* might not be needed */ +} + +static void mp_hangup(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + MP_STATE_LOCK(state); + if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { + mp_flush_buffer(tty); + mp_shutdown(state); + state->count = 0; + state->info->flags &= ~UIF_NORMAL_ACTIVE; + state->info->tty = NULL; + wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->delta_msr_wait); + } + MP_STATE_UNLOCK(state); +} + +static void mp_update_termios(struct sb_uart_state *state) +{ + struct tty_struct *tty = state->info->tty; + struct sb_uart_port *port = state->port; + + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + mp_change_speed(state, NULL); + + if (tty->termios.c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); + } +} + +static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state) +{ + DECLARE_WAITQUEUE(wait, current); + struct sb_uart_info *info = state->info; + struct sb_uart_port *port = state->port; + unsigned int mctrl; + + info->blocked_open++; + state->count--; + + add_wait_queue(&info->open_wait, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + if (tty_hung_up_p(filp) || info->tty == NULL) + break; + + if (!(info->flags & UIF_INITIALIZED)) + break; + + if ((filp->f_flags & O_NONBLOCK) || + (info->tty->termios.c_cflag & CLOCAL) || + (info->tty->flags & (1 << TTY_IO_ERROR))) { + break; + } + + if (info->tty->termios.c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR); + + spin_lock_irq(&port->lock); + port->ops->enable_ms(port); + mctrl = port->ops->get_mctrl(port); + spin_unlock_irq(&port->lock); + if (mctrl & TIOCM_CAR) + break; + + MP_STATE_UNLOCK(state); + schedule(); + MP_STATE_LOCK(state); + + if (signal_pending(current)) + break; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + + state->count++; + info->blocked_open--; + + if (signal_pending(current)) + return -ERESTARTSYS; + + if (!info->tty || tty_hung_up_p(filp)) + return -EAGAIN; + + return 0; +} + +static struct sb_uart_state *uart_get(struct uart_driver *drv, int line) +{ + struct sb_uart_state *state; + + MP_MUTEX_LOCK(mp_mutex); + state = drv->state + line; + if (mutex_lock_interruptible(&state->mutex)) { + state = ERR_PTR(-ERESTARTSYS); + goto out; + } + state->count++; + if (!state->port) { + state->count--; + MP_STATE_UNLOCK(state); + state = ERR_PTR(-ENXIO); + goto out; + } + + if (!state->info) { + state->info = kmalloc(sizeof(struct sb_uart_info), GFP_KERNEL); + if (state->info) { + memset(state->info, 0, sizeof(struct sb_uart_info)); + init_waitqueue_head(&state->info->open_wait); + init_waitqueue_head(&state->info->delta_msr_wait); + + state->port->info = state->info; + + tasklet_init(&state->info->tlet, mp_tasklet_action, + (unsigned long)state); + } else { + state->count--; + MP_STATE_UNLOCK(state); + state = ERR_PTR(-ENOMEM); + } + } + +out: + MP_MUTEX_UNLOCK(mp_mutex); + return state; +} + +static int mp_open(struct tty_struct *tty, struct file *filp) +{ + struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; + struct sb_uart_state *state; + int retval; + int line = tty->index; + struct mp_port *mtpt; + + retval = -ENODEV; + if (line >= tty->driver->num) + goto fail; + + state = uart_get(drv, line); + + mtpt = (struct mp_port *)state->port; + + if (IS_ERR(state)) { + retval = PTR_ERR(state); + goto fail; + } + + tty->driver_data = state; + tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; + tty->alt_speed = 0; + state->info->tty = tty; + + if (tty_hung_up_p(filp)) { + retval = -EAGAIN; + state->count--; + MP_STATE_UNLOCK(state); + goto fail; + } + + if (state->count == 1) + mp_change_pm(state, 0); + + retval = mp_startup(state, 0); + + if (retval == 0) + retval = mp_block_til_ready(filp, state); + MP_STATE_UNLOCK(state); + + if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { + state->info->flags |= UIF_NORMAL_ACTIVE; + + mp_update_termios(state); + } + + uart_clear_mctrl(state->port, TIOCM_RTS); + try_module_get(THIS_MODULE); +fail: + return retval; +} + + +static const char *mp_type(struct sb_uart_port *port) +{ + const char *str = NULL; + + if (port->ops->type) + str = port->ops->type(port); + + if (!str) + str = "unknown"; + + return str; +} + +static void mp_change_pm(struct sb_uart_state *state, int pm_state) +{ + struct sb_uart_port *port = state->port; + if (port->ops->pm) + port->ops->pm(port, pm_state, state->pm_state); + state->pm_state = pm_state; +} + +static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port) +{ + char address[64]; + + switch (port->iotype) { + case UPIO_PORT: + snprintf(address, sizeof(address),"I/O 0x%x", port->iobase); + break; + case UPIO_HUB6: + snprintf(address, sizeof(address),"I/O 0x%x offset 0x%x", port->iobase, port->hub6); + break; + case UPIO_MEM: + snprintf(address, sizeof(address),"MMIO 0x%lx", port->mapbase); + break; + default: + snprintf(address, sizeof(address),"*unknown*" ); + strlcpy(address, "*unknown*", sizeof(address)); + break; + } + + printk( "%s%d at %s (irq = %d) is a %s\n", + drv->dev_name, port->line, address, port->irq, mp_type(port)); + +} + +static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port) +{ + unsigned int flags; + + + if (!port->iobase && !port->mapbase && !port->membase) + { + DPRINTK("%s error \n",__FUNCTION__); + return; + } + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + if (port->flags & UPF_BOOT_AUTOCONF) { + port->type = PORT_UNKNOWN; + port->ops->config_port(port, flags); + } + + if (port->type != PORT_UNKNOWN) { + unsigned long flags; + + mp_report_port(drv, port); + + spin_lock_irqsave(&port->lock, flags); + port->ops->set_mctrl(port, 0); + spin_unlock_irqrestore(&port->lock, flags); + + mp_change_pm(state, 3); + } +} + +static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state) +{ + struct sb_uart_port *port = state->port; + struct sb_uart_info *info = state->info; + + if (info && info->tty) + tty_hangup(info->tty); + + MP_STATE_LOCK(state); + + state->info = NULL; + + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + port->type = PORT_UNKNOWN; + + if (info) { + tasklet_kill(&info->tlet); + kfree(info); + } + + MP_STATE_UNLOCK(state); +} +static struct tty_operations mp_ops = { + .open = mp_open, + .close = mp_close, + .write = mp_write, + .put_char = mp_put_char, + .flush_chars = mp_put_chars, + .write_room = mp_write_room, + .chars_in_buffer= mp_chars_in_buffer, + .flush_buffer = mp_flush_buffer, + .ioctl = mp_ioctl, + .throttle = mp_throttle, + .unthrottle = mp_unthrottle, + .send_xchar = mp_send_xchar, + .set_termios = mp_set_termios, + .stop = mp_stop, + .start = mp_start, + .hangup = mp_hangup, + .break_ctl = mp_break_ctl, + .wait_until_sent= mp_wait_until_sent, +#ifdef CONFIG_PROC_FS + .proc_fops = NULL, +#endif + .tiocmget = mp_tiocmget, + .tiocmset = mp_tiocmset, +}; + +static int mp_register_driver(struct uart_driver *drv) +{ + struct tty_driver *normal = NULL; + int i, retval; + + drv->state = kmalloc(sizeof(struct sb_uart_state) * drv->nr, GFP_KERNEL); + retval = -ENOMEM; + if (!drv->state) + { + printk("SB PCI Error: Kernel memory allocation error!\n"); + goto out; + } + memset(drv->state, 0, sizeof(struct sb_uart_state) * drv->nr); + + normal = alloc_tty_driver(drv->nr); + if (!normal) + { + printk("SB PCI Error: tty allocation error!\n"); + goto out; + } + + drv->tty_driver = normal; + + normal->owner = drv->owner; + normal->magic = TTY_DRIVER_MAGIC; + normal->driver_name = drv->driver_name; + normal->name = drv->dev_name; + normal->major = drv->major; + normal->minor_start = drv->minor; + + normal->num = MAX_MP_PORT ; + + normal->type = TTY_DRIVER_TYPE_SERIAL; + normal->subtype = SERIAL_TYPE_NORMAL; + normal->init_termios = tty_std_termios; + normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + normal->driver_state = drv; + + tty_set_operations(normal, &mp_ops); + +for (i = 0; i < drv->nr; i++) { + struct sb_uart_state *state = drv->state + i; + + state->close_delay = 500; + state->closing_wait = 30000; + + mutex_init(&state->mutex); + } + + retval = tty_register_driver(normal); +out: + if (retval < 0) { + printk("Register tty driver Fail!\n"); + put_tty_driver(normal); + kfree(drv->state); + } + + return retval; +} + +void mp_unregister_driver(struct uart_driver *drv) +{ + struct tty_driver *normal = NULL; + + normal = drv->tty_driver; + + if (!normal) + { + return; + } + + tty_unregister_driver(normal); + put_tty_driver(normal); + drv->tty_driver = NULL; + + + if (drv->state) + { + kfree(drv->state); + } + +} + +static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port) +{ + struct sb_uart_state *state; + int ret = 0; + + + if (port->line >= drv->nr) + return -EINVAL; + + state = drv->state + port->line; + + MP_MUTEX_LOCK(mp_mutex); + if (state->port) { + ret = -EINVAL; + goto out; + } + + state->port = port; + + spin_lock_init(&port->lock); + port->cons = drv->cons; + port->info = state->info; + + mp_configure_port(drv, state, port); + + tty_register_device(drv->tty_driver, port->line, port->dev); + +out: + MP_MUTEX_UNLOCK(mp_mutex); + + + return ret; +} + +static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port) +{ + struct sb_uart_state *state = drv->state + port->line; + + if (state->port != port) + printk(KERN_ALERT "Removing wrong port: %p != %p\n", + state->port, port); + + MP_MUTEX_LOCK(mp_mutex); + + tty_unregister_device(drv->tty_driver, port->line); + + mp_unconfigure_port(drv, state); + state->port = NULL; + MP_MUTEX_UNLOCK(mp_mutex); + + return 0; +} + +static void autoconfig(struct mp_port *mtpt, unsigned int probeflags) +{ + unsigned char status1, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + unsigned long flags; + + unsigned char u_type; + unsigned char b_ret = 0; + + if (!mtpt->port.iobase && !mtpt->port.mapbase && !mtpt->port.membase) + return; + + DEBUG_AUTOCONF("ttyMP%d: autoconf (0x%04x, 0x%p): ", + mtpt->port.line, mtpt->port.iobase, mtpt->port.membase); + + spin_lock_irqsave(&mtpt->port.lock, flags); + + if (!(mtpt->port.flags & UPF_BUGGY_UART)) { + scratch = serial_inp(mtpt, UART_IER); + serial_outp(mtpt, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(mtpt, UART_IER) & 0x0f; + serial_outp(mtpt, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(mtpt, UART_IER) & 0x0F; + serial_outp(mtpt, UART_IER, scratch); + if (scratch2 != 0 || scratch3 != 0x0F) { + DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", + scratch2, scratch3); + goto out; + } + } + + save_mcr = serial_in(mtpt, UART_MCR); + save_lcr = serial_in(mtpt, UART_LCR); + + if (!(mtpt->port.flags & UPF_SKIP_TEST)) { + serial_outp(mtpt, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(mtpt, UART_MSR) & 0xF0; + serial_outp(mtpt, UART_MCR, save_mcr); + if (status1 != 0x90) { + DEBUG_AUTOCONF("LOOP test failed (%02x) ", + status1); + goto out; + } + } + + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, 0); + serial_outp(mtpt, UART_LCR, 0); + + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(mtpt, UART_IIR) >> 6; + + DEBUG_AUTOCONF("iir=%d ", scratch); + if(mtpt->device->nr_ports >= 8) + b_ret = read_option_register(mtpt,(MP_OPTR_DIR0 + ((mtpt->port.line)/8))); + else + b_ret = read_option_register(mtpt,MP_OPTR_DIR0); + u_type = (b_ret & 0xf0) >> 4; + if(mtpt->port.type == PORT_UNKNOWN ) + { + switch (u_type) + { + case DIR_UART_16C550: + mtpt->port.type = PORT_16C55X; + break; + case DIR_UART_16C1050: + mtpt->port.type = PORT_16C105X; + break; + case DIR_UART_16C1050A: + if (mtpt->port.line < 2) + { + mtpt->port.type = PORT_16C105XA; + } + else + { + if (mtpt->device->device_id & 0x50) + { + mtpt->port.type = PORT_16C55X; + } + else + { + mtpt->port.type = PORT_16C105X; + } + } + break; + default: + mtpt->port.type = PORT_UNKNOWN; + break; + } + } + + if(mtpt->port.type == PORT_UNKNOWN ) + { +printk("unknow2\n"); + switch (scratch) { + case 0: + case 1: + mtpt->port.type = PORT_UNKNOWN; + break; + case 2: + case 3: + mtpt->port.type = PORT_16C55X; + break; + } + } + + serial_outp(mtpt, UART_LCR, save_lcr); + + mtpt->port.fifosize = uart_config[mtpt->port.type].dfl_xmit_fifo_size; + mtpt->capabilities = uart_config[mtpt->port.type].flags; + + if (mtpt->port.type == PORT_UNKNOWN) + goto out; + serial_outp(mtpt, UART_MCR, save_mcr); + serial_outp(mtpt, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(mtpt, UART_FCR, 0); + (void)serial_in(mtpt, UART_RX); + serial_outp(mtpt, UART_IER, 0); + +out: + spin_unlock_irqrestore(&mtpt->port.lock, flags); + DEBUG_AUTOCONF("type=%s\n", uart_config[mtpt->port.type].name); +} + +static void autoconfig_irq(struct mp_port *mtpt) +{ + unsigned char save_mcr, save_ier; + unsigned long irqs; + int irq; + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(mtpt, UART_MCR); + save_ier = serial_inp(mtpt, UART_IER); + serial_outp(mtpt, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(mtpt, UART_MCR, 0); + serial_outp(mtpt, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + + serial_outp(mtpt, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(mtpt, UART_LSR); + (void)serial_inp(mtpt, UART_RX); + (void)serial_inp(mtpt, UART_IIR); + (void)serial_inp(mtpt, UART_MSR); + serial_outp(mtpt, UART_TX, 0xFF); + irq = probe_irq_off(irqs); + + serial_outp(mtpt, UART_MCR, save_mcr); + serial_outp(mtpt, UART_IER, save_ier); + + mtpt->port.irq = (irq > 0) ? irq : 0; +} + +static void multi_stop_tx(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + if (mtpt->ier & UART_IER_THRI) { + mtpt->ier &= ~UART_IER_THRI; + serial_out(mtpt, UART_IER, mtpt->ier); + } + + tasklet_schedule(&port->info->tlet); +} + +static void multi_start_tx(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + if (!(mtpt->ier & UART_IER_THRI)) { + mtpt->ier |= UART_IER_THRI; + serial_out(mtpt, UART_IER, mtpt->ier); + } +} + +static void multi_stop_rx(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + mtpt->ier &= ~UART_IER_RLSI; + mtpt->port.read_status_mask &= ~UART_LSR_DR; + serial_out(mtpt, UART_IER, mtpt->ier); +} + +static void multi_enable_ms(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + mtpt->ier |= UART_IER_MSI; + serial_out(mtpt, UART_IER, mtpt->ier); +} + + +static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status ) +{ + struct tty_struct *tty = mtpt->port.info->tty; + unsigned char lsr = *status; + int max_count = 256; + unsigned char ch; + char flag; + + //lsr &= mtpt->port.read_status_mask; + + do { + if ((lsr & UART_LSR_PE) && (mtpt->port.mdmode & MDMODE_ENABLE)) + { + ch = serial_inp(mtpt, UART_RX); + } + else if (lsr & UART_LSR_SPECIAL) + { + flag = 0; + ch = serial_inp(mtpt, UART_RX); + + if (lsr & UART_LSR_BI) + { + + mtpt->port.icount.brk++; + flag = TTY_BREAK; + + if (sb_uart_handle_break(&mtpt->port)) + goto ignore_char; + } + if (lsr & UART_LSR_PE) + { + mtpt->port.icount.parity++; + flag = TTY_PARITY; + } + if (lsr & UART_LSR_FE) + { + mtpt->port.icount.frame++; + flag = TTY_FRAME; + } + if (lsr & UART_LSR_OE) + { + mtpt->port.icount.overrun++; + flag = TTY_OVERRUN; + } + tty_insert_flip_char(tty, ch, flag); + } + else + { + ch = serial_inp(mtpt, UART_RX); + tty_insert_flip_char(tty, ch, 0); + } +ignore_char: + lsr = serial_inp(mtpt, UART_LSR); + } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); + + tty_flip_buffer_push(tty); +} + + + + +static _INLINE_ void transmit_chars(struct mp_port *mtpt) +{ + struct circ_buf *xmit = &mtpt->port.info->xmit; + int count; + + if (mtpt->port.x_char) { + serial_outp(mtpt, UART_TX, mtpt->port.x_char); + mtpt->port.icount.tx++; + mtpt->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&mtpt->port)) { + multi_stop_tx(&mtpt->port); + return; + } + + count = uart_circ_chars_pending(xmit); + + if(count > mtpt->port.fifosize) + { + count = mtpt->port.fifosize; + } + + printk("[%d] mdmode: %x\n", mtpt->port.line, mtpt->port.mdmode); + do { +#if 0 + /* check multi-drop mode */ + if ((mtpt->port.mdmode & (MDMODE_ENABLE | MDMODE_ADDR)) == (MDMODE_ENABLE | MDMODE_ADDR)) + { + printk("send address\n"); + /* send multi-drop address */ + serial_out(mtpt, UART_SCR, xmit->buf[xmit->tail]); + } + else +#endif + { + serial_out(mtpt, UART_TX, xmit->buf[xmit->tail]); + } + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + mtpt->port.icount.tx++; + } while (--count > 0); +} + + + +static _INLINE_ void check_modem_status(struct mp_port *mtpt) +{ + int status; + + status = serial_in(mtpt, UART_MSR); + + if ((status & UART_MSR_ANY_DELTA) == 0) + return; + + if (status & UART_MSR_TERI) + mtpt->port.icount.rng++; + if (status & UART_MSR_DDSR) + mtpt->port.icount.dsr++; + if (status & UART_MSR_DDCD) + sb_uart_handle_dcd_change(&mtpt->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + sb_uart_handle_cts_change(&mtpt->port, status & UART_MSR_CTS); + + wake_up_interruptible(&mtpt->port.info->delta_msr_wait); +} + +static inline void multi_handle_port(struct mp_port *mtpt) +{ + unsigned int status = serial_inp(mtpt, UART_LSR); + + //printk("lsr: %x\n", status); + + if ((status & UART_LSR_DR) || (status & UART_LSR_SPECIAL)) + receive_chars(mtpt, &status); + check_modem_status(mtpt); + if (status & UART_LSR_THRE) + { + if ((mtpt->port.type == PORT_16C105X) + || (mtpt->port.type == PORT_16C105XA)) + transmit_chars(mtpt); + else + { + if (mtpt->interface >= RS485NE) + uart_set_mctrl(&mtpt->port, TIOCM_RTS); + + transmit_chars(mtpt); + + + if (mtpt->interface >= RS485NE) + { + while((status=serial_in(mtpt,UART_LSR) &0x60)!=0x60); + uart_clear_mctrl(&mtpt->port, TIOCM_RTS); + } + } + } +} + + + +static irqreturn_t multi_interrupt(int irq, void *dev_id) +{ + struct irq_info *iinfo = dev_id; + struct list_head *lhead, *end = NULL; + int pass_counter = 0; + + + spin_lock(&iinfo->lock); + + lhead = iinfo->head; + do { + struct mp_port *mtpt; + unsigned int iir; + + mtpt = list_entry(lhead, struct mp_port, list); + + iir = serial_in(mtpt, UART_IIR); + printk("intrrupt! port %d, iir 0x%x\n", mtpt->port.line, iir); //wlee + if (!(iir & UART_IIR_NO_INT)) + { + printk("interrupt handle\n"); + spin_lock(&mtpt->port.lock); + multi_handle_port(mtpt); + spin_unlock(&mtpt->port.lock); + + end = NULL; + } else if (end == NULL) + end = lhead; + + lhead = lhead->next; + if (lhead == iinfo->head && pass_counter++ > PASS_LIMIT) + { + printk(KERN_ERR "multi: too much work for " + "irq%d\n", irq); + printk( "multi: too much work for " + "irq%d\n", irq); + break; + } + } while (lhead != end); + + spin_unlock(&iinfo->lock); + + + return IRQ_HANDLED; +} + +static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &mtpt->list) + i->head = i->head->next; + list_del(&mtpt->list); + } else { + i->head = NULL; + } + + spin_unlock_irq(&i->lock); +} + +static int serial_link_irq_chain(struct mp_port *mtpt) +{ + struct irq_info *i = irq_lists + mtpt->port.irq; + int ret, irq_flags = mtpt->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0; + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&mtpt->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&mtpt->list); + i->head = &mtpt->list; + spin_unlock_irq(&i->lock); + + ret = request_irq(mtpt->port.irq, multi_interrupt, + irq_flags, "serial", i); + if (ret < 0) + serial_do_unlink(i, mtpt); + } + + return ret; +} + + + + +static void serial_unlink_irq_chain(struct mp_port *mtpt) +{ + struct irq_info *i = irq_lists + mtpt->port.irq; + + if (list_empty(i->head)) + { + free_irq(mtpt->port.irq, i); + } + serial_do_unlink(i, mtpt); +} + +static void multi_timeout(unsigned long data) +{ + struct mp_port *mtpt = (struct mp_port *)data; + + + spin_lock(&mtpt->port.lock); + multi_handle_port(mtpt); + spin_unlock(&mtpt->port.lock); + + mod_timer(&mtpt->timer, jiffies+1 ); +} + +static unsigned int multi_tx_empty(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&mtpt->port.lock, flags); + ret = serial_in(mtpt, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&mtpt->port.lock, flags); + + return ret; +} + + +static unsigned int multi_get_mctrl(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned char status; + unsigned int ret; + + status = serial_in(mtpt, UART_MSR); + + ret = 0; + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned char mcr = 0; + + mctrl &= 0xff; + + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + + serial_out(mtpt, UART_MCR, mcr); +} + + +static void multi_break_ctl(struct sb_uart_port *port, int break_state) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + + spin_lock_irqsave(&mtpt->port.lock, flags); + if (break_state == -1) + mtpt->lcr |= UART_LCR_SBC; + else + mtpt->lcr &= ~UART_LCR_SBC; + serial_out(mtpt, UART_LCR, mtpt->lcr); + spin_unlock_irqrestore(&mtpt->port.lock, flags); +} + + + +static int multi_startup(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + int retval; + + mtpt->capabilities = uart_config[mtpt->port.type].flags; + mtpt->mcr = 0; + + if (mtpt->capabilities & UART_CLEAR_FIFO) { + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(mtpt, UART_FCR, 0); + } + + (void) serial_inp(mtpt, UART_LSR); + (void) serial_inp(mtpt, UART_RX); + (void) serial_inp(mtpt, UART_IIR); + (void) serial_inp(mtpt, UART_MSR); + //test-wlee 9-bit disable + serial_outp(mtpt, UART_MSR, 0); + + + if (!(mtpt->port.flags & UPF_BUGGY_UART) && + (serial_inp(mtpt, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", mtpt->port.line); + //return -ENODEV; + } + + if ((!is_real_interrupt(mtpt->port.irq)) || (mtpt->poll_type==TYPE_POLL)) { + unsigned int timeout = mtpt->port.timeout; + + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + + mtpt->timer.data = (unsigned long)mtpt; + mod_timer(&mtpt->timer, jiffies + timeout); + } + else + { + retval = serial_link_irq_chain(mtpt); + if (retval) + return retval; + } + + serial_outp(mtpt, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&mtpt->port.lock, flags); + if ((is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_INTERRUPT)) + mtpt->port.mctrl |= TIOCM_OUT2; + + multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); + spin_unlock_irqrestore(&mtpt->port.lock, flags); + + + mtpt->ier = UART_IER_RLSI | UART_IER_RDI; + serial_outp(mtpt, UART_IER, mtpt->ier); + + (void) serial_inp(mtpt, UART_LSR); + (void) serial_inp(mtpt, UART_RX); + (void) serial_inp(mtpt, UART_IIR); + (void) serial_inp(mtpt, UART_MSR); + + return 0; +} + + + +static void multi_shutdown(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + + + mtpt->ier = 0; + serial_outp(mtpt, UART_IER, 0); + + spin_lock_irqsave(&mtpt->port.lock, flags); + mtpt->port.mctrl &= ~TIOCM_OUT2; + + multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); + spin_unlock_irqrestore(&mtpt->port.lock, flags); + + serial_out(mtpt, UART_LCR, serial_inp(mtpt, UART_LCR) & ~UART_LCR_SBC); + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_outp(mtpt, UART_FCR, 0); + + + (void) serial_in(mtpt, UART_RX); + + if ((!is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_POLL)) + { + del_timer_sync(&mtpt->timer); + } + else + { + serial_unlink_irq_chain(mtpt); + } +} + + + +static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud) +{ + unsigned int quot; + + if ((port->flags & UPF_MAGIC_MULTIPLIER) && + baud == (port->uartclk/4)) + quot = 0x8001; + else if ((port->flags & UPF_MAGIC_MULTIPLIER) && + baud == (port->uartclk/8)) + quot = 0x8002; + else + quot = sb_uart_get_divisor(port, baud); + + return quot; +} + + + + +static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned char cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + default: + case CS8: + cval = 0x03; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= 0x04; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + +#ifdef CMSPAR + if (termios->c_cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + baud = sb_uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = multi_get_divisor(port, baud); + + if (mtpt->capabilities & UART_USE_FIFO) { + //if (baud < 2400) + // fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + //else + // fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + + // fcr = UART_FCR_ENABLE_FIFO | 0x90; + fcr = fcr_arr[mtpt->port.line]; + } + + spin_lock_irqsave(&mtpt->port.lock, flags); + + sb_uart_update_timeout(port, termios->c_cflag, baud); + + mtpt->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + mtpt->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + mtpt->port.read_status_mask |= UART_LSR_BI; + + mtpt->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + mtpt->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + mtpt->port.ignore_status_mask |= UART_LSR_BI; + if (termios->c_iflag & IGNPAR) + mtpt->port.ignore_status_mask |= UART_LSR_OE; + } + + if ((termios->c_cflag & CREAD) == 0) + mtpt->port.ignore_status_mask |= UART_LSR_DR; + + mtpt->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&mtpt->port, termios->c_cflag)) + mtpt->ier |= UART_IER_MSI; + + serial_out(mtpt, UART_IER, mtpt->ier); + + if (mtpt->capabilities & UART_STARTECH) { + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, + termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0); + } + + serial_outp(mtpt, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + + serial_outp(mtpt, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(mtpt, UART_DLM, quot >> 8); /* MS of divisor */ + + serial_outp(mtpt, UART_LCR, cval); /* reset DLAB */ + mtpt->lcr = cval; /* Save LCR */ + + if (fcr & UART_FCR_ENABLE_FIFO) { + /* emulated UARTs (Lucent Venus 167x) need two steps */ + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); + } + + serial_outp(mtpt, UART_FCR, fcr); /* set fcr */ + + + if ((mtpt->port.type == PORT_16C105X) + || (mtpt->port.type == PORT_16C105XA)) + { + if(deep[mtpt->port.line]!=0) + set_deep_fifo(port, ENABLE); + + if (mtpt->interface != RS232) + set_auto_rts(port,mtpt->interface); + + } + else + { + if (mtpt->interface >= RS485NE) + { + uart_clear_mctrl(&mtpt->port, TIOCM_RTS); + } + } + + if(mtpt->device->device_id == PCI_DEVICE_ID_MP4M) + { + SendATCommand(mtpt); + printk("SendATCommand\n"); + } + multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); + spin_unlock_irqrestore(&mtpt->port.lock, flags); +} + +static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate) +{ + struct mp_port *mtpt = (struct mp_port *)port; + if (state) { + if (mtpt->capabilities & UART_STARTECH) { + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, UART_EFR_ECB); + serial_outp(mtpt, UART_LCR, 0); + serial_outp(mtpt, UART_IER, UART_IERX_SLEEP); + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, 0); + serial_outp(mtpt, UART_LCR, 0); + } + + if (mtpt->pm) + mtpt->pm(port, state, oldstate); + } + else + { + if (mtpt->capabilities & UART_STARTECH) { + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, UART_EFR_ECB); + serial_outp(mtpt, UART_LCR, 0); + serial_outp(mtpt, UART_IER, 0); + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, 0); + serial_outp(mtpt, UART_LCR, 0); + } + + if (mtpt->pm) + mtpt->pm(port, state, oldstate); + } +} + +static void multi_release_std_resource(struct mp_port *mtpt) +{ + unsigned int size = 8 << mtpt->port.regshift; + + switch (mtpt->port.iotype) { + case UPIO_MEM: + if (!mtpt->port.mapbase) + break; + + if (mtpt->port.flags & UPF_IOREMAP) { + iounmap(mtpt->port.membase); + mtpt->port.membase = NULL; + } + + release_mem_region(mtpt->port.mapbase, size); + break; + + case UPIO_HUB6: + case UPIO_PORT: + release_region(mtpt->port.iobase,size); + break; + } +} + +static void multi_release_port(struct sb_uart_port *port) +{ +} + +static int multi_request_port(struct sb_uart_port *port) +{ + return 0; +} + +static void multi_config_port(struct sb_uart_port *port, int flags) +{ + struct mp_port *mtpt = (struct mp_port *)port; + int probeflags = PROBE_ANY; + + if (flags & UART_CONFIG_TYPE) + autoconfig(mtpt, probeflags); + if (mtpt->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) + autoconfig_irq(mtpt); + + if (mtpt->port.type == PORT_UNKNOWN) + multi_release_std_resource(mtpt); +} + +static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser) +{ + if (ser->irq >= NR_IRQS || ser->irq < 0 || + ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || + ser->type == PORT_STARTECH) + return -EINVAL; + return 0; +} + +static const char * multi_type(struct sb_uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct sb_uart_ops multi_pops = { + .tx_empty = multi_tx_empty, + .set_mctrl = multi_set_mctrl, + .get_mctrl = multi_get_mctrl, + .stop_tx = multi_stop_tx, + .start_tx = multi_start_tx, + .stop_rx = multi_stop_rx, + .enable_ms = multi_enable_ms, + .break_ctl = multi_break_ctl, + .startup = multi_startup, + .shutdown = multi_shutdown, + .set_termios = multi_set_termios, + .pm = multi_pm, + .type = multi_type, + .release_port = multi_release_port, + .request_port = multi_request_port, + .config_port = multi_config_port, + .verify_port = multi_verify_port, +}; + +static struct uart_driver multi_reg = { + .owner = THIS_MODULE, + .driver_name = "goldel_tulip", + .dev_name = "ttyMP", + .major = SB_TTY_MP_MAJOR, + .minor = 0, + .nr = MAX_MP_PORT, + .cons = NULL, +}; + +static void __init multi_init_ports(void) +{ + struct mp_port *mtpt; + static int first = 1; + int i,j,k; + unsigned char osc; + unsigned char b_ret = 0; + static struct mp_device_t * sbdev; + + if (!first) + return; + first = 0; + + mtpt = multi_ports; + + for (k=0;knr_ports; i++, mtpt++) + { + mtpt->device = sbdev; + mtpt->port.iobase = sbdev->uart_access_addr + 8*i; + mtpt->port.irq = sbdev->irq; + if ( ((sbdev->device_id == PCI_DEVICE_ID_MP4)&&(sbdev->revision==0x91))) + mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i; + else if (sbdev->revision == 0xc0) + mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + (i & 0x1); + else + mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i/8; + + mtpt->option_base_addr = sbdev->option_reg_addr; + + mtpt->poll_type = sbdev->poll_type; + + mtpt->port.uartclk = BASE_BAUD * 16; + + /* get input clock infomation */ + osc = inb(sbdev->option_reg_addr + MP_OPTR_DIR0 + i/8) & 0x0F; + if (osc==0x0f) + osc = 0; + for(j=0;jport.uartclk *= 2; + mtpt->port.flags |= STD_COM_FLAGS | UPF_SHARE_IRQ ; + mtpt->port.iotype = UPIO_PORT; + mtpt->port.ops = &multi_pops; + + if (sbdev->revision == 0xc0) + { + /* for SB16C1053APCI */ + b_ret = sb1053a_get_interface(mtpt, i); + } + else + { + b_ret = read_option_register(mtpt,(MP_OPTR_IIR0 + i/8)); + printk("IIR_RET = %x\n",b_ret); + } + + if(IIR_RS232 == (b_ret & IIR_RS232)) + { + mtpt->interface = RS232; + } + if(IIR_RS422 == (b_ret & IIR_RS422)) + { + mtpt->interface = RS422PTP; + } + if(IIR_RS485 == (b_ret & IIR_RS485)) + { + mtpt->interface = RS485NE; + } + } + } +} + +static void __init multi_register_ports(struct uart_driver *drv) +{ + int i; + + multi_init_ports(); + + for (i = 0; i < NR_PORTS; i++) { + struct mp_port *mtpt = &multi_ports[i]; + + mtpt->port.line = i; + mtpt->port.ops = &multi_pops; + init_timer(&mtpt->timer); + mtpt->timer.function = multi_timeout; + mp_add_one_port(drv, &mtpt->port); + } +} + +/** + * pci_remap_base - remap BAR value of pci device + * + * PARAMETERS + * pcidev - pci_dev structure address + * offset - BAR offset PCI_BASE_ADDRESS_0 ~ PCI_BASE_ADDRESS_4 + * address - address to be changed BAR value + * size - size of address space + * + * RETURNS + * If this function performs successful, it returns 0. Otherwise, It returns -1. + */ +static int pci_remap_base(struct pci_dev *pcidev, unsigned int offset, + unsigned int address, unsigned int size) +{ +#if 0 + struct resource *root; + unsigned index = (offset - 0x10) >> 2; +#endif + + pci_write_config_dword(pcidev, offset, address); +#if 0 + root = pcidev->resource[index].parent; + release_resource(&pcidev->resource[index]); + address &= ~0x1; + pcidev->resource[index].start = address; + pcidev->resource[index].end = address + size - 1; + + if (request_resource(root, &pcidev->resource[index]) != NULL) + { + printk(KERN_ERR "pci remap conflict!! 0x%x\n", address); + return (-1); + } +#endif + + return (0); +} + +static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd) +{ + static struct mp_device_t * sbdev = mp_devs; + unsigned long addr = 0; + int j; + struct resource * ret = NULL; + + sbdev->device_id = brd.device_id; + pci_read_config_byte(pcidev, PCI_CLASS_REVISION, &(sbdev->revision)); + sbdev->name = brd.name; + sbdev->uart_access_addr = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK; + + /* check revision. The SB16C1053APCI's option i/o address is BAR4 */ + if (sbdev->revision == 0xc0) + { + /* SB16C1053APCI */ + sbdev->option_reg_addr = pcidev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK; + } + else + { + sbdev->option_reg_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; + } +#if 1 + if (sbdev->revision == 0xc0) + { + outb(0x00, sbdev->option_reg_addr + MP_OPTR_GPOCR); + inb(sbdev->option_reg_addr + MP_OPTR_GPOCR); + outb(0x83, sbdev->option_reg_addr + MP_OPTR_GPOCR); + } +#endif + + sbdev->irq = pcidev->irq; + + if ((brd.device_id & 0x0800) || !(brd.device_id &0xff00)) + { + sbdev->poll_type = TYPE_INTERRUPT; + } + else + { + sbdev->poll_type = TYPE_POLL; + } + + /* codes which is specific to each board*/ + switch(brd.device_id){ + case PCI_DEVICE_ID_MP1 : + case PCIE_DEVICE_ID_MP1 : + case PCIE_DEVICE_ID_MP1E : + case PCIE_DEVICE_ID_GT_MP1 : + sbdev->nr_ports = 1; + break; + case PCI_DEVICE_ID_MP2 : + case PCIE_DEVICE_ID_MP2 : + case PCIE_DEVICE_ID_GT_MP2 : + case PCIE_DEVICE_ID_MP2B : + case PCIE_DEVICE_ID_MP2E : + sbdev->nr_ports = 2; + + /* serial base address remap */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + } + break; + case PCI_DEVICE_ID_MP4 : + case PCI_DEVICE_ID_MP4A : + case PCIE_DEVICE_ID_MP4 : + case PCI_DEVICE_ID_GT_MP4 : + case PCI_DEVICE_ID_GT_MP4A : + case PCIE_DEVICE_ID_GT_MP4 : + case PCI_DEVICE_ID_MP4M : + case PCIE_DEVICE_ID_MP4B : + sbdev->nr_ports = 4; + + if(sbdev->revision == 0x91){ + sbdev->reserved_addr[0] = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK; + outb(0x03 , sbdev->reserved_addr[0] + 0x01); + outb(0x03 , sbdev->reserved_addr[0] + 0x02); + outb(0x01 , sbdev->reserved_addr[0] + 0x20); + outb(0x00 , sbdev->reserved_addr[0] + 0x21); + request_region(sbdev->reserved_addr[0], 32, sbdev->name); + sbdev->uart_access_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; + sbdev->option_reg_addr = pcidev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK; + } + + /* SB16C1053APCI */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 8); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 24, 8); + } + break; + case PCI_DEVICE_ID_MP6 : + case PCI_DEVICE_ID_MP6A : + case PCI_DEVICE_ID_GT_MP6 : + case PCI_DEVICE_ID_GT_MP6A : + sbdev->nr_ports = 6; + + /* SB16C1053APCI */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 16); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 32, 16); + } + break; + case PCI_DEVICE_ID_MP8 : + case PCIE_DEVICE_ID_MP8 : + case PCI_DEVICE_ID_GT_MP8 : + case PCIE_DEVICE_ID_GT_MP8 : + case PCIE_DEVICE_ID_MP8B : + sbdev->nr_ports = 8; + break; + case PCI_DEVICE_ID_MP32 : + case PCIE_DEVICE_ID_MP32 : + case PCI_DEVICE_ID_GT_MP32 : + case PCIE_DEVICE_ID_GT_MP32 : + { + int portnum_hex=0; + portnum_hex = inb(sbdev->option_reg_addr); + sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16); + } + break; + case PCI_DEVICE_ID_MP2S1P : + sbdev->nr_ports = 2; + + /* SB16C1053APCI */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + } + + /* add PC compatible parallel port */ + parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0); + break; + case PCI_DEVICE_ID_MP1P : + /* add PC compatible parallel port */ + parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0); + break; + } + + ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name); + + if (sbdev->revision == 0xc0) + { + ret = request_region(sbdev->option_reg_addr, 0x40, sbdev->name); + } + else + { + ret = request_region(sbdev->option_reg_addr, 0x20, sbdev->name); + } + + + NR_BOARD++; + NR_PORTS += sbdev->nr_ports; + + /* Enable PCI interrupt */ + addr = sbdev->option_reg_addr + MP_OPTR_IMR0; + for(j=0; j < (sbdev->nr_ports/8)+1; j++) + { + if (sbdev->poll_type == TYPE_INTERRUPT) + { + outb(0xff,addr +j); + } + } + sbdev++; + + return 0; +} + +static int __init multi_init(void) +{ + int ret, i; + struct pci_dev *dev = NULL; + + if(fcr_count==0) + { + for(i=0;i<256;i++) + { + fcr_arr[i] = 0x01; + + } + } + if(deep_count==0) + { + for(i=0;i<256;i++) + { + deep[i] = 1; + + } + } + if(rtr_count==0) + { + for(i=0;i<256;i++) + { + rtr[i] = 0x10; + } + } + if(ttr_count==0) + { + for(i=0;i<256;i++) + { + ttr[i] = 0x38; + } + } + + +printk("MULTI INIT\n"); + for( i=0; i< mp_nrpcibrds; i++) + { + + while( (dev = pci_get_device(mp_pciboards[i].vendor_id, mp_pciboards[i].device_id, dev) ) ) + + { +printk("FOUND~~~\n"); +// Cent OS bug fix +// if (mp_pciboards[i].device_id & 0x0800) + { + int status; + pci_disable_device(dev); + status = pci_enable_device(dev); + + if (status != 0) + { + printk("Multiport Board Enable Fail !\n\n"); + status = -ENXIO; + return status; + } + } + + init_mp_dev(dev, mp_pciboards[i]); + } + } + + for (i = 0; i < NR_IRQS; i++) + spin_lock_init(&irq_lists[i].lock); + + ret = mp_register_driver(&multi_reg); + + if (ret >= 0) + multi_register_ports(&multi_reg); + + return ret; +} + +static void __exit multi_exit(void) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + mp_remove_one_port(&multi_reg, &multi_ports[i].port); + + mp_unregister_driver(&multi_reg); +} + +module_init(multi_init); +module_exit(multi_exit); + +MODULE_DESCRIPTION("SystemBase Multiport PCI/PCIe CORE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/sb105x/sb_pci_mp.h b/drivers/staging/sb105x/sb_pci_mp.h new file mode 100644 index 00000000000..f33efde07b9 --- /dev/null +++ b/drivers/staging/sb105x/sb_pci_mp.h @@ -0,0 +1,293 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + + +#define MP_TERMIOS ktermios + +#include "sb_mp_register.h" +#include "sb_ser_core.h" + +#define DRIVER_VERSION "1.1" +#define DRIVER_DATE "2012/01/05" +#define DRIVER_AUTHOR "SYSTEMBASE" +#define DRIVER_DESC "SystemBase PCI/PCIe Multiport Core" + +#define SB_TTY_MP_MAJOR 54 +#define PCI_VENDOR_ID_MULTIPORT 0x14A1 + +#define PCI_DEVICE_ID_MP1 0x4d01 +#define PCI_DEVICE_ID_MP2 0x4d02 +#define PCI_DEVICE_ID_MP4 0x4d04 +#define PCI_DEVICE_ID_MP4A 0x4d54 +#define PCI_DEVICE_ID_MP6 0x4d06 +#define PCI_DEVICE_ID_MP6A 0x4d56 +#define PCI_DEVICE_ID_MP8 0x4d08 +#define PCI_DEVICE_ID_MP32 0x4d32 +/* Parallel port */ +#define PCI_DEVICE_ID_MP1P 0x4301 +#define PCI_DEVICE_ID_MP2S1P 0x4303 + +#define PCIE_DEVICE_ID_MP1 0x4501 +#define PCIE_DEVICE_ID_MP2 0x4502 +#define PCIE_DEVICE_ID_MP4 0x4504 +#define PCIE_DEVICE_ID_MP8 0x4508 +#define PCIE_DEVICE_ID_MP32 0x4532 + +#define PCIE_DEVICE_ID_MP1E 0x4e01 +#define PCIE_DEVICE_ID_MP2E 0x4e02 +#define PCIE_DEVICE_ID_MP2B 0x4b02 +#define PCIE_DEVICE_ID_MP4B 0x4b04 +#define PCIE_DEVICE_ID_MP8B 0x4b08 + +#define PCI_DEVICE_ID_GT_MP4 0x0004 +#define PCI_DEVICE_ID_GT_MP4A 0x0054 +#define PCI_DEVICE_ID_GT_MP6 0x0006 +#define PCI_DEVICE_ID_GT_MP6A 0x0056 +#define PCI_DEVICE_ID_GT_MP8 0x0008 +#define PCI_DEVICE_ID_GT_MP32 0x0032 + +#define PCIE_DEVICE_ID_GT_MP1 0x1501 +#define PCIE_DEVICE_ID_GT_MP2 0x1502 +#define PCIE_DEVICE_ID_GT_MP4 0x1504 +#define PCIE_DEVICE_ID_GT_MP8 0x1508 +#define PCIE_DEVICE_ID_GT_MP32 0x1532 + +#define PCI_DEVICE_ID_MP4M 0x4604 //modem + +#define MAX_MP_DEV 8 +#define BD_MAX_PORT 32 /* Max serial port in one board */ +#define MAX_MP_PORT 256 /* Max serial port in one PC */ + +#define PORT_16C105XA 3 +#define PORT_16C105X 2 +#define PORT_16C55X 1 + +#define ENABLE 1 +#define DISABLE 0 + +/* ioctls */ +#define TIOCGNUMOFPORT 0x545F +#define TIOCSMULTIECHO 0x5440 +#define TIOCSPTPNOECHO 0x5441 + +#define TIOCGOPTIONREG 0x5461 +#define TIOCGDISABLEIRQ 0x5462 +#define TIOCGENABLEIRQ 0x5463 +#define TIOCGSOFTRESET 0x5464 +#define TIOCGSOFTRESETR 0x5465 +#define TIOCGREGINFO 0x5466 +#define TIOCGGETLSR 0x5467 +#define TIOCGGETDEVID 0x5468 +#define TIOCGGETBDNO 0x5469 +#define TIOCGGETINTERFACE 0x546A +#define TIOCGGETREV 0x546B +#define TIOCGGETNRPORTS 0x546C +#define TIOCGGETPORTTYPE 0x546D +#define GETDEEPFIFO 0x54AA +#define SETDEEPFIFO 0x54AB +#define SETFCR 0x54BA +#define SETTTR 0x54B1 +#define SETRTR 0x54B2 +#define GETTTR 0x54B3 +#define GETRTR 0x54B4 + +/* multi-drop mode related ioctl commands */ +#define TIOCSMULTIDROP 0x5470 +#define TIOCSMDADDR 0x5471 +#define TIOCGMDADDR 0x5472 +#define TIOCSENDADDR 0x5473 + + +/* serial interface */ +#define RS232 1 +#define RS422PTP 2 +#define RS422MD 3 +#define RS485NE 4 +#define RS485ECHO 5 + +#define serial_inp(up, offset) serial_in(up, offset) +#define serial_outp(up, offset, value) serial_out(up, offset, value) + +#define PASS_LIMIT 256 +#define is_real_interrupt(irq) ((irq) != 0) + +#define PROBE_ANY (~0) + +static DEFINE_MUTEX(mp_mutex); +#define MP_MUTEX_LOCK(x) mutex_lock(&(x)) +#define MP_MUTEX_UNLOCK(x) mutex_unlock(&(x)) +#define MP_STATE_LOCK(x) mutex_lock(&((x)->mutex)) +#define MP_STATE_UNLOCK(x) mutex_unlock(&((x)->mutex)) + + +#define UART_LSR_SPECIAL 0x1E + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) +#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) + + +//#define MP_DEBUG 1 +#undef MP_DEBUG + +#ifdef MP_DEBUG +#define DPRINTK(x...) printk(x) +#else +#define DPRINTK(x...) do { } while (0) +#endif + +#ifdef MP_DEBUG +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#ifdef MP_DEBUG +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +#define SERIAL_INLINE +#endif +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +#define TYPE_POLL 1 +#define TYPE_INTERRUPT 2 + + +struct mp_device_t { + unsigned short device_id; + unsigned char revision; + char *name; + unsigned long uart_access_addr; + unsigned long option_reg_addr; + unsigned long reserved_addr[4]; + int irq; + int nr_ports; + int poll_type; +}; + +typedef struct mppcibrd { + char *name; + unsigned short vendor_id; + unsigned short device_id; +} mppcibrd_t; + +static mppcibrd_t mp_pciboards[] = { + + { "Multi-1 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP1} , + { "Multi-2 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP2} , + { "Multi-4 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4} , + { "Multi-4 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4A} , + { "Multi-6 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP6} , + { "Multi-6 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP6A} , + { "Multi-8 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP8} , + { "Multi-32 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP32} , + + { "Multi-1P PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP1P} , + { "Multi-2S1P PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP2S1P} , + + { "Multi-4(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP4} , + { "Multi-4(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP4A} , + { "Multi-6(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP6} , + { "Multi-6(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP6A} , + { "Multi-8(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP8} , + { "Multi-32(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP32} , + + { "Multi-1 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP1} , + { "Multi-2 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2} , + { "Multi-4 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP4} , + { "Multi-8 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP8} , + { "Multi-32 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP32} , + + { "Multi-1 PCIe E", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP1E} , + { "Multi-2 PCIe E", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2E} , + { "Multi-2 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2B} , + { "Multi-4 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP4B} , + { "Multi-8 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP8B} , + + { "Multi-1(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP1} , + { "Multi-2(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP2} , + { "Multi-4(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP4} , + { "Multi-8(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP8} , + { "Multi-32(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP32} , + + { "Multi-4M PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4M} , +}; + +struct mp_port { + struct sb_uart_port port; + + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned int capabilities; /* port capabilities */ + unsigned short rev; + unsigned char acr; + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned char mcr_mask; /* mask of user bits */ + unsigned char mcr_force; /* mask of forced bits */ + unsigned char lsr_break_flag; + + void (*pm)(struct sb_uart_port *port, + unsigned int state, unsigned int old); + struct mp_device_t *device; + unsigned long interface_config_addr; + unsigned long option_base_addr; + unsigned char interface; + unsigned char poll_type; +}; + +struct irq_info { + spinlock_t lock; + struct list_head *head; +}; + +struct sb105x_uart_config { + char *name; + int dfl_xmit_fifo_size; + int flags; +}; + +static const struct sb105x_uart_config uart_config[] = { + { "unknown", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "SB16C1050", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "SB16C1050A", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, +}; + + + diff --git a/drivers/staging/sb105x/sb_ser_core.h b/drivers/staging/sb105x/sb_ser_core.h new file mode 100644 index 00000000000..c8fb9917329 --- /dev/null +++ b/drivers/staging/sb105x/sb_ser_core.h @@ -0,0 +1,368 @@ +#include + +#define UART_CONFIG_TYPE (1 << 0) +#define UART_CONFIG_IRQ (1 << 1) +#define UPIO_PORT (0) +#define UPIO_HUB6 (1) +#define UPIO_MEM (2) +#define UPIO_MEM32 (3) +#define UPIO_AU (4) /* Au1x00 type IO */ +#define UPIO_TSI (5) /* Tsi108/109 type IO */ +#define UPF_FOURPORT (1 << 1) +#define UPF_SAK (1 << 2) +#define UPF_SPD_MASK (0x1030) +#define UPF_SPD_HI (0x0010) +#define UPF_SPD_VHI (0x0020) +#define UPF_SPD_CUST (0x0030) +#define UPF_SPD_SHI (0x1000) +#define UPF_SPD_WARP (0x1010) +#define UPF_SKIP_TEST (1 << 6) +#define UPF_AUTO_IRQ (1 << 7) +#define UPF_HARDPPS_CD (1 << 11) +#define UPF_LOW_LATENCY (1 << 13) +#define UPF_BUGGY_UART (1 << 14) +#define UPF_MAGIC_MULTIPLIER (1 << 16) +#define UPF_CONS_FLOW (1 << 23) +#define UPF_SHARE_IRQ (1 << 24) +#define UPF_BOOT_AUTOCONF (1 << 28) +#define UPF_DEAD (1 << 30) +#define UPF_IOREMAP (1 << 31) +#define UPF_CHANGE_MASK (0x17fff) +#define UPF_USR_MASK (UPF_SPD_MASK|UPF_LOW_LATENCY) +#define USF_CLOSING_WAIT_INF (0) +#define USF_CLOSING_WAIT_NONE (~0U) + +#define UART_XMIT_SIZE PAGE_SIZE + +#define UIF_CHECK_CD (1 << 25) +#define UIF_CTS_FLOW (1 << 26) +#define UIF_NORMAL_ACTIVE (1 << 29) +#define UIF_INITIALIZED (1 << 31) +#define UIF_SUSPENDED (1 << 30) + +#define WAKEUP_CHARS 256 + +#define uart_circ_empty(circ) ((circ)->head == (circ)->tail) +#define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0) + +#define uart_circ_chars_pending(circ) \ + (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE)) + +#define uart_circ_chars_free(circ) \ + (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) + +#define uart_tx_stopped(port) \ + ((port)->info->tty->stopped || (port)->info->tty->hw_stopped) + +#define UART_ENABLE_MS(port,cflag) ((port)->flags & UPF_HARDPPS_CD || \ + (cflag) & CRTSCTS || \ + !((cflag) & CLOCAL)) + + +struct sb_uart_port; +struct sb_uart_info; +struct serial_struct; +struct device; + +struct sb_uart_ops { + unsigned int (*tx_empty)(struct sb_uart_port *); + void (*set_mctrl)(struct sb_uart_port *, unsigned int mctrl); + unsigned int (*get_mctrl)(struct sb_uart_port *); + void (*stop_tx)(struct sb_uart_port *); + void (*start_tx)(struct sb_uart_port *); + void (*send_xchar)(struct sb_uart_port *, char ch); + void (*stop_rx)(struct sb_uart_port *); + void (*enable_ms)(struct sb_uart_port *); + void (*break_ctl)(struct sb_uart_port *, int ctl); + int (*startup)(struct sb_uart_port *); + void (*shutdown)(struct sb_uart_port *); + void (*set_termios)(struct sb_uart_port *, struct MP_TERMIOS *new, + struct MP_TERMIOS *old); + void (*pm)(struct sb_uart_port *, unsigned int state, + unsigned int oldstate); + int (*set_wake)(struct sb_uart_port *, unsigned int state); + + const char *(*type)(struct sb_uart_port *); + + void (*release_port)(struct sb_uart_port *); + + int (*request_port)(struct sb_uart_port *); + void (*config_port)(struct sb_uart_port *, int); + int (*verify_port)(struct sb_uart_port *, struct serial_struct *); + int (*ioctl)(struct sb_uart_port *, unsigned int, unsigned long); +}; + + +struct sb_uart_icount { + __u32 cts; + __u32 dsr; + __u32 rng; + __u32 dcd; + __u32 rx; + __u32 tx; + __u32 frame; + __u32 overrun; + __u32 parity; + __u32 brk; + __u32 buf_overrun; +}; +typedef unsigned int upf_t; + +struct sb_uart_port { + spinlock_t lock; /* port lock */ + unsigned int iobase; /* in/out[bwl] */ + unsigned char __iomem *membase; /* read/write[bwl] */ + unsigned int irq; /* irq number */ + unsigned int uartclk; /* base uart clock */ + unsigned int fifosize; /* tx fifo size */ + unsigned char x_char; /* xon/xoff char */ + unsigned char regshift; /* reg offset shift */ + unsigned char iotype; /* io access style */ + unsigned char unused1; + + + unsigned int read_status_mask; /* driver specific */ + unsigned int ignore_status_mask; /* driver specific */ + struct sb_uart_info *info; /* pointer to parent info */ + struct sb_uart_icount icount; /* statistics */ + + struct console *cons; /* struct console, if any */ +#ifdef CONFIG_SERIAL_CORE_CONSOLE + unsigned long sysrq; /* sysrq timeout */ +#endif + + upf_t flags; + + unsigned int mctrl; /* current modem ctrl settings */ + unsigned int timeout; /* character-based timeout */ + unsigned int type; /* port type */ + const struct sb_uart_ops *ops; + unsigned int custom_divisor; + unsigned int line; /* port index */ + unsigned long mapbase; /* for ioremap */ + struct device *dev; /* parent device */ + unsigned char hub6; /* this should be in the 8250 driver */ + unsigned char unused[3]; +}; + +#define mdmode unused[2] +#define MDMODE_ADDR 0x1 +#define MDMODE_ENABLE 0x2 +#define MDMODE_AUTO 0x4 +#define MDMODE_ADDRSEND 0x8 + +struct sb_uart_state { + unsigned int close_delay; /* msec */ + unsigned int closing_wait; /* msec */ + + + int count; + int pm_state; + struct sb_uart_info *info; + struct sb_uart_port *port; + + struct mutex mutex; +}; + +typedef unsigned int uif_t; + +struct sb_uart_info { + struct tty_struct *tty; + struct circ_buf xmit; + uif_t flags; + + int blocked_open; + + struct tasklet_struct tlet; + + wait_queue_head_t open_wait; + wait_queue_head_t delta_msr_wait; +}; + + +struct module; +struct tty_driver; + +struct uart_driver { + struct module *owner; + const char *driver_name; + const char *dev_name; + int major; + int minor; + int nr; + struct console *cons; + + struct sb_uart_state *state; + struct tty_driver *tty_driver; +}; + +void sb_uart_write_wakeup(struct sb_uart_port *port) +{ + struct sb_uart_info *info = port->info; + tasklet_schedule(&info->tlet); +} + +void sb_uart_update_timeout(struct sb_uart_port *port, unsigned int cflag, + unsigned int baud) +{ + unsigned int bits; + + switch (cflag & CSIZE) + { + case CS5: + bits = 7; + break; + + case CS6: + bits = 8; + break; + + case CS7: + bits = 9; + break; + + default: + bits = 10; + break; + } + + if (cflag & CSTOPB) + { + bits++; + } + + if (cflag & PARENB) + { + bits++; + } + + bits = bits * port->fifosize; + + port->timeout = (HZ * bits) / baud + HZ/50; +} +unsigned int sb_uart_get_baud_rate(struct sb_uart_port *port, struct MP_TERMIOS *termios, + struct MP_TERMIOS *old, unsigned int min, + unsigned int max) +{ + unsigned int try, baud, altbaud = 38400; + upf_t flags = port->flags & UPF_SPD_MASK; + + if (flags == UPF_SPD_HI) + altbaud = 57600; + if (flags == UPF_SPD_VHI) + altbaud = 115200; + if (flags == UPF_SPD_SHI) + altbaud = 230400; + if (flags == UPF_SPD_WARP) + altbaud = 460800; + + for (try = 0; try < 2; try++) { + + switch (termios->c_cflag & (CBAUD | CBAUDEX)) + { + case B921600 : baud = 921600; break; + case B460800 : baud = 460800; break; + case B230400 : baud = 230400; break; + case B115200 : baud = 115200; break; + case B57600 : baud = 57600; break; + case B38400 : baud = 38400; break; + case B19200 : baud = 19200; break; + case B9600 : baud = 9600; break; + case B4800 : baud = 4800; break; + case B2400 : baud = 2400; break; + case B1800 : baud = 1800; break; + case B1200 : baud = 1200; break; + case B600 : baud = 600; break; + case B300 : baud = 300; break; + case B200 : baud = 200; break; + case B150 : baud = 150; break; + case B134 : baud = 134; break; + case B110 : baud = 110; break; + case B75 : baud = 75; break; + case B50 : baud = 50; break; + default : baud = 9600; break; + } + + if (baud == 38400) + baud = altbaud; + + if (baud == 0) + baud = 9600; + + if (baud >= min && baud <= max) + return baud; + + termios->c_cflag &= ~CBAUD; + if (old) { + termios->c_cflag |= old->c_cflag & CBAUD; + old = NULL; + continue; + } + + termios->c_cflag |= B9600; + } + + return 0; +} +unsigned int sb_uart_get_divisor(struct sb_uart_port *port, unsigned int baud) +{ + unsigned int quot; + + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) + quot = port->custom_divisor; + else + quot = (port->uartclk + (8 * baud)) / (16 * baud); + + return quot; +} + + + +static inline int sb_uart_handle_break(struct sb_uart_port *port) +{ + struct sb_uart_info *info = port->info; + + if (port->flags & UPF_SAK) + do_SAK(info->tty); + return 0; +} + +static inline void sb_uart_handle_dcd_change(struct sb_uart_port *port, unsigned int status) +{ + struct sb_uart_info *info = port->info; + + port->icount.dcd++; + + if (info->flags & UIF_CHECK_CD) { + if (status) + wake_up_interruptible(&info->open_wait); + else if (info->tty) + tty_hangup(info->tty); + } +} + +static inline void sb_uart_handle_cts_change(struct sb_uart_port *port, unsigned int status) +{ + struct sb_uart_info *info = port->info; + struct tty_struct *tty = info->tty; + + port->icount.cts++; + + if (info->flags & UIF_CTS_FLOW) { + if (tty->hw_stopped) { + if (status) { + tty->hw_stopped = 0; + port->ops->start_tx(port); + sb_uart_write_wakeup(port); + } + } else { + if (!status) { + tty->hw_stopped = 1; + port->ops->stop_tx(port); + } + } + } +} + + + From a547e5e0d8b85ccc640756eb40b3dd6b33796cf8 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Sat, 17 Nov 2012 16:43:25 +0900 Subject: [PATCH 108/146] staging: dgrp: Fix typo in dgrp driver Correct spelling typo in staging/dgrp driver Signed-off-by: Masanari Iida Signed-off-by: Greg Kroah-Hartman --- drivers/staging/dgrp/dgrp_net_ops.c | 6 +++--- drivers/staging/dgrp/dgrp_tty.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/dgrp/dgrp_net_ops.c b/drivers/staging/dgrp/dgrp_net_ops.c index 0788357fd3c..8185a57d31c 100644 --- a/drivers/staging/dgrp/dgrp_net_ops.c +++ b/drivers/staging/dgrp/dgrp_net_ops.c @@ -2495,7 +2495,7 @@ data: /* * Fabricate and insert a data packet header to - * preceed the remaining data when it comes in. + * preced the remaining data when it comes in. */ if (remain < plen) { @@ -2664,7 +2664,7 @@ data: } /* - * Handle delayed response arrival preceeding + * Handle delayed response arrival preceding * the open response we are waiting for. */ @@ -3502,7 +3502,7 @@ void dgrp_poll_handler(unsigned long arg) /* * Decrement statistics. These are only for use with * KME, so don't worry that the operations are done - * unlocked, and so the results are occassionally wrong. + * unlocked, and so the results are occasionally wrong. */ nd->nd_read_count -= (nd->nd_read_count + diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c index 0db4c0514f6..e3fa6eac53c 100644 --- a/drivers/staging/dgrp/dgrp_tty.c +++ b/drivers/staging/dgrp/dgrp_tty.c @@ -432,7 +432,7 @@ static void drp_param(struct ch_struct *ch) /* * From the POSIX.1 spec (7.1.2.6): "If {_POSIX_VDISABLE} * is defined for the terminal device file, and the value - * of one of the changable special control characters (see + * of one of the changeable special control characters (see * 7.1.1.9) is {_POSIX_VDISABLE}, that function shall be * disabled, that is, no input data shall be recognized as * the disabled special character." @@ -2699,7 +2699,7 @@ static int dgrp_tty_ioctl(struct tty_struct *tty, unsigned int cmd, - looking at the tty_ioctl code, these command all call our tty_set_termios at the driver's end, when a TCSETA* is sent, it is expecting the tty to have a termio structure, - NOT a termios stucture. These two structures differ in size + NOT a termios structure. These two structures differ in size and the tty_ioctl code does a conversion before processing them both. - we should treat the TCSETAW TCSETAF ioctls the same, and let the tty_ioctl code do the conversion stuff. @@ -2996,7 +2996,7 @@ static void dgrp_tty_start(struct tty_struct *tty) } /* - * Stop the reciever + * Stop the receiver */ static void dgrp_tty_input_stop(struct tty_struct *tty) { From 1862c14e107a5a21402852b0256b3c87bc1a5b00 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 19 Nov 2012 20:11:42 +0100 Subject: [PATCH 109/146] TTY: isdn/gigaset destroy tty_port properly Currently, the port may be destroyed twice or destroyed without being initialized. Fix this by sticking the destroy call in the right place as suggested by Tilman. Signed-off-by: Jiri Slaby Reported-by: Tilman Schmidt Cc: Hansjoerg Lipp Cc: gigaset307x-common@lists.sourceforge.net Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/gigaset/common.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index bc9d89a8c4f..6849a11a1b2 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -507,6 +507,7 @@ void gigaset_freecs(struct cardstate *cs) gig_dbg(DEBUG_INIT, "clearing at_state"); clear_at_state(&cs->at_state); dealloc_temp_at_states(cs); + tty_port_destroy(&cs->port); /* fall through */ case 0: /* error in basic setup */ @@ -518,7 +519,6 @@ f_bcs: gig_dbg(DEBUG_INIT, "freeing bcs[]"); kfree(cs->bcs); f_cs: gig_dbg(DEBUG_INIT, "freeing cs"); mutex_unlock(&cs->mutex); - tty_port_destroy(&cs->port); free_cs(cs); } EXPORT_SYMBOL_GPL(gigaset_freecs); @@ -752,14 +752,14 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, gig_dbg(DEBUG_INIT, "setting up iif"); if (gigaset_isdn_regdev(cs, modulename) < 0) { pr_err("error registering ISDN device\n"); - goto error_port; + goto error; } make_valid(cs, VALID_ID); ++cs->cs_init; gig_dbg(DEBUG_INIT, "setting up hw"); if (cs->ops->initcshw(cs) < 0) - goto error_port; + goto error; ++cs->cs_init; @@ -774,7 +774,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, gig_dbg(DEBUG_INIT, "setting up bcs[%d]", i); if (gigaset_initbcs(cs->bcs + i, cs, i) < 0) { pr_err("could not allocate channel %d data\n", i); - goto error_port; + goto error; } } @@ -787,8 +787,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, gig_dbg(DEBUG_INIT, "cs initialized"); return cs; -error_port: - tty_port_destroy(&cs->port); + error: gig_dbg(DEBUG_INIT, "failed"); gigaset_freecs(cs); From 25e6c11fe2306f08d1e8614acb232dd067adbd3b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 19 Nov 2012 20:11:43 +0100 Subject: [PATCH 110/146] TTY: let me eat my own cooking I am the one introducing the most potential bugs. And people should know the urchin to whip. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index bb0b27db673..4513948c9e2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7416,6 +7416,7 @@ K: ^Subject:.*(?i)trivial TTY LAYER M: Greg Kroah-Hartman +M: Jiri Slaby S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git F: drivers/tty/ From a5e04b760a033c5247478dbc957970cd9d968fc6 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 18 Nov 2012 21:27:41 -0800 Subject: [PATCH 111/146] drivers/tty/vt/vt_ioctl.c: Include for pm_set_vt_switch C files should include the header files that prototype their functions. This keeps the types in sync, and eliminates warnings from GCC (-Wmissing-prototypes) and Sparse (-Wdecl). Signed-off-by: Josh Triplett Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt_ioctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index b841f56d2e6..98ff1735eaf 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include From 2520e2745cf35c8fd01476dd01eacfc813649f7a Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 18 Nov 2012 21:27:47 -0800 Subject: [PATCH 112/146] tty: Mark tty_del_file and __tty_hangup static Nothing outside of drivers/tty/tty_io.c references these functions, so mark them static. Signed-off-by: Josh Triplett Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 739ea86c1cf..da9fde85075 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -236,7 +236,7 @@ void tty_free_file(struct file *file) } /* Delete file from its tty */ -void tty_del_file(struct file *file) +static void tty_del_file(struct file *file) { struct tty_file_private *priv = file->private_data; @@ -554,7 +554,7 @@ EXPORT_SYMBOL_GPL(tty_wakeup); * tasklist_lock to walk task list for hangup event * ->siglock to protect ->signal/->sighand */ -void __tty_hangup(struct tty_struct *tty) +static void __tty_hangup(struct tty_struct *tty) { struct file *cons_filp = NULL; struct file *filp, *f = NULL; From 91116cba5da0c33f3093b804e487bea02b830bfb Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Mon, 19 Nov 2012 13:21:06 -0500 Subject: [PATCH 113/146] tty: remove use of __devexit_p CONFIG_HOTPLUG is going away as an option so __devexit_p is no longer needed. Signed-off-by: Bill Pemberton Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/cyclades.c | 2 +- drivers/tty/hvc/hvc_opal.c | 2 +- drivers/tty/hvc/hvcs.c | 2 +- drivers/tty/isicom.c | 2 +- drivers/tty/moxa.c | 2 +- drivers/tty/mxser.c | 2 +- drivers/tty/nozomi.c | 2 +- drivers/tty/synclink.c | 2 +- drivers/tty/synclink_gt.c | 2 +- drivers/tty/synclinkmp.c | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index de25774b5ec..2f4d84f3c15 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -3973,7 +3973,7 @@ static struct pci_driver cy_pci_driver = { .name = "cyclades", .id_table = cy_pci_dev_id, .probe = cy_pci_probe, - .remove = __devexit_p(cy_pci_remove) + .remove = cy_pci_remove }; #endif diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index 0d2ea0c224c..442bfb0d41d 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -239,7 +239,7 @@ static int __devexit hvc_opal_remove(struct platform_device *dev) static struct platform_driver hvc_opal_driver = { .probe = hvc_opal_probe, - .remove = __devexit_p(hvc_opal_remove), + .remove = hvc_opal_remove, .driver = { .name = hvc_opal_name, .owner = THIS_MODULE, diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 744c3b8eea4..888af583fe7 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -874,7 +874,7 @@ static int __devexit hvcs_remove(struct vio_dev *dev) static struct vio_driver hvcs_vio_driver = { .id_table = hvcs_driver_table, .probe = hvcs_probe, - .remove = __devexit_p(hvcs_remove), + .remove = hvcs_remove, .name = hvcs_driver_name, }; diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index 67f288d7e76..774e595e1fb 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -168,7 +168,7 @@ static struct pci_driver isicom_driver = { .name = "isicom", .id_table = isicom_pci_tbl, .probe = isicom_probe, - .remove = __devexit_p(isicom_remove) + .remove = isicom_remove }; static int prev_card = 3; /* start servicing isi_card[0] */ diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index d628176fb6d..e025e065ae9 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -1033,7 +1033,7 @@ static struct pci_driver moxa_pci_driver = { .name = "moxa", .id_table = moxa_pcibrds, .probe = moxa_pci_probe, - .remove = __devexit_p(moxa_pci_remove) + .remove = moxa_pci_remove }; #endif /* CONFIG_PCI */ diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 802a248e7ab..a2fd58c336e 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -2677,7 +2677,7 @@ static struct pci_driver mxser_driver = { .name = "mxser", .id_table = mxser_pcibrds, .probe = mxser_probe, - .remove = __devexit_p(mxser_remove) + .remove = mxser_remove }; static int __init mxser_module_init(void) diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index cb764d29775..442efc3d265 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -1908,7 +1908,7 @@ static struct pci_driver nozomi_driver = { .name = NOZOMI_NAME, .id_table = nozomi_pci_tbl, .probe = nozomi_card_init, - .remove = __devexit_p(nozomi_card_exit), + .remove = nozomi_card_exit, }; static __init int nozomi_init(void) diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index e4b5c39fc4e..31db13be469 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -898,7 +898,7 @@ static struct pci_driver synclink_pci_driver = { .name = "synclink", .id_table = synclink_pci_tbl, .probe = synclink_init_one, - .remove = __devexit_p(synclink_remove_one), + .remove = synclink_remove_one, }; static struct tty_driver *serial_driver; diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 6e4c34011b7..595f2f48193 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -110,7 +110,7 @@ static struct pci_driver pci_driver = { .name = "synclink_gt", .id_table = pci_table, .probe = init_one, - .remove = __devexit_p(remove_one), + .remove = remove_one, }; static bool pci_registered; diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index 40745beb258..71f3eb22c97 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -492,7 +492,7 @@ static struct pci_driver synclinkmp_pci_driver = { .name = "synclinkmp", .id_table = synclinkmp_pci_tbl, .probe = synclinkmp_init_one, - .remove = __devexit_p(synclinkmp_remove_one), + .remove = synclinkmp_remove_one, }; From 2d47b7160243b1422006b91debf438484a4fde58 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Mon, 19 Nov 2012 13:21:34 -0500 Subject: [PATCH 114/146] tty: serial: remove use of __devexit_p CONFIG_HOTPLUG is going away as an option so __devexit_p is no longer needed. Signed-off-by: Bill Pemberton Cc: Alan Cox Cc: Lucas Tavares Cc: "David S. Miller" Cc: Peter Korsgaard Cc: Tony Prisk Acked-by: Tobias Klauser Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.c | 2 +- drivers/tty/serial/8250/8250_acorn.c | 2 +- drivers/tty/serial/8250/8250_dw.c | 2 +- drivers/tty/serial/8250/8250_em.c | 2 +- drivers/tty/serial/8250/8250_hp300.c | 2 +- drivers/tty/serial/8250/8250_pci.c | 50 ++++++++++++++-------------- drivers/tty/serial/8250/8250_pnp.c | 2 +- drivers/tty/serial/altera_jtaguart.c | 2 +- drivers/tty/serial/altera_uart.c | 2 +- drivers/tty/serial/ar933x_uart.c | 2 +- drivers/tty/serial/arc_uart.c | 2 +- drivers/tty/serial/atmel_serial.c | 2 +- drivers/tty/serial/bcm63xx_uart.c | 2 +- drivers/tty/serial/bfin_sport_uart.c | 2 +- drivers/tty/serial/bfin_uart.c | 2 +- drivers/tty/serial/clps711x.c | 2 +- drivers/tty/serial/efm32-uart.c | 2 +- drivers/tty/serial/icom.c | 2 +- drivers/tty/serial/ifx6x60.c | 2 +- drivers/tty/serial/jsm/jsm_driver.c | 2 +- drivers/tty/serial/lpc32xx_hs.c | 2 +- drivers/tty/serial/max3100.c | 2 +- drivers/tty/serial/max310x.c | 2 +- drivers/tty/serial/mcf.c | 2 +- drivers/tty/serial/mfd.c | 2 +- drivers/tty/serial/mrst_max3110.c | 2 +- drivers/tty/serial/msm_serial_hs.c | 2 +- drivers/tty/serial/mux.c | 4 +-- drivers/tty/serial/mxs-auart.c | 2 +- drivers/tty/serial/omap-serial.c | 2 +- drivers/tty/serial/pch_uart.c | 2 +- drivers/tty/serial/samsung.c | 2 +- drivers/tty/serial/sc26xx.c | 2 +- drivers/tty/serial/sccnxp.c | 2 +- drivers/tty/serial/serial_txx9.c | 4 +-- drivers/tty/serial/sirfsoc_uart.c | 2 +- drivers/tty/serial/sunhv.c | 2 +- drivers/tty/serial/sunsab.c | 2 +- drivers/tty/serial/sunsu.c | 2 +- drivers/tty/serial/sunzilog.c | 2 +- drivers/tty/serial/timbuart.c | 2 +- drivers/tty/serial/uartlite.c | 2 +- drivers/tty/serial/vr41xx_siu.c | 2 +- drivers/tty/serial/vt8500_serial.c | 2 +- 44 files changed, 70 insertions(+), 70 deletions(-) diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 5ccbd90540c..870c5f2d0c8 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -3078,7 +3078,7 @@ static int serial8250_resume(struct platform_device *dev) static struct platform_driver serial8250_isa_driver = { .probe = serial8250_probe, - .remove = __devexit_p(serial8250_remove), + .remove = serial8250_remove, .suspend = serial8250_suspend, .resume = serial8250_resume, .driver = { diff --git a/drivers/tty/serial/8250/8250_acorn.c b/drivers/tty/serial/8250/8250_acorn.c index 857498312a9..b5e4b494cb0 100644 --- a/drivers/tty/serial/8250/8250_acorn.c +++ b/drivers/tty/serial/8250/8250_acorn.c @@ -116,7 +116,7 @@ static const struct ecard_id serial_cids[] = { static struct ecard_driver serial_card_driver = { .probe = serial_card_probe, - .remove = __devexit_p(serial_card_remove), + .remove = serial_card_remove, .id_table = serial_cids, .drv = { .name = "8250_acorn", diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index b19b8c54780..2db80d03b0b 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -197,7 +197,7 @@ static struct platform_driver dw8250_platform_driver = { .of_match_table = dw8250_match, }, .probe = dw8250_probe, - .remove = __devexit_p(dw8250_remove), + .remove = dw8250_remove, .suspend = dw8250_suspend, .resume = dw8250_resume, }; diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index 3a0363e7f3a..80c0a626c13 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -176,7 +176,7 @@ static struct platform_driver serial8250_em_platform_driver = { .owner = THIS_MODULE, }, .probe = serial8250_em_probe, - .remove = __devexit_p(serial8250_em_remove), + .remove = serial8250_em_remove, }; module_platform_driver(serial8250_em_platform_driver); diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c index f3d0edf4664..89e88559f48 100644 --- a/drivers/tty/serial/8250/8250_hp300.c +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -52,7 +52,7 @@ static struct dio_driver hpdca_driver = { .name = "hpdca", .id_table = hpdca_dio_tbl, .probe = hpdca_init_one, - .remove = __devexit_p(hpdca_remove_one), + .remove = hpdca_remove_one, }; #endif diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 508063b2a4b..c049cfa06f5 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1314,7 +1314,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ite887x_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ite887x_exit), + .exit = pci_ite887x_exit, }, /* * National Instruments @@ -1326,7 +1326,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1335,7 +1335,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1344,7 +1344,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1353,7 +1353,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1362,7 +1362,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1371,7 +1371,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1380,7 +1380,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1389,7 +1389,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1398,7 +1398,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1407,7 +1407,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1416,7 +1416,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1425,7 +1425,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8420_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), + .exit = pci_ni8420_exit, }, { .vendor = PCI_VENDOR_ID_NI, @@ -1434,7 +1434,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_ni8430_init, .setup = pci_ni8430_setup, - .exit = __devexit_p(pci_ni8430_exit), + .exit = pci_ni8430_exit, }, /* * Panacom @@ -1446,7 +1446,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_plx9050_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), + .exit = pci_plx9050_exit, }, { .vendor = PCI_VENDOR_ID_PANACOM, @@ -1455,7 +1455,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .init = pci_plx9050_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), + .exit = pci_plx9050_exit, }, /* * PLX @@ -1474,7 +1474,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_SUBDEVICE_ID_EXSYS_4055, .init = pci_plx9050_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), + .exit = pci_plx9050_exit, }, { .vendor = PCI_VENDOR_ID_PLX, @@ -1483,7 +1483,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_SUBDEVICE_ID_KEYSPAN_SX2, .init = pci_plx9050_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), + .exit = pci_plx9050_exit, }, { .vendor = PCI_VENDOR_ID_PLX, @@ -1492,7 +1492,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_SUBDEVICE_ID_UNKNOWN_0x1584, .init = pci_plx9050_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), + .exit = pci_plx9050_exit, }, { .vendor = PCI_VENDOR_ID_PLX, @@ -1501,7 +1501,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, .init = pci_plx9050_init, .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), + .exit = pci_plx9050_exit, }, /* * SBS Technologies, Inc., PMC-OCTALPRO 232 @@ -1513,7 +1513,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_SUBDEVICE_ID_OCTPRO232, .init = sbs_init, .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), + .exit = sbs_exit, }, /* * SBS Technologies, Inc., PMC-OCTALPRO 422 @@ -1525,7 +1525,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_SUBDEVICE_ID_OCTPRO422, .init = sbs_init, .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), + .exit = sbs_exit, }, /* * SBS Technologies, Inc., P-Octal 232 @@ -1537,7 +1537,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_SUBDEVICE_ID_POCTAL232, .init = sbs_init, .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), + .exit = sbs_exit, }, /* * SBS Technologies, Inc., P-Octal 422 @@ -1549,7 +1549,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_SUBDEVICE_ID_POCTAL422, .init = sbs_init, .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), + .exit = sbs_exit, }, /* * SIIG cards - these may be called via parport_serial @@ -4323,7 +4323,7 @@ static const struct pci_error_handlers serial8250_err_handler = { static struct pci_driver serial_pci_driver = { .name = "serial", .probe = pciserial_init_one, - .remove = __devexit_p(pciserial_remove_one), + .remove = pciserial_remove_one, #ifdef CONFIG_PM .suspend = pciserial_suspend_one, .resume = pciserial_resume_one, diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index f8ee25001dd..e566220f187 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -511,7 +511,7 @@ static int serial_pnp_resume(struct pnp_dev *dev) static struct pnp_driver serial_pnp_driver = { .name = "serial", .probe = serial_pnp_probe, - .remove = __devexit_p(serial_pnp_remove), + .remove = serial_pnp_remove, .suspend = serial_pnp_suspend, .resume = serial_pnp_resume, .id_table = pnp_dev_table, diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index 530181e49f6..ef16b0aa383 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -477,7 +477,7 @@ MODULE_DEVICE_TABLE(of, altera_jtaguart_match); static struct platform_driver altera_jtaguart_platform_driver = { .probe = altera_jtaguart_probe, - .remove = __devexit_p(altera_jtaguart_remove), + .remove = altera_jtaguart_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 15d80b9fb30..117ea2c8963 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -621,7 +621,7 @@ MODULE_DEVICE_TABLE(of, altera_uart_match); static struct platform_driver altera_uart_platform_driver = { .probe = altera_uart_probe, - .remove = __devexit_p(altera_uart_remove), + .remove = altera_uart_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 0f5b28afc2a..77115448af5 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -725,7 +725,7 @@ static int __devexit ar933x_uart_remove(struct platform_device *pdev) static struct platform_driver ar933x_uart_platform_driver = { .probe = ar933x_uart_probe, - .remove = __devexit_p(ar933x_uart_remove), + .remove = ar933x_uart_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index e9c61d1b1c7..d6525698db3 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -697,7 +697,7 @@ static int __devexit arc_serial_remove(struct platform_device *pdev) static struct platform_driver arc_platform_driver = { .probe = arc_serial_probe, - .remove = __devexit_p(arc_serial_remove), + .remove = arc_serial_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 3d7e1ee2fa5..27eae4b2355 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1876,7 +1876,7 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev) static struct platform_driver atmel_serial_driver = { .probe = atmel_serial_probe, - .remove = __devexit_p(atmel_serial_remove), + .remove = atmel_serial_remove, .suspend = atmel_serial_suspend, .resume = atmel_serial_resume, .driver = { diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index c0b68b9cad9..7f631d4a5c4 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -865,7 +865,7 @@ static int __devexit bcm_uart_remove(struct platform_device *pdev) */ static struct platform_driver bcm_uart_platform_driver = { .probe = bcm_uart_probe, - .remove = __devexit_p(bcm_uart_remove), + .remove = bcm_uart_remove, .driver = { .owner = THIS_MODULE, .name = "bcm63xx_uart", diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index 7fbc3a08f10..b4a18c7ffdf 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -871,7 +871,7 @@ static int __devexit sport_uart_remove(struct platform_device *pdev) static struct platform_driver sport_uart_driver = { .probe = sport_uart_probe, - .remove = __devexit_p(sport_uart_remove), + .remove = sport_uart_remove, .driver = { .name = DRV_NAME, #ifdef CONFIG_PM diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index ca64c4a83ce..f1f8210a613 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -1409,7 +1409,7 @@ static int __devexit bfin_serial_remove(struct platform_device *pdev) static struct platform_driver bfin_serial_driver = { .probe = bfin_serial_probe, - .remove = __devexit_p(bfin_serial_remove), + .remove = bfin_serial_remove, .suspend = bfin_serial_suspend, .resume = bfin_serial_resume, .driver = { diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index a0a6db5c32f..d631ef52f4f 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -512,7 +512,7 @@ static struct platform_driver clps711x_uart_driver = { .owner = THIS_MODULE, }, .probe = uart_clps711x_probe, - .remove = __devexit_p(uart_clps711x_remove), + .remove = uart_clps711x_remove, }; module_platform_driver(clps711x_uart_driver); diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c index 615e4647049..1e8bacf95b7 100644 --- a/drivers/tty/serial/efm32-uart.c +++ b/drivers/tty/serial/efm32-uart.c @@ -791,7 +791,7 @@ MODULE_DEVICE_TABLE(of, efm32_uart_dt_ids); static struct platform_driver efm32_uart_driver = { .probe = efm32_uart_probe, - .remove = __devexit_p(efm32_uart_remove), + .remove = efm32_uart_remove, .driver = { .name = DRIVER_NAME, diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index defc4e3393a..f0fc2fff170 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -1617,7 +1617,7 @@ static struct pci_driver icom_pci_driver = { .name = ICOM_DRIVER_NAME, .id_table = icom_pci_table, .probe = icom_probe, - .remove = __devexit_p(icom_remove), + .remove = icom_remove, }; static int __init icom_init(void) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 91125bb151c..2783d96ff61 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -1405,7 +1405,7 @@ static struct spi_driver ifx_spi_driver = { .owner = THIS_MODULE}, .probe = ifx_spi_spi_probe, .shutdown = ifx_spi_spi_shutdown, - .remove = __devexit_p(ifx_spi_spi_remove), + .remove = ifx_spi_spi_remove, .suspend = ifx_spi_spi_suspend, .resume = ifx_spi_spi_resume, .id_table = ifx_id_table diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 8e05ce94bd4..bbd459226ee 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -217,7 +217,7 @@ static struct pci_driver jsm_driver = { .name = "jsm", .id_table = jsm_pci_tbl, .probe = jsm_probe_one, - .remove = __devexit_p(jsm_remove_one), + .remove = jsm_remove_one, .err_handler = &jsm_err_handler, }; diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index ba3af3bf6d4..7b0f5b4e592 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -783,7 +783,7 @@ MODULE_DEVICE_TABLE(of, serial_hs_lpc32xx_dt_ids); static struct platform_driver serial_hs_lpc32xx_driver = { .probe = serial_hs_lpc32xx_probe, - .remove = __devexit_p(serial_hs_lpc32xx_remove), + .remove = serial_hs_lpc32xx_remove, .suspend = serial_hs_lpc32xx_suspend, .resume = serial_hs_lpc32xx_resume, .driver = { diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 0f24486be53..2ffd7f091cc 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -907,7 +907,7 @@ static struct spi_driver max3100_driver = { }, .probe = max3100_probe, - .remove = __devexit_p(max3100_remove), + .remove = max3100_remove, .suspend = max3100_suspend, .resume = max3100_resume, }; diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 1ab1d2c66de..a332327163a 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1249,7 +1249,7 @@ static struct spi_driver max310x_driver = { .owner = THIS_MODULE, }, .probe = max310x_probe, - .remove = __devexit_p(max310x_remove), + .remove = max310x_remove, .suspend = max310x_suspend, .resume = max310x_resume, .id_table = max310x_id_table, diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index 9afca093d6e..e3de7856afb 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -617,7 +617,7 @@ static int __devexit mcf_remove(struct platform_device *pdev) static struct platform_driver mcf_platform_driver = { .probe = mcf_probe, - .remove = __devexit_p(mcf_remove), + .remove = mcf_remove, .driver = { .name = "mcfuart", .owner = THIS_MODULE, diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index 79fe59b6cc4..8a74d59270e 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -1471,7 +1471,7 @@ static struct pci_driver hsu_pci_driver = { .name = "HSU serial", .id_table = pci_ids, .probe = serial_hsu_probe, - .remove = __devexit_p(serial_hsu_remove), + .remove = serial_hsu_remove, .suspend = serial_hsu_suspend, .resume = serial_hsu_resume, .driver = { diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index df2a2240a3a..649ce126804 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -879,7 +879,7 @@ static struct spi_driver uart_max3110_driver = { .owner = THIS_MODULE, }, .probe = serial_m3110_probe, - .remove = __devexit_p(serial_m3110_remove), + .remove = serial_m3110_remove, .suspend = serial_m3110_suspend, .resume = serial_m3110_resume, }; diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index fca13dc73e2..1361ad5e1d5 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1838,7 +1838,7 @@ static const struct dev_pm_ops msm_hs_dev_pm_ops = { static struct platform_driver msm_serial_hs_platform_driver = { .probe = msm_hs_probe, - .remove = __devexit_p(msm_hs_remove), + .remove = msm_hs_remove, .driver = { .name = "msm_serial_hs", .owner = THIS_MODULE, diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c index 7ea8a263fd9..27834646d01 100644 --- a/drivers/tty/serial/mux.c +++ b/drivers/tty/serial/mux.c @@ -571,14 +571,14 @@ static struct parisc_driver builtin_serial_mux_driver = { .name = "builtin_serial_mux", .id_table = builtin_mux_tbl, .probe = mux_probe, - .remove = __devexit_p(mux_remove), + .remove = mux_remove, }; static struct parisc_driver serial_mux_driver = { .name = "serial_mux", .id_table = mux_tbl, .probe = mux_probe, - .remove = __devexit_p(mux_remove), + .remove = mux_remove, }; /** diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index d5b9e303901..479acc88c17 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1155,7 +1155,7 @@ static int __devexit mxs_auart_remove(struct platform_device *pdev) static struct platform_driver mxs_auart_driver = { .probe = mxs_auart_probe, - .remove = __devexit_p(mxs_auart_remove), + .remove = mxs_auart_remove, .driver = { .name = "mxs-auart", .owner = THIS_MODULE, diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index c34735cf627..24e2375c579 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1586,7 +1586,7 @@ MODULE_DEVICE_TABLE(of, omap_serial_of_match); static struct platform_driver serial_omap_driver = { .probe = serial_omap_probe, - .remove = __devexit_p(serial_omap_remove), + .remove = serial_omap_remove, .driver = { .name = DRIVER_NAME, .pm = &serial_omap_dev_pm_ops, diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 4cd6c238152..f5fb9bd1a14 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1869,7 +1869,7 @@ static struct pci_driver pch_uart_pci_driver = { .name = "pch_uart", .id_table = pch_uart_pci_id, .probe = pch_uart_pci_probe, - .remove = __devexit_p(pch_uart_pci_remove), + .remove = pch_uart_pci_remove, .suspend = pch_uart_pci_suspend, .resume = pch_uart_pci_resume, }; diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 740458ca62c..6568beb4d37 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1712,7 +1712,7 @@ MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); static struct platform_driver samsung_serial_driver = { .probe = s3c24xx_serial_probe, - .remove = __devexit_p(s3c24xx_serial_remove), + .remove = s3c24xx_serial_remove, .id_table = s3c24xx_serial_driver_ids, .driver = { .name = "samsung-uart", diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c index 9d664242b31..9a40659ec52 100644 --- a/drivers/tty/serial/sc26xx.c +++ b/drivers/tty/serial/sc26xx.c @@ -733,7 +733,7 @@ static int __exit sc26xx_driver_remove(struct platform_device *dev) static struct platform_driver sc26xx_driver = { .probe = sc26xx_probe, - .remove = __devexit_p(sc26xx_driver_remove), + .remove = sc26xx_driver_remove, .driver = { .name = "SC26xx", .owner = THIS_MODULE, diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index e821068cd95..810853f5fd0 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -981,7 +981,7 @@ static struct platform_driver sccnxp_uart_driver = { .owner = THIS_MODULE, }, .probe = sccnxp_probe, - .remove = __devexit_p(sccnxp_remove), + .remove = sccnxp_remove, .id_table = sccnxp_id_table, }; module_platform_driver(sccnxp_uart_driver); diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index 6ae2a58d62f..9d979a9e41a 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -1171,7 +1171,7 @@ static int serial_txx9_resume(struct platform_device *dev) static struct platform_driver serial_txx9_plat_driver = { .probe = serial_txx9_probe, - .remove = __devexit_p(serial_txx9_remove), + .remove = serial_txx9_remove, #ifdef CONFIG_PM .suspend = serial_txx9_suspend, .resume = serial_txx9_resume, @@ -1261,7 +1261,7 @@ static const struct pci_device_id serial_txx9_pci_tbl[] = { static struct pci_driver serial_txx9_pci_driver = { .name = "serial_txx9", .probe = pciserial_txx9_init_one, - .remove = __devexit_p(pciserial_txx9_remove_one), + .remove = pciserial_txx9_remove_one, #ifdef CONFIG_PM .suspend = pciserial_txx9_suspend_one, .resume = pciserial_txx9_resume_one, diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index a9e2bd1ab53..49849843ff8 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -735,7 +735,7 @@ MODULE_DEVICE_TABLE(of, sirfsoc_serial_of_match); static struct platform_driver sirfsoc_uart_driver = { .probe = sirfsoc_uart_probe, - .remove = __devexit_p(sirfsoc_uart_remove), + .remove = sirfsoc_uart_remove, .suspend = sirfsoc_uart_suspend, .resume = sirfsoc_uart_resume, .driver = { diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 505961cfd93..949b2d3dcc5 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -636,7 +636,7 @@ static struct platform_driver hv_driver = { .of_match_table = hv_match, }, .probe = hv_probe, - .remove = __devexit_p(hv_remove), + .remove = hv_remove, }; static int __init sunhv_init(void) diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index f0d93eb7e6e..bbb07bc74f6 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -1100,7 +1100,7 @@ static struct platform_driver sab_driver = { .of_match_table = sab_match, }, .probe = sab_probe, - .remove = __devexit_p(sab_remove), + .remove = sab_remove, }; static int __init sunsab_init(void) diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index b97913dcdbf..c0658f0b51a 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1556,7 +1556,7 @@ static struct platform_driver su_driver = { .of_match_table = su_match, }, .probe = su_probe, - .remove = __devexit_p(su_remove), + .remove = su_remove, }; static int __init sunsu_init(void) diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index babd9470982..c2ef47594d6 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -1548,7 +1548,7 @@ static struct platform_driver zs_driver = { .of_match_table = zs_match, }, .probe = zs_probe, - .remove = __devexit_p(zs_remove), + .remove = zs_remove, }; static int __init sunzilog_init(void) diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 70f97491d8f..5fc11f2a8be 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -510,7 +510,7 @@ static struct platform_driver timbuart_platform_driver = { .owner = THIS_MODULE, }, .probe = timbuart_probe, - .remove = __devexit_p(timbuart_remove), + .remove = timbuart_remove, }; module_platform_driver(timbuart_platform_driver); diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 6579ffdd8e9..1d4438306ae 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -603,7 +603,7 @@ MODULE_ALIAS("platform:uartlite"); static struct platform_driver ulite_platform_driver = { .probe = ulite_probe, - .remove = __devexit_p(ulite_remove), + .remove = ulite_remove, .driver = { .owner = THIS_MODULE, .name = "uartlite", diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c index cf0d9485ec0..9d3bf75e55a 100644 --- a/drivers/tty/serial/vr41xx_siu.c +++ b/drivers/tty/serial/vr41xx_siu.c @@ -952,7 +952,7 @@ static int siu_resume(struct platform_device *dev) static struct platform_driver siu_device_driver = { .probe = siu_probe, - .remove = __devexit_p(siu_remove), + .remove = siu_remove, .suspend = siu_suspend, .resume = siu_resume, .driver = { diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 4354fe565f6..dbcc909291b 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -652,7 +652,7 @@ static const struct of_device_id wmt_dt_ids[] = { static struct platform_driver vt8500_platform_driver = { .probe = vt8500_serial_probe, - .remove = __devexit_p(vt8500_serial_remove), + .remove = vt8500_serial_remove, .driver = { .name = "vt8500_serial", .owner = THIS_MODULE, From 9671f09921d93e722a28ae9610d478e092ac5466 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Mon, 19 Nov 2012 13:21:50 -0500 Subject: [PATCH 115/146] tty: remove use of __devinit CONFIG_HOTPLUG is going away as an option so __devinit is no longer needed. Signed-off-by: Bill Pemberton Cc: Jiri Slaby Cc: Alan Cox Cc: Lucas Tavares Cc: "David S. Miller" Cc: Peter Korsgaard Cc: Tony Prisk Acked-by: Tobias Klauser Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/cyclades.c | 16 ++++++++-------- drivers/tty/ehv_bytechan.c | 2 +- drivers/tty/hvc/hvc_opal.c | 2 +- drivers/tty/hvc/hvc_vio.c | 2 +- drivers/tty/hvc/hvc_xen.c | 2 +- drivers/tty/hvc/hvcs.c | 8 ++++---- drivers/tty/isicom.c | 6 +++--- drivers/tty/moxa.c | 2 +- drivers/tty/mxser.c | 6 +++--- drivers/tty/nozomi.c | 2 +- drivers/tty/serial/8250/8250.c | 2 +- drivers/tty/serial/8250/8250_acorn.c | 2 +- drivers/tty/serial/8250/8250_dw.c | 2 +- drivers/tty/serial/8250/8250_em.c | 2 +- drivers/tty/serial/8250/8250_hp300.c | 4 ++-- drivers/tty/serial/8250/8250_pci.c | 4 ++-- drivers/tty/serial/8250/8250_pnp.c | 8 ++++---- drivers/tty/serial/altera_jtaguart.c | 2 +- drivers/tty/serial/altera_uart.c | 2 +- drivers/tty/serial/apbuart.c | 2 +- drivers/tty/serial/ar933x_uart.c | 2 +- drivers/tty/serial/arc_uart.c | 10 +++++----- drivers/tty/serial/atmel_serial.c | 6 +++--- drivers/tty/serial/bcm63xx_uart.c | 2 +- drivers/tty/serial/bfin_sport_uart.c | 2 +- drivers/tty/serial/clps711x.c | 2 +- drivers/tty/serial/cpm_uart/cpm_uart_core.c | 2 +- drivers/tty/serial/efm32-uart.c | 2 +- drivers/tty/serial/icom.c | 10 +++++----- drivers/tty/serial/ioc3_serial.c | 2 +- drivers/tty/serial/jsm/jsm_driver.c | 2 +- drivers/tty/serial/jsm/jsm_tty.c | 2 +- drivers/tty/serial/lpc32xx_hs.c | 2 +- drivers/tty/serial/max3100.c | 2 +- drivers/tty/serial/max310x.c | 6 +++--- drivers/tty/serial/mcf.c | 2 +- drivers/tty/serial/mpc52xx_uart.c | 2 +- drivers/tty/serial/mrst_max3110.c | 2 +- drivers/tty/serial/msm_serial_hs.c | 4 ++-- drivers/tty/serial/mxs-auart.c | 2 +- drivers/tty/serial/of_serial.c | 4 ++-- drivers/tty/serial/omap-serial.c | 6 +++--- drivers/tty/serial/pch_uart.c | 2 +- drivers/tty/serial/sa1100.c | 2 +- drivers/tty/serial/sc26xx.c | 4 ++-- drivers/tty/serial/sccnxp.c | 2 +- drivers/tty/serial/serial_txx9.c | 6 +++--- drivers/tty/serial/sh-sci.c | 14 +++++++------- drivers/tty/serial/sunhv.c | 2 +- drivers/tty/serial/sunsab.c | 4 ++-- drivers/tty/serial/sunsu.c | 6 +++--- drivers/tty/serial/sunzilog.c | 8 ++++---- drivers/tty/serial/timbuart.c | 2 +- drivers/tty/serial/uartlite.c | 6 +++--- drivers/tty/serial/vr41xx_siu.c | 4 ++-- drivers/tty/serial/vt8500_serial.c | 2 +- drivers/tty/serial/xilinx_uartps.c | 2 +- drivers/tty/synclink.c | 2 +- drivers/tty/synclink_gt.c | 2 +- drivers/tty/synclinkmp.c | 2 +- 60 files changed, 113 insertions(+), 113 deletions(-) diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 2f4d84f3c15..2dff87cdce2 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -3099,7 +3099,7 @@ static const struct tty_port_operations cyz_port_ops = { * --------------------------------------------------------------------- */ -static int __devinit cy_init_card(struct cyclades_card *cinfo) +static int cy_init_card(struct cyclades_card *cinfo) { struct cyclades_port *info; unsigned int channel, port; @@ -3196,7 +3196,7 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) /* initialize chips on Cyclom-Y card -- return number of valid chips (which is number of ports/4) */ -static unsigned short __devinit cyy_init_card(void __iomem *true_base_addr, +static unsigned short cyy_init_card(void __iomem *true_base_addr, int index) { unsigned int chip_number; @@ -3405,7 +3405,7 @@ static int __init cy_detect_isa(void) } /* cy_detect_isa */ #ifdef CONFIG_PCI -static inline int __devinit cyc_isfwstr(const char *str, unsigned int size) +static inline int cyc_isfwstr(const char *str, unsigned int size) { unsigned int a; @@ -3420,7 +3420,7 @@ static inline int __devinit cyc_isfwstr(const char *str, unsigned int size) return 0; } -static inline void __devinit cyz_fpga_copy(void __iomem *fpga, const u8 *data, +static inline void cyz_fpga_copy(void __iomem *fpga, const u8 *data, unsigned int size) { for (; size > 0; size--) { @@ -3429,7 +3429,7 @@ static inline void __devinit cyz_fpga_copy(void __iomem *fpga, const u8 *data, } } -static void __devinit plx_init(struct pci_dev *pdev, int irq, +static void plx_init(struct pci_dev *pdev, int irq, struct RUNTIME_9060 __iomem *addr) { /* Reset PLX */ @@ -3449,7 +3449,7 @@ static void __devinit plx_init(struct pci_dev *pdev, int irq, pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq); } -static int __devinit __cyz_load_fw(const struct firmware *fw, +static int __cyz_load_fw(const struct firmware *fw, const char *name, const u32 mailbox, void __iomem *base, void __iomem *fpga) { @@ -3526,7 +3526,7 @@ static int __devinit __cyz_load_fw(const struct firmware *fw, return 0; } -static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr, +static int cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr, struct RUNTIME_9060 __iomem *ctl_addr, int irq) { const struct firmware *fw; @@ -3692,7 +3692,7 @@ err: return retval; } -static int __devinit cy_pci_probe(struct pci_dev *pdev, +static int cy_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct cyclades_card *card; diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 4193afb74a0..c117d775a22 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -699,7 +699,7 @@ static const struct tty_port_operations ehv_bc_tty_port_ops = { .shutdown = ehv_bc_tty_port_shutdown, }; -static int __devinit ehv_bc_tty_probe(struct platform_device *pdev) +static int ehv_bc_tty_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct ehv_bc_data *bc; diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index 442bfb0d41d..79f2b5e17f8 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -161,7 +161,7 @@ static const struct hv_ops hvc_opal_hvsi_ops = { .tiocmset = hvc_opal_hvsi_tiocmset, }; -static int __devinit hvc_opal_probe(struct platform_device *dev) +static int hvc_opal_probe(struct platform_device *dev) { const struct hv_ops *ops; struct hvc_struct *hp; diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index 070c0ee6864..77bde6c2cfa 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -293,7 +293,7 @@ static int udbg_hvc_getc(void) } } -static int __devinit hvc_vio_probe(struct vio_dev *vdev, +static int hvc_vio_probe(struct vio_dev *vdev, const struct vio_device_id *id) { const struct hv_ops *ops; diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index f4abfe238f9..19843ec3f80 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -422,7 +422,7 @@ static int xencons_connect_backend(struct xenbus_device *dev, return ret; } -static int __devinit xencons_probe(struct xenbus_device *dev, +static int xencons_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) { int ret, devid; diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 888af583fe7..506a28e5564 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -330,12 +330,12 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp); static void hvcs_close(struct tty_struct *tty, struct file *filp); static void hvcs_hangup(struct tty_struct * tty); -static int __devinit hvcs_probe(struct vio_dev *dev, +static int hvcs_probe(struct vio_dev *dev, const struct vio_device_id *id); static int __devexit hvcs_remove(struct vio_dev *dev); static int __init hvcs_module_init(void); static void __exit hvcs_module_exit(void); -static int __devinit hvcs_initialize(void); +static int hvcs_initialize(void); #define HVCS_SCHED_READ 0x00000001 #define HVCS_QUICK_READ 0x00000002 @@ -756,7 +756,7 @@ static int hvcs_get_index(void) return -1; } -static int __devinit hvcs_probe( +static int hvcs_probe( struct vio_dev *dev, const struct vio_device_id *id) { @@ -1478,7 +1478,7 @@ static void hvcs_free_index_list(void) hvcs_index_count = 0; } -static int __devinit hvcs_initialize(void) +static int hvcs_initialize(void) { int rc, num_ttys_to_alloc; diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index 774e595e1fb..82661889b32 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -1307,7 +1307,7 @@ static const struct tty_port_operations isicom_port_ops = { .shutdown = isicom_shutdown, }; -static int __devinit reset_card(struct pci_dev *pdev, +static int reset_card(struct pci_dev *pdev, const unsigned int card, unsigned int *signature) { struct isi_board *board = pci_get_drvdata(pdev); @@ -1368,7 +1368,7 @@ end: return retval; } -static int __devinit load_firmware(struct pci_dev *pdev, +static int load_firmware(struct pci_dev *pdev, const unsigned int index, const unsigned int signature) { struct isi_board *board = pci_get_drvdata(pdev); @@ -1548,7 +1548,7 @@ end: */ static unsigned int card_count; -static int __devinit isicom_probe(struct pci_dev *pdev, +static int isicom_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { unsigned int uninitialized_var(signature), index; diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index e025e065ae9..60ea74e76d3 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -945,7 +945,7 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) } #ifdef CONFIG_PCI -static int __devinit moxa_pci_probe(struct pci_dev *pdev, +static int moxa_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct moxa_board_conf *board; diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index a2fd58c336e..7f5e0ccf96e 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -487,7 +487,7 @@ static void mxser_disable_must_rx_software_flow_control(unsigned long baseio) } #ifdef CONFIG_PCI -static int __devinit CheckIsMoxaMust(unsigned long io) +static int CheckIsMoxaMust(unsigned long io) { u8 oldmcr, hwid; int i; @@ -2369,7 +2369,7 @@ static void mxser_release_ISA_res(struct mxser_board *brd) mxser_release_vector(brd); } -static int __devinit mxser_initbrd(struct mxser_board *brd, +static int mxser_initbrd(struct mxser_board *brd, struct pci_dev *pdev) { struct mxser_port *info; @@ -2547,7 +2547,7 @@ err_irqconflict: return -EIO; } -static int __devinit mxser_probe(struct pci_dev *pdev, +static int mxser_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { #ifdef CONFIG_PCI diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 442efc3d265..61de2a46547 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -1360,7 +1360,7 @@ static void remove_sysfs_files(struct nozomi *dc) } /* Allocate memory for one device */ -static int __devinit nozomi_card_init(struct pci_dev *pdev, +static int nozomi_card_init(struct pci_dev *pdev, const struct pci_device_id *ent) { resource_size_t start; diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 870c5f2d0c8..40ba8cc0985 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -2989,7 +2989,7 @@ void serial8250_resume_port(int line) * list is terminated with a zero flags entry, which means we expect * all entries to have at least UPF_BOOT_AUTOCONF set. */ -static int __devinit serial8250_probe(struct platform_device *dev) +static int serial8250_probe(struct platform_device *dev) { struct plat_serial8250_port *p = dev->dev.platform_data; struct uart_8250_port uart; diff --git a/drivers/tty/serial/8250/8250_acorn.c b/drivers/tty/serial/8250/8250_acorn.c index b5e4b494cb0..ed095eb2e3f 100644 --- a/drivers/tty/serial/8250/8250_acorn.c +++ b/drivers/tty/serial/8250/8250_acorn.c @@ -38,7 +38,7 @@ struct serial_card_info { void __iomem *vaddr; }; -static int __devinit +static int serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) { struct serial_card_info *info; diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 2db80d03b0b..7664750c2bd 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -87,7 +87,7 @@ static int dw8250_handle_irq(struct uart_port *p) return 0; } -static int __devinit dw8250_probe(struct platform_device *pdev) +static int dw8250_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}; struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index 80c0a626c13..f59bff5907c 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -89,7 +89,7 @@ static void serial8250_em_serial_dl_write(struct uart_8250_port *up, int value) serial_out(up, UART_DLM_EM, value >> 8 & 0xff); } -static int __devinit serial8250_em_probe(struct platform_device *pdev) +static int serial8250_em_probe(struct platform_device *pdev) { struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c index 89e88559f48..2b945052ee0 100644 --- a/drivers/tty/serial/8250/8250_hp300.c +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -36,7 +36,7 @@ static struct hp300_port *hp300_ports; #ifdef CONFIG_HPDCA -static int __devinit hpdca_init_one(struct dio_dev *d, +static int hpdca_init_one(struct dio_dev *d, const struct dio_device_id *ent); static void __devexit hpdca_remove_one(struct dio_dev *d); @@ -159,7 +159,7 @@ int __init hp300_setup_serial_console(void) #endif /* CONFIG_SERIAL_8250_CONSOLE */ #ifdef CONFIG_HPDCA -static int __devinit hpdca_init_one(struct dio_dev *d, +static int hpdca_init_one(struct dio_dev *d, const struct dio_device_id *ent) { struct uart_8250_port uart; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index c049cfa06f5..a5acb57b5ba 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -2691,7 +2691,7 @@ static const struct pci_device_id blacklist[] = { * guess what the configuration might be, based on the pitiful PCI * serial specs. Returns 0 on success, 1 on failure. */ -static int __devinit +static int serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board) { const struct pci_device_id *bldev; @@ -2917,7 +2917,7 @@ EXPORT_SYMBOL_GPL(pciserial_resume_ports); * Probe one serial board. Unfortunately, there is no rhyme nor reason * to the arrangement of serial ports on a PCI card. */ -static int __devinit +static int pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { struct pci_serial_quirk *quirk; diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index e566220f187..2b8a6ac173f 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -377,7 +377,7 @@ static char *modem_names[] __devinitdata = { "33600", "28800", "14400", "V.90", "V.34", "V.32", NULL }; -static int __devinit check_name(char *name) +static int check_name(char *name) { char **tmp; @@ -388,7 +388,7 @@ static int __devinit check_name(char *name) return 0; } -static int __devinit check_resources(struct pnp_dev *dev) +static int check_resources(struct pnp_dev *dev) { resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8}; int i; @@ -412,7 +412,7 @@ static int __devinit check_resources(struct pnp_dev *dev) * PnP modems, alternatively we must hardcode all modems in pnp_devices[] * table. */ -static int __devinit serial_pnp_guess_board(struct pnp_dev *dev) +static int serial_pnp_guess_board(struct pnp_dev *dev) { if (!(check_name(pnp_dev_name(dev)) || (dev->card && check_name(dev->card->name)))) @@ -424,7 +424,7 @@ static int __devinit serial_pnp_guess_board(struct pnp_dev *dev) return -ENODEV; } -static int __devinit +static int serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { struct uart_8250_port uart; diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index ef16b0aa383..ef5c705fa2b 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -406,7 +406,7 @@ static struct uart_driver altera_jtaguart_driver = { .cons = ALTERA_JTAGUART_CONSOLE, }; -static int __devinit altera_jtaguart_probe(struct platform_device *pdev) +static int altera_jtaguart_probe(struct platform_device *pdev) { struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data; struct uart_port *port; diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 117ea2c8963..066b5035e10 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -532,7 +532,7 @@ static int altera_uart_get_of_uartclk(struct platform_device *pdev, } #endif /* CONFIG_OF */ -static int __devinit altera_uart_probe(struct platform_device *pdev) +static int altera_uart_probe(struct platform_device *pdev) { struct altera_uart_platform_uart *platp = pdev->dev.platform_data; struct uart_port *port; diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index 7162f70d926..59ae2b53e76 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -554,7 +554,7 @@ static struct uart_driver grlib_apbuart_driver = { /* OF Platform Driver */ /* ======================================================================== */ -static int __devinit apbuart_probe(struct platform_device *op) +static int apbuart_probe(struct platform_device *op) { int i; struct uart_port *port = NULL; diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 77115448af5..ad171508b9a 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -627,7 +627,7 @@ static struct uart_driver ar933x_uart_driver = { .cons = AR933X_SERIAL_CONSOLE, }; -static int __devinit ar933x_uart_probe(struct platform_device *pdev) +static int ar933x_uart_probe(struct platform_device *pdev) { struct ar933x_uart_platform_data *pdata; struct ar933x_uart_port *up; diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index d6525698db3..158d798a520 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -525,7 +525,7 @@ static struct uart_ops arc_serial_pops = { #endif }; -static int __devinit +static int arc_uart_init_one(struct platform_device *pdev, struct arc_uart_port *uart) { struct resource *res, *res2; @@ -577,7 +577,7 @@ arc_uart_init_one(struct platform_device *pdev, struct arc_uart_port *uart) #ifdef CONFIG_SERIAL_ARC_CONSOLE -static int __devinit arc_serial_console_setup(struct console *co, char *options) +static int arc_serial_console_setup(struct console *co, char *options) { struct uart_port *port; int baud = 115200; @@ -655,7 +655,7 @@ static struct __initdata console arc_early_serial_console = { .index = -1 }; -static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev) +static int arc_serial_probe_earlyprintk(struct platform_device *pdev) { arc_early_serial_console.index = pdev->id; @@ -667,13 +667,13 @@ static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev) return 0; } #else -static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev) +static int arc_serial_probe_earlyprintk(struct platform_device *pdev) { return -ENODEV; } #endif /* CONFIG_SERIAL_ARC_CONSOLE */ -static int __devinit arc_serial_probe(struct platform_device *pdev) +static int arc_serial_probe(struct platform_device *pdev) { struct arc_uart_port *uart; int rc; diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 27eae4b2355..02540cbf16a 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1423,7 +1423,7 @@ static struct uart_ops atmel_pops = { #endif }; -static void __devinit atmel_of_init_port(struct atmel_uart_port *atmel_port, +static void atmel_of_init_port(struct atmel_uart_port *atmel_port, struct device_node *np) { u32 rs485_delay[2]; @@ -1458,7 +1458,7 @@ static void __devinit atmel_of_init_port(struct atmel_uart_port *atmel_port, /* * Configure the port from the platform device resource info. */ -static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, +static void atmel_init_port(struct atmel_uart_port *atmel_port, struct platform_device *pdev) { struct uart_port *port = &atmel_port->uart; @@ -1766,7 +1766,7 @@ static int atmel_serial_resume(struct platform_device *pdev) #define atmel_serial_resume NULL #endif -static int __devinit atmel_serial_probe(struct platform_device *pdev) +static int atmel_serial_probe(struct platform_device *pdev) { struct atmel_uart_port *port; struct device_node *np = pdev->dev.of_node; diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 7f631d4a5c4..e54d1703be1 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -801,7 +801,7 @@ static struct uart_driver bcm_uart_driver = { /* * platform driver probe/remove callback */ -static int __devinit bcm_uart_probe(struct platform_device *pdev) +static int bcm_uart_probe(struct platform_device *pdev) { struct resource *res_mem, *res_irq; struct uart_port *port; diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index b4a18c7ffdf..a47e00b056e 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -740,7 +740,7 @@ static struct dev_pm_ops bfin_sport_uart_dev_pm_ops = { }; #endif -static int __devinit sport_uart_probe(struct platform_device *pdev) +static int sport_uart_probe(struct platform_device *pdev) { struct resource *res; struct sport_uart_port *sport; diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index d631ef52f4f..006d283bbde 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -429,7 +429,7 @@ static int uart_clps711x_console_setup(struct console *co, char *options) } #endif -static int __devinit uart_clps711x_probe(struct platform_device *pdev) +static int uart_clps711x_probe(struct platform_device *pdev) { struct clps711x_port *s; int ret, i; diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index d0dd9194cec..de3f0f6eba7 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1373,7 +1373,7 @@ static struct uart_driver cpm_reg = { static int probe_index; -static int __devinit cpm_uart_probe(struct platform_device *ofdev) +static int cpm_uart_probe(struct platform_device *ofdev) { int index = probe_index++; struct uart_cpm_port *pinfo = &cpm_uart_ports[index]; diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c index 1e8bacf95b7..833c33a2751 100644 --- a/drivers/tty/serial/efm32-uart.c +++ b/drivers/tty/serial/efm32-uart.c @@ -690,7 +690,7 @@ static int efm32_uart_probe_dt(struct platform_device *pdev, } -static int __devinit efm32_uart_probe(struct platform_device *pdev) +static int efm32_uart_probe(struct platform_device *pdev) { struct efm32_uart_port *efm_port; struct resource *res; diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index f0fc2fff170..a8267956ac8 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -175,7 +175,7 @@ static void free_port_memory(struct icom_port *icom_port) } } -static int __devinit get_port_memory(struct icom_port *icom_port) +static int get_port_memory(struct icom_port *icom_port) { int index; unsigned long stgAddr; @@ -1314,7 +1314,7 @@ static struct uart_driver icom_uart_driver = { .cons = ICOM_CONSOLE, }; -static int __devinit icom_init_ports(struct icom_adapter *icom_adapter) +static int icom_init_ports(struct icom_adapter *icom_adapter) { u32 subsystem_id = icom_adapter->subsystem_id; int i; @@ -1381,7 +1381,7 @@ static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *i 0x8024 + 2 - 2 * (icom_port->port - 2); } } -static int __devinit icom_load_ports(struct icom_adapter *icom_adapter) +static int icom_load_ports(struct icom_adapter *icom_adapter) { struct icom_port *icom_port; int port_num; @@ -1407,7 +1407,7 @@ static int __devinit icom_load_ports(struct icom_adapter *icom_adapter) return 0; } -static int __devinit icom_alloc_adapter(struct icom_adapter +static int icom_alloc_adapter(struct icom_adapter **icom_adapter_ref) { int adapter_count = 0; @@ -1487,7 +1487,7 @@ static void icom_kref_release(struct kref *kref) icom_remove_adapter(icom_adapter); } -static int __devinit icom_probe(struct pci_dev *dev, +static int icom_probe(struct pci_dev *dev, const struct pci_device_id *ent) { int index; diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c index 5ac52898a0b..d8f1d1d5447 100644 --- a/drivers/tty/serial/ioc3_serial.c +++ b/drivers/tty/serial/ioc3_serial.c @@ -2010,7 +2010,7 @@ static int ioc3uart_remove(struct ioc3_submodule *is, * @idd: ioc3 driver data for this card */ -static int __devinit +static int ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) { struct pci_dev *pdev = idd->pdev; diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index bbd459226ee..5b57c8eecfc 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -64,7 +64,7 @@ int jsm_debug; module_param(jsm_debug, int, 0); MODULE_PARM_DESC(jsm_debug, "Driver debugging level"); -static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) +static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) { int rc = 0; struct jsm_board *brd; diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 7d2c1f3aa36..4c00c5550b1 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -371,7 +371,7 @@ static struct uart_ops jsm_ops = { * Init the tty subsystem. Called once per board after board has been * downloaded and init'ed. */ -int __devinit jsm_tty_init(struct jsm_board *brd) +int jsm_tty_init(struct jsm_board *brd) { int i; void __iomem *vaddr; diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index 7b0f5b4e592..3651dab2009 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -686,7 +686,7 @@ static struct uart_ops serial_lpc32xx_pops = { /* * Register a set of serial devices attached to a platform device */ -static int __devinit serial_hs_lpc32xx_probe(struct platform_device *pdev) +static int serial_hs_lpc32xx_probe(struct platform_device *pdev) { struct lpc32xx_hsuart_port *p = &lpc32xx_hs_ports[uarts_registered]; int ret = 0; diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 2ffd7f091cc..8dd6189a40e 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -742,7 +742,7 @@ static struct uart_driver max3100_uart_driver = { }; static int uart_driver_registered; -static int __devinit max3100_probe(struct spi_device *spi) +static int max3100_probe(struct spi_device *spi) { int i, retval; struct plat_max3100 *pdata; diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index a332327163a..88a227f9fe8 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -378,7 +378,7 @@ static void max310x_wait_pll(struct max310x_port *s) } } -static int __devinit max310x_update_best_err(unsigned long f, long *besterr) +static int max310x_update_best_err(unsigned long f, long *besterr) { /* Use baudrate 115200 for calculate error */ long err = f % (115200 * 16); @@ -391,7 +391,7 @@ static int __devinit max310x_update_best_err(unsigned long f, long *besterr) return 1; } -static int __devinit max310x_set_ref_clk(struct max310x_port *s) +static int max310x_set_ref_clk(struct max310x_port *s) { unsigned int div, clksrc, pllcfg = 0; long besterr = -1; @@ -995,7 +995,7 @@ static struct max310x_pdata generic_plat_data = { .frequency = 26000000, }; -static int __devinit max310x_probe(struct spi_device *spi) +static int max310x_probe(struct spi_device *spi) { struct max310x_port *s; struct device *dev = &spi->dev; diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index e3de7856afb..e2b93d2f8f8 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -571,7 +571,7 @@ static struct uart_driver mcf_driver = { /****************************************************************************/ -static int __devinit mcf_probe(struct platform_device *pdev) +static int mcf_probe(struct platform_device *pdev) { struct mcf_platform_uart *platp = pdev->dev.platform_data; struct uart_port *port; diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 8cf577008ad..7c23c4f4c58 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1308,7 +1308,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = { {}, }; -static int __devinit mpc52xx_uart_of_probe(struct platform_device *op) +static int mpc52xx_uart_of_probe(struct platform_device *op) { int idx = -1; unsigned int uartclk; diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 649ce126804..41497fd3d36 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -773,7 +773,7 @@ static int serial_m3110_resume(struct spi_device *spi) #define serial_m3110_resume NULL #endif -static int __devinit serial_m3110_probe(struct spi_device *spi) +static int serial_m3110_probe(struct spi_device *spi) { struct uart_max3110 *max; void *buffer; diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 1361ad5e1d5..02fb63e944e 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1521,7 +1521,7 @@ err_msm_hs_init_clk: } /* Initialize tx and rx data structures */ -static int __devinit uartdm_init_port(struct uart_port *uport) +static int uartdm_init_port(struct uart_port *uport) { int ret = 0; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); @@ -1614,7 +1614,7 @@ err_tx_command_ptr_ptr: return ret; } -static int __devinit msm_hs_probe(struct platform_device *pdev) +static int msm_hs_probe(struct platform_device *pdev) { int ret; struct uart_port *uport; diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 479acc88c17..18b55c2d1d2 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1046,7 +1046,7 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, return 0; } -static int __devinit mxs_auart_probe(struct platform_device *pdev) +static int mxs_auart_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(mxs_auart_dt_ids, &pdev->dev); diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index b9fdccb2259..1bce344ca79 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -52,7 +52,7 @@ EXPORT_SYMBOL_GPL(tegra_serial_handle_break); /* * Fill a struct uart_port for a given device node */ -static int __devinit of_platform_serial_setup(struct platform_device *ofdev, +static int of_platform_serial_setup(struct platform_device *ofdev, int type, struct uart_port *port, struct of_serial_info *info) { @@ -138,7 +138,7 @@ out: * 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) +static int of_platform_serial_probe(struct platform_device *ofdev) { const struct of_device_id *match; struct of_serial_info *info; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 24e2375c579..e777b16c4d1 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1238,7 +1238,7 @@ static int serial_omap_resume(struct device *dev) } #endif -static void __devinit omap_serial_fill_features_erratas(struct uart_omap_port *up) +static void omap_serial_fill_features_erratas(struct uart_omap_port *up) { u32 mvr, scheme; u16 revision, major, minor; @@ -1291,7 +1291,7 @@ static void __devinit omap_serial_fill_features_erratas(struct uart_omap_port *u } } -static __devinit struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) +static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) { struct omap_uart_port_info *omap_up_info; @@ -1304,7 +1304,7 @@ static __devinit struct omap_uart_port_info *of_get_uart_port_info(struct device return omap_up_info; } -static int __devinit serial_omap_probe(struct platform_device *pdev) +static int serial_omap_probe(struct platform_device *pdev) { struct uart_omap_port *up; struct resource *mem, *irq; diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index f5fb9bd1a14..8318925fbf6 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1839,7 +1839,7 @@ static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = { {0,}, }; -static int __devinit pch_uart_pci_probe(struct pci_dev *pdev, +static int pch_uart_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index 2ca5959ec3f..da56c8a0fdc 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -637,7 +637,7 @@ static void __init sa1100_init_ports(void) PPSR |= PPC_TXD1 | PPC_TXD3; } -void __devinit sa1100_register_uart_fns(struct sa1100_port_fns *fns) +void sa1100_register_uart_fns(struct sa1100_port_fns *fns) { if (fns->get_mctrl) sa1100_pops.get_mctrl = fns->get_mctrl; diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c index 9a40659ec52..aced1dd923d 100644 --- a/drivers/tty/serial/sc26xx.c +++ b/drivers/tty/serial/sc26xx.c @@ -621,7 +621,7 @@ static u8 sc26xx_flags2mask(unsigned int flags, unsigned int bitpos) return bit ? (1 << (bit - 1)) : 0; } -static void __devinit sc26xx_init_masks(struct uart_sc26xx_port *up, +static void sc26xx_init_masks(struct uart_sc26xx_port *up, int line, unsigned int data) { up->dtr_mask[line] = sc26xx_flags2mask(data, 0); @@ -632,7 +632,7 @@ static void __devinit sc26xx_init_masks(struct uart_sc26xx_port *up, up->ri_mask[line] = sc26xx_flags2mask(data, 20); } -static int __devinit sc26xx_probe(struct platform_device *dev) +static int sc26xx_probe(struct platform_device *dev) { struct resource *res; struct uart_sc26xx_port *up; diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 810853f5fd0..1ddace83263 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -740,7 +740,7 @@ static int sccnxp_console_setup(struct console *co, char *options) } #endif -static int __devinit sccnxp_probe(struct platform_device *pdev) +static int sccnxp_probe(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); int chiptype = pdev->id_entry->driver_data; diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index 9d979a9e41a..23b28b8f467 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -1030,7 +1030,7 @@ static DEFINE_MUTEX(serial_txx9_mutex); * * On success the port is ready to use and the line number is returned. */ -static int __devinit serial_txx9_register_port(struct uart_port *port) +static int serial_txx9_register_port(struct uart_port *port) { int i; struct uart_txx9_port *uart; @@ -1096,7 +1096,7 @@ static void __devexit serial_txx9_unregister_port(int line) /* * Register a set of serial devices attached to a platform device. */ -static int __devinit serial_txx9_probe(struct platform_device *dev) +static int serial_txx9_probe(struct platform_device *dev) { struct uart_port *p = dev->dev.platform_data; struct uart_port port; @@ -1187,7 +1187,7 @@ static struct platform_driver serial_txx9_plat_driver = { * Probe one serial board. Unfortunately, there is no rhyme nor reason * to the arrangement of serial ports on a PCI card. */ -static int __devinit +static int pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { struct uart_port port; diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index d38c0f54603..61477567423 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1126,7 +1126,7 @@ static const char *sci_gpio_str(unsigned int index) return sci_gpio_names[index]; } -static void __devinit sci_init_gpios(struct sci_port *port) +static void sci_init_gpios(struct sci_port *port) { struct uart_port *up = &port->port; int i; @@ -2069,7 +2069,7 @@ static struct uart_ops sci_uart_ops = { #endif }; -static int __devinit sci_init_single(struct platform_device *dev, +static int sci_init_single(struct platform_device *dev, struct sci_port *sci_port, unsigned int index, struct plat_sci_port *p) @@ -2240,7 +2240,7 @@ static void serial_console_write(struct console *co, const char *s, local_irq_restore(flags); } -static int __devinit serial_console_setup(struct console *co, char *options) +static int serial_console_setup(struct console *co, char *options) { struct sci_port *sci_port; struct uart_port *port; @@ -2294,7 +2294,7 @@ static struct console early_serial_console = { static char early_serial_buf[32]; -static int __devinit sci_probe_earlyprintk(struct platform_device *pdev) +static int sci_probe_earlyprintk(struct platform_device *pdev) { struct plat_sci_port *cfg = pdev->dev.platform_data; @@ -2317,7 +2317,7 @@ static int __devinit sci_probe_earlyprintk(struct platform_device *pdev) #define SCI_CONSOLE (&serial_console) #else -static inline int __devinit sci_probe_earlyprintk(struct platform_device *pdev) +static inline int sci_probe_earlyprintk(struct platform_device *pdev) { return -EINVAL; } @@ -2353,7 +2353,7 @@ static int sci_remove(struct platform_device *dev) return 0; } -static int __devinit sci_probe_single(struct platform_device *dev, +static int sci_probe_single(struct platform_device *dev, unsigned int index, struct plat_sci_port *p, struct sci_port *sciport) @@ -2383,7 +2383,7 @@ static int __devinit sci_probe_single(struct platform_device *dev, return 0; } -static int __devinit sci_probe(struct platform_device *dev) +static int sci_probe(struct platform_device *dev) { struct plat_sci_port *p = dev->dev.platform_data; struct sci_port *sp = &sci_ports[dev->id]; diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 949b2d3dcc5..cb58867036a 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -519,7 +519,7 @@ static struct console sunhv_console = { .data = &sunhv_reg, }; -static int __devinit hv_probe(struct platform_device *op) +static int hv_probe(struct platform_device *op) { struct uart_port *port; unsigned long minor; diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index bbb07bc74f6..9a13c54d5f8 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -954,7 +954,7 @@ static inline struct console *SUNSAB_CONSOLE(void) #define sunsab_console_init() do { } while (0) #endif -static int __devinit sunsab_init_one(struct uart_sunsab_port *up, +static int sunsab_init_one(struct uart_sunsab_port *up, struct platform_device *op, unsigned long offset, int line) @@ -1007,7 +1007,7 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up, return 0; } -static int __devinit sab_probe(struct platform_device *op) +static int sab_probe(struct platform_device *op) { static int inst; struct uart_sunsab_port *up; diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index c0658f0b51a..049bbc5bc76 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1185,7 +1185,7 @@ static struct uart_driver sunsu_reg = { .major = TTY_MAJOR, }; -static int __devinit sunsu_kbd_ms_init(struct uart_sunsu_port *up) +static int sunsu_kbd_ms_init(struct uart_sunsu_port *up) { int quot, baud; #ifdef CONFIG_SERIO @@ -1391,7 +1391,7 @@ static inline struct console *SUNSU_CONSOLE(void) #define sunsu_serial_console_init() do { } while (0) #endif -static enum su_type __devinit su_get_type(struct device_node *dp) +static enum su_type su_get_type(struct device_node *dp) { struct device_node *ap = of_find_node_by_path("/aliases"); @@ -1412,7 +1412,7 @@ static enum su_type __devinit su_get_type(struct device_node *dp) return SU_PORT_PORT; } -static int __devinit su_probe(struct platform_device *op) +static int su_probe(struct platform_device *op) { static int inst; struct device_node *dp = op->dev.of_node; diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index c2ef47594d6..02c058fbefe 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -1282,7 +1282,7 @@ static inline struct console *SUNZILOG_CONSOLE(void) #define SUNZILOG_CONSOLE() (NULL) #endif -static void __devinit sunzilog_init_kbdms(struct uart_sunzilog_port *up) +static void sunzilog_init_kbdms(struct uart_sunzilog_port *up) { int baud, brg; @@ -1302,7 +1302,7 @@ static void __devinit sunzilog_init_kbdms(struct uart_sunzilog_port *up) } #ifdef CONFIG_SERIO -static void __devinit sunzilog_register_serio(struct uart_sunzilog_port *up) +static void sunzilog_register_serio(struct uart_sunzilog_port *up) { struct serio *serio = &up->serio; @@ -1331,7 +1331,7 @@ static void __devinit sunzilog_register_serio(struct uart_sunzilog_port *up) } #endif -static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up) +static void sunzilog_init_hw(struct uart_sunzilog_port *up) { struct zilog_channel __iomem *channel; unsigned long flags; @@ -1400,7 +1400,7 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up) static int zilog_irq; -static int __devinit zs_probe(struct platform_device *op) +static int zs_probe(struct platform_device *op) { static int kbm_inst, uart_inst; int inst; diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 5fc11f2a8be..c833f50980b 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -426,7 +426,7 @@ static struct uart_driver timbuart_driver = { .nr = 1 }; -static int __devinit timbuart_probe(struct platform_device *dev) +static int timbuart_probe(struct platform_device *dev) { int err, irq; struct timbuart_port *uart; diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 1d4438306ae..df9eeb451ae 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -408,7 +408,7 @@ static void ulite_console_write(struct console *co, const char *s, spin_unlock_irqrestore(&port->lock, flags); } -static int __devinit ulite_console_setup(struct console *co, char *options) +static int ulite_console_setup(struct console *co, char *options) { struct uart_port *port; int baud = 9600; @@ -486,7 +486,7 @@ static struct uart_driver ulite_uart_driver = { * * Returns: 0 on success, <0 otherwise */ -static int __devinit ulite_assign(struct device *dev, int id, u32 base, int irq) +static int ulite_assign(struct device *dev, int id, u32 base, int irq) { struct uart_port *port; int rc; @@ -570,7 +570,7 @@ static struct of_device_id ulite_of_match[] __devinitdata = { MODULE_DEVICE_TABLE(of, ulite_of_match); #endif /* CONFIG_OF */ -static int __devinit ulite_probe(struct platform_device *pdev) +static int ulite_probe(struct platform_device *pdev) { struct resource *res, *res2; int id = pdev->id; diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c index 9d3bf75e55a..c046c995534 100644 --- a/drivers/tty/serial/vr41xx_siu.c +++ b/drivers/tty/serial/vr41xx_siu.c @@ -823,7 +823,7 @@ static struct console siu_console = { .data = &siu_uart_driver, }; -static int __devinit siu_console_init(void) +static int siu_console_init(void) { struct uart_port *port; int i; @@ -867,7 +867,7 @@ static struct uart_driver siu_uart_driver = { .cons = SERIAL_VR41XX_CONSOLE, }; -static int __devinit siu_probe(struct platform_device *dev) +static int siu_probe(struct platform_device *dev) { struct uart_port *port; int num, i, retval; diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index dbcc909291b..80530c7d002 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -554,7 +554,7 @@ static struct uart_driver vt8500_uart_driver = { .cons = VT8500_CONSOLE, }; -static int __devinit vt8500_serial_probe(struct platform_device *pdev) +static int vt8500_serial_probe(struct platform_device *pdev) { struct vt8500_port *vt8500_port; struct resource *mmres, *irqres; diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 23efe17be44..a1cd2df51c9 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -939,7 +939,7 @@ static struct uart_driver xuartps_uart_driver = { * * Returns 0 on success, negative error otherwise **/ -static int __devinit xuartps_probe(struct platform_device *pdev) +static int xuartps_probe(struct platform_device *pdev) { int rc; struct uart_port *port; diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 31db13be469..4798dd5c55d 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -8065,7 +8065,7 @@ static void hdlcdev_exit(struct mgsl_struct *info) #endif /* CONFIG_HDLC */ -static int __devinit synclink_init_one (struct pci_dev *dev, +static int synclink_init_one (struct pci_dev *dev, const struct pci_device_id *ent) { struct mgsl_struct *info; diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 595f2f48193..a84c4089f56 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3698,7 +3698,7 @@ static void device_init(int adapter_num, struct pci_dev *pdev) } } -static int __devinit init_one(struct pci_dev *dev, +static int init_one(struct pci_dev *dev, const struct pci_device_id *ent) { if (pci_enable_device(dev)) { diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index 71f3eb22c97..d301110b4d1 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -5595,7 +5595,7 @@ static void write_control_reg(SLMP_INFO * info) } -static int __devinit synclinkmp_init_one (struct pci_dev *dev, +static int synclinkmp_init_one (struct pci_dev *dev, const struct pci_device_id *ent) { if (pci_enable_device(dev)) { From 6b1cb9305094052d282e039b11a6e667ac48559e Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Mon, 19 Nov 2012 13:24:04 -0500 Subject: [PATCH 116/146] tty: remove use of __devinitdata CONFIG_HOTPLUG is going away as an option so __devinitdata is no longer needed. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 506a28e5564..5afe3b6041c 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -676,7 +676,7 @@ static int khvcsd(void *unused) return 0; } -static struct vio_device_id hvcs_driver_table[] __devinitdata= { +static struct vio_device_id hvcs_driver_table[] = { {"serial-server", "hvterm2"}, { "", "" } }; From de88b34042752c03771b779d1d985060909ab44a Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Mon, 19 Nov 2012 13:24:32 -0500 Subject: [PATCH 117/146] tty: remove use of __devinitdata CONFIG_HOTPLUG is going away as an option so __devinitdata is no longer needed. Signed-off-by: Bill Pemberton Cc: Alan Cox Cc: Peter Korsgaard Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvc_opal.c | 2 +- drivers/tty/hvc/hvc_vio.c | 2 +- drivers/tty/rocket.c | 2 +- drivers/tty/serial/8250/8250_pci.c | 2 +- drivers/tty/serial/8250/8250_pnp.c | 2 +- drivers/tty/serial/of_serial.c | 2 +- drivers/tty/serial/sirfsoc_uart.c | 2 +- drivers/tty/serial/uartlite.c | 2 +- drivers/tty/serial/xilinx_uartps.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index 79f2b5e17f8..61da5cd093f 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -41,7 +41,7 @@ static const char hvc_opal_name[] = "hvc_opal"; -static struct of_device_id hvc_opal_match[] __devinitdata = { +static struct of_device_id hvc_opal_match[] = { { .name = "serial", .compatible = "ibm,opal-console-raw" }, { .name = "serial", .compatible = "ibm,opal-console-hvsi" }, { }, diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index 77bde6c2cfa..282d143a643 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -53,7 +53,7 @@ static const char hvc_driver_name[] = "hvc_console"; -static struct vio_device_id hvc_driver_table[] __devinitdata = { +static struct vio_device_id hvc_driver_table[] = { {"serial", "hvterm1"}, #ifndef HVC_OLD_HVSI {"serial", "hvterm-protocol"}, diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index d9056dac4ea..e42009a0052 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -1758,7 +1758,7 @@ static void rp_flush_buffer(struct tty_struct *tty) #ifdef CONFIG_PCI -static struct pci_device_id __devinitdata __used rocket_pci_ids[] = { +static struct pci_device_id __used rocket_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) }, { } }; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index a5acb57b5ba..3252c5d47ff 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1987,7 +1987,7 @@ enum pci_board_num_t { * see first lines of serial_in() and serial_out() in 8250.c */ -static struct pciserial_board pci_boards[] __devinitdata = { +static struct pciserial_board pci_boards[] = { [pbn_default] = { .flags = FL_BASE0, .num_ports = 1, diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 2b8a6ac173f..71daae90fb5 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -370,7 +370,7 @@ static const struct pnp_device_id pnp_dev_table[] = { MODULE_DEVICE_TABLE(pnp, pnp_dev_table); -static char *modem_names[] __devinitdata = { +static char *modem_names[] = { "MODEM", "Modem", "modem", "FAX", "Fax", "fax", "56K", "56k", "K56", "33.6", "28.8", "14.4", "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 1bce344ca79..e7cae1c2d7d 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -231,7 +231,7 @@ static int of_platform_serial_remove(struct platform_device *ofdev) /* * A few common types, add more as needed. */ -static struct of_device_id __devinitdata of_platform_serial_table[] = { +static struct of_device_id of_platform_serial_table[] = { { .compatible = "ns8250", .data = (void *)PORT_8250, }, { .compatible = "ns16450", .data = (void *)PORT_16450, }, { .compatible = "ns16550a", .data = (void *)PORT_16550A, }, diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 49849843ff8..5da5cb96276 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -727,7 +727,7 @@ static int sirfsoc_uart_resume(struct platform_device *pdev) return 0; } -static struct of_device_id sirfsoc_uart_ids[] __devinitdata = { +static struct of_device_id sirfsoc_uart_ids[] = { { .compatible = "sirf,prima2-uart", }, {} }; diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index df9eeb451ae..2d20b012b44 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -562,7 +562,7 @@ static int __devexit ulite_release(struct device *dev) #if defined(CONFIG_OF) /* Match table for of_platform binding */ -static struct of_device_id ulite_of_match[] __devinitdata = { +static struct of_device_id ulite_of_match[] = { { .compatible = "xlnx,opb-uartlite-1.00.b", }, { .compatible = "xlnx,xps-uartlite-1.00.a", }, {} diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index a1cd2df51c9..61fa71433a0 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1040,7 +1040,7 @@ static int xuartps_resume(struct platform_device *pdev) } /* Match table for of_platform binding */ -static struct of_device_id xuartps_of_match[] __devinitdata = { +static struct of_device_id xuartps_of_match[] = { { .compatible = "xlnx,xuartps", }, {} }; From 512f82a064e397e437845c3f03a3c6dc3e610e8b Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Mon, 19 Nov 2012 13:25:19 -0500 Subject: [PATCH 118/146] tty: remove use of __devinitconst CONFIG_HOTPLUG is going away as an option so __devinitconst is no longer needed. Signed-off-by: Bill Pemberton Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/nozomi.c | 2 +- drivers/tty/serial/8250/8250_em.c | 2 +- drivers/tty/serial/mfd.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 61de2a46547..2445aa4d2fa 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -400,7 +400,7 @@ struct buffer { } __attribute__ ((packed)); /* Global variables */ -static const struct pci_device_id nozomi_pci_tbl[] __devinitconst = { +static const struct pci_device_id nozomi_pci_tbl[] = { {PCI_DEVICE(0x1931, 0x000c)}, /* Nozomi HSDPA */ {}, }; diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index f59bff5907c..430bf42374c 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -163,7 +163,7 @@ static int __devexit serial8250_em_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id serial8250_em_dt_ids[] __devinitconst = { +static const struct of_device_id serial8250_em_dt_ids[] = { { .compatible = "renesas,em-uart", }, {}, }; diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index 8a74d59270e..2c01344dc33 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -1459,7 +1459,7 @@ static void serial_hsu_remove(struct pci_dev *pdev) } /* First 3 are UART ports, and the 4th is the DMA */ -static const struct pci_device_id pci_ids[] __devinitconst = { +static const struct pci_device_id pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) }, From ae8d8a146725a966bd7c59c94f4d0016dcf7a04f Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Mon, 19 Nov 2012 13:26:18 -0500 Subject: [PATCH 119/146] tty: remove use of __devexit CONFIG_HOTPLUG is going away as an option so __devexit is no longer needed. Signed-off-by: Bill Pemberton Cc: Jiri Slaby Cc: Alan Cox Acked-by: Tobias Klauser Cc: Lucas Tavares Cc: Daniel Walker Cc: Bryan Huntsman Cc: "David S. Miller" Cc: Peter Korsgaard Cc: Tony Prisk Acked-by: David Brown Signed-off-by: Greg Kroah-Hartman --- drivers/tty/cyclades.c | 2 +- drivers/tty/hvc/hvc_opal.c | 2 +- drivers/tty/hvc/hvc_vio.c | 2 +- drivers/tty/hvc/hvcs.c | 4 ++-- drivers/tty/isicom.c | 4 ++-- drivers/tty/moxa.c | 2 +- drivers/tty/mxser.c | 2 +- drivers/tty/nozomi.c | 4 ++-- drivers/tty/serial/8250/8250.c | 2 +- drivers/tty/serial/8250/8250_acorn.c | 2 +- drivers/tty/serial/8250/8250_dw.c | 2 +- drivers/tty/serial/8250/8250_em.c | 2 +- drivers/tty/serial/8250/8250_hp300.c | 4 ++-- drivers/tty/serial/8250/8250_pci.c | 12 ++++++------ drivers/tty/serial/8250/8250_pnp.c | 2 +- drivers/tty/serial/altera_jtaguart.c | 2 +- drivers/tty/serial/altera_uart.c | 2 +- drivers/tty/serial/ar933x_uart.c | 2 +- drivers/tty/serial/arc_uart.c | 2 +- drivers/tty/serial/atmel_serial.c | 2 +- drivers/tty/serial/bcm63xx_uart.c | 2 +- drivers/tty/serial/bfin_sport_uart.c | 2 +- drivers/tty/serial/bfin_uart.c | 2 +- drivers/tty/serial/clps711x.c | 2 +- drivers/tty/serial/cpm_uart/cpm_uart_core.c | 2 +- drivers/tty/serial/efm32-uart.c | 2 +- drivers/tty/serial/icom.c | 2 +- drivers/tty/serial/jsm/jsm_driver.c | 2 +- drivers/tty/serial/lpc32xx_hs.c | 2 +- drivers/tty/serial/max3100.c | 2 +- drivers/tty/serial/max310x.c | 2 +- drivers/tty/serial/mcf.c | 2 +- drivers/tty/serial/mrst_max3110.c | 2 +- drivers/tty/serial/msm_serial.c | 2 +- drivers/tty/serial/msm_serial_hs.c | 2 +- drivers/tty/serial/mux.c | 2 +- drivers/tty/serial/mxs-auart.c | 2 +- drivers/tty/serial/omap-serial.c | 2 +- drivers/tty/serial/samsung.c | 2 +- drivers/tty/serial/sccnxp.c | 2 +- drivers/tty/serial/serial_txx9.c | 6 +++--- drivers/tty/serial/sunhv.c | 2 +- drivers/tty/serial/sunsab.c | 2 +- drivers/tty/serial/sunsu.c | 2 +- drivers/tty/serial/sunzilog.c | 4 ++-- drivers/tty/serial/timbuart.c | 2 +- drivers/tty/serial/uartlite.c | 4 ++-- drivers/tty/serial/vr41xx_siu.c | 2 +- drivers/tty/serial/vt8500_serial.c | 2 +- drivers/tty/serial/xilinx_uartps.c | 2 +- drivers/tty/synclink.c | 2 +- drivers/tty/synclink_gt.c | 2 +- drivers/tty/synclinkmp.c | 2 +- 53 files changed, 66 insertions(+), 66 deletions(-) diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 2dff87cdce2..b09c8d1f9a6 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -3931,7 +3931,7 @@ err: return retval; } -static void __devexit cy_pci_remove(struct pci_dev *pdev) +static void cy_pci_remove(struct pci_dev *pdev) { struct cyclades_card *cinfo = pci_get_drvdata(pdev); unsigned int i, channel; diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index 61da5cd093f..be1a9a1e749 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -222,7 +222,7 @@ static int hvc_opal_probe(struct platform_device *dev) return 0; } -static int __devexit hvc_opal_remove(struct platform_device *dev) +static int hvc_opal_remove(struct platform_device *dev) { struct hvc_struct *hp = dev_get_drvdata(&dev->dev); int rc, termno; diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index 282d143a643..ed6f5f1f5a5 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -362,7 +362,7 @@ static int hvc_vio_probe(struct vio_dev *vdev, return 0; } -static int __devexit hvc_vio_remove(struct vio_dev *vdev) +static int hvc_vio_remove(struct vio_dev *vdev) { struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); int rc, termno; diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 5afe3b6041c..87763573395 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -332,7 +332,7 @@ static void hvcs_hangup(struct tty_struct * tty); static int hvcs_probe(struct vio_dev *dev, const struct vio_device_id *id); -static int __devexit hvcs_remove(struct vio_dev *dev); +static int hvcs_remove(struct vio_dev *dev); static int __init hvcs_module_init(void); static void __exit hvcs_module_exit(void); static int hvcs_initialize(void); @@ -835,7 +835,7 @@ static int hvcs_probe( return 0; } -static int __devexit hvcs_remove(struct vio_dev *dev) +static int hvcs_remove(struct vio_dev *dev) { struct hvcs_struct *hvcsd = dev_get_drvdata(&dev->dev); unsigned long flags; diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index 82661889b32..3205b2e9090 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -148,7 +148,7 @@ #endif static int isicom_probe(struct pci_dev *, const struct pci_device_id *); -static void __devexit isicom_remove(struct pci_dev *); +static void isicom_remove(struct pci_dev *); static struct pci_device_id isicom_pci_tbl[] = { { PCI_DEVICE(VENDOR_ID, 0x2028) }, @@ -1635,7 +1635,7 @@ err: return retval; } -static void __devexit isicom_remove(struct pci_dev *pdev) +static void isicom_remove(struct pci_dev *pdev) { struct isi_board *board = pci_get_drvdata(pdev); unsigned int i; diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 60ea74e76d3..f9d28503bde 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -1020,7 +1020,7 @@ err: return retval; } -static void __devexit moxa_pci_remove(struct pci_dev *pdev) +static void moxa_pci_remove(struct pci_dev *pdev) { struct moxa_board_conf *brd = pci_get_drvdata(pdev); diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 7f5e0ccf96e..40113868bec 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -2658,7 +2658,7 @@ err: #endif } -static void __devexit mxser_remove(struct pci_dev *pdev) +static void mxser_remove(struct pci_dev *pdev) { #ifdef CONFIG_PCI struct mxser_board *brd = pci_get_drvdata(pdev); diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 2445aa4d2fa..a0c69ab0439 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -1507,7 +1507,7 @@ err: return ret; } -static void __devexit tty_exit(struct nozomi *dc) +static void tty_exit(struct nozomi *dc) { unsigned int i; @@ -1530,7 +1530,7 @@ static void __devexit tty_exit(struct nozomi *dc) } /* Deallocate memory for one device */ -static void __devexit nozomi_card_exit(struct pci_dev *pdev) +static void nozomi_card_exit(struct pci_dev *pdev) { int i; struct ctrl_ul ctrl; diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 40ba8cc0985..2af83a24649 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -3035,7 +3035,7 @@ static int serial8250_probe(struct platform_device *dev) /* * Remove serial ports registered against a platform device. */ -static int __devexit serial8250_remove(struct platform_device *dev) +static int serial8250_remove(struct platform_device *dev) { int i; diff --git a/drivers/tty/serial/8250/8250_acorn.c b/drivers/tty/serial/8250/8250_acorn.c index ed095eb2e3f..549aa07c0d2 100644 --- a/drivers/tty/serial/8250/8250_acorn.c +++ b/drivers/tty/serial/8250/8250_acorn.c @@ -80,7 +80,7 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) return 0; } -static void __devexit serial_card_remove(struct expansion_card *ec) +static void serial_card_remove(struct expansion_card *ec) { struct serial_card_info *info = ecard_get_drvdata(ec); int i; diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 7664750c2bd..1d0dba2d562 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -152,7 +152,7 @@ static int dw8250_probe(struct platform_device *pdev) return 0; } -static int __devexit dw8250_remove(struct platform_device *pdev) +static int dw8250_remove(struct platform_device *pdev) { struct dw8250_data *data = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index 430bf42374c..916cc19fbbd 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -152,7 +152,7 @@ static int serial8250_em_probe(struct platform_device *pdev) return ret; } -static int __devexit serial8250_em_remove(struct platform_device *pdev) +static int serial8250_em_remove(struct platform_device *pdev) { struct serial8250_em_priv *priv = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c index 2b945052ee0..5bdaf271d39 100644 --- a/drivers/tty/serial/8250/8250_hp300.c +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -38,7 +38,7 @@ static struct hp300_port *hp300_ports; static int hpdca_init_one(struct dio_dev *d, const struct dio_device_id *ent); -static void __devexit hpdca_remove_one(struct dio_dev *d); +static void hpdca_remove_one(struct dio_dev *d); static struct dio_device_id hpdca_dio_tbl[] = { { DIO_ID_DCA0 }, @@ -288,7 +288,7 @@ static int __init hp300_8250_init(void) } #ifdef CONFIG_HPDCA -static void __devexit hpdca_remove_one(struct dio_dev *d) +static void hpdca_remove_one(struct dio_dev *d) { int line; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 3252c5d47ff..97058c1d7d4 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -288,7 +288,7 @@ static int pci_plx9050_init(struct pci_dev *dev) return 0; } -static void __devexit pci_plx9050_exit(struct pci_dev *dev) +static void pci_plx9050_exit(struct pci_dev *dev) { u8 __iomem *p; @@ -313,7 +313,7 @@ static void __devexit pci_plx9050_exit(struct pci_dev *dev) #define NI8420_INT_ENABLE_REG 0x38 #define NI8420_INT_ENABLE_BIT 0x2000 -static void __devexit pci_ni8420_exit(struct pci_dev *dev) +static void pci_ni8420_exit(struct pci_dev *dev) { void __iomem *p; unsigned long base, len; @@ -345,7 +345,7 @@ static void __devexit pci_ni8420_exit(struct pci_dev *dev) #define MITE_LCIMR2_CLR_CPU_IE (1 << 30) -static void __devexit pci_ni8430_exit(struct pci_dev *dev) +static void pci_ni8430_exit(struct pci_dev *dev) { void __iomem *p; unsigned long base, len; @@ -422,7 +422,7 @@ static int sbs_init(struct pci_dev *dev) * Disables the global interrupt of PMC-OctalPro */ -static void __devexit sbs_exit(struct pci_dev *dev) +static void sbs_exit(struct pci_dev *dev) { u8 __iomem *p; @@ -991,7 +991,7 @@ static int pci_ite887x_init(struct pci_dev *dev) return ret; } -static void __devexit pci_ite887x_exit(struct pci_dev *dev) +static void pci_ite887x_exit(struct pci_dev *dev) { u32 ioport; /* the ioport is bit 0-15 in POSIO0R */ @@ -2988,7 +2988,7 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) return rc; } -static void __devexit pciserial_remove_one(struct pci_dev *dev) +static void pciserial_remove_one(struct pci_dev *dev) { struct serial_private *priv = pci_get_drvdata(dev); diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 71daae90fb5..35d9ab95c5c 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -476,7 +476,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) return 0; } -static void __devexit serial_pnp_remove(struct pnp_dev *dev) +static void serial_pnp_remove(struct pnp_dev *dev) { long line = (long)pnp_get_drvdata(dev); if (line) diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index ef5c705fa2b..872f14ae43d 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -453,7 +453,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev) return 0; } -static int __devexit altera_jtaguart_remove(struct platform_device *pdev) +static int altera_jtaguart_remove(struct platform_device *pdev) { struct uart_port *port; int i = pdev->id; diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 066b5035e10..684a0808e1c 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -598,7 +598,7 @@ static int altera_uart_probe(struct platform_device *pdev) return 0; } -static int __devexit altera_uart_remove(struct platform_device *pdev) +static int altera_uart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index ad171508b9a..505c490c0b4 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -707,7 +707,7 @@ err_free_up: return ret; } -static int __devexit ar933x_uart_remove(struct platform_device *pdev) +static int ar933x_uart_remove(struct platform_device *pdev) { struct ar933x_uart_port *up; diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 158d798a520..3e0b3fac6a0 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -689,7 +689,7 @@ static int arc_serial_probe(struct platform_device *pdev) return uart_add_one_port(&arc_uart_driver, &uart->port); } -static int __devexit arc_serial_remove(struct platform_device *pdev) +static int arc_serial_remove(struct platform_device *pdev) { /* This will never be called */ return 0; diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 02540cbf16a..d2a98da2613 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1851,7 +1851,7 @@ err: return ret; } -static int __devexit atmel_serial_remove(struct platform_device *pdev) +static int atmel_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index e54d1703be1..c76a226080f 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -848,7 +848,7 @@ static int bcm_uart_probe(struct platform_device *pdev) return 0; } -static int __devexit bcm_uart_remove(struct platform_device *pdev) +static int bcm_uart_remove(struct platform_device *pdev) { struct uart_port *port; diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index a47e00b056e..f5d117379b6 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -850,7 +850,7 @@ out_error_free_mem: return ret; } -static int __devexit sport_uart_remove(struct platform_device *pdev) +static int sport_uart_remove(struct platform_device *pdev) { struct sport_uart_port *sport = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index f1f8210a613..18cf45a29d4 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -1389,7 +1389,7 @@ out_error_free_mem: return ret; } -static int __devexit bfin_serial_remove(struct platform_device *pdev) +static int bfin_serial_remove(struct platform_device *pdev) { struct bfin_serial_port *uart = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 006d283bbde..3fd2526d121 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -491,7 +491,7 @@ err_out: return ret; } -static int __devexit uart_clps711x_remove(struct platform_device *pdev) +static int uart_clps711x_remove(struct platform_device *pdev) { struct clps711x_port *s = platform_get_drvdata(pdev); int i; diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index de3f0f6eba7..ad0caf17680 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1396,7 +1396,7 @@ static int cpm_uart_probe(struct platform_device *ofdev) return uart_add_one_port(&cpm_reg, &pinfo->port); } -static int __devexit cpm_uart_remove(struct platform_device *ofdev) +static int cpm_uart_remove(struct platform_device *ofdev) { struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev); return uart_remove_one_port(&cpm_reg, &pinfo->port); diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c index 833c33a2751..a8cbb267052 100644 --- a/drivers/tty/serial/efm32-uart.c +++ b/drivers/tty/serial/efm32-uart.c @@ -764,7 +764,7 @@ err_get_base: return ret; } -static int __devexit efm32_uart_remove(struct platform_device *pdev) +static int efm32_uart_remove(struct platform_device *pdev) { struct efm32_uart_port *efm_port = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index a8267956ac8..6197a69adb4 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -1596,7 +1596,7 @@ probe_exit0: return retval; } -static void __devexit icom_remove(struct pci_dev *dev) +static void icom_remove(struct pci_dev *dev) { struct icom_adapter *icom_adapter; struct list_head *tmp; diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 5b57c8eecfc..a47d882d674 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -178,7 +178,7 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) return rc; } -static void __devexit jsm_remove_one(struct pci_dev *pdev) +static void jsm_remove_one(struct pci_dev *pdev) { struct jsm_board *brd = pci_get_drvdata(pdev); int i = 0; diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index 3651dab2009..0e86bff3fe2 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -740,7 +740,7 @@ static int serial_hs_lpc32xx_probe(struct platform_device *pdev) /* * Remove serial ports registered against a platform device. */ -static int __devexit serial_hs_lpc32xx_remove(struct platform_device *pdev) +static int serial_hs_lpc32xx_remove(struct platform_device *pdev) { struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 8dd6189a40e..7ce3197087b 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -818,7 +818,7 @@ static int max3100_probe(struct spi_device *spi) return 0; } -static int __devexit max3100_remove(struct spi_device *spi) +static int max3100_remove(struct spi_device *spi) { struct max3100_port *s = dev_get_drvdata(&spi->dev); int i; diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 88a227f9fe8..3bb809d083a 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1202,7 +1202,7 @@ err_out: return ret; } -static int __devexit max310x_remove(struct spi_device *spi) +static int max310x_remove(struct spi_device *spi) { struct device *dev = &spi->dev; struct max310x_port *s = dev_get_drvdata(dev); diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index e2b93d2f8f8..fcd56ab6053 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -599,7 +599,7 @@ static int mcf_probe(struct platform_device *pdev) /****************************************************************************/ -static int __devexit mcf_remove(struct platform_device *pdev) +static int mcf_remove(struct platform_device *pdev) { struct uart_port *port; int i; diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 41497fd3d36..58734d7e746 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -855,7 +855,7 @@ err_get_page: return ret; } -static int __devexit serial_m3110_remove(struct spi_device *dev) +static int serial_m3110_remove(struct spi_device *dev) { struct uart_max3110 *max = spi_get_drvdata(dev); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 033e0bc9eba..95fd39be293 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -917,7 +917,7 @@ static int __init msm_serial_probe(struct platform_device *pdev) return uart_add_one_port(&msm_uart_driver, port); } -static int __devexit msm_serial_remove(struct platform_device *pdev) +static int msm_serial_remove(struct platform_device *pdev) { struct msm_port *msm_port = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 02fb63e944e..1fa92284ade 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -401,7 +401,7 @@ static int msm_hs_request_port(struct uart_port *port) return 0; } -static int __devexit msm_hs_remove(struct platform_device *pdev) +static int msm_hs_remove(struct platform_device *pdev) { struct msm_hs_port *msm_uport; diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c index 27834646d01..e2775b6df5a 100644 --- a/drivers/tty/serial/mux.c +++ b/drivers/tty/serial/mux.c @@ -520,7 +520,7 @@ static int __init mux_probe(struct parisc_device *dev) return 0; } -static int __devexit mux_remove(struct parisc_device *dev) +static int mux_remove(struct parisc_device *dev) { int i, j; int port_count = (long)dev_get_drvdata(&dev->dev); diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 18b55c2d1d2..3860ff27467 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1137,7 +1137,7 @@ out: return ret; } -static int __devexit mxs_auart_remove(struct platform_device *pdev) +static int mxs_auart_remove(struct platform_device *pdev) { struct mxs_auart_port *s = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index e777b16c4d1..b538e2e4ae5 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1441,7 +1441,7 @@ err_port_line: return ret; } -static int __devexit serial_omap_remove(struct platform_device *dev) +static int serial_omap_remove(struct platform_device *dev) { struct uart_omap_port *up = platform_get_drvdata(dev); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 6568beb4d37..82b48f60aa0 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1256,7 +1256,7 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) return ret; } -static int __devexit s3c24xx_serial_remove(struct platform_device *dev) +static int s3c24xx_serial_remove(struct platform_device *dev) { struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 1ddace83263..418b495e323 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -943,7 +943,7 @@ err_out: return ret; } -static int __devexit sccnxp_remove(struct platform_device *pdev) +static int sccnxp_remove(struct platform_device *pdev) { int i; struct sccnxp_port *s = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index 23b28b8f467..b52b21aeb25 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -1078,7 +1078,7 @@ static int serial_txx9_register_port(struct uart_port *port) * Remove one serial port. This may not be called from interrupt * context. We hand the port back to the our control. */ -static void __devexit serial_txx9_unregister_port(int line) +static void serial_txx9_unregister_port(int line) { struct uart_txx9_port *uart = &serial_txx9_ports[line]; @@ -1126,7 +1126,7 @@ static int serial_txx9_probe(struct platform_device *dev) /* * Remove serial ports registered against a platform device. */ -static int __devexit serial_txx9_remove(struct platform_device *dev) +static int serial_txx9_remove(struct platform_device *dev) { int i; @@ -1217,7 +1217,7 @@ pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent) return 0; } -static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev) +static void pciserial_txx9_remove_one(struct pci_dev *dev) { struct uart_txx9_port *up = pci_get_drvdata(dev); diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index cb58867036a..b9bf9c53f7f 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -598,7 +598,7 @@ out_free_port: return err; } -static int __devexit hv_remove(struct platform_device *dev) +static int hv_remove(struct platform_device *dev) { struct uart_port *port = dev_get_drvdata(&dev->dev); diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index 9a13c54d5f8..bd8b3b63410 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -1063,7 +1063,7 @@ out: return err; } -static int __devexit sab_remove(struct platform_device *op) +static int sab_remove(struct platform_device *op) { struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 049bbc5bc76..220da3f9724 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1503,7 +1503,7 @@ out_unmap: return err; } -static int __devexit su_remove(struct platform_device *op) +static int su_remove(struct platform_device *op) { struct uart_sunsu_port *up = dev_get_drvdata(&op->dev); bool kbdms = false; diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index 02c058fbefe..aef4fab957c 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -1507,7 +1507,7 @@ static int zs_probe(struct platform_device *op) return 0; } -static void __devexit zs_remove_one(struct uart_sunzilog_port *up) +static void zs_remove_one(struct uart_sunzilog_port *up) { if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) { #ifdef CONFIG_SERIO @@ -1517,7 +1517,7 @@ static void __devexit zs_remove_one(struct uart_sunzilog_port *up) uart_remove_one_port(&sunzilog_reg, &up->port); } -static int __devexit zs_remove(struct platform_device *op) +static int zs_remove(struct platform_device *op) { struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev); struct zilog_layout __iomem *regs; diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index c833f50980b..5be0d68fece 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -492,7 +492,7 @@ err_mem: return err; } -static int __devexit timbuart_remove(struct platform_device *dev) +static int timbuart_remove(struct platform_device *dev) { struct timbuart_port *uart = platform_get_drvdata(dev); diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 2d20b012b44..89eee43c4e2 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -542,7 +542,7 @@ static int ulite_assign(struct device *dev, int id, u32 base, int irq) * * @dev: pointer to device structure */ -static int __devexit ulite_release(struct device *dev) +static int ulite_release(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); int rc = 0; @@ -593,7 +593,7 @@ static int ulite_probe(struct platform_device *pdev) return ulite_assign(&pdev->dev, id, res->start, res2->start); } -static int __devexit ulite_remove(struct platform_device *pdev) +static int ulite_remove(struct platform_device *pdev) { return ulite_release(&pdev->dev); } diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c index c046c995534..62ee0166bc6 100644 --- a/drivers/tty/serial/vr41xx_siu.c +++ b/drivers/tty/serial/vr41xx_siu.c @@ -901,7 +901,7 @@ static int siu_probe(struct platform_device *dev) return 0; } -static int __devexit siu_remove(struct platform_device *dev) +static int siu_remove(struct platform_device *dev) { struct uart_port *port; int i; diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 80530c7d002..8fd181436a6 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -634,7 +634,7 @@ err: return ret; } -static int __devexit vt8500_serial_remove(struct platform_device *pdev) +static int vt8500_serial_remove(struct platform_device *pdev) { struct vt8500_port *vt8500_port = platform_get_drvdata(pdev); diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 61fa71433a0..9ab910370c5 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -997,7 +997,7 @@ static int xuartps_probe(struct platform_device *pdev) * * Returns 0 on success, negative error otherwise **/ -static int __devexit xuartps_remove(struct platform_device *pdev) +static int xuartps_remove(struct platform_device *pdev) { struct uart_port *port = dev_get_drvdata(&pdev->dev); int rc = 0; diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 4798dd5c55d..9e071f6985f 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -8117,7 +8117,7 @@ static int synclink_init_one (struct pci_dev *dev, return 0; } -static void __devexit synclink_remove_one (struct pci_dev *dev) +static void synclink_remove_one (struct pci_dev *dev) { } diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index a84c4089f56..aba1e59f4a8 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3710,7 +3710,7 @@ static int init_one(struct pci_dev *dev, return 0; } -static void __devexit remove_one(struct pci_dev *dev) +static void remove_one(struct pci_dev *dev) { } diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index d301110b4d1..fd43fb6f7ce 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -5606,6 +5606,6 @@ static int synclinkmp_init_one (struct pci_dev *dev, return 0; } -static void __devexit synclinkmp_remove_one (struct pci_dev *dev) +static void synclinkmp_remove_one (struct pci_dev *dev) { } From dc96efb72054985c0912f831da009a2da4e9f6dd Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Mon, 19 Nov 2012 09:12:04 -0600 Subject: [PATCH 120/146] Serial: Add support for new devices: Exar's XR17V35x family of multi-port PCIe UARTs Add support for new devices: Exar's XR17V35x family of multi-port PCIe UARTs. Signed-off-by: Matt Schulte Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.c | 71 ++++++++++++++++++++++ drivers/tty/serial/8250/8250_pci.c | 96 ++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 3 + include/uapi/linux/serial_core.h | 3 +- include/uapi/linux/serial_reg.h | 6 ++ 5 files changed, 178 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 2af83a24649..3624df674a3 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -282,6 +282,15 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, }, + [PORT_XR17V35X] = { + .name = "XR17V35X", + .fifo_size = 256, + .tx_loadsz = 256, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11 | + UART_FCR_T_TRIG_11, + .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR | + UART_CAP_SLEEP, + }, [PORT_LPC3220] = { .name = "LPC3220", .fifo_size = 64, @@ -455,6 +464,7 @@ static void io_serial_out(struct uart_port *p, int offset, int value) } static int serial8250_default_handle_irq(struct uart_port *port); +static int exar_handle_irq(struct uart_port *port); static void set_io_from_upio(struct uart_port *p) { @@ -574,6 +584,18 @@ EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); */ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) { + /* + * Exar UARTs have a SLEEP register that enables or disables + * each UART to enter sleep mode separately. On the XR17V35x the + * register is accessible to each UART at the UART_EXAR_SLEEP + * offset but the UART channel may only write to the corresponding + * bit. + */ + if (p->port.type == PORT_XR17V35X) { + serial_out(p, UART_EXAR_SLEEP, 0xff); + return; + } + if (p->capabilities & UART_CAP_SLEEP) { if (p->capabilities & UART_CAP_EFR) { serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B); @@ -881,6 +903,27 @@ static void autoconfig_16550a(struct uart_8250_port *up) up->port.type = PORT_16550A; up->capabilities |= UART_CAP_FIFO; + /* + * XR17V35x UARTs have an extra divisor register, DLD + * that gets enabled with when DLAB is set which will + * cause the device to incorrectly match and assign + * port type to PORT_16650. The EFR for this UART is + * found at offset 0x09. Instead check the Deice ID (DVID) + * register for a 2, 4 or 8 port UART. + */ + status1 = serial_in(up, UART_EXAR_DVID); + if (status1 == 0x82 || status1 == 0x84 || status1 == 0x88) { + if (up->port.flags & UPF_EXAR_EFR) { + DEBUG_AUTOCONF("Exar XR17V35x "); + up->port.type = PORT_XR17V35X; + up->capabilities |= UART_CAP_AFE | UART_CAP_EFR | + UART_CAP_SLEEP; + + return; + } + + } + /* * Check for presence of the EFR when DLAB is set. * Only ST16C650V1 UARTs pass this test. @@ -1515,6 +1558,30 @@ static int serial8250_default_handle_irq(struct uart_port *port) return serial8250_handle_irq(port, iir); } +/* + * These Exar UARTs have an extra interrupt indicator that could + * fire for a few unimplemented interrupts. One of which is a + * wakeup event when coming out of sleep. Put this here just + * to be on the safe side that these interrupts don't go unhandled. + */ +static int exar_handle_irq(struct uart_port *port) +{ + unsigned char int0, int1, int2, int3; + unsigned int iir = serial_port_in(port, UART_IIR); + int ret; + + ret = serial8250_handle_irq(port, iir); + + if (port->type == PORT_XR17V35X) { + int0 = serial_port_in(port, 0x80); + int1 = serial_port_in(port, 0x81); + int2 = serial_port_in(port, 0x82); + int3 = serial_port_in(port, 0x83); + } + + return ret; +} + /* * This is the serial driver's interrupt routine. * @@ -2614,6 +2681,10 @@ static void serial8250_config_port(struct uart_port *port, int flags) serial8250_release_rsa_resource(up); if (port->type == PORT_UNKNOWN) serial8250_release_std_resource(up); + + /* Fixme: probably not the best place for this */ + if (port->type == PORT_XR17V35X) + port->handle_irq = exar_handle_irq; } static int diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 97058c1d7d4..2285d3283b3 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1164,6 +1164,39 @@ pci_xr17c154_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +static int +pci_xr17v35x_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + u8 __iomem *p; + + p = pci_ioremap_bar(priv->dev, 0); + + port->port.flags |= UPF_EXAR_EFR; + + /* + * Setup Multipurpose Input/Output pins. + */ + if (idx == 0) { + writeb(0x00, p + 0x8f); /*MPIOINT[7:0]*/ + writeb(0x00, p + 0x90); /*MPIOLVL[7:0]*/ + writeb(0x00, p + 0x91); /*MPIO3T[7:0]*/ + writeb(0x00, p + 0x92); /*MPIOINV[7:0]*/ + writeb(0x00, p + 0x93); /*MPIOSEL[7:0]*/ + writeb(0x00, p + 0x94); /*MPIOOD[7:0]*/ + writeb(0x00, p + 0x95); /*MPIOINT[15:8]*/ + writeb(0x00, p + 0x96); /*MPIOLVL[15:8]*/ + writeb(0x00, p + 0x97); /*MPIO3T[15:8]*/ + writeb(0x00, p + 0x98); /*MPIOINV[15:8]*/ + writeb(0x00, p + 0x99); /*MPIOSEL[15:8]*/ + writeb(0x00, p + 0x9a); /*MPIOOD[15:8]*/ + } + iounmap(p); + + return pci_default_setup(priv, board, port, idx); +} + static int pci_wch_ch353_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1622,6 +1655,27 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_xr17c154_setup, }, + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17V352, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17V354, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17V358, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, /* * Xircom cards */ @@ -1962,6 +2016,9 @@ enum pci_board_num_t { pbn_exar_XR17C152, pbn_exar_XR17C154, pbn_exar_XR17C158, + pbn_exar_XR17V352, + pbn_exar_XR17V354, + pbn_exar_XR17V358, pbn_exar_ibm_saturn, pbn_pasemi_1682M, pbn_ni8430_2, @@ -2580,6 +2637,30 @@ static struct pciserial_board pci_boards[] = { .base_baud = 921600, .uart_offset = 0x200, }, + [pbn_exar_XR17V352] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 7812500, + .uart_offset = 0x400, + .reg_shift = 0, + .first_offset = 0, + }, + [pbn_exar_XR17V354] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 7812500, + .uart_offset = 0x400, + .reg_shift = 0, + .first_offset = 0, + }, + [pbn_exar_XR17V358] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 7812500, + .uart_offset = 0x400, + .reg_shift = 0, + .first_offset = 0, + }, [pbn_exar_ibm_saturn] = { .flags = FL_BASE0, .num_ports = 1, @@ -3826,6 +3907,21 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_exar_XR17C158 }, + /* + * Exar Corp. XR17V35[248] Dual/Quad/Octal PCIe UARTs + */ + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V352, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V352 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V354, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V354 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V358, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V358 }, /* * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 9d36b829533..0199a7a76fc 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1985,6 +1985,9 @@ #define PCI_DEVICE_ID_EXAR_XR17C152 0x0152 #define PCI_DEVICE_ID_EXAR_XR17C154 0x0154 #define PCI_DEVICE_ID_EXAR_XR17C158 0x0158 +#define PCI_DEVICE_ID_EXAR_XR17V352 0x0352 +#define PCI_DEVICE_ID_EXAR_XR17V354 0x0354 +#define PCI_DEVICE_ID_EXAR_XR17V358 0x0358 #define PCI_VENDOR_ID_MICROGATE 0x13c0 #define PCI_DEVICE_ID_MICROGATE_USC 0x0010 diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index ebcc73f0418..78f99d97475 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -49,7 +49,8 @@ #define PORT_XR17D15X 21 /* Exar XR17D15x UART */ #define PORT_LPC3220 22 /* NXP LPC32xx SoC "Standard" UART */ #define PORT_8250_CIR 23 /* CIR infrared port, has its own driver */ -#define PORT_MAX_8250 23 /* max port ID */ +#define PORT_XR17V35X 24 /* Exar XR17V35x UARTs */ +#define PORT_MAX_8250 24 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 5ed325e88a8..d0b47607b90 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -367,5 +367,11 @@ #define UART_OMAP_MDR1_CIR_MODE 0x06 /* CIR mode */ #define UART_OMAP_MDR1_DISABLE 0x07 /* Disable (default state) */ +/* + * These are definitions for the XR17V35X and XR17D15X + */ +#define UART_EXAR_SLEEP 0x8b /* Sleep mode */ +#define UART_EXAR_DVID 0x8d /* Device identification */ + #endif /* _LINUX_SERIAL_REG_H */ From 159a8e92fdf6967cb67e7639832f819fbc607242 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 18 Nov 2012 21:27:40 -0800 Subject: [PATCH 121/146] pty: Mark pty_resize static Nothing outside of drivers/tty/pty.c references pty_resize. Signed-off-by: Josh Triplett Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index a541ec87593..be6a373601b 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -277,7 +277,7 @@ static void pty_set_termios(struct tty_struct *tty, * peform a terminal resize correctly */ -int pty_resize(struct tty_struct *tty, struct winsize *ws) +static int pty_resize(struct tty_struct *tty, struct winsize *ws) { struct pid *pgrp, *rpgrp; unsigned long flags; From 1cd3f2d2c99892209c4751155ae56ff18b1b253e Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 19 Nov 2012 14:40:56 +0800 Subject: [PATCH 122/146] serial: bfin_uart: Don't switch baud rate untill the transfer buffer is empty. set_termios may be invoked before the former data transfer is completed. Block until the tranfer is done. Signed-off-by: Sonic Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_uart.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 18cf45a29d4..e6a008f4939 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -799,6 +799,7 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long flags; unsigned int baud, quot; unsigned int ier, lcr = 0; + unsigned long timeout; switch (termios->c_cflag & CSIZE) { case CS8: @@ -868,6 +869,14 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15); + /* Wait till the transfer buffer is empty */ + timeout = jiffies + msecs_to_jiffies(10); + while (UART_GET_GCTL(uart) & UCEN && !(UART_GET_LSR(uart) & TEMT)) + if (time_after(jiffies, timeout)) { + dev_warn(port->dev, "timeout waiting for TX buffer empty\n"); + break; + } + /* Disable UART */ ier = UART_GET_IER(uart); UART_PUT_GCTL(uart, UART_GET_GCTL(uart) & ~UCEN); From d02f81555362e0032080af62154dca00d5ec99e0 Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Tue, 20 Nov 2012 11:21:17 -0600 Subject: [PATCH 123/146] Add register definitions used in several Exar PCI/PCIe UARTs Add register definitions used in several Exar PCI/PCIe UARTs Signed-off-by: Matt Schulte Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/serial_reg.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index d0b47607b90..e6322605b13 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -368,10 +368,22 @@ #define UART_OMAP_MDR1_DISABLE 0x07 /* Disable (default state) */ /* - * These are definitions for the XR17V35X and XR17D15X + * These are definitions for the Exar XR17V35X and XR17(C|D)15X */ +#define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */ #define UART_EXAR_SLEEP 0x8b /* Sleep mode */ #define UART_EXAR_DVID 0x8d /* Device identification */ +#define UART_EXAR_FCTR 0x08 /* Feature Control Register */ +#define UART_FCTR_EXAR_IRDA 0x08 /* IrDa data encode select */ +#define UART_FCTR_EXAR_485 0x10 /* Auto 485 half duplex dir ctl */ +#define UART_FCTR_EXAR_TRGA 0x00 /* FIFO trigger table A */ +#define UART_FCTR_EXAR_TRGB 0x60 /* FIFO trigger table B */ +#define UART_FCTR_EXAR_TRGC 0x80 /* FIFO trigger table C */ +#define UART_FCTR_EXAR_TRGD 0xc0 /* FIFO trigger table D programmable */ + +#define UART_EXAR_TXTRG 0x0a /* Tx FIFO trigger level write-only */ +#define UART_EXAR_RXTRG 0x0b /* Rx FIFO trigger level write-only */ + #endif /* _LINUX_SERIAL_REG_H */ From b7a7e14f654c80dfd3aa514adee663c74cfa4be9 Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Tue, 20 Nov 2012 11:23:56 -0600 Subject: [PATCH 124/146] serial: Optimization: check for presence of UPF_EXAR_EFR flag before serial_in Optimization: check for presence of UPF_EXAR_EFR flag before serial_in Signed-off-by: Matt Schulte Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 3624df674a3..39d970719f7 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -911,9 +911,9 @@ static void autoconfig_16550a(struct uart_8250_port *up) * found at offset 0x09. Instead check the Deice ID (DVID) * register for a 2, 4 or 8 port UART. */ - status1 = serial_in(up, UART_EXAR_DVID); - if (status1 == 0x82 || status1 == 0x84 || status1 == 0x88) { - if (up->port.flags & UPF_EXAR_EFR) { + if (up->port.flags & UPF_EXAR_EFR) { + status1 = serial_in(up, UART_EXAR_DVID); + if (status1 == 0x82 || status1 == 0x84 || status1 == 0x88) { DEBUG_AUTOCONF("Exar XR17V35x "); up->port.type = PORT_XR17V35X; up->capabilities |= UART_CAP_AFE | UART_CAP_EFR | From f965b9c46bf832daff8e31796486e7e424bd72c5 Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Tue, 20 Nov 2012 11:25:40 -0600 Subject: [PATCH 125/146] serial: Add initialization of sampling mode and tx/rx triggers to pci_xr17v35x_setup Add initialization of sampling mode and tx/rx triggers to pci_xr17v35x_setup Signed-off-by: Matt Schulte Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 2285d3283b3..089fd43e378 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1192,6 +1192,10 @@ pci_xr17v35x_setup(struct serial_private *priv, writeb(0x00, p + 0x99); /*MPIOSEL[15:8]*/ writeb(0x00, p + 0x9a); /*MPIOOD[15:8]*/ } + writeb(0x00, p + UART_EXAR_8XMODE); + writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); + writeb(128, p + UART_EXAR_TXTRG); + writeb(128, p + UART_EXAR_RXTRG); iounmap(p); return pci_default_setup(priv, board, port, idx); From 81db0772dc16b31185418f51ce6a1c0098a84367 Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Wed, 21 Nov 2012 09:39:07 -0600 Subject: [PATCH 126/146] tty/8250: Add sleep capability to XR17D15X ports Add sleep capability to XR17D15X ports Signed-off-by: Matt Schulte Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 39d970719f7..4ab8af797ad 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -280,7 +280,8 @@ static const struct serial8250_config uart_config[] = { .fifo_size = 64, .tx_loadsz = 64, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, + .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR | + UART_CAP_SLEEP, }, [PORT_XR17V35X] = { .name = "XR17V35X", @@ -591,7 +592,8 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) * offset but the UART channel may only write to the corresponding * bit. */ - if (p->port.type == PORT_XR17V35X) { + if ((p->port.type == PORT_XR17V35X) || + (p->port.type == PORT_XR17D15X)) { serial_out(p, UART_EXAR_SLEEP, 0xff); return; } @@ -1056,8 +1058,12 @@ static void autoconfig_16550a(struct uart_8250_port *up) * Exar uarts have EFR in a weird location */ if (up->port.flags & UPF_EXAR_EFR) { + DEBUG_AUTOCONF("Exar XR17D15x "); up->port.type = PORT_XR17D15X; - up->capabilities |= UART_CAP_AFE | UART_CAP_EFR; + up->capabilities |= UART_CAP_AFE | UART_CAP_EFR | + UART_CAP_SLEEP; + + return; } /* From 30fa96a34694d93bf76311944c9521bbcdd4e58e Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Wed, 21 Nov 2012 09:40:49 -0600 Subject: [PATCH 127/146] tty/8250 Add XR17D15x devices to the exar_handle_irq override Add XR17D15x devices to the exar_handle_irq override: they have the same extra interrupt register that could fire and never be serviced by the standard handle_irq. Signed-off-by: Matt Schulte Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 4ab8af797ad..d085e3a8ec0 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -1578,7 +1578,8 @@ static int exar_handle_irq(struct uart_port *port) ret = serial8250_handle_irq(port, iir); - if (port->type == PORT_XR17V35X) { + if ((port->type == PORT_XR17V35X) || + (port->type == PORT_XR17D15X)) { int0 = serial_port_in(port, 0x80); int1 = serial_port_in(port, 0x81); int2 = serial_port_in(port, 0x82); @@ -2689,7 +2690,8 @@ static void serial8250_config_port(struct uart_port *port, int flags) serial8250_release_std_resource(up); /* Fixme: probably not the best place for this */ - if (port->type == PORT_XR17V35X) + if ((port->type == PORT_XR17V35X) || + (port->type == PORT_XR17D15X)) port->handle_irq = exar_handle_irq; } From 14faa8cce88e18ed4daf5c3643f0faa4198863f9 Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Wed, 21 Nov 2012 10:35:15 -0600 Subject: [PATCH 128/146] tty/8250 Add support for Commtech's Fastcom Async-335 and Fastcom Async-PCIe cards Add support for Commtech's Fastcom Async-335 and Fastcom Async-PCIe cards Signed-off-by: Matt Schulte Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 163 +++++++++++++++++++++++++++++ include/linux/pci_ids.h | 2 + 2 files changed, 165 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 089fd43e378..a795bd971ea 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1201,6 +1201,55 @@ pci_xr17v35x_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +#define PCI_DEVICE_ID_COMMTECH_4222PCI335 0x0004 +#define PCI_DEVICE_ID_COMMTECH_4224PCI335 0x0002 +#define PCI_DEVICE_ID_COMMTECH_2324PCI335 0x000a +#define PCI_DEVICE_ID_COMMTECH_2328PCI335 0x000b + +static int +pci_fastcom335_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + u8 __iomem *p; + + p = pci_ioremap_bar(priv->dev, 0); + if (p == NULL) + return -ENOMEM; + + port->port.flags |= UPF_EXAR_EFR; + + /* + * Setup Multipurpose Input/Output pins. + */ + if (idx == 0) { + switch (priv->dev->device) { + case PCI_DEVICE_ID_COMMTECH_4222PCI335: + case PCI_DEVICE_ID_COMMTECH_4224PCI335: + writeb(0x78, p + 0x90); /* MPIOLVL[7:0] */ + writeb(0x00, p + 0x92); /* MPIOINV[7:0] */ + writeb(0x00, p + 0x93); /* MPIOSEL[7:0] */ + break; + case PCI_DEVICE_ID_COMMTECH_2324PCI335: + case PCI_DEVICE_ID_COMMTECH_2328PCI335: + writeb(0x00, p + 0x90); /* MPIOLVL[7:0] */ + writeb(0xc0, p + 0x92); /* MPIOINV[7:0] */ + writeb(0xc0, p + 0x93); /* MPIOSEL[7:0] */ + break; + } + writeb(0x00, p + 0x8f); /* MPIOINT[7:0] */ + writeb(0x00, p + 0x91); /* MPIO3T[7:0] */ + writeb(0x00, p + 0x94); /* MPIOOD[7:0] */ + } + writeb(0x00, p + UART_EXAR_8XMODE); + writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); + writeb(32, p + UART_EXAR_TXTRG); + writeb(32, p + UART_EXAR_RXTRG); + iounmap(p); + + return pci_default_setup(priv, board, port, idx); +} + static int pci_wch_ch353_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1250,6 +1299,10 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_VENDOR_ID_AGESTAR 0x5372 #define PCI_DEVICE_ID_AGESTAR_9375 0x6872 #define PCI_VENDOR_ID_ASIX 0x9710 +#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0019 +#define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020 +#define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 + /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -1845,6 +1898,59 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_asix_setup, }, + /* + * Commtech, Inc. Fastcom adapters + * + */ + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_4222PCI335, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fastcom335_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_4224PCI335, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fastcom335_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_2324PCI335, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fastcom335_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_2328PCI335, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fastcom335_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_4222PCIE, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_4224PCIE, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_4228PCIE, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, /* * Default "match everything" terminator entry */ @@ -1921,6 +2027,10 @@ enum pci_board_num_t { pbn_b0_4_1152000, + pbn_b0_2_1152000_200, + pbn_b0_4_1152000_200, + pbn_b0_8_1152000_200, + pbn_b0_2_1843200, pbn_b0_4_1843200, @@ -2118,6 +2228,27 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 8, }, + [pbn_b0_2_1152000_200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1152000, + .uart_offset = 0x200, + }, + + [pbn_b0_4_1152000_200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1152000, + .uart_offset = 0x200, + }, + + [pbn_b0_8_1152000_200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1152000, + .uart_offset = 0x200, + }, + [pbn_b0_2_1843200] = { .flags = FL_BASE0, .num_ports = 2, @@ -4356,6 +4487,38 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, + /* + * Commtech, Inc. Fastcom adapters + */ + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4222PCI335, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_b0_2_1152000_200 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4224PCI335, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_b0_4_1152000_200 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_2324PCI335, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_b0_4_1152000_200 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_2328PCI335, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_b0_8_1152000_200 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4222PCIE, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V352 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4224PCIE, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V354 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4228PCIE, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V358 }, + /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 0199a7a76fc..0f8447376dd 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2326,6 +2326,8 @@ #define PCI_VENDOR_ID_TOPSPIN 0x1867 +#define PCI_VENDOR_ID_COMMTECH 0x18f7 + #define PCI_VENDOR_ID_SILAN 0x1904 #define PCI_VENDOR_ID_RENESAS 0x1912 From 13c3237dbcc92fa7d17d3eb06de6477c4bd7fb5d Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Wed, 21 Nov 2012 10:39:18 -0600 Subject: [PATCH 129/146] tty/8250 Add check for pci_ioremap_bar failure Add check for pci_ioremap_bar failure Signed-off-by: Matt Schulte Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index a795bd971ea..26b9dc012ed 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1172,6 +1172,8 @@ pci_xr17v35x_setup(struct serial_private *priv, u8 __iomem *p; p = pci_ioremap_bar(priv->dev, 0); + if (p == NULL) + return -ENOMEM; port->port.flags |= UPF_EXAR_EFR; From daa74a25cc7351a9fb01ba7611af5ef5df80ae4e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 22 Nov 2012 12:18:05 +0530 Subject: [PATCH 130/146] tty: vt: Remove redundant null check before kfree. kfree on a NULL pointer is a no-op. Signed-off-by: Sachin Kamat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/consolemap.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 2aaa0c22840..248381b3072 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -410,10 +410,8 @@ static void con_release_unimap(struct uni_pagedir *p) kfree(p->inverse_translations[i]); p->inverse_translations[i] = NULL; } - if (p->inverse_trans_unicode) { - kfree(p->inverse_trans_unicode); - p->inverse_trans_unicode = NULL; - } + kfree(p->inverse_trans_unicode); + p->inverse_trans_unicode = NULL; } /* Caller must hold the console lock */ From c15c3747ee32b6969f3cfbc86dc94923e3742d0a Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Thu, 22 Nov 2012 18:06:28 +0530 Subject: [PATCH 131/146] serial: samsung: fix potential soft lockup during uart write Certain tty line discipline implementations such slip and bluetooth hci invoke the serial core uart_write() api in their write_wakeup callback. This leads to a soft lockup with samsung serial driver since the uart port lock is taken in the driver's interrupt handler and uart_write() attempts to take the same lock again. Fix this issue by releasing the uart port lock before the call to uart_write_wakeup() in the tx handler. Also move the spin-lock/unlock sequence from s3c64xx_serial_handle_irq() function into the tx and rx irq handlers so that this change is applicable to s3c24xx platforms as well. Reported-by: Kyungmin Park Reported-by: Hyeonkook Kim Signed-off-by: Thomas Abraham Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 82b48f60aa0..9368c3e81cc 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -223,8 +223,11 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) struct uart_port *port = &ourport->port; struct tty_struct *tty = port->state->port.tty; unsigned int ufcon, ch, flag, ufstat, uerstat; + unsigned long flags; int max_count = 64; + spin_lock_irqsave(&port->lock, flags); + while (max_count-- > 0) { ufcon = rd_regl(port, S3C2410_UFCON); ufstat = rd_regl(port, S3C2410_UFSTAT); @@ -299,6 +302,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) tty_flip_buffer_push(tty); out: + spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; } @@ -307,8 +311,11 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) struct s3c24xx_uart_port *ourport = id; struct uart_port *port = &ourport->port; struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; int count = 256; + spin_lock_irqsave(&port->lock, flags); + if (port->x_char) { wr_regb(port, S3C2410_UTXH, port->x_char); port->icount.tx++; @@ -336,13 +343,17 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) port->icount.tx++; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { + spin_unlock(&port->lock); uart_write_wakeup(port); + spin_lock(&port->lock); + } if (uart_circ_empty(xmit)) s3c24xx_serial_stop_tx(port); out: + spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; } @@ -352,10 +363,8 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) struct s3c24xx_uart_port *ourport = id; struct uart_port *port = &ourport->port; unsigned int pend = rd_regl(port, S3C64XX_UINTP); - unsigned long flags; irqreturn_t ret = IRQ_HANDLED; - spin_lock_irqsave(&port->lock, flags); if (pend & S3C64XX_UINTM_RXD_MSK) { ret = s3c24xx_serial_rx_chars(irq, id); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK); @@ -364,7 +373,6 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) ret = s3c24xx_serial_tx_chars(irq, id); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK); } - spin_unlock_irqrestore(&port->lock, flags); return ret; } From 666ca0b9b706ca3ee11f7bcba1f845a127937b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Thu, 22 Nov 2012 11:37:44 +0100 Subject: [PATCH 132/146] serial: samsung: add devicetree properties for non-Exynos SoCs Until now only the Exynos SoCs could use the serial driver via the device tree. This patch adds compatible properties for the other supported SoCs as well. Tested on a s3c2416 based machine. Signed-off-by: Heiko Stuebner Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 9368c3e81cc..fb0e0f0bed0 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1709,6 +1709,16 @@ MODULE_DEVICE_TABLE(platform, s3c24xx_serial_driver_ids); #ifdef CONFIG_OF static const struct of_device_id s3c24xx_uart_dt_match[] = { + { .compatible = "samsung,s3c2410-uart", + .data = (void *)S3C2410_SERIAL_DRV_DATA }, + { .compatible = "samsung,s3c2412-uart", + .data = (void *)S3C2412_SERIAL_DRV_DATA }, + { .compatible = "samsung,s3c2440-uart", + .data = (void *)S3C2440_SERIAL_DRV_DATA }, + { .compatible = "samsung,s3c6400-uart", + .data = (void *)S3C6400_SERIAL_DRV_DATA }, + { .compatible = "samsung,s5pv210-uart", + .data = (void *)S5PV210_SERIAL_DRV_DATA }, { .compatible = "samsung,exynos4210-uart", .data = (void *)EXYNOS4210_SERIAL_DRV_DATA }, {}, From bc6835a41aea115d97a74d67dc95faa0bbc63d5f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 26 Nov 2012 11:07:25 -0800 Subject: [PATCH 133/146] tty/serial: fix ifx6x60.c declaration warning Fix gcc warning of mixed data/code: drivers/tty/serial/ifx6x60.c:516:2: warning: ISO C90 forbids mixed declarations and code Signed-off-by: Randy Dunlap Cc: Russ Gorby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 2783d96ff61..6356dba49e6 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -510,10 +510,11 @@ static int ifx_spi_write(struct tty_struct *tty, const unsigned char *buf, unsigned char *tmp_buf = (unsigned char *)buf; unsigned long flags; bool is_fifo_empty; + int tx_count; spin_lock_irqsave(&ifx_dev->fifo_lock, flags); is_fifo_empty = kfifo_is_empty(&ifx_dev->tx_fifo); - int tx_count = kfifo_in(&ifx_dev->tx_fifo, tmp_buf, count); + tx_count = kfifo_in(&ifx_dev->tx_fifo, tmp_buf, count); spin_unlock_irqrestore(&ifx_dev->fifo_lock, flags); if (is_fifo_empty) mrdy_assert(ifx_dev); From 273a4b8a58840773730e0134fe1af11d399cb7a0 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Thu, 22 Nov 2012 00:07:32 +0400 Subject: [PATCH 134/146] serial: max310x: Setup missing "can_sleep" field for GPIO Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 3bb809d083a..a801f6872ca 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1178,6 +1178,7 @@ static int max310x_probe(struct spi_device *spi) s->gpio.set = max310x_gpio_set; s->gpio.base = pdata->gpio_base; s->gpio.ngpio = s->nr_gpio; + s->gpio.can_sleep = 1; if (gpiochip_add(&s->gpio)) { /* Indicate that we should not call gpiochip_remove */ s->gpio.base = 0; From a5919442bc61846e36011671df0d67c72275337e Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Thu, 22 Nov 2012 15:06:29 +0800 Subject: [PATCH 135/146] serial: mxs-auart: disable the Receive Timeout Interrupt when DMA is enabled When the DMA is enabled, the Receive Timeout interrupt is very easy to be arised in the 3M baud rate. The interrupt handler (aka mxs_auart_irq_handle) will call mxs_auart_rx_chars() to handle the received data. This is not right, we can not get the correct data from the RXFIFO now, the data have been moved to the DMA buffer by the DMA engine. This patch (1) disables the Receive Timeout Interrupt when the DMA is enabled, (2) and invoke the mxs_auart_rx_chars() only when the DMA is not enabled. Signed-off-by: Huang Shijie Tested-by: Lauri Hintsala Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 3860ff27467..f56d6b92cf3 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -695,7 +695,8 @@ static void mxs_auart_settermios(struct uart_port *u, !test_and_set_bit(MXS_AUART_DMA_RX_READY, &s->flags)) { if (!mxs_auart_dma_prep_rx(s)) { /* Disable the normal RX interrupt. */ - writel(AUART_INTR_RXIEN, u->membase + AUART_INTR_CLR); + writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN, + u->membase + AUART_INTR_CLR); } else { mxs_auart_dma_exit(s); dev_err(s->dev, "We can not start up the DMA.\n"); @@ -719,7 +720,8 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) } if (istat & (AUART_INTR_RTIS | AUART_INTR_RXIS)) { - mxs_auart_rx_chars(s); + if (!auart_dma_enabled(s)) + mxs_auart_rx_chars(s); istat &= ~(AUART_INTR_RTIS | AUART_INTR_RXIS); } From d7ffb9329012a517575e4c4d49480b6ce0d1529e Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Thu, 22 Nov 2012 15:06:30 +0800 Subject: [PATCH 136/146] serial: mxs-auart: unmap the scatter list before we copy the data We should first unmap the DMA scatter list for receiving data, and then copy the data from the DMA buffer. The old code misses unmap the scatter list for RX. This patch fixes it. Signed-off-by: Huang Shijie Tested-by: Lauri Hintsala Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index f56d6b92cf3..6db23b035ef 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -461,6 +461,8 @@ static void dma_rx_callback(void *arg) int count; u32 stat; + dma_unmap_sg(s->dev, &s->rx_sgl, 1, DMA_FROM_DEVICE); + stat = readl(s->port.membase + AUART_STAT); stat &= ~(AUART_STAT_OERR | AUART_STAT_BERR | AUART_STAT_PERR | AUART_STAT_FERR); From 72d4724ea54c360115bca7979167850dfa37ec65 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Fri, 23 Nov 2012 06:07:27 -0500 Subject: [PATCH 137/146] serial: ifx6x60: Add modem power off function in the platform reboot process This patch add modem power off function in the reboot process according registering reboot callback to the reboot_notifier_list. Also realizing the spi shutdown function. Signed-off-by: Bi Chao Signed-off-by: Chen Jun Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 56 ++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 6356dba49e6..675d94ab0af 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -60,6 +60,7 @@ #include #include #include +#include #include "ifx6x60.h" @@ -72,8 +73,14 @@ #define IFX_SPI_HEADER_0 (-1) #define IFX_SPI_HEADER_F (-2) +#define PO_POST_DELAY 200 +#define IFX_MDM_RST_PMU 4 + /* forward reference */ static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev); +static int ifx_modem_reboot_callback(struct notifier_block *nfb, + unsigned long event, void *data); +static int ifx_modem_power_off(struct ifx_spi_device *ifx_dev); /* local variables */ static int spi_bpw = 16; /* 8, 16 or 32 bit word length */ @@ -81,6 +88,29 @@ static struct tty_driver *tty_drv; static struct ifx_spi_device *saved_ifx_dev; static struct lock_class_key ifx_spi_key; +static struct notifier_block ifx_modem_reboot_notifier_block = { + .notifier_call = ifx_modem_reboot_callback, +}; + +static int ifx_modem_power_off(struct ifx_spi_device *ifx_dev) +{ + gpio_set_value(IFX_MDM_RST_PMU, 1); + msleep(PO_POST_DELAY); + + return 0; +} + +static int ifx_modem_reboot_callback(struct notifier_block *nfb, + unsigned long event, void *data) +{ + if (saved_ifx_dev) + ifx_modem_power_off(saved_ifx_dev); + else + pr_warn("no ifx modem active;\n"); + + return NOTIFY_OK; +} + /* GPIO/GPE settings */ /** @@ -1287,6 +1317,9 @@ static int ifx_spi_spi_remove(struct spi_device *spi) static void ifx_spi_spi_shutdown(struct spi_device *spi) { + struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi); + + ifx_modem_power_off(ifx_dev); } /* @@ -1422,7 +1455,9 @@ static void __exit ifx_spi_exit(void) { /* unregister */ tty_unregister_driver(tty_drv); + put_tty_driver(tty_drv); spi_unregister_driver((void *)&ifx_spi_driver); + unregister_reboot_notifier(&ifx_modem_reboot_notifier_block); } /** @@ -1457,16 +1492,31 @@ static int __init ifx_spi_init(void) if (result) { pr_err("%s: tty_register_driver failed(%d)", DRVNAME, result); - put_tty_driver(tty_drv); - return result; + goto err_free_tty; } result = spi_register_driver((void *)&ifx_spi_driver); if (result) { pr_err("%s: spi_register_driver failed(%d)", DRVNAME, result); - tty_unregister_driver(tty_drv); + goto err_unreg_tty; } + + result = register_reboot_notifier(&ifx_modem_reboot_notifier_block); + if (result) { + pr_err("%s: register ifx modem reboot notifier failed(%d)", + DRVNAME, result); + goto err_unreg_spi; + } + + return 0; +err_unreg_spi: + spi_unregister_driver((void *)&ifx_spi_driver); +err_unreg_tty: + tty_unregister_driver(tty_drv); +err_free_tty: + put_tty_driver(tty_drv); + return result; } From 3ba89e96610b85b5a2f3a146f65342e38af27a79 Mon Sep 17 00:00:00 2001 From: Kumar Amit Mehta Date: Fri, 23 Nov 2012 01:16:34 +0530 Subject: [PATCH 138/146] staging: dgrp: dgrp_tty.c: Remove the TIOCSSOFTCAR ioctl handler from dgrp driver Remove the TIOCSSOFTCAR ioctl handler from dgrp driver and let the core tty layer to take care of this ioctl instead. Signed-off-by: Kumar Amit Mehta Signed-off-by: Greg Kroah-Hartman --- drivers/staging/dgrp/dgrp_tty.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c index e3fa6eac53c..5796de8ddbe 100644 --- a/drivers/staging/dgrp/dgrp_tty.c +++ b/drivers/staging/dgrp/dgrp_tty.c @@ -2623,13 +2623,6 @@ static int dgrp_tty_ioctl(struct tty_struct *tty, unsigned int cmd, put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) arg); return 0; - case TIOCSSOFTCAR: - get_user(arg, (unsigned long __user *) arg); - tty->termios.c_cflag = - ((tty->termios.c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; - case TIOCMGET: rc = access_ok(VERIFY_WRITE, (void __user *) arg, sizeof(unsigned int)); From d53c57dca2ccb540cceb0b1a228f11e1325cecde Mon Sep 17 00:00:00 2001 From: Kumar Amit Mehta Date: Fri, 23 Nov 2012 01:16:35 +0530 Subject: [PATCH 139/146] staging: dgrp: dgrp_tty.c: Audit the return values of get/put_user() fix for missing audits for return values of get_user() and put_user(). Inspecting the return values of get/put_user() would make the access_ok() redundant, hence removing calls to access_ok() in such scenarios. Signed-off-by: Kumar Amit Mehta Signed-off-by: Greg Kroah-Hartman --- drivers/staging/dgrp/dgrp_tty.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c index 5796de8ddbe..5647068b938 100644 --- a/drivers/staging/dgrp/dgrp_tty.c +++ b/drivers/staging/dgrp/dgrp_tty.c @@ -2265,9 +2265,7 @@ static int get_modem_info(struct ch_struct *ch, unsigned int *value) | ((mlast & DM_RI) ? TIOCM_RNG : 0) | ((mlast & DM_DSR) ? TIOCM_DSR : 0) | ((mlast & DM_CTS) ? TIOCM_CTS : 0); - put_user(mlast, (unsigned int __user *) value); - - return 0; + return put_user(mlast, (unsigned int __user *) value); } /* @@ -2285,7 +2283,8 @@ static int set_modem_info(struct ch_struct *ch, unsigned int command, if (error == 0) return -EFAULT; - get_user(arg, (unsigned int __user *) value); + if (get_user(arg, (unsigned int __user *) value)) + return -EFAULT; mval |= ((arg & TIOCM_RTS) ? DM_RTS : 0) | ((arg & TIOCM_DTR) ? DM_DTR : 0); @@ -2616,12 +2615,8 @@ static int dgrp_tty_ioctl(struct tty_struct *tty, unsigned int cmd, return 0; case TIOCGSOFTCAR: - rc = access_ok(VERIFY_WRITE, (void __user *) arg, - sizeof(long)); - if (rc == 0) - return -EFAULT; - put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) arg); - return 0; + return put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long __user *) arg); case TIOCMGET: rc = access_ok(VERIFY_WRITE, (void __user *) arg, @@ -2844,17 +2839,16 @@ static int dgrp_tty_ioctl(struct tty_struct *tty, unsigned int cmd, break; case DIGI_GETCUSTOMBAUD: - rc = access_ok(VERIFY_WRITE, (void __user *) arg, sizeof(int)); - if (rc == 0) + if (put_user(ch->ch_custom_speed, (unsigned int __user *) arg)) return -EFAULT; - put_user(ch->ch_custom_speed, (unsigned int __user *) arg); break; case DIGI_SETCUSTOMBAUD: { int new_rate; - get_user(new_rate, (unsigned int __user *) arg); + if (get_user(new_rate, (unsigned int __user *) arg)) + return -EFAULT; dgrp_set_custom_speed(ch, new_rate); break; From fd985e1def964bb3a3adf5e2760af10510fd3f58 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 26 Nov 2012 15:47:15 -0800 Subject: [PATCH 140/146] drivers/tty/serial/serial_core.c: clean up HIGH_BITS_OFFSET usage serial_core.c usually does if (HIGH_BITS_OFFSET) expr-involving-HIGH_BITS_OFFSET() at least to avoid generating useless code on 32-bit machines, where HIGH_BITS_OFFSET is zero. Do that in uart_get_attr_port(). Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 61ba24089ef..fb5aa42fde7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2370,9 +2370,13 @@ static ssize_t uart_get_attr_port(struct device *dev, { struct serial_struct tmp; struct tty_port *port = dev_get_drvdata(dev); + unsigned long ioaddr; uart_get_info(port, &tmp); - return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)(tmp.port | (((unsigned long)tmp.port_high) << HIGH_BITS_OFFSET))); + ioaddr = tmp.port; + if (HIGH_BITS_OFFSET) + ioaddr |= (unsigned long)tmp.port_high << HIGH_BITS_OFFSET; + return snprintf(buf, PAGE_SIZE, "0x%lX\n", ioaddr); } static ssize_t uart_get_attr_irq(struct device *dev, From 7355ba3445f2a741bfb9f03c7c8d5fc3e10bf63f Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 2 Nov 2012 08:16:33 -0400 Subject: [PATCH 141/146] staging: fwserial: Add TTY-over-Firewire serial driver This patch provides the kernel driver for high-speed TTY communication over the IEEE 1394 bus. Signed-off-by: Peter Hurley Acked-by: Stefan Richter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/fwserial/Kconfig | 9 + drivers/staging/fwserial/Makefile | 2 + drivers/staging/fwserial/TODO | 37 + drivers/staging/fwserial/dma_fifo.c | 310 +++ drivers/staging/fwserial/dma_fifo.h | 130 ++ drivers/staging/fwserial/fwserial.c | 2946 +++++++++++++++++++++++++++ drivers/staging/fwserial/fwserial.h | 387 ++++ 9 files changed, 3824 insertions(+) create mode 100644 drivers/staging/fwserial/Kconfig create mode 100644 drivers/staging/fwserial/Makefile create mode 100644 drivers/staging/fwserial/TODO create mode 100644 drivers/staging/fwserial/dma_fifo.c create mode 100644 drivers/staging/fwserial/dma_fifo.h create mode 100644 drivers/staging/fwserial/fwserial.c create mode 100644 drivers/staging/fwserial/fwserial.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index f245fd3153d..fc95eaba223 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -146,4 +146,6 @@ source "drivers/staging/dgrp/Kconfig" source "drivers/staging/sb105x/Kconfig" +source "drivers/staging/fwserial/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 94cc3fa5ef8..76db1e24274 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -65,3 +65,4 @@ obj-$(CONFIG_CED1401) += ced1401/ obj-$(CONFIG_DRM_IMX) += imx-drm/ obj-$(CONFIG_DGRP) += dgrp/ obj-$(CONFIG_SB105X) += sb105x/ +obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/ diff --git a/drivers/staging/fwserial/Kconfig b/drivers/staging/fwserial/Kconfig new file mode 100644 index 00000000000..580406cb180 --- /dev/null +++ b/drivers/staging/fwserial/Kconfig @@ -0,0 +1,9 @@ +config FIREWIRE_SERIAL + tristate "TTY over Firewire" + depends on FIREWIRE + help + This enables TTY over IEEE 1394, providing high-speed serial + connectivity to cabled peers. + + To compile this driver as a module, say M here: the module will + be called firewire-serial. diff --git a/drivers/staging/fwserial/Makefile b/drivers/staging/fwserial/Makefile new file mode 100644 index 00000000000..2170869a19b --- /dev/null +++ b/drivers/staging/fwserial/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_FIREWIRE_SERIAL) += firewire-serial.o +firewire-serial-objs := fwserial.o dma_fifo.o diff --git a/drivers/staging/fwserial/TODO b/drivers/staging/fwserial/TODO new file mode 100644 index 00000000000..726900548ea --- /dev/null +++ b/drivers/staging/fwserial/TODO @@ -0,0 +1,37 @@ +TODOs +----- +1. Implement retries for RCODE_BUSY, RCODE_NO_ACK and RCODE_SEND_ERROR + - I/O is handled asynchronously which presents some issues when error + conditions occur. +2. Implement _robust_ console on top of this. The existing prototype console + driver is not ready for the big leagues yet. +3. Expose means of controlling attach/detach of peers via sysfs. Include + GUID-to-port matching/whitelist/blacklist. + +-- Issues with firewire stack -- +1. This driver uses the same unregistered vendor id that the firewire core does + (0xd00d1e). Perhaps this could be exposed as a define in + firewire-constants.h? +2. MAX_ASYNC_PAYLOAD needs to be publicly exposed by core/ohci + - otherwise how will this driver know the max size of address window to + open for one packet write? +3. Maybe device_max_receive() and link_speed_to_max_payload() should be + taken up by the firewire core? +4. To avoid dropping rx data while still limiting the maximum buffering, + the size of the AR context must be known. How to expose this to drivers? +5. Explore if bigger AR context will reduce RCODE_BUSY responses + (or auto-grow to certain max size -- but this would require major surgery + as the current AR is contiguously mapped) + +-- Issues with TTY core -- + 1. Hack for alternate device name scheme + - because udev no longer allows device renaming, devices should have + their proper names on creation. This is an issue for creating the + fwloop device with the fwtty devices because although duplicating + roughly the same operations as tty_port_register_device() isn't difficult, + access to the tty_class & tty_fops is restricted in scope. + + This is currently being worked around in create_loop_device() by + extracting the tty_class ptr and tty_fops ptr from the previously created + tty devices. Perhaps an add'l api can be added -- eg., + tty_{port_}register_named_device(). diff --git a/drivers/staging/fwserial/dma_fifo.c b/drivers/staging/fwserial/dma_fifo.c new file mode 100644 index 00000000000..72aa0533f01 --- /dev/null +++ b/drivers/staging/fwserial/dma_fifo.c @@ -0,0 +1,310 @@ +/* + * DMA-able FIFO implementation + * + * Copyright (C) 2012 Peter Hurley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "dma_fifo.h" + +#ifdef DEBUG_TRACING +#define df_trace(s, args...) pr_debug(s, ##args) +#else +#define df_trace(s, args...) +#endif + +#define FAIL(fifo, condition, format...) ({ \ + fifo->corrupt = !!(condition); \ + if (unlikely(fifo->corrupt)) { \ + __WARN_printf(format); \ + } \ + unlikely(fifo->corrupt); \ +}) + +/* + * private helper fn to determine if check is in open interval (lo,hi) + */ +static bool addr_check(unsigned check, unsigned lo, unsigned hi) +{ + return check - (lo + 1) < (hi - 1) - lo; +} + +/** + * dma_fifo_init: initialize the fifo to a valid but inoperative state + * @fifo: address of in-place "struct dma_fifo" object + */ +void dma_fifo_init(struct dma_fifo *fifo) +{ + memset(fifo, 0, sizeof(*fifo)); + INIT_LIST_HEAD(&fifo->pending); +} + +/** + * dma_fifo_alloc - initialize and allocate dma_fifo + * @fifo: address of in-place "struct dma_fifo" object + * @size: 'apparent' size, in bytes, of fifo + * @align: dma alignment to maintain (should be at least cpu cache alignment), + * must be power of 2 + * @tx_limit: maximum # of bytes transmissable per dma (rounded down to + * multiple of alignment, but at least align size) + * @open_limit: maximum # of outstanding dma transactions allowed + * @gfp_mask: get_free_pages mask, passed to kmalloc() + * + * The 'apparent' size will be rounded up to next greater aligned size. + * Returns 0 if no error, otherwise an error code + */ +int dma_fifo_alloc(struct dma_fifo *fifo, int size, unsigned align, + int tx_limit, int open_limit, gfp_t gfp_mask) +{ + int capacity; + + if (!is_power_of_2(align) || size < 0) + return -EINVAL; + + size = round_up(size, align); + capacity = size + align * open_limit + align * DMA_FIFO_GUARD; + fifo->data = kmalloc(capacity, gfp_mask); + if (!fifo->data) + return -ENOMEM; + + fifo->in = 0; + fifo->out = 0; + fifo->done = 0; + fifo->size = size; + fifo->avail = size; + fifo->align = align; + fifo->tx_limit = max_t(int, round_down(tx_limit, align), align); + fifo->open = 0; + fifo->open_limit = open_limit; + fifo->guard = size + align * open_limit; + fifo->capacity = capacity; + fifo->corrupt = 0; + + return 0; +} + +/** + * dma_fifo_free - frees the fifo + * @fifo: address of in-place "struct dma_fifo" to free + * + * Also reinits the fifo to a valid but inoperative state. This + * allows the fifo to be reused with a different target requiring + * different fifo parameters. + */ +void dma_fifo_free(struct dma_fifo *fifo) +{ + struct dma_pending *pending, *next; + + if (fifo->data == NULL) + return; + + list_for_each_entry_safe(pending, next, &fifo->pending, link) + list_del_init(&pending->link); + kfree(fifo->data); + fifo->data = NULL; +} + +/** + * dma_fifo_reset - dumps the fifo contents and reinits for reuse + * @fifo: address of in-place "struct dma_fifo" to reset + */ +void dma_fifo_reset(struct dma_fifo *fifo) +{ + struct dma_pending *pending, *next; + + if (fifo->data == NULL) + return; + + list_for_each_entry_safe(pending, next, &fifo->pending, link) + list_del_init(&pending->link); + fifo->in = 0; + fifo->out = 0; + fifo->done = 0; + fifo->avail = fifo->size; + fifo->open = 0; + fifo->corrupt = 0; +} + +/** + * dma_fifo_in - copies data into the fifo + * @fifo: address of in-place "struct dma_fifo" to write to + * @src: buffer to copy from + * @n: # of bytes to copy + * + * Returns the # of bytes actually copied, which can be less than requested if + * the fifo becomes full. If < 0, return is error code. + */ +int dma_fifo_in(struct dma_fifo *fifo, const void *src, int n) +{ + int ofs, l; + + if (fifo->data == NULL) + return -ENOENT; + if (fifo->corrupt) + return -ENXIO; + + if (n > fifo->avail) + n = fifo->avail; + if (n <= 0) + return 0; + + ofs = fifo->in % fifo->capacity; + l = min(n, fifo->capacity - ofs); + memcpy(fifo->data + ofs, src, l); + memcpy(fifo->data, src + l, n - l); + + if (FAIL(fifo, addr_check(fifo->done, fifo->in, fifo->in + n) || + fifo->avail < n, + "fifo corrupt: in:%u out:%u done:%u n:%d avail:%d", + fifo->in, fifo->out, fifo->done, n, fifo->avail)) + return -ENXIO; + + fifo->in += n; + fifo->avail -= n; + + df_trace("in:%u out:%u done:%u n:%d avail:%d", fifo->in, fifo->out, + fifo->done, n, fifo->avail); + + return n; +} + +/** + * dma_fifo_out_pend - gets address/len of next avail read and marks as pended + * @fifo: address of in-place "struct dma_fifo" to read from + * @pended: address of structure to fill with read address/len + * The data/len fields will be NULL/0 if no dma is pended. + * + * Returns the # of used bytes remaining in fifo (ie, if > 0, more data + * remains in the fifo that was not pended). If < 0, return is error code. + */ +int dma_fifo_out_pend(struct dma_fifo *fifo, struct dma_pending *pended) +{ + unsigned len, n, ofs, l, limit; + + if (fifo->data == NULL) + return -ENOENT; + if (fifo->corrupt) + return -ENXIO; + + pended->len = 0; + pended->data = NULL; + pended->out = fifo->out; + + len = fifo->in - fifo->out; + if (!len) + return -ENODATA; + if (fifo->open == fifo->open_limit) + return -EAGAIN; + + n = len; + ofs = fifo->out % fifo->capacity; + l = fifo->capacity - ofs; + limit = min_t(unsigned, l, fifo->tx_limit); + if (n > limit) { + n = limit; + fifo->out += limit; + } else if (ofs + n > fifo->guard) { + fifo->out += l; + fifo->in = fifo->out; + } else { + fifo->out += round_up(n, fifo->align); + fifo->in = fifo->out; + } + + df_trace("in: %u out: %u done: %u n: %d len: %u avail: %d", fifo->in, + fifo->out, fifo->done, n, len, fifo->avail); + + pended->len = n; + pended->data = fifo->data + ofs; + pended->next = fifo->out; + list_add_tail(&pended->link, &fifo->pending); + ++fifo->open; + + if (FAIL(fifo, fifo->open > fifo->open_limit, + "past open limit:%d (limit:%d)", + fifo->open, fifo->open_limit)) + return -ENXIO; + if (FAIL(fifo, fifo->out & (fifo->align - 1), + "fifo out unaligned:%u (align:%u)", + fifo->out, fifo->align)) + return -ENXIO; + + return len - n; +} + +/** + * dma_fifo_out_complete - marks pended dma as completed + * @fifo: address of in-place "struct dma_fifo" which was read from + * @complete: address of structure for previously pended dma to mark completed + */ +int dma_fifo_out_complete(struct dma_fifo *fifo, struct dma_pending *complete) +{ + struct dma_pending *pending, *next, *tmp; + + if (fifo->data == NULL) + return -ENOENT; + if (fifo->corrupt) + return -ENXIO; + if (list_empty(&fifo->pending) && fifo->open == 0) + return -EINVAL; + + if (FAIL(fifo, list_empty(&fifo->pending) != (fifo->open == 0), + "pending list disagrees with open count:%d", + fifo->open)) + return -ENXIO; + + tmp = complete->data; + *tmp = *complete; + list_replace(&complete->link, &tmp->link); + dp_mark_completed(tmp); + + /* Only update the fifo in the original pended order */ + list_for_each_entry_safe(pending, next, &fifo->pending, link) { + if (!dp_is_completed(pending)) { + df_trace("still pending: saved out: %u len: %d", + pending->out, pending->len); + break; + } + + if (FAIL(fifo, pending->out != fifo->done || + addr_check(fifo->in, fifo->done, pending->next), + "in:%u out:%u done:%u saved:%u next:%u", + fifo->in, fifo->out, fifo->done, pending->out, + pending->next)) + return -ENXIO; + + list_del_init(&pending->link); + fifo->done = pending->next; + fifo->avail += pending->len; + --fifo->open; + + df_trace("in: %u out: %u done: %u len: %u avail: %d", fifo->in, + fifo->out, fifo->done, pending->len, fifo->avail); + } + + if (FAIL(fifo, fifo->open < 0, "open dma:%d < 0", fifo->open)) + return -ENXIO; + if (FAIL(fifo, fifo->avail > fifo->size, "fifo avail:%d > size:%d", + fifo->avail, fifo->size)) + return -ENXIO; + + return 0; +} diff --git a/drivers/staging/fwserial/dma_fifo.h b/drivers/staging/fwserial/dma_fifo.h new file mode 100644 index 00000000000..a113fe1e6f1 --- /dev/null +++ b/drivers/staging/fwserial/dma_fifo.h @@ -0,0 +1,130 @@ +/* + * DMA-able FIFO interface + * + * Copyright (C) 2012 Peter Hurley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _DMA_FIFO_H_ +#define _DMA_FIFO_H_ + +/** + * The design basis for the DMA FIFO is to provide an output side that + * complies with the streaming DMA API design that can be DMA'd from directly + * (without additional copying), coupled with an input side that maintains a + * logically consistent 'apparent' size (ie, bytes in + bytes avail is static + * for the lifetime of the FIFO). + * + * DMA output transactions originate on a cache line boundary and can be + * variably-sized. DMA output transactions can be retired out-of-order but + * the FIFO will only advance the output in the original input sequence. + * This means the FIFO will eventually stall if a transaction is never retired. + * + * Chunking the output side into cache line multiples means that some FIFO + * memory is unused. For example, if all the avail input has been pended out, + * then the in and out markers are re-aligned to the next cache line. + * The maximum possible waste is + * (cache line alignment - 1) * (max outstanding dma transactions) + * This potential waste requires additional hidden capacity within the FIFO + * to be able to accept input while the 'apparent' size has not been reached. + * + * Additional cache lines (ie, guard area) are used to minimize DMA + * fragmentation when wrapping at the end of the FIFO. Input is allowed into the + * guard area, but the in and out FIFO markers are wrapped when DMA is pended. + */ + +#define DMA_FIFO_GUARD 3 /* # of cache lines to reserve for the guard area */ + +struct dma_fifo { + unsigned in; + unsigned out; /* updated when dma is pended */ + unsigned done; /* updated upon dma completion */ + struct { + unsigned corrupt:1; + }; + int size; /* 'apparent' size of fifo */ + int guard; /* ofs of guard area */ + int capacity; /* size + reserved */ + int avail; /* # of unused bytes in fifo */ + unsigned align; /* must be power of 2 */ + int tx_limit; /* max # of bytes per dma transaction */ + int open_limit; /* max # of outstanding allowed */ + int open; /* # of outstanding dma transactions */ + struct list_head pending; /* fifo markers for outstanding dma */ + void *data; +}; + +struct dma_pending { + struct list_head link; + void *data; + unsigned len; + unsigned next; + unsigned out; +}; + +static inline void dp_mark_completed(struct dma_pending *dp) +{ + dp->data += 1; +} + +static inline bool dp_is_completed(struct dma_pending *dp) +{ + return (unsigned long)dp->data & 1UL; +} + +extern void dma_fifo_init(struct dma_fifo *fifo); +extern int dma_fifo_alloc(struct dma_fifo *fifo, int size, unsigned align, + int tx_limit, int open_limit, gfp_t gfp_mask); +extern void dma_fifo_free(struct dma_fifo *fifo); +extern void dma_fifo_reset(struct dma_fifo *fifo); +extern int dma_fifo_in(struct dma_fifo *fifo, const void *src, int n); +extern int dma_fifo_out_pend(struct dma_fifo *fifo, struct dma_pending *pended); +extern int dma_fifo_out_complete(struct dma_fifo *fifo, + struct dma_pending *complete); + +/* returns the # of used bytes in the fifo */ +static inline int dma_fifo_level(struct dma_fifo *fifo) +{ + return fifo->size - fifo->avail; +} + +/* returns the # of bytes ready for output in the fifo */ +static inline int dma_fifo_out_level(struct dma_fifo *fifo) +{ + return fifo->in - fifo->out; +} + +/* returns the # of unused bytes in the fifo */ +static inline int dma_fifo_avail(struct dma_fifo *fifo) +{ + return fifo->avail; +} + +/* returns true if fifo has max # of outstanding dmas */ +static inline bool dma_fifo_busy(struct dma_fifo *fifo) +{ + return fifo->open == fifo->open_limit; +} + +/* changes the max size of dma returned from dma_fifo_out_pend() */ +static inline int dma_fifo_change_tx_limit(struct dma_fifo *fifo, int tx_limit) +{ + tx_limit = round_down(tx_limit, fifo->align); + fifo->tx_limit = max_t(int, tx_limit, fifo->align); + return 0; +} + +#endif /* _DMA_FIFO_H_ */ diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c new file mode 100644 index 00000000000..5d4d64a3ea8 --- /dev/null +++ b/drivers/staging/fwserial/fwserial.c @@ -0,0 +1,2946 @@ +/* + * FireWire Serial driver + * + * Copyright (C) 2012 Peter Hurley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fwserial.h" + +#define be32_to_u64(hi, lo) ((u64)be32_to_cpu(hi) << 32 | be32_to_cpu(lo)) + +#define LINUX_VENDOR_ID 0xd00d1eU /* same id used in card root directory */ +#define FWSERIAL_VERSION 0x00e81cU /* must be unique within LINUX_VENDOR_ID */ + +/* configurable options */ +static int num_ttys = 4; /* # of std ttys to create per fw_card */ + /* - doubles as loopback port index */ +static bool auto_connect = true; /* try to VIRT_CABLE to every peer */ +static bool create_loop_dev = true; /* create a loopback device for each card */ +bool limit_bw; /* limit async bandwidth to 20% of max */ + +module_param_named(ttys, num_ttys, int, S_IRUGO | S_IWUSR); +module_param_named(auto, auto_connect, bool, S_IRUGO | S_IWUSR); +module_param_named(loop, create_loop_dev, bool, S_IRUGO | S_IWUSR); +module_param(limit_bw, bool, S_IRUGO | S_IWUSR); + +/* + * Threshold below which the tty is woken for writing + * - should be equal to WAKEUP_CHARS in drivers/tty/n_tty.c because + * even if the writer is woken, n_tty_poll() won't set POLLOUT until + * our fifo is below this level + */ +#define WAKEUP_CHARS 256 + +/** + * fwserial_list: list of every fw_serial created for each fw_card + * See discussion in fwserial_probe. + */ +static LIST_HEAD(fwserial_list); +static DEFINE_MUTEX(fwserial_list_mutex); + +/** + * port_table: array of tty ports allocated to each fw_card + * + * tty ports are allocated during probe when an fw_serial is first + * created for a given fw_card. Ports are allocated in a contiguous block, + * each block consisting of 'num_ports' ports. + */ +static struct fwtty_port *port_table[MAX_TOTAL_PORTS]; +static DEFINE_MUTEX(port_table_lock); +static bool port_table_corrupt; +#define FWTTY_INVALID_INDEX MAX_TOTAL_PORTS + +/* total # of tty ports created per fw_card */ +static int num_ports; + +/* slab used as pool for struct fwtty_transactions */ +static struct kmem_cache *fwtty_txn_cache; + +struct fwtty_transaction; +typedef void (*fwtty_transaction_cb)(struct fw_card *card, int rcode, + void *data, size_t length, + struct fwtty_transaction *txn); + +struct fwtty_transaction { + struct fw_transaction fw_txn; + fwtty_transaction_cb callback; + struct fwtty_port *port; + union { + struct dma_pending dma_pended; + }; +}; + +#define to_device(a, b) (a->b) +#define fwtty_err(p, s, v...) dev_err(to_device(p, device), s, ##v) +#define fwtty_info(p, s, v...) dev_info(to_device(p, device), s, ##v) +#define fwtty_notice(p, s, v...) dev_notice(to_device(p, device), s, ##v) +#define fwtty_dbg(p, s, v...) \ + dev_dbg(to_device(p, device), "%s: " s, __func__, ##v) +#define fwtty_err_ratelimited(p, s, v...) \ + dev_err_ratelimited(to_device(p, device), s, ##v) + +#ifdef DEBUG +static inline void debug_short_write(struct fwtty_port *port, int c, int n) +{ + int avail; + + if (n < c) { + spin_lock_bh(&port->lock); + avail = dma_fifo_avail(&port->tx_fifo); + spin_unlock_bh(&port->lock); + fwtty_dbg(port, "short write: avail:%d req:%d wrote:%d", + avail, c, n); + } +} +#else +#define debug_short_write(port, c, n) +#endif + +static struct fwtty_peer *__fwserial_peer_by_node_id(struct fw_card *card, + int generation, int id); + +#ifdef FWTTY_PROFILING + +static void profile_fifo_avail(struct fwtty_port *port, unsigned *stat) +{ + spin_lock_bh(&port->lock); + profile_size_distrib(stat, dma_fifo_avail(&port->tx_fifo)); + spin_unlock_bh(&port->lock); +} + +static void dump_profile(struct seq_file *m, struct stats *stats) +{ + /* for each stat, print sum of 0 to 2^k, then individually */ + int k = 4; + unsigned sum; + int j; + char t[10]; + + snprintf(t, 10, "< %d", 1 << k); + seq_printf(m, "\n%14s %6s", " ", t); + for (j = k + 1; j < DISTRIBUTION_MAX_INDEX; ++j) + seq_printf(m, "%6d", 1 << j); + + ++k; + for (j = 0, sum = 0; j <= k; ++j) + sum += stats->reads[j]; + seq_printf(m, "\n%14s: %6d", "reads", sum); + for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j) + seq_printf(m, "%6d", stats->reads[j]); + + for (j = 0, sum = 0; j <= k; ++j) + sum += stats->writes[j]; + seq_printf(m, "\n%14s: %6d", "writes", sum); + for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j) + seq_printf(m, "%6d", stats->writes[j]); + + for (j = 0, sum = 0; j <= k; ++j) + sum += stats->txns[j]; + seq_printf(m, "\n%14s: %6d", "txns", sum); + for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j) + seq_printf(m, "%6d", stats->txns[j]); + + for (j = 0, sum = 0; j <= k; ++j) + sum += stats->unthrottle[j]; + seq_printf(m, "\n%14s: %6d", "avail @ unthr", sum); + for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j) + seq_printf(m, "%6d", stats->unthrottle[j]); +} + +#else +#define profile_fifo_avail(port, stat) +#define dump_profile(m, stats) +#endif + +/* Returns the max receive packet size for the given card */ +static inline int device_max_receive(struct fw_device *fw_device) +{ + return 1 << (clamp_t(int, fw_device->max_rec, 8U, 13U) + 1); +} + +static void fwtty_log_tx_error(struct fwtty_port *port, int rcode) +{ + switch (rcode) { + case RCODE_SEND_ERROR: + fwtty_err_ratelimited(port, "card busy"); + break; + case RCODE_ADDRESS_ERROR: + fwtty_err_ratelimited(port, "bad unit addr or write length"); + break; + case RCODE_DATA_ERROR: + fwtty_err_ratelimited(port, "failed rx"); + break; + case RCODE_NO_ACK: + fwtty_err_ratelimited(port, "missing ack"); + break; + case RCODE_BUSY: + fwtty_err_ratelimited(port, "remote busy"); + break; + default: + fwtty_err_ratelimited(port, "failed tx: %d", rcode); + } +} + +static void fwtty_txn_constructor(void *this) +{ + struct fwtty_transaction *txn = this; + + init_timer(&txn->fw_txn.split_timeout_timer); +} + +static void fwtty_common_callback(struct fw_card *card, int rcode, + void *payload, size_t len, void *cb_data) +{ + struct fwtty_transaction *txn = cb_data; + struct fwtty_port *port = txn->port; + + if (port && rcode != RCODE_COMPLETE) + fwtty_log_tx_error(port, rcode); + if (txn->callback) + txn->callback(card, rcode, payload, len, txn); + kmem_cache_free(fwtty_txn_cache, txn); +} + +static int fwtty_send_data_async(struct fwtty_peer *peer, int tcode, + unsigned long long addr, void *payload, + size_t len, fwtty_transaction_cb callback, + struct fwtty_port *port) +{ + struct fwtty_transaction *txn; + int generation; + + txn = kmem_cache_alloc(fwtty_txn_cache, GFP_ATOMIC); + if (!txn) + return -ENOMEM; + + txn->callback = callback; + txn->port = port; + + generation = peer->generation; + smp_rmb(); + fw_send_request(peer->serial->card, &txn->fw_txn, tcode, + peer->node_id, generation, peer->speed, addr, payload, + len, fwtty_common_callback, txn); + return 0; +} + +static void fwtty_send_txn_async(struct fwtty_peer *peer, + struct fwtty_transaction *txn, int tcode, + unsigned long long addr, void *payload, + size_t len, fwtty_transaction_cb callback, + struct fwtty_port *port) +{ + int generation; + + txn->callback = callback; + txn->port = port; + + generation = peer->generation; + smp_rmb(); + fw_send_request(peer->serial->card, &txn->fw_txn, tcode, + peer->node_id, generation, peer->speed, addr, payload, + len, fwtty_common_callback, txn); +} + + +static void __fwtty_restart_tx(struct fwtty_port *port) +{ + int len, avail; + + len = dma_fifo_out_level(&port->tx_fifo); + if (len) + schedule_delayed_work(&port->drain, 0); + avail = dma_fifo_avail(&port->tx_fifo); + + fwtty_dbg(port, "fifo len: %d avail: %d", len, avail); +} + +static void fwtty_restart_tx(struct fwtty_port *port) +{ + spin_lock_bh(&port->lock); + __fwtty_restart_tx(port); + spin_unlock_bh(&port->lock); +} + +/** + * fwtty_update_port_status - decodes & dispatches line status changes + * + * Note: in loopback, the port->lock is being held. Only use functions that + * don't attempt to reclaim the port->lock. + */ +static void fwtty_update_port_status(struct fwtty_port *port, unsigned status) +{ + unsigned delta; + struct tty_struct *tty; + + /* simulated LSR/MSR status from remote */ + status &= ~MCTRL_MASK; + delta = (port->mstatus ^ status) & ~MCTRL_MASK; + delta &= ~(status & TIOCM_RNG); + port->mstatus = status; + + if (delta & TIOCM_RNG) + ++port->icount.rng; + if (delta & TIOCM_DSR) + ++port->icount.dsr; + if (delta & TIOCM_CAR) + ++port->icount.dcd; + if (delta & TIOCM_CTS) + ++port->icount.cts; + + fwtty_dbg(port, "status: %x delta: %x", status, delta); + + if (delta & TIOCM_CAR) { + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) { + if (status & TIOCM_CAR) + wake_up_interruptible(&port->port.open_wait); + else + schedule_work(&port->hangup); + } + tty_kref_put(tty); + } + + if (delta & TIOCM_CTS) { + tty = tty_port_tty_get(&port->port); + if (tty && C_CRTSCTS(tty)) { + if (tty->hw_stopped) { + if (status & TIOCM_CTS) { + tty->hw_stopped = 0; + if (port->loopback) + __fwtty_restart_tx(port); + else + fwtty_restart_tx(port); + } + } else { + if (~status & TIOCM_CTS) + tty->hw_stopped = 1; + } + } + tty_kref_put(tty); + + } else if (delta & OOB_TX_THROTTLE) { + tty = tty_port_tty_get(&port->port); + if (tty) { + if (tty->hw_stopped) { + if (~status & OOB_TX_THROTTLE) { + tty->hw_stopped = 0; + if (port->loopback) + __fwtty_restart_tx(port); + else + fwtty_restart_tx(port); + } + } else { + if (status & OOB_TX_THROTTLE) + tty->hw_stopped = 1; + } + } + tty_kref_put(tty); + } + + if (delta & (UART_LSR_BI << 24)) { + if (status & (UART_LSR_BI << 24)) { + port->break_last = jiffies; + schedule_delayed_work(&port->emit_breaks, 0); + } else { + /* run emit_breaks one last time (if pending) */ + mod_delayed_work(system_wq, &port->emit_breaks, 0); + } + } + + if (delta & (TIOCM_DSR | TIOCM_CAR | TIOCM_CTS | TIOCM_RNG)) + wake_up_interruptible(&port->port.delta_msr_wait); +} + +/** + * __fwtty_port_line_status - generate 'line status' for indicated port + * + * This function returns a remote 'MSR' state based on the local 'MCR' state, + * as if a null modem cable was attached. The actual status is a mangling + * of TIOCM_* bits suitable for sending to a peer's status_addr. + * + * Note: caller must be holding port lock + */ +static unsigned __fwtty_port_line_status(struct fwtty_port *port) +{ + unsigned status = 0; + + /* TODO: add module param to tie RNG to DTR as well */ + + if (port->mctrl & TIOCM_DTR) + status |= TIOCM_DSR | TIOCM_CAR; + if (port->mctrl & TIOCM_RTS) + status |= TIOCM_CTS; + if (port->mctrl & OOB_RX_THROTTLE) + status |= OOB_TX_THROTTLE; + /* emulate BRK as add'l line status */ + if (port->break_ctl) + status |= UART_LSR_BI << 24; + + return status; +} + +/** + * __fwtty_write_port_status - send the port line status to peer + * + * Note: caller must be holding the port lock. + */ +static int __fwtty_write_port_status(struct fwtty_port *port) +{ + struct fwtty_peer *peer; + int err = -ENOENT; + unsigned status = __fwtty_port_line_status(port); + + rcu_read_lock(); + peer = rcu_dereference(port->peer); + if (peer) { + err = fwtty_send_data_async(peer, TCODE_WRITE_QUADLET_REQUEST, + peer->status_addr, &status, + sizeof(status), NULL, port); + } + rcu_read_unlock(); + + return err; +} + +/** + * fwtty_write_port_status - same as above but locked by port lock + */ +static int fwtty_write_port_status(struct fwtty_port *port) +{ + int err; + + spin_lock_bh(&port->lock); + err = __fwtty_write_port_status(port); + spin_unlock_bh(&port->lock); + return err; +} + +static void __fwtty_throttle(struct fwtty_port *port, struct tty_struct *tty) +{ + unsigned old; + + old = port->mctrl; + port->mctrl |= OOB_RX_THROTTLE; + if (C_CRTSCTS(tty)) + port->mctrl &= ~TIOCM_RTS; + if (~old & OOB_RX_THROTTLE) + __fwtty_write_port_status(port); +} + +/** + * fwtty_do_hangup - wait for ldisc to deliver all pending rx; only then hangup + * + * When the remote has finished tx, and all in-flight rx has been received and + * and pushed to the flip buffer, the remote may close its device. This will + * drop DTR on the remote which will drop carrier here. Typically, the tty is + * hung up when carrier is dropped or lost. + * + * However, there is a race between the hang up and the line discipline + * delivering its data to the reader. A hangup will cause the ldisc to flush + * (ie., clear) the read buffer and flip buffer. Because of firewire's + * relatively high throughput, the ldisc frequently lags well behind the driver, + * resulting in lost data (which has already been received and written to + * the flip buffer) when the remote closes its end. + * + * Unfortunately, since the flip buffer offers no direct method for determining + * if it holds data, ensuring the ldisc has delivered all data is problematic. + */ + +/* FIXME: drop this workaround when __tty_hangup waits for ldisc completion */ +static void fwtty_do_hangup(struct work_struct *work) +{ + struct fwtty_port *port = to_port(work, hangup); + struct tty_struct *tty; + + schedule_timeout_uninterruptible(msecs_to_jiffies(50)); + + tty = tty_port_tty_get(&port->port); + if (tty) + tty_vhangup(tty); + tty_kref_put(tty); +} + + +static void fwtty_emit_breaks(struct work_struct *work) +{ + struct fwtty_port *port = to_port(to_delayed_work(work), emit_breaks); + struct tty_struct *tty; + static const char buf[16]; + unsigned long now = jiffies; + unsigned long elapsed = now - port->break_last; + int n, t, c, brk = 0; + + tty = tty_port_tty_get(&port->port); + if (!tty) + return; + + /* generate breaks at the line rate (but at least 1) */ + n = (elapsed * port->cps) / HZ + 1; + port->break_last = now; + + fwtty_dbg(port, "sending %d brks", n); + + while (n) { + t = min(n, 16); + c = tty_insert_flip_string_fixed_flag(tty, buf, TTY_BREAK, t); + n -= c; + brk += c; + if (c < t) + break; + } + tty_flip_buffer_push(tty); + + tty_kref_put(tty); + + if (port->mstatus & (UART_LSR_BI << 24)) + schedule_delayed_work(&port->emit_breaks, FREQ_BREAKS); + port->icount.brk += brk; +} + +static void fwtty_pushrx(struct work_struct *work) +{ + struct fwtty_port *port = to_port(work, push); + struct tty_struct *tty; + struct buffered_rx *buf, *next; + int n, c = 0; + + tty = tty_port_tty_get(&port->port); + if (!tty) + return; + + spin_lock_bh(&port->lock); + list_for_each_entry_safe(buf, next, &port->buf_list, list) { + n = tty_insert_flip_string_fixed_flag(tty, buf->data, + TTY_NORMAL, buf->n); + c += n; + port->buffered -= n; + if (n < buf->n) { + if (n > 0) { + memmove(buf->data, buf->data + n, buf->n - n); + buf->n -= n; + } + __fwtty_throttle(port, tty); + break; + } else { + list_del(&buf->list); + kfree(buf); + } + } + if (c > 0) + tty_flip_buffer_push(tty); + + if (list_empty(&port->buf_list)) + clear_bit(BUFFERING_RX, &port->flags); + spin_unlock_bh(&port->lock); + + tty_kref_put(tty); +} + +static int fwtty_buffer_rx(struct fwtty_port *port, unsigned char *d, size_t n) +{ + struct buffered_rx *buf; + size_t size = (n + sizeof(struct buffered_rx) + 0xFF) & ~0xFF; + + if (port->buffered + n > HIGH_WATERMARK) + return 0; + buf = kmalloc(size, GFP_ATOMIC); + if (!buf) + return 0; + INIT_LIST_HEAD(&buf->list); + buf->n = n; + memcpy(buf->data, d, n); + + spin_lock_bh(&port->lock); + list_add_tail(&buf->list, &port->buf_list); + port->buffered += n; + if (port->buffered > port->stats.watermark) + port->stats.watermark = port->buffered; + set_bit(BUFFERING_RX, &port->flags); + spin_unlock_bh(&port->lock); + + return n; +} + +static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len) +{ + struct tty_struct *tty; + int c, n = len; + unsigned lsr; + int err = 0; + + tty = tty_port_tty_get(&port->port); + if (!tty) + return -ENOENT; + + fwtty_dbg(port, "%d", n); + profile_size_distrib(port->stats.reads, n); + + if (port->write_only) { + n = 0; + goto out; + } + + /* disregard break status; breaks are generated by emit_breaks work */ + lsr = (port->mstatus >> 24) & ~UART_LSR_BI; + + if (port->overrun) + lsr |= UART_LSR_OE; + + if (lsr & UART_LSR_OE) + ++port->icount.overrun; + + lsr &= port->status_mask; + if (lsr & ~port->ignore_mask & UART_LSR_OE) { + if (!tty_insert_flip_char(tty, 0, TTY_OVERRUN)) { + err = -EIO; + goto out; + } + } + port->overrun = false; + + if (lsr & port->ignore_mask & ~UART_LSR_OE) { + /* TODO: don't drop SAK and Magic SysRq here */ + n = 0; + goto out; + } + + if (!test_bit(BUFFERING_RX, &port->flags)) { + c = tty_insert_flip_string_fixed_flag(tty, data, TTY_NORMAL, n); + if (c > 0) + tty_flip_buffer_push(tty); + n -= c; + + if (n) { + /* start buffering and throttling */ + n -= fwtty_buffer_rx(port, &data[c], n); + + spin_lock_bh(&port->lock); + __fwtty_throttle(port, tty); + spin_unlock_bh(&port->lock); + } + } else + n -= fwtty_buffer_rx(port, data, n); + + if (n) { + port->overrun = true; + err = -EIO; + } + +out: + tty_kref_put(tty); + + port->icount.rx += len; + port->stats.lost += n; + return err; +} + +/** + * fwtty_port_handler - bus address handler for port reads/writes + * @parameters: fw_address_callback_t as specified by firewire core interface + * + * This handler is responsible for handling inbound read/write dma from remotes. + */ +static void fwtty_port_handler(struct fw_card *card, + struct fw_request *request, + int tcode, int destination, int source, + int generation, + unsigned long long addr, + void *data, size_t len, + void *callback_data) +{ + struct fwtty_port *port = callback_data; + struct fwtty_peer *peer; + int err; + int rcode; + + /* Only accept rx from the peer virtual-cabled to this port */ + rcu_read_lock(); + peer = __fwserial_peer_by_node_id(card, generation, source); + rcu_read_unlock(); + if (!peer || peer != rcu_access_pointer(port->peer)) { + rcode = RCODE_ADDRESS_ERROR; + fwtty_err_ratelimited(port, "ignoring unauthenticated data"); + goto respond; + } + + switch (tcode) { + case TCODE_WRITE_QUADLET_REQUEST: + if (addr != port->rx_handler.offset || len != 4) + rcode = RCODE_ADDRESS_ERROR; + else { + fwtty_update_port_status(port, *(unsigned *)data); + rcode = RCODE_COMPLETE; + } + break; + + case TCODE_WRITE_BLOCK_REQUEST: + if (addr != port->rx_handler.offset + 4 || + len > port->rx_handler.length - 4) { + rcode = RCODE_ADDRESS_ERROR; + } else { + err = fwtty_rx(port, data, len); + switch (err) { + case 0: + rcode = RCODE_COMPLETE; + break; + case -EIO: + rcode = RCODE_DATA_ERROR; + break; + default: + rcode = RCODE_CONFLICT_ERROR; + break; + } + } + break; + + default: + rcode = RCODE_TYPE_ERROR; + } + +respond: + fw_send_response(card, request, rcode); +} + +/** + * fwtty_tx_complete - callback for tx dma + * @data: ignored, has no meaning for write txns + * @length: ignored, has no meaning for write txns + * + * The writer must be woken here if the fifo has been emptied because it + * may have slept if chars_in_buffer was != 0 + */ +static void fwtty_tx_complete(struct fw_card *card, int rcode, + void *data, size_t length, + struct fwtty_transaction *txn) +{ + struct fwtty_port *port = txn->port; + struct tty_struct *tty; + int len; + + fwtty_dbg(port, "rcode: %d", rcode); + + switch (rcode) { + case RCODE_COMPLETE: + spin_lock_bh(&port->lock); + dma_fifo_out_complete(&port->tx_fifo, &txn->dma_pended); + len = dma_fifo_level(&port->tx_fifo); + spin_unlock_bh(&port->lock); + + port->icount.tx += txn->dma_pended.len; + break; + + default: + /* TODO: implement retries */ + spin_lock_bh(&port->lock); + dma_fifo_out_complete(&port->tx_fifo, &txn->dma_pended); + len = dma_fifo_level(&port->tx_fifo); + spin_unlock_bh(&port->lock); + + port->stats.dropped += txn->dma_pended.len; + } + + if (len < WAKEUP_CHARS) { + tty = tty_port_tty_get(&port->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } + } +} + +static int fwtty_tx(struct fwtty_port *port, bool drain) +{ + struct fwtty_peer *peer; + struct fwtty_transaction *txn; + struct tty_struct *tty; + int n, len; + + tty = tty_port_tty_get(&port->port); + if (!tty) + return -ENOENT; + + rcu_read_lock(); + peer = rcu_dereference(port->peer); + if (!peer) { + n = -EIO; + goto out; + } + + if (test_and_set_bit(IN_TX, &port->flags)) { + n = -EALREADY; + goto out; + } + + /* try to write as many dma transactions out as possible */ + n = -EAGAIN; + while (!tty->stopped && !tty->hw_stopped && + !test_bit(STOP_TX, &port->flags)) { + txn = kmem_cache_alloc(fwtty_txn_cache, GFP_ATOMIC); + if (!txn) { + n = -ENOMEM; + break; + } + + spin_lock_bh(&port->lock); + n = dma_fifo_out_pend(&port->tx_fifo, &txn->dma_pended); + spin_unlock_bh(&port->lock); + + fwtty_dbg(port, "out: %u rem: %d", txn->dma_pended.len, n); + + if (n < 0) { + kmem_cache_free(fwtty_txn_cache, txn); + if (n == -EAGAIN) + ++port->stats.tx_stall; + else if (n == -ENODATA) + profile_size_distrib(port->stats.txns, 0); + else { + ++port->stats.fifo_errs; + fwtty_err_ratelimited(port, "fifo err: %d", n); + } + break; + } + + profile_size_distrib(port->stats.txns, txn->dma_pended.len); + + fwtty_send_txn_async(peer, txn, TCODE_WRITE_BLOCK_REQUEST, + peer->fifo_addr, txn->dma_pended.data, + txn->dma_pended.len, fwtty_tx_complete, + port); + ++port->stats.sent; + + /* + * Stop tx if the 'last view' of the fifo is empty or if + * this is the writer and there's not enough data to bother + */ + if (n == 0 || (!drain && n < WRITER_MINIMUM)) + break; + } + + if (n >= 0 || n == -EAGAIN || n == -ENOMEM || n == -ENODATA) { + spin_lock_bh(&port->lock); + len = dma_fifo_out_level(&port->tx_fifo); + if (len) { + unsigned long delay = (n == -ENOMEM) ? HZ : 1; + schedule_delayed_work(&port->drain, delay); + } + len = dma_fifo_level(&port->tx_fifo); + spin_unlock_bh(&port->lock); + + /* wakeup the writer */ + if (drain && len < WAKEUP_CHARS) + tty_wakeup(tty); + } + + clear_bit(IN_TX, &port->flags); + wake_up_interruptible(&port->wait_tx); + +out: + rcu_read_unlock(); + tty_kref_put(tty); + return n; +} + +static void fwtty_drain_tx(struct work_struct *work) +{ + struct fwtty_port *port = to_port(to_delayed_work(work), drain); + + fwtty_tx(port, true); +} + +static void fwtty_write_xchar(struct fwtty_port *port, char ch) +{ + struct fwtty_peer *peer; + + ++port->stats.xchars; + + fwtty_dbg(port, "%02x", ch); + + rcu_read_lock(); + peer = rcu_dereference(port->peer); + if (peer) { + fwtty_send_data_async(peer, TCODE_WRITE_BLOCK_REQUEST, + peer->fifo_addr, &ch, sizeof(ch), + NULL, port); + } + rcu_read_unlock(); +} + +struct fwtty_port *fwtty_port_get(unsigned index) +{ + struct fwtty_port *port; + + if (index >= MAX_TOTAL_PORTS) + return NULL; + + mutex_lock(&port_table_lock); + port = port_table[index]; + if (port) + kref_get(&port->serial->kref); + mutex_unlock(&port_table_lock); + return port; +} +EXPORT_SYMBOL(fwtty_port_get); + +static int fwtty_ports_add(struct fw_serial *serial) +{ + int err = -EBUSY; + int i, j; + + if (port_table_corrupt) + return err; + + mutex_lock(&port_table_lock); + for (i = 0; i + num_ports <= MAX_TOTAL_PORTS; i += num_ports) { + if (!port_table[i]) { + for (j = 0; j < num_ports; ++i, ++j) { + serial->ports[j]->index = i; + port_table[i] = serial->ports[j]; + } + err = 0; + break; + } + } + mutex_unlock(&port_table_lock); + return err; +} + +static void fwserial_destroy(struct kref *kref) +{ + struct fw_serial *serial = to_serial(kref, kref); + struct fwtty_port **ports = serial->ports; + int j, i = ports[0]->index; + + synchronize_rcu(); + + mutex_lock(&port_table_lock); + for (j = 0; j < num_ports; ++i, ++j) { + static bool once; + int corrupt = port_table[i] != ports[j]; + if (corrupt && !once) { + WARN(corrupt, "port_table[%d]: %p != ports[%d]: %p", + i, port_table[i], j, ports[j]); + once = true; + port_table_corrupt = true; + } + + port_table[i] = NULL; + } + mutex_unlock(&port_table_lock); + + for (j = 0; j < num_ports; ++j) { + fw_core_remove_address_handler(&ports[j]->rx_handler); + dma_fifo_free(&ports[j]->tx_fifo); + kfree(ports[j]); + } + kfree(serial); +} + +void fwtty_port_put(struct fwtty_port *port) +{ + kref_put(&port->serial->kref, fwserial_destroy); +} +EXPORT_SYMBOL(fwtty_port_put); + +static void fwtty_port_dtr_rts(struct tty_port *tty_port, int on) +{ + struct fwtty_port *port = to_port(tty_port, port); + + fwtty_dbg(port, "on/off: %d", on); + + spin_lock_bh(&port->lock); + /* Don't change carrier state if this is a console */ + if (!port->port.console) { + if (on) + port->mctrl |= TIOCM_DTR | TIOCM_RTS; + else + port->mctrl &= ~(TIOCM_DTR | TIOCM_RTS); + } + + __fwtty_write_port_status(port); + spin_unlock_bh(&port->lock); +} + +/** + * fwtty_port_carrier_raised: required tty_port operation + * + * This port operation is polled after a tty has been opened and is waiting for + * carrier detect -- see drivers/tty/tty_port:tty_port_block_til_ready(). + */ +static int fwtty_port_carrier_raised(struct tty_port *tty_port) +{ + struct fwtty_port *port = to_port(tty_port, port); + int rc; + + rc = (port->mstatus & TIOCM_CAR); + + fwtty_dbg(port, "%d", rc); + + return rc; +} + +static unsigned set_termios(struct fwtty_port *port, struct tty_struct *tty) +{ + unsigned baud, frame; + + baud = tty_termios_baud_rate(&tty->termios); + tty_termios_encode_baud_rate(&tty->termios, baud, baud); + + /* compute bit count of 2 frames */ + frame = 12 + ((C_CSTOPB(tty)) ? 4 : 2) + ((C_PARENB(tty)) ? 2 : 0); + + switch (C_CSIZE(tty)) { + case CS5: + frame -= (C_CSTOPB(tty)) ? 1 : 0; + break; + case CS6: + frame += 2; + break; + case CS7: + frame += 4; + break; + case CS8: + frame += 6; + break; + } + + port->cps = (baud << 1) / frame; + + port->status_mask = UART_LSR_OE; + if (_I_FLAG(tty, BRKINT | PARMRK)) + port->status_mask |= UART_LSR_BI; + + port->ignore_mask = 0; + if (I_IGNBRK(tty)) { + port->ignore_mask |= UART_LSR_BI; + if (I_IGNPAR(tty)) + port->ignore_mask |= UART_LSR_OE; + } + + port->write_only = !C_CREAD(tty); + + /* turn off echo and newline xlat if loopback */ + if (port->loopback) { + tty->termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHOKE | + ECHONL | ECHOPRT | ECHOCTL); + tty->termios.c_oflag &= ~ONLCR; + } + + return baud; +} + +static int fwtty_port_activate(struct tty_port *tty_port, + struct tty_struct *tty) +{ + struct fwtty_port *port = to_port(tty_port, port); + unsigned baud; + int err; + + set_bit(TTY_IO_ERROR, &tty->flags); + + err = dma_fifo_alloc(&port->tx_fifo, FWTTY_PORT_TXFIFO_LEN, + cache_line_size(), + port->max_payload, + FWTTY_PORT_MAX_PEND_DMA, + GFP_KERNEL); + if (err) + return err; + + spin_lock_bh(&port->lock); + + baud = set_termios(port, tty); + + /* if console, don't change carrier state */ + if (!port->port.console) { + port->mctrl = 0; + if (baud != 0) + port->mctrl = TIOCM_DTR | TIOCM_RTS; + } + + if (C_CRTSCTS(tty) && ~port->mstatus & TIOCM_CTS) + tty->hw_stopped = 1; + + __fwtty_write_port_status(port); + spin_unlock_bh(&port->lock); + + clear_bit(TTY_IO_ERROR, &tty->flags); + + return 0; +} + +/** + * fwtty_port_shutdown + * + * Note: the tty port core ensures this is not the console and + * manages TTY_IO_ERROR properly + */ +static void fwtty_port_shutdown(struct tty_port *tty_port) +{ + struct fwtty_port *port = to_port(tty_port, port); + struct buffered_rx *buf, *next; + + /* TODO: cancel outstanding transactions */ + + cancel_delayed_work_sync(&port->emit_breaks); + cancel_delayed_work_sync(&port->drain); + cancel_work_sync(&port->push); + + spin_lock_bh(&port->lock); + list_for_each_entry_safe(buf, next, &port->buf_list, list) { + list_del(&buf->list); + kfree(buf); + } + port->buffered = 0; + port->flags = 0; + port->break_ctl = 0; + port->overrun = 0; + __fwtty_write_port_status(port); + dma_fifo_free(&port->tx_fifo); + spin_unlock_bh(&port->lock); +} + +static int fwtty_open(struct tty_struct *tty, struct file *fp) +{ + struct fwtty_port *port = tty->driver_data; + + return tty_port_open(&port->port, tty, fp); +} + +static void fwtty_close(struct tty_struct *tty, struct file *fp) +{ + struct fwtty_port *port = tty->driver_data; + + tty_port_close(&port->port, tty, fp); +} + +static void fwtty_hangup(struct tty_struct *tty) +{ + struct fwtty_port *port = tty->driver_data; + + tty_port_hangup(&port->port); +} + +static void fwtty_cleanup(struct tty_struct *tty) +{ + struct fwtty_port *port = tty->driver_data; + + tty->driver_data = NULL; + fwtty_port_put(port); +} + +static int fwtty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct fwtty_port *port = fwtty_port_get(tty->index); + int err; + + err = tty_standard_install(driver, tty); + if (!err) + tty->driver_data = port; + else + fwtty_port_put(port); + return err; +} + +static int fwtty_write(struct tty_struct *tty, const unsigned char *buf, int c) +{ + struct fwtty_port *port = tty->driver_data; + int n, len; + + fwtty_dbg(port, "%d", c); + profile_size_distrib(port->stats.writes, c); + + spin_lock_bh(&port->lock); + n = dma_fifo_in(&port->tx_fifo, buf, c); + len = dma_fifo_out_level(&port->tx_fifo); + if (len < DRAIN_THRESHOLD) + schedule_delayed_work(&port->drain, 1); + spin_unlock_bh(&port->lock); + + if (len >= DRAIN_THRESHOLD) + fwtty_tx(port, false); + + debug_short_write(port, c, n); + + return (n < 0) ? 0 : n; +} + +static int fwtty_write_room(struct tty_struct *tty) +{ + struct fwtty_port *port = tty->driver_data; + int n; + + spin_lock_bh(&port->lock); + n = dma_fifo_avail(&port->tx_fifo); + spin_unlock_bh(&port->lock); + + fwtty_dbg(port, "%d", n); + + return n; +} + +static int fwtty_chars_in_buffer(struct tty_struct *tty) +{ + struct fwtty_port *port = tty->driver_data; + int n; + + spin_lock_bh(&port->lock); + n = dma_fifo_level(&port->tx_fifo); + spin_unlock_bh(&port->lock); + + fwtty_dbg(port, "%d", n); + + return n; +} + +static void fwtty_send_xchar(struct tty_struct *tty, char ch) +{ + struct fwtty_port *port = tty->driver_data; + + fwtty_dbg(port, "%02x", ch); + + fwtty_write_xchar(port, ch); +} + +static void fwtty_throttle(struct tty_struct *tty) +{ + struct fwtty_port *port = tty->driver_data; + + /* + * Ignore throttling (but not unthrottling). + * It only makes sense to throttle when data will no longer be + * accepted by the tty flip buffer. For example, it is + * possible for received data to overflow the tty buffer long + * before the line discipline ever has a chance to throttle the driver. + * Additionally, the driver may have already completed the I/O + * but the tty buffer is still emptying, so the line discipline is + * throttling and unthrottling nothing. + */ + + ++port->stats.throttled; +} + +static void fwtty_unthrottle(struct tty_struct *tty) +{ + struct fwtty_port *port = tty->driver_data; + + fwtty_dbg(port, "CRTSCTS: %d", (C_CRTSCTS(tty) != 0)); + + profile_fifo_avail(port, port->stats.unthrottle); + + schedule_work(&port->push); + + spin_lock_bh(&port->lock); + port->mctrl &= ~OOB_RX_THROTTLE; + if (C_CRTSCTS(tty)) + port->mctrl |= TIOCM_RTS; + __fwtty_write_port_status(port); + spin_unlock_bh(&port->lock); +} + +static int check_msr_delta(struct fwtty_port *port, unsigned long mask, + struct async_icount *prev) +{ + struct async_icount now; + int delta; + + now = port->icount; + + delta = ((mask & TIOCM_RNG && prev->rng != now.rng) || + (mask & TIOCM_DSR && prev->dsr != now.dsr) || + (mask & TIOCM_CAR && prev->dcd != now.dcd) || + (mask & TIOCM_CTS && prev->cts != now.cts)); + + *prev = now; + + return delta; +} + +static int wait_msr_change(struct fwtty_port *port, unsigned long mask) +{ + struct async_icount prev; + + prev = port->icount; + + return wait_event_interruptible(port->port.delta_msr_wait, + check_msr_delta(port, mask, &prev)); +} + +static int get_serial_info(struct fwtty_port *port, + struct serial_struct __user *info) +{ + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + + tmp.type = PORT_UNKNOWN; + tmp.line = port->port.tty->index; + tmp.flags = port->port.flags; + tmp.xmit_fifo_size = FWTTY_PORT_TXFIFO_LEN; + tmp.baud_base = 400000000; + tmp.close_delay = port->port.close_delay; + + return (copy_to_user(info, &tmp, sizeof(*info))) ? -EFAULT : 0; +} + +static int set_serial_info(struct fwtty_port *port, + struct serial_struct __user *info) +{ + struct serial_struct tmp; + + if (copy_from_user(&tmp, info, sizeof(tmp))) + return -EFAULT; + + if (tmp.irq != 0 || tmp.port != 0 || tmp.custom_divisor != 0 || + tmp.baud_base != 400000000) + return -EPERM; + + if (!capable(CAP_SYS_ADMIN)) { + if (((tmp.flags & ~ASYNC_USR_MASK) != + (port->port.flags & ~ASYNC_USR_MASK))) + return -EPERM; + } else + port->port.close_delay = tmp.close_delay * HZ / 100; + + return 0; +} + +static int fwtty_ioctl(struct tty_struct *tty, unsigned cmd, + unsigned long arg) +{ + struct fwtty_port *port = tty->driver_data; + int err; + + switch (cmd) { + case TIOCGSERIAL: + mutex_lock(&port->port.mutex); + err = get_serial_info(port, (void __user *)arg); + mutex_unlock(&port->port.mutex); + break; + + case TIOCSSERIAL: + mutex_lock(&port->port.mutex); + err = set_serial_info(port, (void __user *)arg); + mutex_unlock(&port->port.mutex); + break; + + case TIOCMIWAIT: + err = wait_msr_change(port, arg); + break; + + default: + err = -ENOIOCTLCMD; + } + + return err; +} + +static void fwtty_set_termios(struct tty_struct *tty, struct ktermios *old) +{ + struct fwtty_port *port = tty->driver_data; + unsigned baud; + + spin_lock_bh(&port->lock); + baud = set_termios(port, tty); + + if ((baud == 0) && (old->c_cflag & CBAUD)) + port->mctrl &= ~(TIOCM_DTR | TIOCM_RTS); + else if ((baud != 0) && !(old->c_cflag & CBAUD)) { + if (C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags)) + port->mctrl |= TIOCM_DTR | TIOCM_RTS; + else + port->mctrl |= TIOCM_DTR; + } + __fwtty_write_port_status(port); + spin_unlock_bh(&port->lock); + + if (old->c_cflag & CRTSCTS) { + if (!C_CRTSCTS(tty)) { + tty->hw_stopped = 0; + fwtty_restart_tx(port); + } + } else if (C_CRTSCTS(tty) && ~port->mstatus & TIOCM_CTS) { + tty->hw_stopped = 1; + } +} + +/** + * fwtty_break_ctl - start/stop sending breaks + * + * Signals the remote to start or stop generating simulated breaks. + * First, stop dequeueing from the fifo and wait for writer/drain to leave tx + * before signalling the break line status. This guarantees any pending rx will + * be queued to the line discipline before break is simulated on the remote. + * Conversely, turning off break_ctl requires signalling the line status change, + * then enabling tx. + */ +static int fwtty_break_ctl(struct tty_struct *tty, int state) +{ + struct fwtty_port *port = tty->driver_data; + long ret; + + fwtty_dbg(port, "%d", state); + + if (state == -1) { + set_bit(STOP_TX, &port->flags); + ret = wait_event_interruptible_timeout(port->wait_tx, + !test_bit(IN_TX, &port->flags), + 10); + if (ret == 0 || ret == -ERESTARTSYS) { + clear_bit(STOP_TX, &port->flags); + fwtty_restart_tx(port); + return -EINTR; + } + } + + spin_lock_bh(&port->lock); + port->break_ctl = (state == -1); + __fwtty_write_port_status(port); + spin_unlock_bh(&port->lock); + + if (state == 0) { + spin_lock_bh(&port->lock); + dma_fifo_reset(&port->tx_fifo); + clear_bit(STOP_TX, &port->flags); + spin_unlock_bh(&port->lock); + } + return 0; +} + +static int fwtty_tiocmget(struct tty_struct *tty) +{ + struct fwtty_port *port = tty->driver_data; + unsigned tiocm; + + spin_lock_bh(&port->lock); + tiocm = (port->mctrl & MCTRL_MASK) | (port->mstatus & ~MCTRL_MASK); + spin_unlock_bh(&port->lock); + + fwtty_dbg(port, "%x", tiocm); + + return tiocm; +} + +static int fwtty_tiocmset(struct tty_struct *tty, unsigned set, unsigned clear) +{ + struct fwtty_port *port = tty->driver_data; + + fwtty_dbg(port, "set: %x clear: %x", set, clear); + + /* TODO: simulate loopback if TIOCM_LOOP set */ + + spin_lock_bh(&port->lock); + port->mctrl &= ~(clear & MCTRL_MASK & 0xffff); + port->mctrl |= set & MCTRL_MASK & 0xffff; + __fwtty_write_port_status(port); + spin_unlock_bh(&port->lock); + return 0; +} + +static int fwtty_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct fwtty_port *port = tty->driver_data; + struct stats stats; + + memcpy(&stats, &port->stats, sizeof(stats)); + if (port->port.console) + (*port->fwcon_ops->stats)(&stats, port->con_data); + + icount->cts = port->icount.cts; + icount->dsr = port->icount.dsr; + icount->rng = port->icount.rng; + icount->dcd = port->icount.dcd; + icount->rx = port->icount.rx; + icount->tx = port->icount.tx + stats.xchars; + icount->frame = port->icount.frame; + icount->overrun = port->icount.overrun; + icount->parity = port->icount.parity; + icount->brk = port->icount.brk; + icount->buf_overrun = port->icount.overrun; + return 0; +} + +static void fwtty_proc_show_port(struct seq_file *m, struct fwtty_port *port) +{ + struct stats stats; + + memcpy(&stats, &port->stats, sizeof(stats)); + if (port->port.console) + (*port->fwcon_ops->stats)(&stats, port->con_data); + + seq_printf(m, " tx:%d rx:%d", port->icount.tx + stats.xchars, + port->icount.rx); + seq_printf(m, " cts:%d dsr:%d rng:%d dcd:%d", port->icount.cts, + port->icount.dsr, port->icount.rng, port->icount.dcd); + seq_printf(m, " fe:%d oe:%d pe:%d brk:%d", port->icount.frame, + port->icount.overrun, port->icount.parity, port->icount.brk); + seq_printf(m, " dr:%d st:%d err:%d lost:%d", stats.dropped, + stats.tx_stall, stats.fifo_errs, stats.lost); + seq_printf(m, " pkts:%d thr:%d wtrmk:%d", stats.sent, stats.throttled, + stats.watermark); + seq_printf(m, " addr:%012llx", port->rx_handler.offset); + + if (port->port.console) { + seq_printf(m, "\n "); + (*port->fwcon_ops->proc_show)(m, port->con_data); + } + + dump_profile(m, &port->stats); +} + +static void fwtty_proc_show_peer(struct seq_file *m, struct fwtty_peer *peer) +{ + int generation = peer->generation; + + smp_rmb(); + seq_printf(m, " %s:", dev_name(&peer->unit->device)); + seq_printf(m, " node:%04x gen:%d", peer->node_id, generation); + seq_printf(m, " sp:%d max:%d guid:%016llx", peer->speed, + peer->max_payload, (unsigned long long) peer->guid); + + if (capable(CAP_SYS_ADMIN)) { + seq_printf(m, " mgmt:%012llx", + (unsigned long long) peer->mgmt_addr); + seq_printf(m, " addr:%012llx", + (unsigned long long) peer->status_addr); + } + seq_putc(m, '\n'); +} + +static int fwtty_proc_show(struct seq_file *m, void *v) +{ + struct fwtty_port *port; + struct fw_serial *serial; + struct fwtty_peer *peer; + int i; + + seq_puts(m, "fwserinfo: 1.0 driver: 1.0\n"); + for (i = 0; i < MAX_TOTAL_PORTS && (port = fwtty_port_get(i)); ++i) { + seq_printf(m, "%2d:", i); + if (capable(CAP_SYS_ADMIN)) + fwtty_proc_show_port(m, port); + fwtty_port_put(port); + seq_printf(m, "\n"); + } + seq_putc(m, '\n'); + + rcu_read_lock(); + list_for_each_entry_rcu(serial, &fwserial_list, list) { + seq_printf(m, "card: %s guid: %016llx\n", + dev_name(serial->card->device), + (unsigned long long) serial->card->guid); + list_for_each_entry_rcu(peer, &serial->peer_list, list) + fwtty_proc_show_peer(m, peer); + } + rcu_read_unlock(); + return 0; +} + +static int fwtty_proc_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, fwtty_proc_show, NULL); +} + +static const struct file_operations fwtty_proc_fops = { + .owner = THIS_MODULE, + .open = fwtty_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct tty_port_operations fwtty_port_ops = { + .dtr_rts = fwtty_port_dtr_rts, + .carrier_raised = fwtty_port_carrier_raised, + .shutdown = fwtty_port_shutdown, + .activate = fwtty_port_activate, +}; + +static const struct tty_operations fwtty_ops = { + .open = fwtty_open, + .close = fwtty_close, + .hangup = fwtty_hangup, + .cleanup = fwtty_cleanup, + .install = fwtty_install, + .write = fwtty_write, + .write_room = fwtty_write_room, + .chars_in_buffer = fwtty_chars_in_buffer, + .send_xchar = fwtty_send_xchar, + .throttle = fwtty_throttle, + .unthrottle = fwtty_unthrottle, + .ioctl = fwtty_ioctl, + .set_termios = fwtty_set_termios, + .break_ctl = fwtty_break_ctl, + .tiocmget = fwtty_tiocmget, + .tiocmset = fwtty_tiocmset, + .get_icount = fwtty_get_icount, + .proc_fops = &fwtty_proc_fops, +}; + +static inline int mgmt_pkt_expected_len(__be16 code) +{ + static const struct fwserial_mgmt_pkt pkt; + + switch (be16_to_cpu(code)) { + case FWSC_VIRT_CABLE_PLUG: + return sizeof(pkt.hdr) + sizeof(pkt.plug_req); + + case FWSC_VIRT_CABLE_PLUG_RSP: /* | FWSC_RSP_OK */ + return sizeof(pkt.hdr) + sizeof(pkt.plug_rsp); + + + case FWSC_VIRT_CABLE_UNPLUG: + case FWSC_VIRT_CABLE_UNPLUG_RSP: + case FWSC_VIRT_CABLE_PLUG_RSP | FWSC_RSP_NACK: + case FWSC_VIRT_CABLE_UNPLUG_RSP | FWSC_RSP_NACK: + return sizeof(pkt.hdr); + + default: + return -1; + } +} + +static inline void fill_plug_params(struct virt_plug_params *params, + struct fwtty_port *port) +{ + u64 status_addr = port->rx_handler.offset; + u64 fifo_addr = port->rx_handler.offset + 4; + size_t fifo_len = port->rx_handler.length - 4; + + params->status_hi = cpu_to_be32(status_addr >> 32); + params->status_lo = cpu_to_be32(status_addr); + params->fifo_hi = cpu_to_be32(fifo_addr >> 32); + params->fifo_lo = cpu_to_be32(fifo_addr); + params->fifo_len = cpu_to_be32(fifo_len); +} + +static inline void fill_plug_req(struct fwserial_mgmt_pkt *pkt, + struct fwtty_port *port) +{ + pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_PLUG); + pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code)); + fill_plug_params(&pkt->plug_req, port); +} + +static inline void fill_plug_rsp_ok(struct fwserial_mgmt_pkt *pkt, + struct fwtty_port *port) +{ + pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_PLUG_RSP); + pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code)); + fill_plug_params(&pkt->plug_rsp, port); +} + +static inline void fill_plug_rsp_nack(struct fwserial_mgmt_pkt *pkt) +{ + pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_PLUG_RSP | FWSC_RSP_NACK); + pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code)); +} + +static inline void fill_unplug_req(struct fwserial_mgmt_pkt *pkt) +{ + pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_UNPLUG); + pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code)); +} + +static inline void fill_unplug_rsp_nack(struct fwserial_mgmt_pkt *pkt) +{ + pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_UNPLUG_RSP | FWSC_RSP_NACK); + pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code)); +} + +static inline void fill_unplug_rsp_ok(struct fwserial_mgmt_pkt *pkt) +{ + pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_UNPLUG_RSP); + pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code)); +} + +static void fwserial_virt_plug_complete(struct fwtty_peer *peer, + struct virt_plug_params *params) +{ + struct fwtty_port *port = peer->port; + + peer->status_addr = be32_to_u64(params->status_hi, params->status_lo); + peer->fifo_addr = be32_to_u64(params->fifo_hi, params->fifo_lo); + peer->fifo_len = be32_to_cpu(params->fifo_len); + peer_set_state(peer, FWPS_ATTACHED); + + /* reconfigure tx_fifo optimally for this peer */ + spin_lock_bh(&port->lock); + port->max_payload = min3(peer->max_payload, peer->fifo_len, + MAX_ASYNC_PAYLOAD); + dma_fifo_change_tx_limit(&port->tx_fifo, port->max_payload); + spin_unlock_bh(&peer->port->lock); + + if (port->port.console && port->fwcon_ops->notify != NULL) + (*port->fwcon_ops->notify)(FWCON_NOTIFY_ATTACH, port->con_data); + + fwtty_info(&peer->unit, "peer (guid:%016llx) connected on %s", + (unsigned long long)peer->guid, dev_name(port->device)); +} + +static inline int fwserial_send_mgmt_sync(struct fwtty_peer *peer, + struct fwserial_mgmt_pkt *pkt) +{ + int generation; + int rcode, tries = 5; + + do { + generation = peer->generation; + smp_rmb(); + + rcode = fw_run_transaction(peer->serial->card, + TCODE_WRITE_BLOCK_REQUEST, + peer->node_id, + generation, peer->speed, + peer->mgmt_addr, + pkt, be16_to_cpu(pkt->hdr.len)); + if (rcode == RCODE_BUSY || rcode == RCODE_SEND_ERROR || + rcode == RCODE_GENERATION) { + fwtty_dbg(&peer->unit, "mgmt write error: %d", rcode); + continue; + } else + break; + } while (--tries > 0); + return rcode; +} + +/** + * fwserial_claim_port - attempt to claim port @ index for peer + * + * Returns ptr to claimed port or error code (as ERR_PTR()) + * Can sleep - must be called from process context + */ +static struct fwtty_port *fwserial_claim_port(struct fwtty_peer *peer, + int index) +{ + struct fwtty_port *port; + + if (index < 0 || index >= num_ports) + return ERR_PTR(-EINVAL); + + /* must guarantee that previous port releases have completed */ + synchronize_rcu(); + + port = peer->serial->ports[index]; + spin_lock_bh(&port->lock); + if (!rcu_access_pointer(port->peer)) + rcu_assign_pointer(port->peer, peer); + else + port = ERR_PTR(-EBUSY); + spin_unlock_bh(&port->lock); + + return port; +} + +/** + * fwserial_find_port - find avail port and claim for peer + * + * Returns ptr to claimed port or NULL if none avail + * Can sleep - must be called from process context + */ +static struct fwtty_port *fwserial_find_port(struct fwtty_peer *peer) +{ + struct fwtty_port **ports = peer->serial->ports; + int i; + + /* must guarantee that previous port releases have completed */ + synchronize_rcu(); + + /* TODO: implement optional GUID-to-specific port # matching */ + + /* find an unattached port (but not the loopback port, if present) */ + for (i = 0; i < num_ttys; ++i) { + spin_lock_bh(&ports[i]->lock); + if (!ports[i]->peer) { + /* claim port */ + rcu_assign_pointer(ports[i]->peer, peer); + spin_unlock_bh(&ports[i]->lock); + return ports[i]; + } + spin_unlock_bh(&ports[i]->lock); + } + return NULL; +} + +static void fwserial_release_port(struct fwtty_port *port) +{ + /* drop carrier (and all other line status) */ + fwtty_update_port_status(port, 0); + + spin_lock_bh(&port->lock); + + /* reset dma fifo max transmission size back to S100 */ + port->max_payload = link_speed_to_max_payload(SCODE_100); + dma_fifo_change_tx_limit(&port->tx_fifo, port->max_payload); + + rcu_assign_pointer(port->peer, NULL); + spin_unlock_bh(&port->lock); + + if (port->port.console && port->fwcon_ops->notify != NULL) + (*port->fwcon_ops->notify)(FWCON_NOTIFY_DETACH, port->con_data); +} + +static void fwserial_plug_timeout(unsigned long data) +{ + struct fwtty_peer *peer = (struct fwtty_peer *) data; + struct fwtty_port *port; + + spin_lock_bh(&peer->lock); + if (peer->state != FWPS_PLUG_PENDING) { + spin_unlock_bh(&peer->lock); + return; + } + + port = peer_revert_state(peer); + spin_unlock_bh(&peer->lock); + + if (port) + fwserial_release_port(port); +} + +/** + * fwserial_connect_peer - initiate virtual cable with peer + * + * Returns 0 if VIRT_CABLE_PLUG request was successfully sent, + * otherwise error code. Must be called from process context. + */ +static int fwserial_connect_peer(struct fwtty_peer *peer) +{ + struct fwtty_port *port; + struct fwserial_mgmt_pkt *pkt; + int err, rcode; + + pkt = kmalloc(sizeof(*pkt), GFP_KERNEL); + if (!pkt) + return -ENOMEM; + + port = fwserial_find_port(peer); + if (!port) { + fwtty_err(&peer->unit, "avail ports in use"); + err = -EBUSY; + goto free_pkt; + } + + spin_lock_bh(&peer->lock); + + /* only initiate VIRT_CABLE_PLUG if peer is currently not attached */ + if (peer->state != FWPS_NOT_ATTACHED) { + err = -EBUSY; + goto release_port; + } + + peer->port = port; + peer_set_state(peer, FWPS_PLUG_PENDING); + + fill_plug_req(pkt, peer->port); + + setup_timer(&peer->timer, fwserial_plug_timeout, (unsigned long)peer); + mod_timer(&peer->timer, jiffies + VIRT_CABLE_PLUG_TIMEOUT); + spin_unlock_bh(&peer->lock); + + rcode = fwserial_send_mgmt_sync(peer, pkt); + + spin_lock_bh(&peer->lock); + if (peer->state == FWPS_PLUG_PENDING && rcode != RCODE_COMPLETE) { + if (rcode == RCODE_CONFLICT_ERROR) + err = -EAGAIN; + else + err = -EIO; + goto cancel_timer; + } + spin_unlock_bh(&peer->lock); + + kfree(pkt); + return 0; + +cancel_timer: + del_timer(&peer->timer); + peer_revert_state(peer); +release_port: + spin_unlock_bh(&peer->lock); + fwserial_release_port(port); +free_pkt: + kfree(pkt); + return err; +} + +/** + * fwserial_close_port - + * HUP the tty (if the tty exists) and unregister the tty device. + * Only used by the unit driver upon unit removal to disconnect and + * cleanup all attached ports + * + * The port reference is put by fwtty_cleanup (if a reference was + * ever taken). + */ +static void fwserial_close_port(struct fwtty_port *port) +{ + struct tty_struct *tty; + + mutex_lock(&port->port.mutex); + tty = tty_port_tty_get(&port->port); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + mutex_unlock(&port->port.mutex); + + tty_unregister_device(fwtty_driver, port->index); +} + +/** + * fwserial_lookup - finds first fw_serial associated with card + * @card: fw_card to match + * + * NB: caller must be holding fwserial_list_mutex + */ +static struct fw_serial *fwserial_lookup(struct fw_card *card) +{ + struct fw_serial *serial; + + list_for_each_entry(serial, &fwserial_list, list) { + if (card == serial->card) + return serial; + } + + return NULL; +} + +/** + * __fwserial_lookup_rcu - finds first fw_serial associated with card + * @card: fw_card to match + * + * NB: caller must be inside rcu_read_lock() section + */ +static struct fw_serial *__fwserial_lookup_rcu(struct fw_card *card) +{ + struct fw_serial *serial; + + list_for_each_entry_rcu(serial, &fwserial_list, list) { + if (card == serial->card) + return serial; + } + + return NULL; +} + +/** + * __fwserial_peer_by_node_id - finds a peer matching the given generation + id + * + * If a matching peer could not be found for the specified generation/node id, + * this could be because: + * a) the generation has changed and one of the nodes hasn't updated yet + * b) the remote node has created its remote unit device before this + * local node has created its corresponding remote unit device + * In either case, the remote node should retry + * + * Note: caller must be in rcu_read_lock() section + */ +static struct fwtty_peer *__fwserial_peer_by_node_id(struct fw_card *card, + int generation, int id) +{ + struct fw_serial *serial; + struct fwtty_peer *peer; + + serial = __fwserial_lookup_rcu(card); + if (!serial) { + /* + * Something is very wrong - there should be a matching + * fw_serial structure for every fw_card. Maybe the remote node + * has created its remote unit device before this driver has + * been probed for any unit devices... + */ + fwtty_err(card, "unknown card (guid %016llx)", + (unsigned long long) card->guid); + return NULL; + } + + list_for_each_entry_rcu(peer, &serial->peer_list, list) { + int g = peer->generation; + smp_rmb(); + if (generation == g && id == peer->node_id) + return peer; + } + + return NULL; +} + +#ifdef DEBUG +static void __dump_peer_list(struct fw_card *card) +{ + struct fw_serial *serial; + struct fwtty_peer *peer; + + serial = __fwserial_lookup_rcu(card); + if (!serial) + return; + + list_for_each_entry_rcu(peer, &serial->peer_list, list) { + int g = peer->generation; + smp_rmb(); + fwtty_dbg(card, "peer(%d:%x) guid: %016llx\n", g, + peer->node_id, (unsigned long long) peer->guid); + } +} +#else +#define __dump_peer_list(s) +#endif + +static void fwserial_auto_connect(struct work_struct *work) +{ + struct fwtty_peer *peer = to_peer(to_delayed_work(work), connect); + int err; + + err = fwserial_connect_peer(peer); + if (err == -EAGAIN && ++peer->connect_retries < MAX_CONNECT_RETRIES) + schedule_delayed_work(&peer->connect, CONNECT_RETRY_DELAY); +} + +/** + * fwserial_add_peer - add a newly probed 'serial' unit device as a 'peer' + * @serial: aggregate representing the specific fw_card to add the peer to + * @unit: 'peer' to create and add to peer_list of serial + * + * Adds a 'peer' (ie, a local or remote 'serial' unit device) to the list of + * peers for a specific fw_card. Optionally, auto-attach this peer to an + * available tty port. This function is called either directly or indirectly + * as a result of a 'serial' unit device being created & probed. + * + * Note: this function is serialized with fwserial_remove_peer() by the + * fwserial_list_mutex held in fwserial_probe(). + * + * A 1:1 correspondence between an fw_unit and an fwtty_peer is maintained + * via the dev_set_drvdata() for the device of the fw_unit. + */ +static int fwserial_add_peer(struct fw_serial *serial, struct fw_unit *unit) +{ + struct device *dev = &unit->device; + struct fw_device *parent = fw_parent_device(unit); + struct fwtty_peer *peer; + struct fw_csr_iterator ci; + int key, val; + int generation; + + peer = kzalloc(sizeof(*peer), GFP_KERNEL); + if (!peer) + return -ENOMEM; + + peer_set_state(peer, FWPS_NOT_ATTACHED); + + dev_set_drvdata(dev, peer); + peer->unit = unit; + peer->guid = (u64)parent->config_rom[3] << 32 | parent->config_rom[4]; + peer->speed = parent->max_speed; + peer->max_payload = min(device_max_receive(parent), + link_speed_to_max_payload(peer->speed)); + + generation = parent->generation; + smp_rmb(); + peer->node_id = parent->node_id; + smp_wmb(); + peer->generation = generation; + + /* retrieve the mgmt bus addr from the unit directory */ + fw_csr_iterator_init(&ci, unit->directory); + while (fw_csr_iterator_next(&ci, &key, &val)) { + if (key == (CSR_OFFSET | CSR_DEPENDENT_INFO)) { + peer->mgmt_addr = CSR_REGISTER_BASE + 4 * val; + break; + } + } + if (peer->mgmt_addr == 0ULL) { + /* + * No mgmt address effectively disables VIRT_CABLE_PLUG - + * this peer will not be able to attach to a remote + */ + peer_set_state(peer, FWPS_NO_MGMT_ADDR); + } + + spin_lock_init(&peer->lock); + peer->port = NULL; + + init_timer(&peer->timer); + INIT_WORK(&peer->work, NULL); + INIT_DELAYED_WORK(&peer->connect, fwserial_auto_connect); + + /* associate peer with specific fw_card */ + peer->serial = serial; + list_add_rcu(&peer->list, &serial->peer_list); + + fwtty_info(&peer->unit, "peer added (guid:%016llx)", + (unsigned long long)peer->guid); + + /* identify the local unit & virt cable to loopback port */ + if (parent->is_local) { + serial->self = peer; + if (create_loop_dev) { + struct fwtty_port *port; + port = fwserial_claim_port(peer, num_ttys); + if (!IS_ERR(port)) { + struct virt_plug_params params; + + spin_lock_bh(&peer->lock); + peer->port = port; + fill_plug_params(¶ms, port); + fwserial_virt_plug_complete(peer, ¶ms); + spin_unlock_bh(&peer->lock); + + fwtty_write_port_status(port); + } + } + + } else if (auto_connect) { + /* auto-attach to remote units only (if policy allows) */ + schedule_delayed_work(&peer->connect, 1); + } + + return 0; +} + +/** + * fwserial_remove_peer - remove a 'serial' unit device as a 'peer' + * + * Remove a 'peer' from its list of peers. This function is only + * called by fwserial_remove() on bus removal of the unit device. + * + * Note: this function is serialized with fwserial_add_peer() by the + * fwserial_list_mutex held in fwserial_remove(). + */ +static void fwserial_remove_peer(struct fwtty_peer *peer) +{ + struct fwtty_port *port; + + spin_lock_bh(&peer->lock); + peer_set_state(peer, FWPS_GONE); + spin_unlock_bh(&peer->lock); + + cancel_delayed_work_sync(&peer->connect); + cancel_work_sync(&peer->work); + + spin_lock_bh(&peer->lock); + /* if this unit is the local unit, clear link */ + if (peer == peer->serial->self) + peer->serial->self = NULL; + + /* cancel the request timeout timer (if running) */ + del_timer(&peer->timer); + + port = peer->port; + peer->port = NULL; + + list_del_rcu(&peer->list); + + fwtty_info(&peer->unit, "peer removed (guid:%016llx)", + (unsigned long long)peer->guid); + + spin_unlock_bh(&peer->lock); + + if (port) + fwserial_release_port(port); + + synchronize_rcu(); + kfree(peer); +} + +/** + * create_loop_device - create a loopback tty device + * @tty_driver: tty_driver to own loopback device + * @prototype: ptr to already-assigned 'prototype' tty port + * @index: index to associate this device with the tty port + * @parent: device to child to + * + * HACK - this is basically tty_port_register_device() with an + * alternate naming scheme. Suggest tty_port_register_named_device() + * helper api. + * + * Creates a loopback tty device named 'fwloop' which is attached to + * the local unit in fwserial_add_peer(). Note that in the device + * name advances in increments of port allocation blocks, ie., for port + * indices 0..3, the device name will be 'fwloop0'; for 4..7, 'fwloop1', + * and so on. + * + * Only one loopback device should be created per fw_card. + */ +static void release_loop_device(struct device *dev) +{ + kfree(dev); +} + +static struct device *create_loop_device(struct tty_driver *driver, + struct fwtty_port *prototype, + struct fwtty_port *port, + struct device *parent) +{ + char name[64]; + int index = port->index; + dev_t devt = MKDEV(driver->major, driver->minor_start) + index; + struct device *dev = NULL; + int err; + + if (index >= fwtty_driver->num) + return ERR_PTR(-EINVAL); + + snprintf(name, 64, "%s%d", loop_dev_name, index / num_ports); + + tty_port_link_device(&port->port, driver, index); + + cdev_init(&driver->cdevs[index], driver->cdevs[prototype->index].ops); + driver->cdevs[index].owner = driver->owner; + err = cdev_add(&driver->cdevs[index], devt, 1); + if (err) + return ERR_PTR(err); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + cdev_del(&driver->cdevs[index]); + return ERR_PTR(-ENOMEM); + } + + dev->devt = devt; + dev->class = prototype->device->class; + dev->parent = parent; + dev->release = release_loop_device; + dev_set_name(dev, "%s", name); + dev->groups = NULL; + dev_set_drvdata(dev, NULL); + + err = device_register(dev); + if (err) { + put_device(dev); + cdev_del(&driver->cdevs[index]); + return ERR_PTR(err); + } + + return dev; +} + +/** + * fwserial_create - init everything to create TTYs for a specific fw_card + * @unit: fw_unit for first 'serial' unit device probed for this fw_card + * + * This function inits the aggregate structure (an fw_serial instance) + * used to manage the TTY ports registered by a specific fw_card. Also, the + * unit device is added as the first 'peer'. + * + * This unit device may represent a local unit device (as specified by the + * config ROM unit directory) or it may represent a remote unit device + * (as specified by the reading of the remote node's config ROM). + * + * Returns 0 to indicate "ownership" of the unit device, or a negative errno + * value to indicate which error. + */ +static int fwserial_create(struct fw_unit *unit) +{ + struct fw_device *parent = fw_parent_device(unit); + struct fw_card *card = parent->card; + struct fw_serial *serial; + struct fwtty_port *port; + struct device *tty_dev; + int i, j; + int err; + + serial = kzalloc(sizeof(*serial), GFP_KERNEL); + if (!serial) + return -ENOMEM; + + kref_init(&serial->kref); + serial->card = card; + INIT_LIST_HEAD(&serial->peer_list); + + for (i = 0; i < num_ports; ++i) { + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) { + err = -ENOMEM; + goto free_ports; + } + tty_port_init(&port->port); + port->index = FWTTY_INVALID_INDEX; + port->port.ops = &fwtty_port_ops; + port->serial = serial; + + spin_lock_init(&port->lock); + INIT_DELAYED_WORK(&port->drain, fwtty_drain_tx); + INIT_DELAYED_WORK(&port->emit_breaks, fwtty_emit_breaks); + INIT_WORK(&port->hangup, fwtty_do_hangup); + INIT_WORK(&port->push, fwtty_pushrx); + INIT_LIST_HEAD(&port->buf_list); + init_waitqueue_head(&port->wait_tx); + port->max_payload = link_speed_to_max_payload(SCODE_100); + dma_fifo_init(&port->tx_fifo); + + rcu_assign_pointer(port->peer, NULL); + serial->ports[i] = port; + + /* get unique bus addr region for port's status & recv fifo */ + port->rx_handler.length = FWTTY_PORT_RXFIFO_LEN + 4; + port->rx_handler.address_callback = fwtty_port_handler; + port->rx_handler.callback_data = port; + /* + * XXX: use custom memory region above cpu physical memory addrs + * this will ease porting to 64-bit firewire adapters + */ + err = fw_core_add_address_handler(&port->rx_handler, + &fw_high_memory_region); + if (err) { + kfree(port); + goto free_ports; + } + } + /* preserve i for error cleanup */ + + err = fwtty_ports_add(serial); + if (err) { + fwtty_err(&unit, "no space in port table"); + goto free_ports; + } + + for (j = 0; j < num_ttys; ++j) { + tty_dev = tty_port_register_device(&serial->ports[j]->port, + fwtty_driver, + serial->ports[j]->index, + card->device); + if (IS_ERR(tty_dev)) { + err = PTR_ERR(tty_dev); + fwtty_err(&unit, "register tty device error (%d)", err); + goto unregister_ttys; + } + + serial->ports[j]->device = tty_dev; + } + /* preserve j for error cleanup */ + + if (create_loop_dev) { + struct device *loop_dev; + + loop_dev = create_loop_device(fwtty_driver, + serial->ports[0], + serial->ports[num_ttys], + card->device); + if (IS_ERR(loop_dev)) { + err = PTR_ERR(loop_dev); + fwtty_err(&unit, "create loop device failed (%d)", err); + goto unregister_ttys; + } + serial->ports[num_ttys]->device = loop_dev; + serial->ports[num_ttys]->loopback = true; + } + + list_add_rcu(&serial->list, &fwserial_list); + + fwtty_notice(&unit, "TTY over FireWire on device %s (guid %016llx)", + dev_name(card->device), (unsigned long long) card->guid); + + err = fwserial_add_peer(serial, unit); + if (!err) + return 0; + + fwtty_err(&unit, "unable to add peer unit device (%d)", err); + + /* fall-through to error processing */ + list_del_rcu(&serial->list); +unregister_ttys: + for (--j; j >= 0; --j) + tty_unregister_device(fwtty_driver, serial->ports[j]->index); + kref_put(&serial->kref, fwserial_destroy); + return err; + +free_ports: + for (--i; i >= 0; --i) + kfree(serial->ports[i]); + kfree(serial); + return err; +} + +/** + * fwserial_probe: bus probe function for firewire 'serial' unit devices + * + * A 'serial' unit device is created and probed as a result of: + * - declaring a ieee1394 bus id table for 'devices' matching a fabricated + * 'serial' unit specifier id + * - adding a unit directory to the config ROM(s) for a 'serial' unit + * + * The firewire core registers unit devices by enumerating unit directories + * of a node's config ROM after reading the config ROM when a new node is + * added to the bus topology after a bus reset. + * + * The practical implications of this are: + * - this probe is called for both local and remote nodes that have a 'serial' + * unit directory in their config ROM (that matches the specifiers in + * fwserial_id_table). + * - no specific order is enforced for local vs. remote unit devices + * + * This unit driver copes with the lack of specific order in the same way the + * firewire net driver does -- each probe, for either a local or remote unit + * device, is treated as a 'peer' (has a struct fwtty_peer instance) and the + * first peer created for a given fw_card (tracked by the global fwserial_list) + * creates the underlying TTYs (aggregated in a fw_serial instance). + * + * NB: an early attempt to differentiate local & remote unit devices by creating + * peers only for remote units and fw_serial instances (with their + * associated TTY devices) only for local units was discarded. Managing + * the peer lifetimes on device removal proved too complicated. + * + * fwserial_probe/fwserial_remove are effectively serialized by the + * fwserial_list_mutex. This is necessary because the addition of the first peer + * for a given fw_card will trigger the creation of the fw_serial for that + * fw_card, which must not simultaneously contend with the removal of the + * last peer for a given fw_card triggering the destruction of the same + * fw_serial for the same fw_card. + */ +static int fwserial_probe(struct device *dev) +{ + struct fw_unit *unit = fw_unit(dev); + struct fw_serial *serial; + int err; + + mutex_lock(&fwserial_list_mutex); + serial = fwserial_lookup(fw_parent_device(unit)->card); + if (!serial) + err = fwserial_create(unit); + else + err = fwserial_add_peer(serial, unit); + mutex_unlock(&fwserial_list_mutex); + return err; +} + +/** + * fwserial_remove: bus removal function for firewire 'serial' unit devices + * + * The corresponding 'peer' for this unit device is removed from the list of + * peers for the associated fw_serial (which has a 1:1 correspondence with a + * specific fw_card). If this is the last peer being removed, then trigger + * the destruction of the underlying TTYs. + */ +static int fwserial_remove(struct device *dev) +{ + struct fwtty_peer *peer = dev_get_drvdata(dev); + struct fw_serial *serial = peer->serial; + int i; + + mutex_lock(&fwserial_list_mutex); + fwserial_remove_peer(peer); + + if (list_empty(&serial->peer_list)) { + /* unlink from the fwserial_list here */ + list_del_rcu(&serial->list); + + for (i = 0; i < num_ports; ++i) + fwserial_close_port(serial->ports[i]); + kref_put(&serial->kref, fwserial_destroy); + } + mutex_unlock(&fwserial_list_mutex); + + return 0; +} + +/** + * fwserial_update: bus update function for 'firewire' serial unit devices + * + * Updates the new node_id and bus generation for this peer. Note that locking + * is unnecessary; but careful memory barrier usage is important to enforce the + * load and store order of generation & node_id. + * + * The fw-core orders the write of node_id before generation in the parent + * fw_device to ensure that a stale node_id cannot be used with a current + * bus generation. So the generation value must be read before the node_id. + * + * In turn, this orders the write of node_id before generation in the peer to + * also ensure a stale node_id cannot be used with a current bus generation. + */ +static void fwserial_update(struct fw_unit *unit) +{ + struct fw_device *parent = fw_parent_device(unit); + struct fwtty_peer *peer = dev_get_drvdata(&unit->device); + int generation; + + generation = parent->generation; + smp_rmb(); + peer->node_id = parent->node_id; + smp_wmb(); + peer->generation = generation; +} + +static const struct ieee1394_device_id fwserial_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .specifier_id = LINUX_VENDOR_ID, + .version = FWSERIAL_VERSION, + }, + { } +}; + +static struct fw_driver fwserial_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + .probe = fwserial_probe, + .remove = fwserial_remove, + }, + .update = fwserial_update, + .id_table = fwserial_id_table, +}; + +#define FW_UNIT_SPECIFIER(id) ((CSR_SPECIFIER_ID << 24) | (id)) +#define FW_UNIT_VERSION(ver) ((CSR_VERSION << 24) | (ver)) +#define FW_UNIT_ADDRESS(ofs) (((CSR_OFFSET | CSR_DEPENDENT_INFO) << 24) \ + | (((ofs) - CSR_REGISTER_BASE) >> 2)) +/* XXX: config ROM definitons could be improved with semi-automated offset + * and length calculation + */ +#define FW_ROM_DESCRIPTOR(ofs) (((CSR_LEAF | CSR_DESCRIPTOR) << 24) | (ofs)) + +struct fwserial_unit_directory_data { + u16 crc; + u16 len; + u32 unit_specifier; + u32 unit_sw_version; + u32 unit_addr_offset; + u32 desc1_ofs; + u16 desc1_crc; + u16 desc1_len; + u32 desc1_data[5]; +} __packed; + +static struct fwserial_unit_directory_data fwserial_unit_directory_data = { + .len = 4, + .unit_specifier = FW_UNIT_SPECIFIER(LINUX_VENDOR_ID), + .unit_sw_version = FW_UNIT_VERSION(FWSERIAL_VERSION), + .desc1_ofs = FW_ROM_DESCRIPTOR(1), + .desc1_len = 5, + .desc1_data = { + 0x00000000, /* type = text */ + 0x00000000, /* enc = ASCII, lang EN */ + 0x4c696e75, /* 'Linux TTY' */ + 0x78205454, + 0x59000000, + }, +}; + +static struct fw_descriptor fwserial_unit_directory = { + .length = sizeof(fwserial_unit_directory_data) / sizeof(u32), + .key = (CSR_DIRECTORY | CSR_UNIT) << 24, + .data = (u32 *)&fwserial_unit_directory_data, +}; + +/* + * The management address is in the unit space region but above other known + * address users (to keep wild writes from causing havoc) + */ +const struct fw_address_region fwserial_mgmt_addr_region = { + .start = CSR_REGISTER_BASE + 0x1e0000ULL, + .end = 0x1000000000000ULL, +}; + +static struct fw_address_handler fwserial_mgmt_addr_handler; + +/** + * fwserial_handle_plug_req - handle VIRT_CABLE_PLUG request work + * @work: ptr to peer->work + * + * Attempts to complete the VIRT_CABLE_PLUG handshake sequence for this peer. + * + * This checks for a collided request-- ie, that a VIRT_CABLE_PLUG request was + * already sent to this peer. If so, the collision is resolved by comparing + * guid values; the loser sends the plug response. + * + * Note: if an error prevents a response, don't do anything -- the + * remote will timeout its request. + */ +static void fwserial_handle_plug_req(struct work_struct *work) +{ + struct fwtty_peer *peer = to_peer(work, work); + struct virt_plug_params *plug_req = &peer->work_params.plug_req; + struct fwtty_port *port; + struct fwserial_mgmt_pkt *pkt; + int rcode; + + pkt = kmalloc(sizeof(*pkt), GFP_KERNEL); + if (!pkt) + return; + + port = fwserial_find_port(peer); + + spin_lock_bh(&peer->lock); + + switch (peer->state) { + case FWPS_NOT_ATTACHED: + if (!port) { + fwtty_err(&peer->unit, "no more ports avail"); + fill_plug_rsp_nack(pkt); + } else { + peer->port = port; + fill_plug_rsp_ok(pkt, peer->port); + peer_set_state(peer, FWPS_PLUG_RESPONDING); + /* don't release claimed port */ + port = NULL; + } + break; + + case FWPS_PLUG_PENDING: + if (peer->serial->card->guid > peer->guid) + goto cleanup; + + /* We lost - hijack the already-claimed port and send ok */ + del_timer(&peer->timer); + fill_plug_rsp_ok(pkt, peer->port); + peer_set_state(peer, FWPS_PLUG_RESPONDING); + break; + + default: + fill_plug_rsp_nack(pkt); + } + + spin_unlock_bh(&peer->lock); + if (port) + fwserial_release_port(port); + + rcode = fwserial_send_mgmt_sync(peer, pkt); + + spin_lock_bh(&peer->lock); + if (peer->state == FWPS_PLUG_RESPONDING) { + if (rcode == RCODE_COMPLETE) { + struct fwtty_port *tmp = peer->port; + + fwserial_virt_plug_complete(peer, plug_req); + spin_unlock_bh(&peer->lock); + + fwtty_write_port_status(tmp); + spin_lock_bh(&peer->lock); + } else { + fwtty_err(&peer->unit, "PLUG_RSP error (%d)", rcode); + port = peer_revert_state(peer); + } + } +cleanup: + spin_unlock_bh(&peer->lock); + if (port) + fwserial_release_port(port); + kfree(pkt); + return; +} + +static void fwserial_handle_unplug_req(struct work_struct *work) +{ + struct fwtty_peer *peer = to_peer(work, work); + struct fwtty_port *port = NULL; + struct fwserial_mgmt_pkt *pkt; + int rcode; + + pkt = kmalloc(sizeof(*pkt), GFP_KERNEL); + if (!pkt) + return; + + spin_lock_bh(&peer->lock); + + switch (peer->state) { + case FWPS_ATTACHED: + fill_unplug_rsp_ok(pkt); + peer_set_state(peer, FWPS_UNPLUG_RESPONDING); + break; + + case FWPS_UNPLUG_PENDING: + if (peer->serial->card->guid > peer->guid) + goto cleanup; + + /* We lost - send unplug rsp */ + del_timer(&peer->timer); + fill_unplug_rsp_ok(pkt); + peer_set_state(peer, FWPS_UNPLUG_RESPONDING); + break; + + default: + fill_unplug_rsp_nack(pkt); + } + + spin_unlock_bh(&peer->lock); + + rcode = fwserial_send_mgmt_sync(peer, pkt); + + spin_lock_bh(&peer->lock); + if (peer->state == FWPS_UNPLUG_RESPONDING) { + if (rcode == RCODE_COMPLETE) + port = peer_revert_state(peer); + else + fwtty_err(&peer->unit, "UNPLUG_RSP error (%d)", rcode); + } +cleanup: + spin_unlock_bh(&peer->lock); + if (port) + fwserial_release_port(port); + kfree(pkt); + return; +} + +static int fwserial_parse_mgmt_write(struct fwtty_peer *peer, + struct fwserial_mgmt_pkt *pkt, + unsigned long long addr, + size_t len) +{ + struct fwtty_port *port = NULL; + int rcode; + + if (addr != fwserial_mgmt_addr_handler.offset || len < sizeof(pkt->hdr)) + return RCODE_ADDRESS_ERROR; + + if (len != be16_to_cpu(pkt->hdr.len) || + len != mgmt_pkt_expected_len(pkt->hdr.code)) + return RCODE_DATA_ERROR; + + spin_lock_bh(&peer->lock); + if (peer->state == FWPS_GONE) { + /* + * This should never happen - it would mean that the + * remote unit that just wrote this transaction was + * already removed from the bus -- and the removal was + * processed before we rec'd this transaction + */ + fwtty_err(&peer->unit, "peer already removed"); + spin_unlock_bh(&peer->lock); + return RCODE_ADDRESS_ERROR; + } + + rcode = RCODE_COMPLETE; + + fwtty_dbg(&peer->unit, "mgmt: hdr.code: %04hx", pkt->hdr.code); + + switch (be16_to_cpu(pkt->hdr.code) & FWSC_CODE_MASK) { + case FWSC_VIRT_CABLE_PLUG: + if (work_pending(&peer->work)) { + fwtty_err(&peer->unit, "plug req: busy"); + rcode = RCODE_CONFLICT_ERROR; + + } else { + peer->work_params.plug_req = pkt->plug_req; + PREPARE_WORK(&peer->work, fwserial_handle_plug_req); + queue_work(system_unbound_wq, &peer->work); + } + break; + + case FWSC_VIRT_CABLE_PLUG_RSP: + if (peer->state != FWPS_PLUG_PENDING) { + rcode = RCODE_CONFLICT_ERROR; + + } else if (be16_to_cpu(pkt->hdr.code) & FWSC_RSP_NACK) { + fwtty_notice(&peer->unit, "NACK plug rsp"); + port = peer_revert_state(peer); + + } else { + struct fwtty_port *tmp = peer->port; + + fwserial_virt_plug_complete(peer, &pkt->plug_rsp); + spin_unlock_bh(&peer->lock); + + fwtty_write_port_status(tmp); + spin_lock_bh(&peer->lock); + } + break; + + case FWSC_VIRT_CABLE_UNPLUG: + if (work_pending(&peer->work)) { + fwtty_err(&peer->unit, "unplug req: busy"); + rcode = RCODE_CONFLICT_ERROR; + } else { + PREPARE_WORK(&peer->work, fwserial_handle_unplug_req); + queue_work(system_unbound_wq, &peer->work); + } + break; + + case FWSC_VIRT_CABLE_UNPLUG_RSP: + if (peer->state != FWPS_UNPLUG_PENDING) + rcode = RCODE_CONFLICT_ERROR; + else { + if (be16_to_cpu(pkt->hdr.code) & FWSC_RSP_NACK) + fwtty_notice(&peer->unit, "NACK unplug?"); + port = peer_revert_state(peer); + } + break; + + default: + fwtty_err(&peer->unit, "unknown mgmt code %d", + be16_to_cpu(pkt->hdr.code)); + rcode = RCODE_DATA_ERROR; + } + spin_unlock_bh(&peer->lock); + + if (port) + fwserial_release_port(port); + + return rcode; +} + +/** + * fwserial_mgmt_handler: bus address handler for mgmt requests + * @parameters: fw_address_callback_t as specified by firewire core interface + * + * This handler is responsible for handling virtual cable requests from remotes + * for all cards. + */ +static void fwserial_mgmt_handler(struct fw_card *card, + struct fw_request *request, + int tcode, int destination, int source, + int generation, + unsigned long long addr, + void *data, size_t len, + void *callback_data) +{ + struct fwserial_mgmt_pkt *pkt = data; + struct fwtty_peer *peer; + int rcode; + + rcu_read_lock(); + peer = __fwserial_peer_by_node_id(card, generation, source); + if (!peer) { + fwtty_dbg(card, "peer(%d:%x) not found", generation, source); + __dump_peer_list(card); + rcode = RCODE_CONFLICT_ERROR; + + } else { + switch (tcode) { + case TCODE_WRITE_BLOCK_REQUEST: + rcode = fwserial_parse_mgmt_write(peer, pkt, addr, len); + break; + + default: + rcode = RCODE_TYPE_ERROR; + } + } + + rcu_read_unlock(); + fw_send_response(card, request, rcode); +} + +static int __init fwserial_init(void) +{ + int err, num_loops = !!(create_loop_dev); + + /* num_ttys/num_ports must not be set above the static alloc avail */ + if (num_ttys + num_loops > MAX_CARD_PORTS) + num_ttys = MAX_CARD_PORTS - num_loops; + num_ports = num_ttys + num_loops; + + fwtty_driver = alloc_tty_driver(MAX_TOTAL_PORTS); + if (!fwtty_driver) { + err = -ENOMEM; + return err; + } + + fwtty_driver->driver_name = KBUILD_MODNAME; + fwtty_driver->name = tty_dev_name; + fwtty_driver->major = 0; + fwtty_driver->minor_start = 0; + fwtty_driver->type = TTY_DRIVER_TYPE_SERIAL; + fwtty_driver->subtype = SERIAL_TYPE_NORMAL; + fwtty_driver->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; + + fwtty_driver->init_termios = tty_std_termios; + fwtty_driver->init_termios.c_cflag |= CLOCAL; + tty_set_operations(fwtty_driver, &fwtty_ops); + + err = tty_register_driver(fwtty_driver); + if (err) { + driver_err("register tty driver failed (%d)", err); + goto put_tty; + } + + fwtty_txn_cache = kmem_cache_create("fwtty_txn_cache", + sizeof(struct fwtty_transaction), + 0, 0, fwtty_txn_constructor); + if (!fwtty_txn_cache) { + err = -ENOMEM; + goto unregister_driver; + } + + /* + * Ideally, this address handler would be registered per local node + * (rather than the same handler for all local nodes). However, + * since the firewire core requires the config rom descriptor *before* + * the local unit device(s) are created, a single management handler + * must suffice for all local serial units. + */ + fwserial_mgmt_addr_handler.length = sizeof(struct fwserial_mgmt_pkt); + fwserial_mgmt_addr_handler.address_callback = fwserial_mgmt_handler; + + err = fw_core_add_address_handler(&fwserial_mgmt_addr_handler, + &fwserial_mgmt_addr_region); + if (err) { + driver_err("add management handler failed (%d)", err); + goto destroy_cache; + } + + fwserial_unit_directory_data.unit_addr_offset = + FW_UNIT_ADDRESS(fwserial_mgmt_addr_handler.offset); + err = fw_core_add_descriptor(&fwserial_unit_directory); + if (err) { + driver_err("add unit descriptor failed (%d)", err); + goto remove_handler; + } + + err = driver_register(&fwserial_driver.driver); + if (err) { + driver_err("register fwserial driver failed (%d)", err); + goto remove_descriptor; + } + + return 0; + +remove_descriptor: + fw_core_remove_descriptor(&fwserial_unit_directory); +remove_handler: + fw_core_remove_address_handler(&fwserial_mgmt_addr_handler); +destroy_cache: + kmem_cache_destroy(fwtty_txn_cache); +unregister_driver: + tty_unregister_driver(fwtty_driver); +put_tty: + put_tty_driver(fwtty_driver); + return err; +} + +static void __exit fwserial_exit(void) +{ + driver_unregister(&fwserial_driver.driver); + fw_core_remove_descriptor(&fwserial_unit_directory); + fw_core_remove_address_handler(&fwserial_mgmt_addr_handler); + kmem_cache_destroy(fwtty_txn_cache); + tty_unregister_driver(fwtty_driver); + put_tty_driver(fwtty_driver); +} + +module_init(fwserial_init); +module_exit(fwserial_exit); + +MODULE_AUTHOR("Peter Hurley (peter@hurleysoftware.com)"); +MODULE_DESCRIPTION("FireWire Serial TTY Driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(ieee1394, fwserial_id_table); +MODULE_PARM_DESC(ttys, "Number of ttys to create for each local firewire node"); +MODULE_PARM_DESC(auto, "Auto-connect a tty to each firewire node discovered"); +MODULE_PARM_DESC(loop, "Create a loopback device, fwloop, with ttys"); +MODULE_PARM_DESC(limit_bw, "Limit bandwidth utilization to 20%."); diff --git a/drivers/staging/fwserial/fwserial.h b/drivers/staging/fwserial/fwserial.h new file mode 100644 index 00000000000..8b572edf956 --- /dev/null +++ b/drivers/staging/fwserial/fwserial.h @@ -0,0 +1,387 @@ +#ifndef _FIREWIRE_FWSERIAL_H +#define _FIREWIRE_FWSERIAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dma_fifo.h" + +#ifdef FWTTY_PROFILING +#define DISTRIBUTION_MAX_SIZE 8192 +#define DISTRIBUTION_MAX_INDEX (ilog2(DISTRIBUTION_MAX_SIZE) + 1) +static inline void profile_size_distrib(unsigned stat[], unsigned val) +{ + int n = (val) ? min(ilog2(val) + 1, DISTRIBUTION_MAX_INDEX) : 0; + ++stat[n]; +} +#else +#define DISTRIBUTION_MAX_INDEX 0 +#define profile_size_distrib(st, n) +#endif + +/* Parameters for both VIRT_CABLE_PLUG & VIRT_CABLE_PLUG_RSP mgmt codes */ +struct virt_plug_params { + __be32 status_hi; + __be32 status_lo; + __be32 fifo_hi; + __be32 fifo_lo; + __be32 fifo_len; +}; + +struct peer_work_params { + union { + struct virt_plug_params plug_req; + }; +}; + +/** + * fwtty_peer: structure representing local & remote unit devices + * @unit: unit child device of fw_device node + * @serial: back pointer to associated fw_serial aggregate + * @guid: unique 64-bit guid for this unit device + * @generation: most recent bus generation + * @node_id: most recent node_id + * @speed: link speed of peer (0 = S100, 2 = S400, ... 5 = S3200) + * @mgmt_addr: bus addr region to write mgmt packets to + * @status_addr: bus addr register to write line status to + * @fifo_addr: bus addr region to write serial output to + * @fifo_len: max length for single write to fifo_addr + * @list: link for insertion into fw_serial's peer_list + * @rcu: for deferring peer reclamation + * @lock: spinlock to synchonize changes to state & port fields + * @work: only one work item can be queued at any one time + * Note: pending work is canceled prior to removal, so this + * peer is valid for at least the lifetime of the work function + * @work_params: parameter block for work functions + * @timer: timer for resetting peer state if remote request times out + * @state: current state + * @connect: work item for auto-connecting + * @connect_retries: # of connections already attempted + * @port: associated tty_port (usable if state == FWSC_ATTACHED) + */ +struct fwtty_peer { + struct fw_unit *unit; + struct fw_serial *serial; + u64 guid; + int generation; + int node_id; + unsigned speed; + int max_payload; + u64 mgmt_addr; + + /* these are usable only if state == FWSC_ATTACHED */ + u64 status_addr; + u64 fifo_addr; + int fifo_len; + + struct list_head list; + struct rcu_head rcu; + + spinlock_t lock; + struct work_struct work; + struct peer_work_params work_params; + struct timer_list timer; + int state; + struct delayed_work connect; + int connect_retries; + + struct fwtty_port *port; +}; + +#define to_peer(ptr, field) (container_of(ptr, struct fwtty_peer, field)) + +/* state values for fwtty_peer.state field */ +enum fwtty_peer_state { + FWPS_GONE, + FWPS_NOT_ATTACHED, + FWPS_ATTACHED, + FWPS_PLUG_PENDING, + FWPS_PLUG_RESPONDING, + FWPS_UNPLUG_PENDING, + FWPS_UNPLUG_RESPONDING, + + FWPS_NO_MGMT_ADDR = -1, +}; + +#define CONNECT_RETRY_DELAY HZ +#define MAX_CONNECT_RETRIES 10 + +/* must be holding peer lock for these state funclets */ +static inline void peer_set_state(struct fwtty_peer *peer, int new) +{ + peer->state = new; +} + +static inline struct fwtty_port *peer_revert_state(struct fwtty_peer *peer) +{ + struct fwtty_port *port = peer->port; + + peer->port = NULL; + peer_set_state(peer, FWPS_NOT_ATTACHED); + return port; +} + +struct fwserial_mgmt_pkt { + struct { + __be16 len; + __be16 code; + } hdr; + union { + struct virt_plug_params plug_req; + struct virt_plug_params plug_rsp; + }; +} __packed; + +/* fwserial_mgmt_packet codes */ +#define FWSC_RSP_OK 0x0000 +#define FWSC_RSP_NACK 0x8000 +#define FWSC_CODE_MASK 0x0fff + +#define FWSC_VIRT_CABLE_PLUG 1 +#define FWSC_VIRT_CABLE_UNPLUG 2 +#define FWSC_VIRT_CABLE_PLUG_RSP 3 +#define FWSC_VIRT_CABLE_UNPLUG_RSP 4 + +/* 1 min. plug timeout -- suitable for userland authorization */ +#define VIRT_CABLE_PLUG_TIMEOUT (60 * HZ) + +struct stats { + unsigned xchars; + unsigned dropped; + unsigned tx_stall; + unsigned fifo_errs; + unsigned sent; + unsigned lost; + unsigned throttled; + unsigned watermark; + unsigned reads[DISTRIBUTION_MAX_INDEX + 1]; + unsigned writes[DISTRIBUTION_MAX_INDEX + 1]; + unsigned txns[DISTRIBUTION_MAX_INDEX + 1]; + unsigned unthrottle[DISTRIBUTION_MAX_INDEX + 1]; +}; + +struct fwconsole_ops { + void (*notify)(int code, void *data); + void (*stats)(struct stats *stats, void *data); + void (*proc_show)(struct seq_file *m, void *data); +}; + +/* codes for console ops notify */ +#define FWCON_NOTIFY_ATTACH 1 +#define FWCON_NOTIFY_DETACH 2 + +struct buffered_rx { + struct list_head list; + size_t n; + unsigned char data[0]; +}; + +/** + * fwtty_port: structure used to track/represent underlying tty_port + * @port: underlying tty_port + * @device: tty device + * @index: index into port_table for this particular port + * note: minor = index + FWSERIAL_TTY_START_MINOR + * @serial: back pointer to the containing fw_serial + * @rx_handler: bus address handler for unique addr region used by remotes + * to communicate with this port. Every port uses + * fwtty_port_handler() for per port transactions. + * @fwcon_ops: ops for attached fw_console (if any) + * @con_data: private data for fw_console + * @wait_tx: waitqueue for sleeping until writer/drain completes tx + * @emit_breaks: delayed work responsible for generating breaks when the + * break line status is active + * @cps : characters per second computed from the termios settings + * @break_last: timestamp in jiffies from last emit_breaks + * @hangup: work responsible for HUPing when carrier is dropped/lost + * @mstatus: loose virtualization of LSR/MSR + * bits 15..0 correspond to TIOCM_* bits + * bits 19..16 reserved for mctrl + * bit 20 OOB_TX_THROTTLE + * bits 23..21 reserved + * bits 31..24 correspond to UART_LSR_* bits + * @lock: spinlock for protecting concurrent access to fields below it + * @mctrl: loose virtualization of MCR + * bits 15..0 correspond to TIOCM_* bits + * bit 16 OOB_RX_THROTTLE + * bits 19..17 reserved + * bits 31..20 reserved for mstatus + * @drain: delayed work scheduled to ensure that writes are flushed. + * The work can race with the writer but concurrent sending is + * prevented with the IN_TX flag. Scheduled under lock to + * limit scheduling when fifo has just been drained. + * @push: work responsible for pushing buffered rx to the ldisc. + * rx can become buffered if the tty buffer is filled before the + * ldisc throttles the sender. + * @buf_list: list of buffered rx yet to be sent to ldisc + * @buffered: byte count of buffered rx + * @tx_fifo: fifo used to store & block-up writes for dma to remote + * @max_payload: max bytes transmissable per dma (based on peer's max_payload) + * @status_mask: UART_LSR_* bitmask significant to rx (based on termios) + * @ignore_mask: UART_LSR_* bitmask of states to ignore (also based on termios) + * @break_ctl: if set, port is 'sending break' to remote + * @write_only: self-explanatory + * @overrun: previous rx was lost (partially or completely) + * @loopback: if set, port is in loopback mode + * @flags: atomic bit flags + * bit 0: IN_TX - gate to allow only one cpu to send from the dma fifo + * at a time. + * bit 1: STOP_TX - force tx to exit while sending + * @peer: rcu-pointer to associated fwtty_peer (if attached) + * NULL if no peer attached + * @icount: predefined statistics reported by the TIOCGICOUNT ioctl + * @stats: additional statistics reported in /proc/tty/driver/firewire_serial + */ +struct fwtty_port { + struct tty_port port; + struct device *device; + unsigned index; + struct fw_serial *serial; + struct fw_address_handler rx_handler; + + struct fwconsole_ops *fwcon_ops; + void *con_data; + + wait_queue_head_t wait_tx; + struct delayed_work emit_breaks; + unsigned cps; + unsigned long break_last; + + struct work_struct hangup; + + unsigned mstatus; + + spinlock_t lock; + unsigned mctrl; + struct delayed_work drain; + struct work_struct push; + struct list_head buf_list; + int buffered; + struct dma_fifo tx_fifo; + int max_payload; + unsigned status_mask; + unsigned ignore_mask; + unsigned break_ctl:1, + write_only:1, + overrun:1, + loopback:1; + unsigned long flags; + + struct fwtty_peer *peer; + + struct async_icount icount; + struct stats stats; +}; + +#define to_port(ptr, field) (container_of(ptr, struct fwtty_port, field)) + +/* bit #s for flags field */ +#define IN_TX 0 +#define STOP_TX 1 +#define BUFFERING_RX 2 + +/* bitmasks for special mctrl/mstatus bits */ +#define OOB_RX_THROTTLE 0x00010000 +#define MCTRL_RSRVD 0x000e0000 +#define OOB_TX_THROTTLE 0x00100000 +#define MSTATUS_RSRVD 0x00e00000 + +#define MCTRL_MASK (TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 | TIOCM_OUT2 | \ + TIOCM_LOOP | OOB_RX_THROTTLE | MCTRL_RSRVD) + +/* XXX even every 1/50th secs. may be unnecessarily accurate */ +/* delay in jiffies between brk emits */ +#define FREQ_BREAKS (HZ / 50) + +/* Ports are allocated in blocks of num_ports for each fw_card */ +#define MAX_CARD_PORTS 32 /* max # of ports per card */ +#define MAX_TOTAL_PORTS 64 /* max # of ports total */ + +/* tuning parameters */ +#define FWTTY_PORT_TXFIFO_LEN 4096 +#define FWTTY_PORT_MAX_PEND_DMA 8 /* costs a cache line per pend */ +#define DRAIN_THRESHOLD 1024 +#define MAX_ASYNC_PAYLOAD 4096 /* ohci-defined limit */ +#define WRITER_MINIMUM 128 +/* TODO: how to set watermark to AR context size? see fwtty_rx() */ +#define HIGH_WATERMARK 32768 /* AR context is 32K */ + +/* + * Size of bus addr region above 4GB used per port as the recv addr + * - must be at least as big as the MAX_ASYNC_PAYLOAD + */ +#define FWTTY_PORT_RXFIFO_LEN MAX_ASYNC_PAYLOAD + +/** + * fw_serial: aggregate used to associate tty ports with specific fw_card + * @card: fw_card associated with this fw_serial device (1:1 association) + * @kref: reference-counted multi-port management allows delayed destroy + * @self: local unit device as 'peer'. Not valid until local unit device + * is enumerated. + * @list: link for insertion into fwserial_list + * @peer_list: list of local & remote unit devices attached to this card + * @ports: fixed array of tty_ports provided by this serial device + */ +struct fw_serial { + struct fw_card *card; + struct kref kref; + + struct fwtty_peer *self; + + struct list_head list; + struct list_head peer_list; + + struct fwtty_port *ports[MAX_CARD_PORTS]; +}; + +#define to_serial(ptr, field) (container_of(ptr, struct fw_serial, field)) + +#define TTY_DEV_NAME "fwtty" /* ttyFW was taken */ +static const char tty_dev_name[] = TTY_DEV_NAME; +static const char loop_dev_name[] = "fwloop"; +extern bool limit_bw; + +struct tty_driver *fwtty_driver; + +#define driver_err(s, v...) pr_err(KBUILD_MODNAME ": " s, ##v) + +struct fwtty_port *fwtty_port_get(unsigned index); +void fwtty_port_put(struct fwtty_port *port); + +static inline void fwtty_bind_console(struct fwtty_port *port, + struct fwconsole_ops *fwcon_ops, + void *data) +{ + port->con_data = data; + port->fwcon_ops = fwcon_ops; +} + +/* + * Returns the max send async payload size in bytes based on the unit device + * link speed - if set to limit bandwidth to max 20%, use lookup table + */ +static inline int link_speed_to_max_payload(unsigned speed) +{ + static const int max_async[] = { 307, 614, 1229, 2458, 4916, 9832, }; + BUILD_BUG_ON(ARRAY_SIZE(max_async) - 1 != SCODE_3200); + + speed = clamp(speed, (unsigned) SCODE_100, (unsigned) SCODE_3200); + if (limit_bw) + return max_async[speed]; + else + return 1 << (speed + 9); +} + +#endif /* _FIREWIRE_FWSERIAL_H */ From eef6e7b286e6dcf39a9b8c1d31477ee489451a8a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 27 Nov 2012 09:30:45 -0500 Subject: [PATCH 142/146] staging/fwserial: Fix build breakage when !CONFIG_BUG Use WARN() as intended. Reported-by: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/staging/fwserial/dma_fifo.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/fwserial/dma_fifo.c b/drivers/staging/fwserial/dma_fifo.c index 72aa0533f01..5e846344550 100644 --- a/drivers/staging/fwserial/dma_fifo.c +++ b/drivers/staging/fwserial/dma_fifo.c @@ -33,10 +33,7 @@ #define FAIL(fifo, condition, format...) ({ \ fifo->corrupt = !!(condition); \ - if (unlikely(fifo->corrupt)) { \ - __WARN_printf(format); \ - } \ - unlikely(fifo->corrupt); \ + WARN(fifo->corrupt, format); \ }) /* From a321846492f9ce3bab27f826e6579217fbc35732 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 27 Nov 2012 21:37:11 -0500 Subject: [PATCH 143/146] staging/fwserial: Destruct embedded tty_port on teardown For TTY drivers that manage the port lifetime, the tty_port should to be specifically destructed when the port lifetime ends. Now that a method has been added to do this, use it. Signed-off-by: Peter Hurley Cc: Jiri Slaby Cc: Alan Cox --- drivers/staging/fwserial/fwserial.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index 5d4d64a3ea8..99a2d2dbd32 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -955,6 +955,7 @@ static void fwserial_destroy(struct kref *kref) for (j = 0; j < num_ports; ++j) { fw_core_remove_address_handler(&ports[j]->rx_handler); dma_fifo_free(&ports[j]->tx_fifo); + tty_port_destroy(&ports[j]->port); kfree(ports[j]); } kfree(serial); @@ -2369,8 +2370,10 @@ unregister_ttys: return err; free_ports: - for (--i; i >= 0; --i) + for (--i; i >= 0; --i) { + tty_port_destroy(&serial->ports[i]->port); kfree(serial->ports[i]); + } kfree(serial); return err; } From 49b2746e1426ffa188421996a0323bdd47657108 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 27 Nov 2012 21:37:12 -0500 Subject: [PATCH 144/146] staging/fwserial: Use WARN_ONCE when port table is corrupted Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/staging/fwserial/fwserial.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index 99a2d2dbd32..0681967337f 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -939,14 +939,9 @@ static void fwserial_destroy(struct kref *kref) mutex_lock(&port_table_lock); for (j = 0; j < num_ports; ++i, ++j) { - static bool once; - int corrupt = port_table[i] != ports[j]; - if (corrupt && !once) { - WARN(corrupt, "port_table[%d]: %p != ports[%d]: %p", - i, port_table[i], j, ports[j]); - once = true; - port_table_corrupt = true; - } + port_table_corrupt |= port_table[i] != ports[j]; + WARN_ONCE(port_table_corrupt, "port_table[%d]: %p != ports[%d]: %p", + i, port_table[i], j, ports[j]); port_table[i] = NULL; } From 218cbbd82132d48c6f4e042e31beb6019f92db01 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 27 Nov 2012 21:37:13 -0500 Subject: [PATCH 145/146] staging/fwserial: Remove superfluous free Now that the dma fifo is allocated on activate and freed on shutdown, this extra free is harmless but unnecessary. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/staging/fwserial/fwserial.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index 0681967337f..61ee29083b2 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -949,7 +949,6 @@ static void fwserial_destroy(struct kref *kref) for (j = 0; j < num_ports; ++j) { fw_core_remove_address_handler(&ports[j]->rx_handler); - dma_fifo_free(&ports[j]->tx_fifo); tty_port_destroy(&ports[j]->port); kfree(ports[j]); } From b0ab02361167faa82198b783a8d555eb6f58901c Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 28 Nov 2012 21:30:01 -0500 Subject: [PATCH 146/146] staging: sb105x: fix potential NULL pointer dereference in mp_chars_in_buffer() The dereference to 'state' should be moved below the NULL test. Cc: Steven Rostedt Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sb105x/sb_pci_mp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index fbebf88226d..edb2a85b9d5 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -689,13 +689,14 @@ static int mp_chars_in_buffer(struct tty_struct *tty) static void mp_flush_buffer(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; - struct sb_uart_port *port = state->port; + struct sb_uart_port *port; unsigned long flags; if (!state || !state->info) { return; } + port = state->port; spin_lock_irqsave(&port->lock, flags); uart_circ_clear(&state->info->xmit); spin_unlock_irqrestore(&port->lock, flags);